/*
** Copyright (c) 2007 Sendmail, Inc. and its suppliers.
** All rights reserved.
*/
#ifndef lint
static char dkim_util_c_id[] = "@(#)$Id: dkim-util.c,v 1.18 2007/12/15 01:48:17 msk Exp $";
#endif /* !lint */
/* system includes */
#include <sys/param.h>
#include <sys/types.h>
#include <assert.h>
#include <unistd.h>
#include <limits.h>
#include <string.h>
#include <errno.h>
/* libsm includes */
#include <sm/string.h>
/* libdkim includes */
#include "dkim.h"
#include "dkim-util.h"
#include "dkim-types.h"
/* prototypes */
extern void dkim_error __P((DKIM *, const char *, ...));
/*
** DKIM_MALLOC -- allocate memory
**
** Parameters:
** libhandle -- DKIM library context in which this is performed
** closure -- opaque closure handle for the allocation
** nbytes -- number of bytes desired
**
** Return value:
** Pointer to allocated memory, or NULL on failure.
*/
void *
dkim_malloc(DKIM_LIB *libhandle, void *closure, size_t nbytes)
{
assert(libhandle != NULL);
if (libhandle->dkiml_malloc == NULL)
return malloc(nbytes);
else
return libhandle->dkiml_malloc(closure, nbytes);
}
/*
** DKIM_MFREE -- release memory
**
** Parameters:
** dkim -- DKIM context in which this is performed
** closure -- opaque closure handle for the allocation
** ptr -- pointer to memory to be freed
**
** Return value:
** None.
*/
void
dkim_mfree(DKIM_LIB *libhandle, void *closure, void *ptr)
{
assert(libhandle != NULL);
if (libhandle->dkiml_free == NULL)
free(ptr);
else
libhandle->dkiml_free(closure, ptr);
}
/*
** DKIM_STRDUP -- duplicate a string
**
** Parameters:
** dkim -- DKIM handle
** str -- string to clone
** len -- bytes to copy (0 == copy to NULL byte)
**
** Return value:
** Pointer to a new copy of "str" allocated from within the appropriate
** context, or NULL on failure.
*/
unsigned char *
dkim_strdup(DKIM *dkim, const unsigned char *str, size_t len)
{
unsigned char *new;
assert(dkim != NULL);
assert(str != NULL);
if (len == 0)
len = strlen(str);
new = dkim_malloc(dkim->dkim_libhandle, dkim->dkim_closure, len + 1);
if (new != NULL)
{
memcpy(new, str, len);
new[len] = '\0';
}
else
dkim_error(dkim, "unable to allocate %d byte(s)", len + 1);
return new;
}
/*
** DKIM_TMPFILE -- open a temporary file
**
** Parameters:
** dkim -- DKIM handle
** fp -- descriptor (returned)
** keep -- if FALSE, unlink() the file once created
**
** Return value:
** A DKIM_STAT_* constant.
*/
DKIM_STAT
dkim_tmpfile(DKIM *dkim, int *fp, bool keep)
{
int fd;
char path[MAXPATHLEN + 1];
assert(dkim != NULL);
assert(fp != NULL);
if (dkim->dkim_id != NULL)
{
snprintf(path, MAXPATHLEN, "%s/dkim.%s.XXXXXX",
dkim->dkim_libhandle->dkiml_tmpdir, dkim->dkim_id);
}
else
{
snprintf(path, MAXPATHLEN, "%s/dkim.XXXXXX",
dkim->dkim_libhandle->dkiml_tmpdir);
}
fd = mkstemp(path);
if (fd == -1)
{
dkim_error(dkim, "can't create temporary file at %s: %s",
path, strerror(errno));
return DKIM_STAT_NORESOURCE;
}
*fp = fd;
if (!keep)
(void) unlink(path);
return DKIM_STAT_OK;
}
/*
** DKIM_DSTRING_RESIZE -- resize a dynamic string (dstring)
**
** Parameters:
** dstr -- DKIM_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
dkim_dstring_resize(struct dkim_dstring *dstr, int len)
{
int newsz;
char *new;
DKIM *dkim;
DKIM_LIB *lib;
assert(dstr != NULL);
assert(len > 0);
if (dstr->ds_alloc >= len)
return TRUE;
dkim = dstr->ds_dkim;
lib = dkim->dkim_libhandle;
/* 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;
}
dkim_error(dkim, "maximum string size exceeded");
return FALSE;
}
/* check for overflow */
if (newsz > INT_MAX / 2)
{
/* next iteration will overflow "newsz" */
dkim_error(dkim, "internal string limit reached");
return FALSE;
}
}
new = dkim_malloc(lib, dkim->dkim_closure, newsz);
if (new == NULL)
{
dkim_error(dkim, "unable to allocate %d byte(s)", newsz);
return FALSE;
}
memcpy(new, dstr->ds_buf, dstr->ds_alloc);
dkim_mfree(lib, dkim->dkim_closure, dstr->ds_buf);
dstr->ds_alloc = newsz;
dstr->ds_buf = new;
return TRUE;
}
/*
** DKIM_DSTRING_NEW -- make a new dstring
**
** Parameters:
** dkim -- DKIM handle
** len -- initial number of bytes
** maxlen -- maximum allowed length, including the NULL byte
** (0 == unbounded)
**
** Return value:
** A DKIM_DSTRING handle, or NULL on failure.
*/
struct dkim_dstring *
dkim_dstring_new(DKIM *dkim, int len, int maxlen)
{
struct dkim_dstring *new;
DKIM_LIB *lib;
assert(dkim != NULL);
/* fail on invalid parameters */
if ((maxlen > 0 && len > maxlen) || len == 0)
return NULL;
lib = dkim->dkim_libhandle;
if (len < BUFRSZ)
len = BUFRSZ;
new = dkim_malloc(lib, dkim->dkim_closure, sizeof(struct dkim_dstring));
if (new == NULL)
{
dkim_error(dkim, "unable to allocate %d byte(s)",
sizeof(struct dkim_dstring));
return NULL;
}
new->ds_buf = dkim_malloc(lib, dkim->dkim_closure, len);
if (new->ds_buf == NULL)
{
dkim_error(dkim, "unable to allocate %d byte(s)",
sizeof(struct dkim_dstring));
dkim_mfree(lib, dkim->dkim_closure, new);
return NULL;
}
memset(new->ds_buf, '\0', len);
new->ds_alloc = len;
new->ds_len = 0;
new->ds_max = maxlen;
new->ds_dkim = dkim;
return new;
}
/*
** DKIM_DSTRING_FREE -- destroy an existing dstring
**
** Parameters:
** dstr -- DKIM_DSTRING handle to be destroyed
**
** Return value:
** None.
*/
void
dkim_dstring_free(struct dkim_dstring *dstr)
{
DKIM_LIB *lib;
DKIM *dkim;
assert(dstr != NULL);
dkim = dstr->ds_dkim;
lib = dkim->dkim_libhandle;
dkim_mfree(lib, dkim->dkim_closure, dstr->ds_buf);
dkim_mfree(lib, dkim->dkim_closure, dstr);
}
/*
** DKIM_DSTRING_COPY -- copy data into a dstring
**
** Parameters:
** dstr -- DKIM_DSTRING handle to update
** str -- input string
**
** Return value:
** TRUE iff the copy succeeded.
**
** Side effects:
** The dstring may be resized.
*/
bool
dkim_dstring_copy(struct dkim_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 (!dkim_dstring_resize(dstr, len + 1))
return FALSE;
}
/* copy */
memcpy(dstr->ds_buf, str, len + 1);
dstr->ds_len = len;
return TRUE;
}
/*
** DKIM_DSTRING_CAT -- append data onto a dstring
**
** Parameters:
** dstr -- DKIM_DSTRING handle to update
** str -- input string
**
** Return value:
** TRUE iff the update succeeded.
**
** Side effects:
** The dstring may be resized.
*/
bool
dkim_dstring_cat(struct dkim_dstring *dstr, char *str)
{
size_t len;
size_t needed;
assert(dstr != NULL);
assert(str != NULL);
len = strlen(str);
needed = dstr->ds_len + len;
/* too big? */
if (dstr->ds_max > 0 && needed >= dstr->ds_max)
return FALSE;
/* fits now? */
if (dstr->ds_alloc <= needed)
{
/* nope; try to resize */
if (!dkim_dstring_resize(dstr, needed + 1))
return FALSE;
}
/* append */
memcpy(dstr->ds_buf + dstr->ds_len, str, len + 1);
dstr->ds_len += len;
return TRUE;
}
/*
** DKIM_DSTRING_CAT1 -- append one byte onto a dstring
**
** Parameters:
** dstr -- DKIM_DSTRING handle to update
** c -- input character
**
** Return value:
** TRUE iff the update succeeded.
**
** Side effects:
** The dstring may be resized.
*/
bool
dkim_dstring_cat1(struct dkim_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 (!dkim_dstring_resize(dstr, len + 1))
return FALSE;
}
/* append */
dstr->ds_buf[dstr->ds_len++] = c;
dstr->ds_buf[dstr->ds_len] = '\0';
return TRUE;
}
/*
** DKIM_DSTRING_CATN -- append 'n' bytes onto a dstring
**
** Parameters:
** dstr -- DKIM_DSTRING handle to update
** str -- input string
** nbytes -- number of bytes to append
**
** Return value:
** TRUE iff the update succeeded.
**
** Side effects:
** The dstring may be resized.
*/
bool
dkim_dstring_catn(struct dkim_dstring *dstr, char *str, size_t nbytes)
{
size_t needed;
assert(dstr != NULL);
assert(str != NULL);
needed = dstr->ds_len + nbytes;
/* too big? */
if (dstr->ds_max > 0 && needed >= dstr->ds_max)
return FALSE;
/* fits now? */
if (dstr->ds_alloc <= needed)
{
/* nope; try to resize */
if (!dkim_dstring_resize(dstr, needed + 1))
return FALSE;
}
/* append */
memcpy(dstr->ds_buf + dstr->ds_len, str, nbytes);
dstr->ds_len += nbytes;
dstr->ds_buf[dstr->ds_len] = '\0';
return TRUE;
}
/*
** DKIM_DSTRING_GET -- retrieve data in a dstring
**
** Parameters:
** dstr -- DKIM_STRING handle whose string should be retrieved
**
** Return value:
** Pointer to the NULL-terminated contents of "dstr".
*/
char *
dkim_dstring_get(struct dkim_dstring *dstr)
{
assert(dstr != NULL);
return dstr->ds_buf;
}
/*
** DKIM_DSTRING_LEN -- retrieve length of data in a dstring
**
** Parameters:
** dstr -- DKIM_STRING handle whose string should be retrieved
**
** Return value:
** Number of bytes in a dstring.
*/
int
dkim_dstring_len(struct dkim_dstring *dstr)
{
assert(dstr != NULL);
return dstr->ds_len;
}
/*
** DKIM_DSTRING_BLANK -- clear out the contents of a dstring
**
** Parameters:
** dstr -- DKIM_STRING handle whose string should be cleared
**
** Return value:
** None.
*/
void
dkim_dstring_blank(struct dkim_dstring *dstr)
{
assert(dstr != NULL);
dstr->ds_len = 0;
dstr->ds_buf[0] = '\0';
}
syntax highlighted by Code2HTML, v. 0.9.1