/*
** 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 <sys/param.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/file.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <assert.h>
#include <syslog.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <netdb.h>
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#ifdef _FFR_REPLACE_RULES
# include <regex.h>
#endif /* _FFR_REPLACE_RULES */
/* libsm includes */
#include <sm/gen.h>
#include <sm/string.h>
#if POPAUTH
/* system includes */
# include <pthread.h>
/* libdb includes */
# include <db.h>
#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;
}
syntax highlighted by Code2HTML, v. 0.9.1