/*
* Copyright (c) 2004, 2005 Sendmail, Inc. and its suppliers.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*/
#include "sm/generic.h"
SM_RCSID("@(#)$Id: sockmapimpl.c,v 1.14 2006/07/16 02:07:40 ca Exp $")
#include "sm/assert.h"
#include "sm/error.h"
#include "sm/memops.h"
#include "sm/heap.h"
#include "sm/string.h"
#include "sockmap.h"
/* --------- socket map basic implementation ------ */
/*
** SOCKMAP_FREE -- free socket map context
**
** Parameters:
** db -- pointer to socket map
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sockmap_free(sm_sockmap_P db)
{
if (db != NULL)
sm_free_size(db, sizeof(*db));
return SM_SUCCESS;
}
/*
** SOCKMAP_NEW -- allocate socket map context
**
** Parameters:
** pdb -- pointer to pointer to socket map
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sockmap_new(sm_sockmap_P *pdb)
{
sm_sockmap_P db;
SM_REQUIRE(pdb != NULL);
*pdb = NULL;
db = sm_zalloc(sizeof(*db));
if (db == NULL)
return sm_error_perm(SM_EM_MAP, ENOMEM);
/* default for socket map timeout */
db->sockmap_tmout = 5;
*pdb = db;
return SM_SUCCESS;
}
/*
** SOCKMAP_DESTROY -- destroy socket map
**
** Parameters:
** pdb -- pointer to pointer to socket map
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sockmap_destroy(sm_sockmap_P *pdb)
{
sm_ret_T ret;
SM_REQUIRE(pdb != NULL);
ret = SM_SUCCESS;
if (*pdb != NULL)
{
if ((*pdb)->sockmap_fp != NULL)
{
ret = sm_io_close((*pdb)->sockmap_fp, SM_IO_CF_NONE);
(*pdb)->sockmap_fp = NULL;
}
sockmap_free(*pdb);
*pdb = NULL;
}
return ret;
}
/*
** SOCKMAP_CLOSE -- close socket map
**
** Parameters:
** db -- pointer to socket map
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sockmap_close(sm_sockmap_P db)
{
sm_ret_T ret;
ret = SM_SUCCESS;
if (db != NULL && db->sockmap_fp != NULL)
{
ret = sm_io_close(db->sockmap_fp, SM_IO_CF_NONE);
db->sockmap_fp = NULL;
}
return ret;
}
/*
** SOCKMAP_OPEN -- open socket map
**
** Parameters:
** pdb -- pointer to pointer to socket map
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sockmap_open(sm_sockmap_P db)
{
sm_ret_T ret;
int fd;
SM_REQUIRE(db != NULL);
db->sockmap_fp = NULL;
if (db->sockmap_path != NULL)
{
ret = unix_client_connect(db->sockmap_path, &fd);
}
else
{
ret = net_client_connectipv4(db->sockmap_ipv4,
db->sockmap_port, &fd);
}
if (sm_is_err(ret))
goto fail;
ret = sm_io_open(SmStStdiofd, (void *) &fd, SM_IO_RDWR,
&db->sockmap_fp, SM_IO_WHAT_END);
if (sm_is_err(ret))
goto fail;
ret = sm_io_setinfo(db->sockmap_fp, SM_IO_DOUBLE, NULL);
if (sm_is_err(ret))
goto fail;
ret = sm_io_setinfo(db->sockmap_fp, SM_IO_WHAT_TIMEOUT,
(void *)&db->sockmap_tmout);
if (sm_is_err(ret))
goto fail;
return ret;
fail:
/* cleanup? */
sockmap_close(db);
return ret;
}
/*
** SOCKMAP_LOOKUP -- lookup a key in SOCKMAP, return data if found
**
** Parameters:
** map -- map context
** key -- key
** data -- data (output)
**
** Returns:
** usual sm_error code
*/
sm_ret_T
sockmap_lookup(sm_sockmap_P map, sm_str_P key, sm_str_P data)
{
sm_ret_T ret;
uint len, replylen;
int c;
ssize_t recvlen;
char *value, *status;
sm_file_T *fp;
char statbuf[16];
SM_REQUIRE(map != NULL);
SM_REQUIRE(key != NULL);
SM_REQUIRE(data != NULL);
fp = map->sockmap_fp;
if (fp == NULL)
{
ret = sockmap_open(map);
if (sm_is_err(ret))
return ret;
}
len = strlen(map->sockmap_name) + 1 + sm_str_getlen(key);
SM_ASSERT(len > strlen(map->sockmap_name));
SM_ASSERT(len > sm_str_getlen(key));
if ((sm_io_fprintf(fp, "%u:%s %S,", len, map->sockmap_name,
key) == SM_IO_EOF) ||
(sm_io_flush(fp) != 0) || (sm_io_error(fp) != 0))
{
ret = sm_error_temp(SM_EM_MAP, errno);
goto errcl;
}
if (sm_io_fscanf(fp, "%9u", &replylen) != 1)
{
ret = sm_error_temp(SM_EM_MAP, errno);
goto errcl;
}
if (replylen > SOCKETMAP_MAXL)
{
ret = sm_error_temp(SM_EM_MAP, SM_E_PR_ERR);
goto errcl;
}
if (sm_io_getc(fp) != ':')
{
ret = sm_error_temp(SM_EM_MAP, SM_E_PR_ERR);
goto error;
}
len = 0;
sm_memzero(statbuf, sizeof(statbuf));
if (replylen >= sm_str_getsize(data))
{
/* not enough space: read status and discard rest */
while (replylen-- > 0 && (c = sm_io_getc(fp)) != SM_IO_EOF)
{
if (len < sizeof(statbuf) - 1)
statbuf[len++] = c;
}
}
else
{
/* read status first */
c = '\0';
while (replylen-- > 0 && (c = sm_io_getc(fp)) != SM_IO_EOF
&& c != ' ')
{
if (len < sizeof(statbuf) - 1)
statbuf[len++] = c;
}
if (replylen > 0 && c == ' ')
{
ret = sm_io_read(fp, sm_str_data(data), replylen,
&recvlen);
if (sm_is_err(ret))
goto errcl;
else if (recvlen < replylen)
{
ret = sm_error_temp(SM_EM_MAP, errno);
goto errcl;
}
SM_STR_SETLEN(data, recvlen);
}
}
if (sm_io_getc(fp) != ',')
{
ret = sm_error_temp(SM_EM_MAP, SM_E_PR_ERR);
goto errcl;
}
statbuf[sizeof(statbuf) - 1] = '\0';
status = statbuf;
value = strchr(status, ' ');
if (value != NULL)
{
*value = '\0';
value++;
}
if (sm_streq(status, "OK"))
{
ret = SM_SUCCESS;
}
else if (sm_streq(status, "NOTFOUND"))
{
return sm_error_perm(SM_EM_MAP, SM_E_NOTFOUND);
}
else
{
if (sm_streq(status, "TEMP") || sm_streq(status, "TIMEOUT"))
ret = sm_error_temp(SM_EM_MAP, SM_E_TEMPMAP);
else if (sm_streq(status, "PERM"))
ret = sm_error_perm(SM_EM_MAP, SM_E_PERMMAP);
else
ret = sm_error_temp(SM_EM_MAP, SM_E_PR_ERR);
}
return ret;
errcl:
sockmap_close(map);
error:
return ret;
}
syntax highlighted by Code2HTML, v. 0.9.1