/* ** Copyright (c) 2005-2007 Sendmail, Inc. and its suppliers. ** All rights reserved. ** ** $Id: util.c,v 1.62 2007/10/31 18:34:11 msk Exp $ */ #ifndef lint static char util_c_id[] = "@(#)$Id: util.c,v 1.62 2007/10/31 18:34:11 msk Exp $"; #endif /* !lint */ /* system includes */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef _FFR_REPLACE_RULES # include #endif /* _FFR_REPLACE_RULES */ /* libsm includes */ #include #include #if POPAUTH /* system includes */ # include /* libdb includes */ # include #endif /* POPAUTH */ /* dkim-filter includes */ #include "dkim-filter.h" #include "util.h" /* missing definitions */ #ifndef INADDR_NONE # define INADDR_NONE ((uint32_t) -1) #endif /* ! INADDR_NONE */ /* globals */ #if POPAUTH static pthread_mutex_t pop_lock; #endif /* POPAUTH */ static char *optlist[] = { #if DEBUG "DEBUG", #endif /* DEBUG */ #if POPAUTH "POPAUTH", #endif /* POPAUTH */ #if QUERY_CACHE "QUERY_CACHE", #endif /* QUERY_CACHE */ #if VERIFY_DOMAINKEYS "VERIFY_DOMAINKEYS", #endif /* VERIFY_DOMAINKEYS */ #if _FFR_ANTICIPATE_SENDMAIL_MUNGE "_FFR_ANTICIPATE_SENDMAIL_MUNGE", #endif /* _FFR_ANTICIPATE_SENDMAIL_MUNGE */ #if _FFR_CAPTURE_UNKNOWN_ERRORS "_FFR_CAPTURE_UNKNOWN_ERRORS", #endif /* _FFR_CAPTURE_UNKNOWN_ERRORS */ #if _FFR_DIFFHEADERS "_FFR_DIFFHEADERS", #endif /* _FFR_DIFFHEADERS */ #if _FFR_DNS_UPGRADE "_FFR_DNS_UPGRADE", #endif /* _FFR_DNS_UPGRADE */ #if _FFR_PARSE_TIME "_FFR_PARSE_TIME", #endif /* _FFR_PARSE_TIME */ #if _FFR_REPLACE_RULES "_FFR_REPLACE_RULES", #endif /* _FFR_REPLACE_RULES */ #if _FFR_REQUIRED_HEADERS "_FFR_REQUIRED_HEADERS", #endif /* _FFR_REQUIRED_HEADERS */ #if _FFR_SELECT_CANONICALIZATION "_FFR_SELECT_CANONICALIZATION", #endif /* _FFR_SELECT_CANONICALIZATION */ #if _FFR_SELECTOR_HEADER "_FFR_SELECTOR_HEADER", #endif /* _FFR_SELECTOR_HEADER */ #if _FFR_STATS "_FFR_STATS", #endif /* _FFR_STATS */ #if _FFR_VBR "_FFR_VBR", #endif /* _FFR_VBR */ #if _FFR_ZTAGS "_FFR_ZTAGS", #endif /* _FFR_ZTAGS */ NULL }; /* struct dkimf_dstring -- a dynamically-sized string */ struct dkimf_dstring { int ds_alloc; int ds_max; int ds_len; char * ds_buf; }; #ifdef _FFR_REPLACE_RULES /* ** DKIMF_ISBLANK -- return TRUE iff a string contains only whitespace ** ** Parameters: ** str -- string to check ** ** Return value: ** TRUE if "str" is either zero-length or contains only whitespace */ bool dkimf_isblank(char *str) { char *p; for (p = str; *p != '\0'; p++) { if (isascii(*p) && isspace(*p)) continue; return FALSE; } return TRUE; } #endif /* _FFR_REPLACE_RULES */ /* ** DKIMF_OPTLIST -- print active FFRs ** ** Parameters: ** where -- where to write the list ** ** Return value: ** None. */ void dkimf_optlist(FILE *where) { bool first = TRUE; int c; assert(where != NULL); for (c = 0; optlist[c] != NULL; c++) { if (first) { fprintf(where, "\tActive code options:\n"); first = FALSE; } fprintf(where, "\t\t%s\n", optlist[c]); } } /* ** DKIMF_SETMAXFD -- increase the file descriptor limit as much as possible ** ** Parameters: ** None. ** ** Return value: ** None. */ void dkimf_setmaxfd(void) { struct rlimit rlp; if (getrlimit(RLIMIT_NOFILE, &rlp) != 0) { syslog(LOG_WARNING, "getrlimit(): %s", strerror(errno)); } else { rlp.rlim_cur = rlp.rlim_max; if (setrlimit(RLIMIT_NOFILE, &rlp) != 0) { syslog(LOG_WARNING, "setrlimit(): %s", strerror(errno)); } } } /* ** DKIMF_LOWERCASE -- lowercase-ize a string ** ** Parameters: ** str -- string to convert ** ** Return value: ** None. */ void dkimf_lowercase(u_char *str) { u_char *p; assert(str != NULL); for (p = str; *p != '\0'; p++) { if (isascii(*p) && isupper(*p)) *p = tolower(*p); } } /* ** DKIMF_LIST_LOOKUP -- look up a name in a peerlist ** ** Parameters: ** list -- list of records to check ** data -- record to find ** ** Return value: ** TRUE if found, FALSE otherwise */ static bool dkimf_list_lookup(Peer list, char *data) { Peer current; assert(data != NULL); for (current = list; current != NULL; current = current->peer_next) { if (strcasecmp(data, current->peer_info) == 0) return TRUE; } return FALSE; } /* ** DKIMF_CHECKHOST -- check the peerlist for a host and its wildcards ** ** Parameters: ** list -- list of records to check ** host -- hostname to find ** ** Return value: ** TRUE if there's a match, FALSE otherwise. */ bool dkimf_checkhost(Peer list, char *host) { bool out = FALSE; char *p; Peer node; assert(host != NULL); /* short circuit */ if (list == NULL) return FALSE; /* walk the list */ for (node = list; node != NULL; node = node->peer_next) { if (strcasecmp(host, node->peer_info) == 0) { out = !node->peer_neg; continue; } for (p = strchr(host, '.'); p != NULL; p = strchr(p + 1, '.')) { if (strcasecmp(p, node->peer_info) == 0) { out = !node->peer_neg; break; } } } return out; } /* ** DKIMF_CHECKIP -- check a peerlist table for an IP address or its matching ** wildcards ** ** Parameters: ** list -- list to check ** ip -- IP address to find ** ** Return value: ** TRUE if there's a match, FALSE otherwise. */ bool dkimf_checkip(Peer list, struct sockaddr *ip) { bool out = FALSE; int bits; char *p; char *q; struct Peer *node; struct in_addr addr; struct in_addr mask; struct in_addr compare; char ipbuf[DKIM_MAXHOSTNAMELEN + 1]; assert(ip != NULL); /* short circuit */ if (list == NULL) return FALSE; #if NETINET6 if (ip->sa_family == AF_INET6) { struct in6_addr addr; memcpy(&addr, &((struct sockaddr_in6 *)ip)->sin6_addr, sizeof addr); if (IN6_IS_ADDR_V4MAPPED(&addr)) { inet_ntop(AF_INET, &addr.s6_addr[INET6_ADDRSTRLEN - INET_ADDRSTRLEN], ipbuf, sizeof ipbuf); } else { char *dst; size_t sz; size_t dst_len; dst = ipbuf; dst_len = sizeof ipbuf; memset(ipbuf, '\0', sizeof ipbuf); sz = sm_strlcpy(ipbuf, "IPv6:", sizeof ipbuf); if (sz >= sizeof ipbuf) return FALSE; dst += sz; dst_len -= sz; inet_ntop(AF_INET6, &addr, dst, dst_len); } return (dkimf_list_lookup(list, ipbuf)); } #endif /* NETINET6 */ /* walk the list */ for (node = list; node != NULL; node = node->peer_next) { memcpy(&addr.s_addr, &((struct sockaddr_in *)ip)->sin_addr, sizeof(addr.s_addr)); /* try the IP direct match */ (void) dkimf_inet_ntoa(addr, ipbuf, sizeof ipbuf); if (strcmp(ipbuf, node->peer_info) == 0) { out = !node->peer_neg; continue; } /* try the IP/CIDR and IP/mask possibilities */ p = strchr(node->peer_info, '/'); if (p == NULL) continue; *p = '\0'; compare.s_addr = inet_addr(node->peer_info); if (compare.s_addr == INADDR_NONE) { *p = '/'; continue; } bits = strtoul(p + 1, &q, 10); if (*q == '.') { mask.s_addr = inet_addr(p + 1); if (mask.s_addr == INADDR_NONE) { *p = '/'; continue; } } else if (*q != '\0') { *p = '/'; continue; } else { int c; mask.s_addr = 0; for (c = 0; c < bits; c++) mask.s_addr |= htonl(1 << (31 - c)); } if ((addr.s_addr & mask.s_addr) == (compare.s_addr & mask.s_addr)) out = !node->peer_neg; *p = '/'; } return out; } #if POPAUTH /* ** DKIMF_INITPOPAUTH -- initialize POPAUTH stuff ** ** Parameters: ** None. ** ** Return value: ** 0 on success, an error code on failure. See pthread_mutex_init(). */ int dkimf_initpopauth(void) { return pthread_mutex_init(&pop_lock, NULL); } /* ** DKIMF_CHECKPOPAUTH -- check a POP before SMTP database for client ** authentication ** ** Parameters: ** db -- DB handle to use for searching ** ip -- IP address to find ** ** Return value: ** TRUE iff the database could be opened and the client was verified. ** ** Notes: ** - should be a shared handle rather than a per-thread handle to ** reduce descriptor consumption ** - needs locking ** - should be able to return TRUE/FALSE as well as errors ** - path should be runtime-configurable rather than hard-coded ** - does the key contain anything meaningful, like an expiry time? */ bool dkimf_checkpopauth(DB *db, struct sockaddr *ip) { bool ret = FALSE; int status; int fd; struct sockaddr_in *sin; struct in_addr addr; DBT d; DBT q; char ipbuf[DKIM_MAXHOSTNAMELEN + 1]; assert(ip != NULL); if (db == NULL) return FALSE; /* skip anything not IPv4 (for now) */ if (ip->sa_family != AF_INET) return FALSE; else sin = (struct sockaddr_in *) ip; memset(&d, 0, sizeof d); memset(&q, 0, sizeof q); memcpy(&addr.s_addr, &sin->sin_addr, sizeof addr.s_addr); dkimf_inet_ntoa(addr, ipbuf, sizeof ipbuf); q.data = ipbuf; q.size = strlen(q.data); /* we don't care about the value for now */ # if DB_VERSION_MAJOR > 2 d.flags = DB_DBT_USERMEM|DB_DBT_PARTIAL; # endif /* DB_VERSION_MAJOR > 2 */ /* establish read-lock */ fd = -1; # if DB_VERSION_MAJOR >= 2 status = -1; status = db->fd(db, &fd); # else /* DB_VERSION_MAJOR >= 2 */ status = 0; fd = db->fd(db); # endif /* DB_VERSION_MAJOR >= 2 */ /* XXX -- allow multiple readers? */ (void) pthread_mutex_lock(&pop_lock); if (status == 0 && fd != -1) { # ifdef LOCK_SH status = flock(fd, LOCK_SH); if (status != 0 && dolog) { syslog(LOG_WARNING, "flock(LOCK_SH): %s", strerror(errno)); } # else /* LOCK_SH */ struct flock l; l.l_start = 0; l.l_len = 0; l.l_type = F_RDLCK; l.l_whence = SEEK_SET; status = fcntl(fd, F_SETLKW, &l); if (status != 0 && dolog) { syslog(LOG_WARNING, "fcntl(F_RDLCK): %s", strerror(errno)); } # endif /* LOCK_SH */ } # if DB_VERSION_MAJOR < 2 status = db->get(db, &q, &d, 0); # else /* DB_VERSION_MAJOR < 2 */ status = db->get(db, NULL, &q, &d, 0); # endif /* DB_VERSION_MAJOR */ if (status == 0) { ret = TRUE; } else if (status != DB_NOTFOUND && dolog) { syslog(LOG_ERR, "db->get(): %s", db_strerror(status)); } /* surrender read-lock */ if (fd != -1) { # ifdef LOCK_SH status = flock(fd, LOCK_UN); if (status != 0 && dolog) { syslog(LOG_WARNING, "flock(LOCK_UN): %s", strerror(errno)); } # else /* LOCK_SH */ struct flock l; l.l_start = 0; l.l_len = 0; l.l_type = F_UNLCK; l.l_whence = SEEK_SET; status = fcntl(fd, F_SETLKW, &l); if (status != 0 && dolog) { syslog(LOG_WARNING, "fcntl(F_UNLCK): %s", strerror(errno)); } # endif /* LOCK_SH */ } (void) pthread_mutex_unlock(&pop_lock); return ret; } #endif /* POPAUTH */ /* ** DKIMF_ENQUEUE -- enqueue an entry onto a peerlist ** ** Parameters: ** head -- queue head (updated) ** tail -- queue tail (updated) ** str -- entry ** ** Return value: ** TRUE if successful, FALSE otherwise ** ** Side effects: ** Prints an error message when appropriate. */ static bool dkimf_enqueue(struct Peer **head, struct Peer **tail, char *str) { struct Peer *newpeer; assert(head != NULL); assert(tail != NULL); assert(str != NULL); newpeer = (struct Peer *) malloc(sizeof(struct Peer)); if (newpeer == NULL) { fprintf(stderr, "%s: malloc(): %s\n", progname, strerror(errno)); return FALSE; } newpeer->peer_next = NULL; if (*head == NULL) { *head = newpeer; *tail = newpeer; } else { (*tail)->peer_next = newpeer; *tail = newpeer; } if (str[0] == '!') { newpeer->peer_info = strdup(&str[1]); newpeer->peer_neg = TRUE; } else { newpeer->peer_info = strdup(str); newpeer->peer_neg = FALSE; } if (newpeer->peer_info == NULL) { fprintf(stderr, "%s: strdup(): %s\n", progname, strerror(errno)); return FALSE; } return TRUE; } /* ** DKIMF_LOAD_LIST -- load a list of hosts/CIDR blocks into memory ** ** Parameters: ** in -- input stream (or NULL if none) ** deflist -- array of entries to include (or NULL if none) ** list -- resultant list (updated) ** ** Return value: ** TRUE if successful, FALSE otherwise ** ** Side effects: ** Prints an error message when appropriate. */ bool dkimf_load_list(FILE *in, char **deflist, struct Peer **list) { struct Peer *head = NULL; struct Peer *tail = NULL; char peer[BUFRSZ + 1]; assert(list != NULL); memset(peer, '\0', sizeof peer); if (deflist != NULL) { int c; for (c = 0; deflist[c] != NULL; c++) { if (!dkimf_enqueue(&head, &tail, deflist[c])) return FALSE; } } if (in != NULL) { char *p; while (fgets(peer, sizeof(peer) - 1, in) != NULL) { for (p = peer; *p != '\0'; p++) { if (*p == '\n') { *p = '\0'; break; } } if (!dkimf_enqueue(&head, &tail, peer)) return FALSE; } } *list = head; return TRUE; } #ifdef _FFR_REPLACE_RULES /* ** DKIMF_LOAD_REPLIST -- load a list of replace patterns ** ** Parameters: ** in -- input stream (must already be open) ** list -- list to be updated ** ** Return value: ** TRUE if successful, FALSE otherwise ** ** Side effects: ** Prints an error message when appropriate. */ bool dkimf_load_replist(FILE *in, struct replace **list) { int line; int status; char *p; struct replace *newrep; char rule[BUFRSZ + 1]; assert(in != NULL); assert(list != NULL); memset(rule, '\0', sizeof rule); while (fgets(rule, sizeof(rule) - 1, in) != NULL) { line++; for (p = rule; *p != '\0'; p++) { if (*p == '\n' || *p == '#') { *p = '\0'; break; } } if (dkimf_isblank(rule)) continue; newrep = (struct replace *) malloc(sizeof(struct replace)); if (newrep == NULL) { fprintf(stderr, "%s: malloc(): %s\n", progname, strerror(errno)); return FALSE; } p = strrchr(rule, '\t'); if (p == NULL) return FALSE; *p = '\0'; status = regcomp(&newrep->repl_re, rule, 0); if (status != 0) { fprintf(stderr, "%s: regcomp() failed\n", progname); return FALSE; } newrep->repl_txt = strdup(p + 1); if (newrep->repl_txt == NULL) { fprintf(stderr, "%s: strdup(): %s\n", progname, strerror(errno)); return FALSE; } newrep->repl_next = *list; *list = newrep; } return TRUE; } #endif /* _FFR_REPLACE_RULES */ /* ** DKIMF_INET_NTOA -- thread-safe inet_ntoa() ** ** Parameters: ** a -- (struct in_addr) to be converted ** buf -- destination buffer ** buflen -- number of bytes at buf ** ** Return value: ** Size of the resultant string. If the result is greater than buflen, ** then buf does not contain the complete result. */ size_t dkimf_inet_ntoa(struct in_addr a, char *buf, size_t buflen) { in_addr_t addr; assert(buf != NULL); addr = ntohl(a.s_addr); return snprintf(buf, buflen, "%d.%d.%d.%d", (addr >> 24), (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff); } /* ** DKIMF_TRIMSPACES -- trim trailing whitespace ** ** Parameters: ** str -- string to modify ** ** Return value: ** None. */ void dkimf_trimspaces(u_char *str) { u_char *p; u_char *last; assert(str != NULL); last = NULL; for (p = str; *p != '\0'; p++) { if (isascii(*p) && isspace(*p) && last == NULL) { last = p; continue; } if (!isascii(*p) || !isspace(*p)) last = NULL; } if (last != NULL) *last = '\0'; } /* ** DKIMF_STRIPCR -- remove CRs ** ** Parameters: ** str -- string to modify ** ** Return value: ** None. */ void dkimf_stripcr(char *str) { char *p; char *q; assert(str != NULL); for (p = str, q = str; *p != '\0'; p++) { if (*p == '\r') continue; if (q != p) *q = *p; q++; } if (q != p) *q = *p; } /* ** DKIMF_MKPATH -- generate a path ** ** Parameters: ** path -- output buffer ** pathlen -- bytes available at "path" ** root -- root to infer; if empty, use getcwd() ** file -- filename to use ** ** Return value: ** None. */ void dkimf_mkpath(char *path, size_t pathlen, char *root, char *file) { assert(path != NULL); assert(root != NULL); assert(file != NULL); if (file[0] == '/') /* explicit path */ { sm_strlcpy(path, file, pathlen); } else if (root[0] == '\0') /* no root, use cwd */ { (void) getcwd(path, pathlen); sm_strlcat(path, "/", pathlen); sm_strlcat(path, file, pathlen); } else /* use root */ { sm_strlcpy(path, root, pathlen); if (root[strlen(root) - 1] != '/') sm_strlcat(path, "/", pathlen); sm_strlcat(path, file, pathlen); } } /* ** DKIMF_HOSTLIST -- see if a hostname is in a pattern of hosts/domains ** ** Parameters: ** host -- hostname to compare ** list -- NULL-terminated char * array to search ** ** Return value: ** TRUE iff either "host" was in the list or it match a domain pattern ** found in the list. */ bool dkimf_hostlist(char *host, char **list) { int c; char *p; assert(host != NULL); assert(list != NULL); /* walk the entire list */ for (c = 0; list[c] != NULL; c++) { /* first try a full hostname match */ if (strcasecmp(host, list[c]) == 0) return TRUE; /* try each domain */ for (p = strchr(host, '.'); p != NULL; p = strchr(p + 1, '.')) { if (strcasecmp(p, list[c]) == 0) return TRUE; } } /* not found */ return FALSE; } /* ** DKIMF_DSTRING_RESIZE -- resize a dynamic string (dstring) ** ** Parameters: ** dstr -- DKIMF_DSTRING handle ** len -- number of bytes desired ** ** Return value: ** TRUE iff the resize worked (or wasn't needed) ** ** Notes: ** This will actually ensure that there are "len" bytes available. ** The caller must account for the NULL byte when requesting a ** specific size. */ static bool dkimf_dstring_resize(struct dkimf_dstring *dstr, int len) { int newsz; char *new; assert(dstr != NULL); assert(len > 0); if (dstr->ds_alloc >= len) return TRUE; /* must resize */ for (newsz = dstr->ds_alloc * 2; newsz < len; newsz *= 2) { /* impose ds_max limit, if specified */ if (dstr->ds_max > 0 && newsz > dstr->ds_max) { if (len <= dstr->ds_max) { newsz = len; break; } return FALSE; } /* check for overflow */ if (newsz > INT_MAX / 2) { /* next iteration will overflow "newsz" */ return FALSE; } } new = malloc(newsz); if (new == NULL) return FALSE; memcpy(new, dstr->ds_buf, dstr->ds_alloc); free(dstr->ds_buf); dstr->ds_alloc = newsz; dstr->ds_buf = new; return TRUE; } /* ** DKIMF_DSTRING_NEW -- make a new dstring ** ** Parameters: ** dkim -- DKIM handle ** len -- initial number of bytes ** maxlen -- maximum allowed length (0 == unbounded) ** ** Return value: ** A DKIMF_DSTRING handle, or NULL on failure. */ struct dkimf_dstring * dkimf_dstring_new(int len, int maxlen) { struct dkimf_dstring *new; /* fail on invalid parameters */ if ((maxlen > 0 && len > maxlen) || len == 0) return NULL; if (len < BUFRSZ) len = BUFRSZ; new = malloc(sizeof(struct dkimf_dstring)); if (new == NULL) return NULL; new->ds_buf = malloc(len); if (new->ds_buf == NULL) { free(new); return NULL; } memset(new->ds_buf, '\0', len); new->ds_alloc = len; new->ds_len = 0; new->ds_max = maxlen; return new; } /* ** DKIMF_DSTRING_FREE -- destroy an existing dstring ** ** Parameters: ** dstr -- DKIMF_DSTRING handle to be destroyed ** ** Return value: ** None. */ void dkimf_dstring_free(struct dkimf_dstring *dstr) { assert(dstr != NULL); free(dstr->ds_buf); free(dstr); } /* ** DKIMF_DSTRING_COPY -- copy data into a dstring ** ** Parameters: ** dstr -- DKIMF_DSTRING handle to update ** str -- input string ** ** Return value: ** TRUE iff the copy succeeded. ** ** Side effects: ** The dstring may be resized. */ bool dkimf_dstring_copy(struct dkimf_dstring *dstr, char *str) { int len; assert(dstr != NULL); assert(str != NULL); len = strlen(str); /* too big? */ if (dstr->ds_max > 0 && len >= dstr->ds_max) return FALSE; /* fits now? */ if (dstr->ds_alloc <= len) { /* nope; try to resize */ if (!dkimf_dstring_resize(dstr, len + 1)) return FALSE; } /* copy */ dstr->ds_len = sm_strlcpy(dstr->ds_buf, str, dstr->ds_alloc); return TRUE; } /* ** DKIMF_DSTRING_CAT -- append data onto a dstring ** ** Parameters: ** dstr -- DKIMF_DSTRING handle to update ** str -- input string ** ** Return value: ** TRUE iff the update succeeded. ** ** Side effects: ** The dstring may be resized. */ bool dkimf_dstring_cat(struct dkimf_dstring *dstr, char *str) { int len; assert(dstr != NULL); assert(str != NULL); len = strlen(str) + dstr->ds_len; /* too big? */ if (dstr->ds_max > 0 && len >= dstr->ds_max) return FALSE; /* fits now? */ if (dstr->ds_alloc <= len) { /* nope; try to resize */ if (!dkimf_dstring_resize(dstr, len + 1)) return FALSE; } /* append */ dstr->ds_len = sm_strlcat(dstr->ds_buf, str, dstr->ds_alloc); return TRUE; } /* ** DKIMF_DSTRING_CAT1 -- append one byte onto a dstring ** ** Parameters: ** dstr -- DKIMF_DSTRING handle to update ** c -- input character ** ** Return value: ** TRUE iff the update succeeded. ** ** Side effects: ** The dstring may be resized. */ bool dkimf_dstring_cat1(struct dkimf_dstring *dstr, int c) { int len; assert(dstr != NULL); len = dstr->ds_len + 1; /* too big? */ if (dstr->ds_max > 0 && len >= dstr->ds_max) return FALSE; /* fits now? */ if (dstr->ds_alloc <= len) { /* nope; try to resize */ if (!dkimf_dstring_resize(dstr, len + 1)) return FALSE; } /* append */ dstr->ds_buf[dstr->ds_len++] = c; dstr->ds_buf[dstr->ds_len] = '\0'; return TRUE; } /* ** DKIMF_DSTRING_GET -- retrieve data in a dstring ** ** Parameters: ** dstr -- DKIMF_DSTRING handle whose string should be retrieved ** ** Return value: ** Pointer to the NULL-terminated contents of "dstr". */ char * dkimf_dstring_get(struct dkimf_dstring *dstr) { assert(dstr != NULL); return dstr->ds_buf; } /* ** DKIMF_DSTRING_LEN -- retrieve length of data in a dstring ** ** Parameters: ** dstr -- DKIMF_DSTRING handle whose string should be retrieved ** ** Return value: ** Number of bytes in a dstring. */ int dkimf_dstring_len(struct dkimf_dstring *dstr) { assert(dstr != NULL); return dstr->ds_len; } /* ** DKIMF_DSTRING_BLANK -- clear out the contents of a dstring ** ** Parameters: ** dstr -- DKIMF_DSTRING handle whose string should be cleared ** ** Return value: ** None. */ void dkimf_dstring_blank(struct dkimf_dstring *dstr) { assert(dstr != NULL); dstr->ds_len = 0; dstr->ds_buf[0] = '\0'; } /* ** DKIMF_DSTRING_CHOP -- truncate contents of a dstring ** ** Parameters: ** dstr -- DKIMF_DSTRING handle whose string should be cleared ** len -- length after which to clobber ** ** Return value: ** None. */ void dkimf_dstring_chop(struct dkimf_dstring *dstr, int len) { assert(dstr != NULL); if (len < dstr->ds_len) { dstr->ds_len = len; dstr->ds_buf[len] = '\0'; } } /* ** DKIMF_SOCKET_CLEANUP -- try to clean up the socket ** ** Parameters: ** sockspec -- socket specification ** ** Return value: ** 0 -- nothing to cleanup or cleanup successful ** other -- an error code (a la errno) */ int dkimf_socket_cleanup(char *sockspec) { int s; char *colon; struct sockaddr_un sock; assert(sockspec != NULL); /* we only care about "local" or "unix" sockets */ if (sockspec[0] != '/' && strncasecmp(sockspec, "local:", 6) != 0 && strncasecmp(sockspec, "unix:", 5) != 0) return 0; /* find the filename */ colon = strchr(sockspec, ':'); if (colon == NULL || strlen(colon + 1) == 0) return EINVAL; /* get a socket */ s = socket(PF_UNIX, SOCK_STREAM, 0); if (s == -1) return errno; /* set up a connection */ memset(&sock, '\0', sizeof sock); #ifdef BSD sock.sun_len = sizeof sock; #endif /* BSD */ sock.sun_family = PF_UNIX; sm_strlcpy(sock.sun_path, colon + 1, sizeof sock.sun_path); /* try to connect */ if (connect(s, (struct sockaddr *) &sock, (socklen_t) sizeof sock) != 0) { /* if ECONNREFUSED, try to unlink */ if (errno == ECONNREFUSED) { close(s); if (unlink(sock.sun_path) == 0) return 0; else return errno; } /* if ENOENT, the socket's not there */ else if (errno == ENOENT) { close(s); return 0; } /* something else happened */ else { int saveerr; saveerr = errno; close(s); return saveerr; } } /* connection apparently succeeded */ close(s); return EADDRINUSE; }