Merge BSD stack size work (bug #2067)
[asterisk/asterisk.git] / utils.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Utility functions
5  *
6  * Copyright (C) 2004, Digium
7  *
8  * This program is free software, distributed under the terms of
9  * the GNU General Public License
10  */
11
12 #include <ctype.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <errno.h>
16 #include <sys/types.h>
17 #include <sys/socket.h>
18 #include <netinet/in.h>
19 #include <arpa/inet.h>
20 #include <asterisk/lock.h>
21 #include <asterisk/utils.h>
22 #include <asterisk/logger.h>
23
24 static char base64[64];
25 static char b2a[256];
26
27 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
28
29 /* duh? ERANGE value copied from web... */
30 #define ERANGE 34
31 #undef gethostbyname
32
33 AST_MUTEX_DEFINE_STATIC(__mutex);
34
35 /* Recursive replacement for gethostbyname for BSD-based systems */
36 static int gethostbyname_r (const char *name, struct hostent *ret, char *buf,
37                                 size_t buflen, struct hostent **result, 
38                                 int *h_errnop) 
39 {
40         int hsave;
41         struct hostent *ph;
42         ast_mutex_lock(&__mutex); /* begin critical area */
43         hsave = h_errno;
44
45         ph = gethostbyname(name);
46         *h_errnop = h_errno; /* copy h_errno to *h_herrnop */
47         if (ph == NULL) {
48                 *result = NULL;
49         } else {
50                 char **p, **q;
51                 char *pbuf;
52                 int nbytes=0;
53                 int naddr=0, naliases=0;
54                 /* determine if we have enough space in buf */
55
56                 /* count how many addresses */
57                 for (p = ph->h_addr_list; *p != 0; p++) {
58                         nbytes += ph->h_length; /* addresses */
59                         nbytes += sizeof(*p); /* pointers */
60                         naddr++;
61                 }
62                 nbytes += sizeof(*p); /* one more for the terminating NULL */
63
64                 /* count how many aliases, and total length of strings */
65                 for (p = ph->h_aliases; *p != 0; p++) {
66                         nbytes += (strlen(*p)+1); /* aliases */
67                         nbytes += sizeof(*p);  /* pointers */
68                         naliases++;
69                 }
70                 nbytes += sizeof(*p); /* one more for the terminating NULL */
71
72                 /* here nbytes is the number of bytes required in buffer */
73                 /* as a terminator must be there, the minimum value is ph->h_length */
74                 if(nbytes > buflen) {
75                         *result = NULL;
76                         ast_mutex_unlock(&__mutex); /* end critical area */
77                         return ERANGE; /* not enough space in buf!! */
78                 }
79
80                 /* There is enough space. Now we need to do a deep copy! */
81                 /* Allocation in buffer:
82                         from [0] to [(naddr-1) * sizeof(*p)]:
83                         pointers to addresses
84                         at [naddr * sizeof(*p)]:
85                         NULL
86                         from [(naddr+1) * sizeof(*p)] to [(naddr+naliases) * sizeof(*p)] :
87                         pointers to aliases
88                         at [(naddr+naliases+1) * sizeof(*p)]:
89                         NULL
90                         then naddr addresses (fixed length), and naliases aliases (asciiz).
91                 */
92
93                 *ret = *ph;   /* copy whole structure (not its address!) */
94
95                 /* copy addresses */
96                 q = (char **)buf; /* pointer to pointers area (type: char **) */
97                 ret->h_addr_list = q; /* update pointer to address list */
98                 pbuf = buf + ((naddr+naliases+2)*sizeof(*p)); /* skip that area */
99                 for (p = ph->h_addr_list; *p != 0; p++) {
100                         memcpy(pbuf, *p, ph->h_length); /* copy address bytes */
101                         *q++ = pbuf; /* the pointer is the one inside buf... */
102                         pbuf += ph->h_length; /* advance pbuf */
103                 }
104                 *q++ = NULL; /* address list terminator */
105
106                 /* copy aliases */
107                 ret->h_aliases = q; /* update pointer to aliases list */
108                 for (p = ph->h_aliases; *p != 0; p++) {
109                         strcpy(pbuf, *p); /* copy alias strings */
110                         *q++ = pbuf; /* the pointer is the one inside buf... */
111                         pbuf += strlen(*p); /* advance pbuf */
112                         *pbuf++ = 0; /* string terminator */
113                 }
114                 *q++ = NULL; /* terminator */
115
116                 strcpy(pbuf, ph->h_name); /* copy alias strings */
117                 ret->h_name = pbuf;
118                 pbuf += strlen(ph->h_name); /* advance pbuf */
119                 *pbuf++ = 0; /* string terminator */
120
121                 *result = ret;  /* and let *result point to structure */
122
123         }
124         h_errno = hsave;  /* restore h_errno */
125         ast_mutex_unlock(&__mutex); /* end critical area */
126
127         return (*result == NULL); /* return 0 on success, non-zero on error */
128 }
129
130
131 #endif
132
133 /* Recursive thread safe version of gethostbyname that replaces the 
134    standard gethostbyname (which is not recursive)
135 */
136 struct hostent *ast_gethostbyname(const char *host, struct ast_hostent *hp)
137 {
138         int res;
139         int herrno;
140         const char *s;
141         struct hostent *result = NULL;
142         /* Although it is perfectly legitimate to lookup a pure integer, for
143            the sake of the sanity of people who like to name their peers as
144            integers, we break with tradition and refuse to look up a
145            pure integer */
146         s = host;
147         while(s && *s) {
148                 if (!isdigit(*s))
149                         break;
150                 s++;
151         }
152         if (!s || !*s)
153                 return NULL;
154         res = gethostbyname_r(host, &hp->hp, hp->buf, sizeof(hp->buf), &result, &herrno);
155
156         if (res || !result || !hp->hp.h_addr_list || !hp->hp.h_addr_list[0])
157                 return NULL;
158         return &hp->hp;
159 }
160
161
162 /* This is a regression test for recursive mutexes.
163    test_for_thread_safety() will return 0 if recursive mutex locks are
164    working properly, and non-zero if they are not working properly. */
165
166 AST_MUTEX_DEFINE_STATIC(test_lock);
167 AST_MUTEX_DEFINE_STATIC(test_lock2);
168 static pthread_t test_thread; 
169 static int lock_count = 0;
170 static int test_errors = 0;
171
172 static void *test_thread_body(void *data) 
173
174         ast_mutex_lock(&test_lock);
175         lock_count += 10;
176         if (lock_count != 10) 
177                 test_errors++;
178         ast_mutex_lock(&test_lock);
179         lock_count += 10;
180         if (lock_count != 20) 
181                 test_errors++;
182         ast_mutex_lock(&test_lock2);
183         ast_mutex_unlock(&test_lock);
184         lock_count -= 10;
185         if (lock_count != 10) 
186                 test_errors++;
187         ast_mutex_unlock(&test_lock);
188         lock_count -= 10;
189         ast_mutex_unlock(&test_lock2);
190         if (lock_count != 0) 
191                 test_errors++;
192         return NULL;
193
194
195 int test_for_thread_safety(void)
196
197         ast_mutex_lock(&test_lock2);
198         ast_mutex_lock(&test_lock);
199         lock_count += 1;
200         ast_mutex_lock(&test_lock);
201         lock_count += 1;
202         ast_pthread_create(&test_thread, NULL, test_thread_body, NULL); 
203         usleep(100);
204         if (lock_count != 2) 
205                 test_errors++;
206         ast_mutex_unlock(&test_lock);
207         lock_count -= 1;
208         usleep(100); 
209         if (lock_count != 1) 
210                 test_errors++;
211         ast_mutex_unlock(&test_lock);
212         lock_count -= 1;
213         if (lock_count != 0) 
214                 test_errors++;
215         ast_mutex_unlock(&test_lock2);
216         usleep(100);
217         if (lock_count != 0) 
218                 test_errors++;
219         pthread_join(test_thread, NULL);
220         return(test_errors);          /* return 0 on success. */
221 }
222
223 int ast_base64decode(unsigned char *dst, char *src, int max)
224 {
225         int cnt = 0;
226         unsigned int byte = 0;
227         unsigned int bits = 0;
228         int incnt = 0;
229 #if 0
230         unsigned char *odst = dst;
231 #endif
232         while(*src && (cnt < max)) {
233                 /* Shift in 6 bits of input */
234                 byte <<= 6;
235                 byte |= (b2a[(int)(*src)]) & 0x3f;
236                 bits += 6;
237 #if 0
238                 printf("Add: %c %s\n", *src, binary(b2a[(int)(*src)] & 0x3f, 6));
239 #endif
240                 src++;
241                 incnt++;
242                 /* If we have at least 8 bits left over, take that character 
243                    off the top */
244                 if (bits >= 8)  {
245                         bits -= 8;
246                         *dst = (byte >> bits) & 0xff;
247 #if 0
248                         printf("Remove: %02x %s\n", *dst, binary(*dst, 8));
249 #endif
250                         dst++;
251                         cnt++;
252                 }
253         }
254 #if 0
255         dump(odst, cnt);
256 #endif
257         /* Dont worry about left over bits, they're extra anyway */
258         return cnt;
259 }
260
261 int ast_base64encode(char *dst, unsigned char *src, int srclen, int max)
262 {
263         int cnt = 0;
264         unsigned int byte = 0;
265         int bits = 0;
266         int index;
267         int cntin = 0;
268 #if 0
269         char *odst = dst;
270         dump(src, srclen);
271 #endif
272         /* Reserve one bit for end */
273         max--;
274         while((cntin < srclen) && (cnt < max)) {
275                 byte <<= 8;
276 #if 0
277                 printf("Add: %02x %s\n", *src, binary(*src, 8));
278 #endif
279                 byte |= *(src++);
280                 bits += 8;
281                 cntin++;
282                 while((bits >= 6) && (cnt < max)) {
283                         bits -= 6;
284                         /* We want only the top */
285                         index = (byte >> bits) & 0x3f;
286                         *dst = base64[index];
287 #if 0
288                         printf("Remove: %c %s\n", *dst, binary(index, 6));
289 #endif
290                         dst++;
291                         cnt++;
292                 }
293         }
294         if (bits && (cnt < max)) {
295                 /* Add one last character for the remaining bits, 
296                    padding the rest with 0 */
297                 byte <<= (6 - bits);
298                 index = (byte) & 0x3f;
299                 *(dst++) = base64[index];
300                 cnt++;
301         }
302         *dst = '\0';
303         return cnt;
304 }
305
306 static void base64_init(void)
307 {
308         int x;
309         memset(b2a, -1, sizeof(b2a));
310         /* Initialize base-64 Conversion table */
311         for (x=0;x<26;x++) {
312                 /* A-Z */
313                 base64[x] = 'A' + x;
314                 b2a['A' + x] = x;
315                 /* a-z */
316                 base64[x + 26] = 'a' + x;
317                 b2a['a' + x] = x + 26;
318                 /* 0-9 */
319                 if (x < 10) {
320                         base64[x + 52] = '0' + x;
321                         b2a['0' + x] = x + 52;
322                 }
323         }
324         base64[62] = '+';
325         base64[63] = '/';
326         b2a[(int)'+'] = 62;
327         b2a[(int)'/'] = 63;
328 #if 0
329         for (x=0;x<64;x++) {
330                 if (b2a[(int)base64[x]] != x) {
331                         fprintf(stderr, "!!! %d failed\n", x);
332                 } else
333                         fprintf(stderr, "--- %d passed\n", x);
334         }
335 #endif
336 }
337
338 /* Recursive thread safe replacement of inet_ntoa */
339 const char *ast_inet_ntoa(char *buf, int bufsiz, struct in_addr ia)
340 {
341         return inet_ntop(AF_INET, &ia, buf, bufsiz);
342 }
343
344 int ast_utils_init(void)
345 {
346         base64_init();
347         return 0;
348 }
349
350
351 #ifndef LINUX
352 #undef pthread_create /* For ast_pthread_create function only */
353 int ast_pthread_create(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *), void *data)
354 {
355         pthread_attr_t lattr;
356         if (!attr) {
357                 pthread_attr_init(&lattr);
358                 attr = &lattr;
359         }
360         errno = pthread_attr_setstacksize(attr, PTHREAD_ATTR_STACKSIZE);
361         if (errno)
362                 ast_log(LOG_WARNING, "pthread_attr_setstacksize returned non-zero: %s\n", strerror(errno));
363         return pthread_create(thread, attr, start_routine, data); /* We're in ast_pthread_create, so it's okay */
364 }
365 #endif /* ! LINUX */