/* ** 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 #include #include #include #include #include #include /* libsm includes */ #include /* 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'; }