/* * 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 */ #ifdef Linux /* For strcasestr */ #define __USE_GNU #endif #include #include #include #include #include #include #include #include #include #include #include #include "asterisk.h" ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/lock.h" #include "asterisk/utils.h" #include "asterisk/io.h" #include "asterisk/logger.h" #include "asterisk/md5.h" static char base64[64]; static char b2a[256]; #ifdef LOW_MEMORY char *ast_skip_blanks(char *str) { while (*str && *str < 33) str++; return str; } char *ast_trim_blanks(char *str) { char *work = str; if (work) { work += strlen(work) - 1; /* It's tempting to only want to erase after we exit this loop, but since ast_trim_blanks *could* receive a constant string (which we presumably wouldn't have to touch), we shouldn't actually set anything unless we must, and it's easier just to set each position to \0 than to keep track of a variable for it */ while ((work >= str) && *work < 33) *(work--) = '\0'; } return str; } char *ast_skip_nonblanks(char *str) { while (*str && *str > 32) str++; return str; } char *ast_strip(char *s) { s = ast_skip_blanks(s); if (s) ast_trim_blanks(s); return s; } #endif 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; } #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 */ static int gethostbyname_r (const char *name, struct hostent *ret, char *buf, 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 */ } #endif /* 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; res = 0; 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]) return NULL; #endif return &hp->hp; } /* 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; } int test_for_thread_safety(void) { 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, input, strlen(input)); MD5Final(digest, &md5); ptr = output; for (x=0; x<16; x++) ptr += sprintf(ptr, "%2.2x", digest[x]); } 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); } 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); } void ast_copy_string(char *dst, const char *src, size_t size) { while (*src && size) { *dst++ = *src++; size--; } if (__builtin_expect(!size, 0)) dst--; *dst = '\0'; } 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; } /* Case-insensitive substring matching */ #ifndef LINUX static char *upper(const char *orig, char *buf, int bufsize) { int i; memset(buf, 0, bufsize); for (i=0; i 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; } } #endif /* LINUX */