/* * 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; }