Skip to content
Snippets Groups Projects
utils.c 17.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * Asterisk -- A telephony toolkit for Linux.
     *
     * Utility functions
     *
    
     * Copyright (C)  2004 - 2005, Digium, Inc.
    
     *
     * This program is free software, distributed under the terms of
     * the GNU General Public License
     */
    
    
    #include <ctype.h>
    
    #include <string.h>
    
    #include <stdlib.h>
    
    #include <errno.h>
    
    #include <stdio.h>
    
    #include <netinet/in.h>
    
    #include <stdarg.h>
    
    #include "asterisk.h"
    
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
    
    #include "asterisk/lock.h"
    #include "asterisk/io.h"
    #include "asterisk/logger.h"
    #include "asterisk/md5.h"
    
    #define AST_API_MODULE		/* ensure that inlinable API functions will be built in this module if required */
    #include "asterisk/strings.h"
    
    #define AST_API_MODULE		/* ensure that inlinable API functions will be built in this module if required */
    #include "asterisk/time.h"
    
    
    #define AST_API_MODULE		/* ensure that inlinable API functions will be built in this module if required */
    #include "asterisk/utils.h"
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char base64[64];
    static char b2a[256];
    
    
    #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined( __NetBSD__ ) || defined(__APPLE__)
    
    
    /* duh? ERANGE value copied from web... */
    #define ERANGE 34
    #undef gethostbyname
    
    
    AST_MUTEX_DEFINE_STATIC(__mutex);
    
    
    /* Recursive replacement for gethostbyname for BSD-based systems.  This
    routine is derived from code originally written and placed in the public 
    domain by Enzo Michelangeli <em@em.no-ip.com> */
    
    
    static int gethostbyname_r (const char *name, struct hostent *ret, char *buf,
    
    James Golovich's avatar
    James Golovich committed
    				size_t buflen, struct hostent **result, 
    				int *h_errnop) 
    
    {
    	int hsave;
    	struct hostent *ph;
    	ast_mutex_lock(&__mutex); /* begin critical area */
    	hsave = h_errno;
    
    	ph = gethostbyname(name);
    	*h_errnop = h_errno; /* copy h_errno to *h_herrnop */
    	if (ph == NULL) {
    		*result = NULL;
    	} else {
    		char **p, **q;
    		char *pbuf;
    		int nbytes=0;
    		int naddr=0, naliases=0;
    		/* determine if we have enough space in buf */
    
    		/* count how many addresses */
    		for (p = ph->h_addr_list; *p != 0; p++) {
    			nbytes += ph->h_length; /* addresses */
    			nbytes += sizeof(*p); /* pointers */
    			naddr++;
    		}
    		nbytes += sizeof(*p); /* one more for the terminating NULL */
    
    		/* count how many aliases, and total length of strings */
    		for (p = ph->h_aliases; *p != 0; p++) {
    			nbytes += (strlen(*p)+1); /* aliases */
    			nbytes += sizeof(*p);  /* pointers */
    			naliases++;
    		}
    		nbytes += sizeof(*p); /* one more for the terminating NULL */
    
    		/* here nbytes is the number of bytes required in buffer */
    		/* as a terminator must be there, the minimum value is ph->h_length */
    		if(nbytes > buflen) {
    			*result = NULL;
    			ast_mutex_unlock(&__mutex); /* end critical area */
    			return ERANGE; /* not enough space in buf!! */
    		}
    
    		/* There is enough space. Now we need to do a deep copy! */
    		/* Allocation in buffer:
    			from [0] to [(naddr-1) * sizeof(*p)]:
    			pointers to addresses
    			at [naddr * sizeof(*p)]:
    			NULL
    			from [(naddr+1) * sizeof(*p)] to [(naddr+naliases) * sizeof(*p)] :
    			pointers to aliases
    			at [(naddr+naliases+1) * sizeof(*p)]:
    			NULL
    			then naddr addresses (fixed length), and naliases aliases (asciiz).
    		*/
    
    		*ret = *ph;   /* copy whole structure (not its address!) */
    
    		/* copy addresses */
    		q = (char **)buf; /* pointer to pointers area (type: char **) */
    		ret->h_addr_list = q; /* update pointer to address list */
    		pbuf = buf + ((naddr+naliases+2)*sizeof(*p)); /* skip that area */
    		for (p = ph->h_addr_list; *p != 0; p++) {
    			memcpy(pbuf, *p, ph->h_length); /* copy address bytes */
    			*q++ = pbuf; /* the pointer is the one inside buf... */
    			pbuf += ph->h_length; /* advance pbuf */
    		}
    		*q++ = NULL; /* address list terminator */
    
    		/* copy aliases */
    		ret->h_aliases = q; /* update pointer to aliases list */
    		for (p = ph->h_aliases; *p != 0; p++) {
    			strcpy(pbuf, *p); /* copy alias strings */
    			*q++ = pbuf; /* the pointer is the one inside buf... */
    			pbuf += strlen(*p); /* advance pbuf */
    			*pbuf++ = 0; /* string terminator */
    		}
    		*q++ = NULL; /* terminator */
    
    		strcpy(pbuf, ph->h_name); /* copy alias strings */
    		ret->h_name = pbuf;
    		pbuf += strlen(ph->h_name); /* advance pbuf */
    		*pbuf++ = 0; /* string terminator */
    
    		*result = ret;  /* and let *result point to structure */
    
    	}
    	h_errno = hsave;  /* restore h_errno */
    	ast_mutex_unlock(&__mutex); /* end critical area */
    
    
    	return (*result == NULL); /* return 0 on success, non-zero on error */
    
    /* Re-entrant (thread safe) version of gethostbyname that replaces the 
       standard gethostbyname (which is not thread safe)
    
    struct hostent *ast_gethostbyname(const char *host, struct ast_hostent *hp)
    {
    	int res;
    	int herrno;
    
    	const char *s;
    
    	struct hostent *result = NULL;
    
    	/* Although it is perfectly legitimate to lookup a pure integer, for
    	   the sake of the sanity of people who like to name their peers as
    	   integers, we break with tradition and refuse to look up a
    	   pure integer */
    	s = host;
    
    	while(s && *s) {
    		if (!isdigit(*s))
    			break;
    		s++;
    	}
    	if (!s || !*s)
    		return NULL;
    
    #ifdef SOLARIS
    	result = gethostbyname_r(host, &hp->hp, hp->buf, sizeof(hp->buf), &herrno);
    
    	if (!result || !hp->hp.h_addr_list || !hp->hp.h_addr_list[0])
    		return NULL;
    #else
    
    	res = gethostbyname_r(host, &hp->hp, hp->buf, sizeof(hp->buf), &result, &herrno);
    
    
    	if (res || !result || !hp->hp.h_addr_list || !hp->hp.h_addr_list[0])
    
    
    
    /* This is a regression test for recursive mutexes.
       test_for_thread_safety() will return 0 if recursive mutex locks are
       working properly, and non-zero if they are not working properly. */
    
    AST_MUTEX_DEFINE_STATIC(test_lock);
    AST_MUTEX_DEFINE_STATIC(test_lock2);
    static pthread_t test_thread; 
    static int lock_count = 0;
    static int test_errors = 0;
    
    static void *test_thread_body(void *data) 
    { 
    
    	ast_mutex_lock(&test_lock);
    	lock_count += 10;
    	if (lock_count != 10) 
    		test_errors++;
    	ast_mutex_lock(&test_lock);
    	lock_count += 10;
    	if (lock_count != 20) 
    		test_errors++;
    	ast_mutex_lock(&test_lock2);
    	ast_mutex_unlock(&test_lock);
    	lock_count -= 10;
    	if (lock_count != 10) 
    		test_errors++;
    	ast_mutex_unlock(&test_lock);
    	lock_count -= 10;
    	ast_mutex_unlock(&test_lock2);
    	if (lock_count != 0) 
    		test_errors++;
    	return NULL;
    
    	ast_mutex_lock(&test_lock2);
    	ast_mutex_lock(&test_lock);
    	lock_count += 1;
    	ast_mutex_lock(&test_lock);
    	lock_count += 1;
    
    	ast_pthread_create(&test_thread, NULL, test_thread_body, NULL); 
    
    	usleep(100);
    	if (lock_count != 2) 
    		test_errors++;
    	ast_mutex_unlock(&test_lock);
    	lock_count -= 1;
    	usleep(100); 
    	if (lock_count != 1) 
    		test_errors++;
    	ast_mutex_unlock(&test_lock);
    	lock_count -= 1;
    	if (lock_count != 0) 
    		test_errors++;
    	ast_mutex_unlock(&test_lock2);
    	usleep(100);
    	if (lock_count != 0) 
    		test_errors++;
    	pthread_join(test_thread, NULL);
    	return(test_errors);          /* return 0 on success. */
    
    /*--- ast_md5_hash: Produce 16 char MD5 hash of value. ---*/
    void ast_md5_hash(char *output, char *input)
    {
    
    	struct MD5Context md5;
    	unsigned char digest[16];
    	char *ptr;
    	int x;
    
    	MD5Init(&md5);
    
    	MD5Update(&md5, (unsigned char *)input, strlen(input));
    
    	MD5Final(digest, &md5);
    	ptr = output;
    	for (x=0; x<16; x++)
    		ptr += sprintf(ptr, "%2.2x", digest[x]);
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_base64decode(unsigned char *dst, char *src, int max)
    {
    	int cnt = 0;
    	unsigned int byte = 0;
    	unsigned int bits = 0;
    	int incnt = 0;
    #if 0
    	unsigned char *odst = dst;
    #endif
    	while(*src && (cnt < max)) {
    		/* Shift in 6 bits of input */
    		byte <<= 6;
    		byte |= (b2a[(int)(*src)]) & 0x3f;
    		bits += 6;
    #if 0
    		printf("Add: %c %s\n", *src, binary(b2a[(int)(*src)] & 0x3f, 6));
    #endif
    		src++;
    		incnt++;
    		/* If we have at least 8 bits left over, take that character 
    		   off the top */
    		if (bits >= 8)  {
    			bits -= 8;
    			*dst = (byte >> bits) & 0xff;
    #if 0
    			printf("Remove: %02x %s\n", *dst, binary(*dst, 8));
    #endif
    			dst++;
    			cnt++;
    		}
    	}
    #if 0
    	dump(odst, cnt);
    #endif
    	/* Dont worry about left over bits, they're extra anyway */
    	return cnt;
    }
    
    int ast_base64encode(char *dst, unsigned char *src, int srclen, int max)
    {
    	int cnt = 0;
    	unsigned int byte = 0;
    	int bits = 0;
    	int index;
    	int cntin = 0;
    #if 0
    	char *odst = dst;
    	dump(src, srclen);
    #endif
    	/* Reserve one bit for end */
    	max--;
    	while((cntin < srclen) && (cnt < max)) {
    		byte <<= 8;
    #if 0
    		printf("Add: %02x %s\n", *src, binary(*src, 8));
    #endif
    		byte |= *(src++);
    		bits += 8;
    		cntin++;
    		while((bits >= 6) && (cnt < max)) {
    			bits -= 6;
    			/* We want only the top */
    			index = (byte >> bits) & 0x3f;
    			*dst = base64[index];
    #if 0
    			printf("Remove: %c %s\n", *dst, binary(index, 6));
    #endif
    			dst++;
    			cnt++;
    		}
    	}
    	if (bits && (cnt < max)) {
    		/* Add one last character for the remaining bits, 
    		   padding the rest with 0 */
    		byte <<= (6 - bits);
    		index = (byte) & 0x3f;
    		*(dst++) = base64[index];
    		cnt++;
    	}
    	*dst = '\0';
    	return cnt;
    }
    
    static void base64_init(void)
    {
    	int x;
    	memset(b2a, -1, sizeof(b2a));
    	/* Initialize base-64 Conversion table */
    	for (x=0;x<26;x++) {
    		/* A-Z */
    		base64[x] = 'A' + x;
    		b2a['A' + x] = x;
    		/* a-z */
    		base64[x + 26] = 'a' + x;
    		b2a['a' + x] = x + 26;
    		/* 0-9 */
    		if (x < 10) {
    			base64[x + 52] = '0' + x;
    			b2a['0' + x] = x + 52;
    		}
    	}
    	base64[62] = '+';
    	base64[63] = '/';
    	b2a[(int)'+'] = 62;
    	b2a[(int)'/'] = 63;
    #if 0
    	for (x=0;x<64;x++) {
    		if (b2a[(int)base64[x]] != x) {
    			fprintf(stderr, "!!! %d failed\n", x);
    		} else
    			fprintf(stderr, "--- %d passed\n", x);
    	}
    #endif
    }
    
    
    /* Recursive thread safe replacement of inet_ntoa */
    
    const char *ast_inet_ntoa(char *buf, int bufsiz, struct in_addr ia)
    {
    	return inet_ntop(AF_INET, &ia, buf, bufsiz);
    }
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    int ast_utils_init(void)
    {
    	base64_init();
    	return 0;
    }
    
    #ifndef __linux__
    #undef pthread_create /* For ast_pthread_create function only */
    #endif /* ! LINUX */
    
    int ast_pthread_create_stack(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void *data, size_t stacksize)
    
    {
    	pthread_attr_t lattr;
    	if (!attr) {
    		pthread_attr_init(&lattr);
    		attr = &lattr;
    	}
    
    	if (!stacksize)
    		stacksize = AST_STACKSIZE;
    	errno = pthread_attr_setstacksize(attr, stacksize);
    
    	if (errno)
    		ast_log(LOG_WARNING, "pthread_attr_setstacksize returned non-zero: %s\n", strerror(errno));
    	return pthread_create(thread, attr, start_routine, data); /* We're in ast_pthread_create, so it's okay */
    }
    
    int ast_wait_for_input(int fd, int ms)
    {
    	struct pollfd pfd[1];
    	memset(pfd, 0, sizeof(pfd));
    	pfd[0].fd = fd;
    	pfd[0].events = POLLIN|POLLPRI;
    	return poll(pfd, 1, ms);
    }
    
    
    char *ast_strip_quoted(char *s, const char *beg_quotes, const char *end_quotes)
    {
    	char *e;
    	char *q;
    
    	s = ast_strip(s);
    	if ((q = strchr(beg_quotes, *s))) {
    		e = s + strlen(s) - 1;
    		if (*e == *(end_quotes + (q - beg_quotes))) {
    			s++;
    			*e = '\0';
    		}
    	}
    
    	return s;
    }
    
    
    int ast_build_string(char **buffer, size_t *space, const char *fmt, ...)
    {
    	va_list ap;
    	int result;
    
    	if (!buffer || !*buffer || !space || !*space)
    		return -1;
    
    	va_start(ap, fmt);
    	result = vsnprintf(*buffer, *space, fmt, ap);
    	va_end(ap);
    
    	if (result < 0)
    		return -1;
    	else if (result > *space)
    		result = *space;
    
    	*buffer += result;
    	*space -= result;
    	return 0;
    }
    
    
    int ast_true(const char *s)
    {
    	if (!s || ast_strlen_zero(s))
    		return 0;
    
    	/* Determine if this is a true value */
    	if (!strcasecmp(s, "yes") ||
    	    !strcasecmp(s, "true") ||
    	    !strcasecmp(s, "y") ||
    	    !strcasecmp(s, "t") ||
    	    !strcasecmp(s, "1") ||
    	    !strcasecmp(s, "on"))
    		return -1;
    
    	return 0;
    }
    
    int ast_false(const char *s)
    {
    	if (!s || ast_strlen_zero(s))
    		return 0;
    
    	/* Determine if this is a false value */
    	if (!strcasecmp(s, "no") ||
    	    !strcasecmp(s, "false") ||
    	    !strcasecmp(s, "n") ||
    	    !strcasecmp(s, "f") ||
    	    !strcasecmp(s, "0") ||
    	    !strcasecmp(s, "off"))
    		return -1;
    
    	return 0;
    }
    
    
    #define ONE_MILLION	1000000
    /*
     * put timeval in a valid range. usec is 0..999999
     * negative values are not allowed and truncated.
     */
    static struct timeval tvfix(struct timeval a)
    {
    	if (a.tv_usec >= ONE_MILLION) {
    		ast_log(LOG_ERROR, "warning too large timestamp %ld.%ld\n",
    			a.tv_sec, a.tv_usec);
    		a.tv_sec += a.tv_usec % ONE_MILLION;
    		a.tv_usec %= ONE_MILLION;
    	} else if (a.tv_usec < 0) {
    
    		ast_log(LOG_WARNING, "warning negative timestamp %ld.%ld\n",
    
    		a.tv_usec = 0;
    	}
    	return a;
    }
    
    struct timeval ast_tvadd(struct timeval a, struct timeval b)
    {
    	/* consistency checks to guarantee usec in 0..999999 */
    	a = tvfix(a);
    	b = tvfix(b);
    	a.tv_sec += b.tv_sec;
    	a.tv_usec += b.tv_usec;
    	if (a.tv_usec >= ONE_MILLION) {
    		a.tv_sec++;
    		a.tv_usec -= ONE_MILLION;
    	}
    	return a;
    }
    
    struct timeval ast_tvsub(struct timeval a, struct timeval b)
    {
    	/* consistency checks to guarantee usec in 0..999999 */
    	a = tvfix(a);
    	b = tvfix(b);
    	a.tv_sec -= b.tv_sec;
    	a.tv_usec -= b.tv_usec;
    	if (a.tv_usec < 0) {
    		a.tv_sec-- ;
    		a.tv_usec += ONE_MILLION;
    	}
    	return a;
    }
    #undef ONE_MILLION
    
    
    #ifndef HAVE_STRCASESTR
    
    static char *upper(const char *orig, char *buf, int bufsize)
    {
    
    	int i = 0;
    
    	while (i < (bufsize - 1) && orig[i]) {
    
    		buf[i] = toupper(orig[i]);
    
    char *strcasestr(const char *haystack, const char *needle)
    
    	int u1len = strlen(haystack) + 1, u2len = strlen(needle) + 1;
    
    
    	u1 = alloca(u1len);
    	u2 = alloca(u2len);
    	if (u1 && u2) {
    		char *offset;
    		if (u2len > u1len) {
    			/* Needle bigger than haystack */
    			return NULL;
    		}
    		offset = strstr(upper(haystack, u1, u1len), upper(needle, u2, u2len));
    		if (offset) {
    			/* Return the offset into the original string */
    
    			return ((char *)((unsigned long)haystack + (unsigned long)(offset - u1)));
    
    		} else {
    			return NULL;
    		}
    	} else {
    		ast_log(LOG_ERROR, "Out of memory\n");
    		return NULL;
    	}
    }
    
    #ifndef HAVE_STRNLEN
    size_t strnlen(const char *s, size_t n)
    
    {
    	size_t len;
    
    	for (len=0; len < n; len++)
    		if (s[len] == '\0')
    			break;
    
    	return len;
    }
    
    #ifndef HAVE_STRNDUP
    char *strndup(const char *s, size_t n)
    
    	size_t len = strnlen(s, n);
    
    	char *new = malloc(len + 1);
    
    	if (!new)
    		return NULL;
    
    	new[len] = '\0';
    	return memcpy(new, s, len);
    }
    
    #ifndef HAVE_VASPRINTF
    int vasprintf(char **strp, const char *fmt, va_list ap)
    {
    	int size;
    	va_list ap2;
    
    
    	*strp = NULL;
    	va_copy(ap2, ap);
    
    	size = vsnprintf(&s, 1, fmt, ap2);
    
    	va_end(ap2);
    	*strp = malloc(size + 1);
    	if (!*strp)
    		return -1;
    	vsnprintf(*strp, size + 1, fmt, ap);
    
    	return size;
    }
    #endif
    
    
    #ifndef HAVE_STRTOQ
    #define LONG_MIN        (-9223372036854775807L-1L)
                                            /* min value of a "long int" */
    #define LONG_MAX        9223372036854775807L
                                            /* max value of a "long int" */
    
    /*
     * Convert a string to a quad integer.
     *
     * Ignores `locale' stuff.  Assumes that the upper and lower case
     * alphabets and digits are each contiguous.
     */
    uint64_t strtoq(const char *nptr, char **endptr, int base)
    {
            const char *s;
            uint64_t acc;
            unsigned char c;
            uint64_t qbase, cutoff;
            int neg, any, cutlim;
    
            /*
             * Skip white space and pick up leading +/- sign if any.
             * If base is 0, allow 0x for hex and 0 for octal, else
             * assume decimal; if base is already 16, allow 0x.
             */
            s = nptr;
            do {
                    c = *s++;
            } while (isspace(c));
            if (c == '-') {
                    neg = 1;
                    c = *s++;
            } else {
                    neg = 0;
                    if (c == '+')
                            c = *s++;
            }
            if ((base == 0 || base == 16) &&
                c == '\0' && (*s == 'x' || *s == 'X')) {
                    c = s[1];
                    s += 2;
                    base = 16;
            }
            if (base == 0)
                    base = c == '\0' ? 8 : 10;
    
            /*
             * Compute the cutoff value between legal numbers and illegal
             * numbers.  That is the largest legal value, divided by the
             * base.  An input number that is greater than this value, if
             * followed by a legal input character, is too big.  One that
             * is equal to this value may be valid or not; the limit
             * between valid and invalid numbers is then based on the last
             * digit.  For instance, if the range for quads is
             * [-9223372036854775808..9223372036854775807] and the input base
             * is 10, cutoff will be set to 922337203685477580 and cutlim to
             * either 7 (neg==0) or 8 (neg==1), meaning that if we have
             * accumulated a value > 922337203685477580, or equal but the
             * next digit is > 7 (or 8), the number is too big, and we will
             * return a range error.
             *
             * Set any if any `digits' consumed; make it negative to indicate
             * overflow.
             */
            qbase = (unsigned)base;
            cutoff = neg ? (uint64_t)-(LONG_MIN + LONG_MAX) + LONG_MAX : LONG_MAX;
            cutlim = cutoff % qbase;
            cutoff /= qbase;
            for (acc = 0, any = 0;; c = *s++) {
                    if (!isascii(c))
                            break;
                    if (isdigit(c))
                            c -= '\0';
                    else if (isalpha(c))
                            c -= isupper(c) ? 'A' - 10 : 'a' - 10;
                    else
                            break;
                    if (c >= base)
                            break;
                    if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
                            any = -1;
                    else {
                            any = 1;
                            acc *= qbase;
                            acc += c;
                    }
            }
            if (any < 0) {
                    acc = neg ? LONG_MIN : LONG_MAX;
            } else if (neg)
                    acc = -acc;
            if (endptr != 0)
                    *((const char **)endptr) = any ? s - 1 : nptr;
            return (acc);
    }
    #endif