Merge lp:~davewalker/ubuntu/maverick/asterisk/lp_705014 into lp:ubuntu/maverick/asterisk

Proposed by Dave Walker
Status: Needs review
Proposed branch: lp:~davewalker/ubuntu/maverick/asterisk/lp_705014
Merge into: lp:ubuntu/maverick/asterisk
Diff against target: 1986 lines (+1908/-15)
8 files modified
.pc/.quilt_patches (+1/-0)
.pc/.quilt_series (+1/-0)
.pc/AST-2011-001-1.6.2/main/utils.c (+1828/-0)
.pc/applied-patches (+1/-0)
debian/changelog (+10/-0)
debian/patches/AST-2011-001-1.6.2 (+52/-0)
debian/patches/series (+1/-0)
main/utils.c (+14/-15)
To merge this branch: bzr merge lp:~davewalker/ubuntu/maverick/asterisk/lp_705014
Reviewer Review Type Date Requested Status
Jamie Strandboge Approve
Review via email: mp+46999@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Jamie Strandboge (jdstrand) wrote :

Thanks!

review: Approve
Revision history for this message
Jamie Strandboge (jdstrand) wrote :

I did notice a trailing space in the series file, but have fixed that for the upload.

Unmerged revisions

58. By Dave Walker

* SECURITY UPDATE: Stack buffer overflow in SIP channel driver. (LP: #705014)
  - debian/patches/AST-2011-001-1.6.2: The size of the output buffer passed
    to the ast_uri_encode function is now properly respected in main/utils.c.
    Patch courtesy of upstream.
  - CVE-2011-0495

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file '.pc/.quilt_patches'
2--- .pc/.quilt_patches 1970-01-01 00:00:00 +0000
3+++ .pc/.quilt_patches 2011-01-20 23:45:56 +0000
4@@ -0,0 +1,1 @@
5+debian/patches
6
7=== added file '.pc/.quilt_series'
8--- .pc/.quilt_series 1970-01-01 00:00:00 +0000
9+++ .pc/.quilt_series 2011-01-20 23:45:56 +0000
10@@ -0,0 +1,1 @@
11+series
12
13=== added directory '.pc/AST-2011-001-1.6.2'
14=== added file '.pc/AST-2011-001-1.6.2/.timestamp'
15=== added directory '.pc/AST-2011-001-1.6.2/main'
16=== added file '.pc/AST-2011-001-1.6.2/main/utils.c'
17--- .pc/AST-2011-001-1.6.2/main/utils.c 1970-01-01 00:00:00 +0000
18+++ .pc/AST-2011-001-1.6.2/main/utils.c 2011-01-20 23:45:56 +0000
19@@ -0,0 +1,1828 @@
20+/*
21+ * Asterisk -- An open source telephony toolkit.
22+ *
23+ * Copyright (C) 1999 - 2006, Digium, Inc.
24+ *
25+ * See http://www.asterisk.org for more information about
26+ * the Asterisk project. Please do not directly contact
27+ * any of the maintainers of this project for assistance;
28+ * the project provides a web site, mailing lists and IRC
29+ * channels for your use.
30+ *
31+ * This program is free software, distributed under the terms of
32+ * the GNU General Public License Version 2. See the LICENSE file
33+ * at the top of the source tree.
34+ */
35+
36+/*! \file
37+ *
38+ * \brief Utility functions
39+ *
40+ * \note These are important for portability and security,
41+ * so please use them in favour of other routines.
42+ * Please consult the CODING GUIDELINES for more information.
43+ */
44+
45+#include "asterisk.h"
46+
47+ASTERISK_FILE_VERSION(__FILE__, "$Revision: 237743 $")
48+
49+#include <ctype.h>
50+#include <sys/stat.h>
51+
52+#ifdef HAVE_DEV_URANDOM
53+#include <fcntl.h>
54+#endif
55+
56+#include "asterisk/network.h"
57+
58+#define AST_API_MODULE /* ensure that inlinable API functions will be built in lock.h if required */
59+#include "asterisk/lock.h"
60+#include "asterisk/io.h"
61+#include "asterisk/md5.h"
62+#include "asterisk/sha1.h"
63+#include "asterisk/cli.h"
64+#include "asterisk/linkedlists.h"
65+
66+#define AST_API_MODULE /* ensure that inlinable API functions will be built in this module if required */
67+#include "asterisk/strings.h"
68+
69+#define AST_API_MODULE /* ensure that inlinable API functions will be built in this module if required */
70+#include "asterisk/time.h"
71+
72+#define AST_API_MODULE /* ensure that inlinable API functions will be built in this module if required */
73+#include "asterisk/stringfields.h"
74+
75+#define AST_API_MODULE /* ensure that inlinable API functions will be built in this module if required */
76+#include "asterisk/utils.h"
77+
78+#define AST_API_MODULE
79+#include "asterisk/threadstorage.h"
80+
81+#define AST_API_MODULE
82+#include "asterisk/config.h"
83+
84+static char base64[64];
85+static char b2a[256];
86+
87+AST_THREADSTORAGE(inet_ntoa_buf);
88+
89+#if !defined(HAVE_GETHOSTBYNAME_R_5) && !defined(HAVE_GETHOSTBYNAME_R_6)
90+
91+#define ERANGE 34 /*!< duh? ERANGE value copied from web... */
92+#undef gethostbyname
93+
94+AST_MUTEX_DEFINE_STATIC(__mutex);
95+
96+/*! \brief Reentrant replacement for gethostbyname for BSD-based systems.
97+\note This
98+routine is derived from code originally written and placed in the public
99+domain by Enzo Michelangeli <em@em.no-ip.com> */
100+
101+static int gethostbyname_r (const char *name, struct hostent *ret, char *buf,
102+ size_t buflen, struct hostent **result,
103+ int *h_errnop)
104+{
105+ int hsave;
106+ struct hostent *ph;
107+ ast_mutex_lock(&__mutex); /* begin critical area */
108+ hsave = h_errno;
109+
110+ ph = gethostbyname(name);
111+ *h_errnop = h_errno; /* copy h_errno to *h_herrnop */
112+ if (ph == NULL) {
113+ *result = NULL;
114+ } else {
115+ char **p, **q;
116+ char *pbuf;
117+ int nbytes = 0;
118+ int naddr = 0, naliases = 0;
119+ /* determine if we have enough space in buf */
120+
121+ /* count how many addresses */
122+ for (p = ph->h_addr_list; *p != 0; p++) {
123+ nbytes += ph->h_length; /* addresses */
124+ nbytes += sizeof(*p); /* pointers */
125+ naddr++;
126+ }
127+ nbytes += sizeof(*p); /* one more for the terminating NULL */
128+
129+ /* count how many aliases, and total length of strings */
130+ for (p = ph->h_aliases; *p != 0; p++) {
131+ nbytes += (strlen(*p)+1); /* aliases */
132+ nbytes += sizeof(*p); /* pointers */
133+ naliases++;
134+ }
135+ nbytes += sizeof(*p); /* one more for the terminating NULL */
136+
137+ /* here nbytes is the number of bytes required in buffer */
138+ /* as a terminator must be there, the minimum value is ph->h_length */
139+ if (nbytes > buflen) {
140+ *result = NULL;
141+ ast_mutex_unlock(&__mutex); /* end critical area */
142+ return ERANGE; /* not enough space in buf!! */
143+ }
144+
145+ /* There is enough space. Now we need to do a deep copy! */
146+ /* Allocation in buffer:
147+ from [0] to [(naddr-1) * sizeof(*p)]:
148+ pointers to addresses
149+ at [naddr * sizeof(*p)]:
150+ NULL
151+ from [(naddr+1) * sizeof(*p)] to [(naddr+naliases) * sizeof(*p)] :
152+ pointers to aliases
153+ at [(naddr+naliases+1) * sizeof(*p)]:
154+ NULL
155+ then naddr addresses (fixed length), and naliases aliases (asciiz).
156+ */
157+
158+ *ret = *ph; /* copy whole structure (not its address!) */
159+
160+ /* copy addresses */
161+ q = (char **)buf; /* pointer to pointers area (type: char **) */
162+ ret->h_addr_list = q; /* update pointer to address list */
163+ pbuf = buf + ((naddr + naliases + 2) * sizeof(*p)); /* skip that area */
164+ for (p = ph->h_addr_list; *p != 0; p++) {
165+ memcpy(pbuf, *p, ph->h_length); /* copy address bytes */
166+ *q++ = pbuf; /* the pointer is the one inside buf... */
167+ pbuf += ph->h_length; /* advance pbuf */
168+ }
169+ *q++ = NULL; /* address list terminator */
170+
171+ /* copy aliases */
172+ ret->h_aliases = q; /* update pointer to aliases list */
173+ for (p = ph->h_aliases; *p != 0; p++) {
174+ strcpy(pbuf, *p); /* copy alias strings */
175+ *q++ = pbuf; /* the pointer is the one inside buf... */
176+ pbuf += strlen(*p); /* advance pbuf */
177+ *pbuf++ = 0; /* string terminator */
178+ }
179+ *q++ = NULL; /* terminator */
180+
181+ strcpy(pbuf, ph->h_name); /* copy alias strings */
182+ ret->h_name = pbuf;
183+ pbuf += strlen(ph->h_name); /* advance pbuf */
184+ *pbuf++ = 0; /* string terminator */
185+
186+ *result = ret; /* and let *result point to structure */
187+
188+ }
189+ h_errno = hsave; /* restore h_errno */
190+ ast_mutex_unlock(&__mutex); /* end critical area */
191+
192+ return (*result == NULL); /* return 0 on success, non-zero on error */
193+}
194+
195+
196+#endif
197+
198+/*! \brief Re-entrant (thread safe) version of gethostbyname that replaces the
199+ standard gethostbyname (which is not thread safe)
200+*/
201+struct hostent *ast_gethostbyname(const char *host, struct ast_hostent *hp)
202+{
203+ int res;
204+ int herrno;
205+ int dots = 0;
206+ const char *s;
207+ struct hostent *result = NULL;
208+ /* Although it is perfectly legitimate to lookup a pure integer, for
209+ the sake of the sanity of people who like to name their peers as
210+ integers, we break with tradition and refuse to look up a
211+ pure integer */
212+ s = host;
213+ res = 0;
214+ while (s && *s) {
215+ if (*s == '.')
216+ dots++;
217+ else if (!isdigit(*s))
218+ break;
219+ s++;
220+ }
221+ if (!s || !*s) {
222+ /* Forge a reply for IP's to avoid octal IP's being interpreted as octal */
223+ if (dots != 3)
224+ return NULL;
225+ memset(hp, 0, sizeof(struct ast_hostent));
226+ hp->hp.h_addrtype = AF_INET;
227+ hp->hp.h_addr_list = (void *) hp->buf;
228+ hp->hp.h_addr = hp->buf + sizeof(void *);
229+ if (inet_pton(AF_INET, host, hp->hp.h_addr) > 0)
230+ return &hp->hp;
231+ return NULL;
232+
233+ }
234+#ifdef HAVE_GETHOSTBYNAME_R_5
235+ result = gethostbyname_r(host, &hp->hp, hp->buf, sizeof(hp->buf), &herrno);
236+
237+ if (!result || !hp->hp.h_addr_list || !hp->hp.h_addr_list[0])
238+ return NULL;
239+#else
240+ res = gethostbyname_r(host, &hp->hp, hp->buf, sizeof(hp->buf), &result, &herrno);
241+
242+ if (res || !result || !hp->hp.h_addr_list || !hp->hp.h_addr_list[0])
243+ return NULL;
244+#endif
245+ return &hp->hp;
246+}
247+
248+/*! \brief Produce 32 char MD5 hash of value. */
249+void ast_md5_hash(char *output, char *input)
250+{
251+ struct MD5Context md5;
252+ unsigned char digest[16];
253+ char *ptr;
254+ int x;
255+
256+ MD5Init(&md5);
257+ MD5Update(&md5, (unsigned char *)input, strlen(input));
258+ MD5Final(digest, &md5);
259+ ptr = output;
260+ for (x = 0; x < 16; x++)
261+ ptr += sprintf(ptr, "%2.2x", digest[x]);
262+}
263+
264+/*! \brief Produce 40 char SHA1 hash of value. */
265+void ast_sha1_hash(char *output, char *input)
266+{
267+ struct SHA1Context sha;
268+ char *ptr;
269+ int x;
270+ uint8_t Message_Digest[20];
271+
272+ SHA1Reset(&sha);
273+
274+ SHA1Input(&sha, (const unsigned char *) input, strlen(input));
275+
276+ SHA1Result(&sha, Message_Digest);
277+ ptr = output;
278+ for (x = 0; x < 20; x++)
279+ ptr += sprintf(ptr, "%2.2x", Message_Digest[x]);
280+}
281+
282+/*! \brief decode BASE64 encoded text */
283+int ast_base64decode(unsigned char *dst, const char *src, int max)
284+{
285+ int cnt = 0;
286+ unsigned int byte = 0;
287+ unsigned int bits = 0;
288+ int incnt = 0;
289+ while(*src && *src != '=' && (cnt < max)) {
290+ /* Shift in 6 bits of input */
291+ byte <<= 6;
292+ byte |= (b2a[(int)(*src)]) & 0x3f;
293+ bits += 6;
294+ src++;
295+ incnt++;
296+ /* If we have at least 8 bits left over, take that character
297+ off the top */
298+ if (bits >= 8) {
299+ bits -= 8;
300+ *dst = (byte >> bits) & 0xff;
301+ dst++;
302+ cnt++;
303+ }
304+ }
305+ /* Dont worry about left over bits, they're extra anyway */
306+ return cnt;
307+}
308+
309+/*! \brief encode text to BASE64 coding */
310+int ast_base64encode_full(char *dst, const unsigned char *src, int srclen, int max, int linebreaks)
311+{
312+ int cnt = 0;
313+ int col = 0;
314+ unsigned int byte = 0;
315+ int bits = 0;
316+ int cntin = 0;
317+ /* Reserve space for null byte at end of string */
318+ max--;
319+ while ((cntin < srclen) && (cnt < max)) {
320+ byte <<= 8;
321+ byte |= *(src++);
322+ bits += 8;
323+ cntin++;
324+ if ((bits == 24) && (cnt + 4 <= max)) {
325+ *dst++ = base64[(byte >> 18) & 0x3f];
326+ *dst++ = base64[(byte >> 12) & 0x3f];
327+ *dst++ = base64[(byte >> 6) & 0x3f];
328+ *dst++ = base64[byte & 0x3f];
329+ cnt += 4;
330+ col += 4;
331+ bits = 0;
332+ byte = 0;
333+ }
334+ if (linebreaks && (cnt < max) && (col == 64)) {
335+ *dst++ = '\n';
336+ cnt++;
337+ col = 0;
338+ }
339+ }
340+ if (bits && (cnt + 4 <= max)) {
341+ /* Add one last character for the remaining bits,
342+ padding the rest with 0 */
343+ byte <<= 24 - bits;
344+ *dst++ = base64[(byte >> 18) & 0x3f];
345+ *dst++ = base64[(byte >> 12) & 0x3f];
346+ if (bits == 16)
347+ *dst++ = base64[(byte >> 6) & 0x3f];
348+ else
349+ *dst++ = '=';
350+ *dst++ = '=';
351+ cnt += 4;
352+ }
353+ if (linebreaks && (cnt < max)) {
354+ *dst++ = '\n';
355+ cnt++;
356+ }
357+ *dst = '\0';
358+ return cnt;
359+}
360+
361+int ast_base64encode(char *dst, const unsigned char *src, int srclen, int max)
362+{
363+ return ast_base64encode_full(dst, src, srclen, max, 0);
364+}
365+
366+static void base64_init(void)
367+{
368+ int x;
369+ memset(b2a, -1, sizeof(b2a));
370+ /* Initialize base-64 Conversion table */
371+ for (x = 0; x < 26; x++) {
372+ /* A-Z */
373+ base64[x] = 'A' + x;
374+ b2a['A' + x] = x;
375+ /* a-z */
376+ base64[x + 26] = 'a' + x;
377+ b2a['a' + x] = x + 26;
378+ /* 0-9 */
379+ if (x < 10) {
380+ base64[x + 52] = '0' + x;
381+ b2a['0' + x] = x + 52;
382+ }
383+ }
384+ base64[62] = '+';
385+ base64[63] = '/';
386+ b2a[(int)'+'] = 62;
387+ b2a[(int)'/'] = 63;
388+}
389+
390+/*! \brief ast_uri_encode: Turn text string to URI-encoded %XX version
391+\note At this point, we're converting from ISO-8859-x (8-bit), not UTF8
392+ as in the SIP protocol spec
393+ If doreserved == 1 we will convert reserved characters also.
394+ RFC 2396, section 2.4
395+ outbuf needs to have more memory allocated than the instring
396+ to have room for the expansion. Every char that is converted
397+ is replaced by three ASCII characters.
398+
399+ Note: The doreserved option is needed for replaces header in
400+ SIP transfers.
401+*/
402+char *ast_uri_encode(const char *string, char *outbuf, int buflen, int doreserved)
403+{
404+ char *reserved = ";/?:@&=+$,# "; /* Reserved chars */
405+
406+ const char *ptr = string; /* Start with the string */
407+ char *out = NULL;
408+ char *buf = NULL;
409+
410+ ast_copy_string(outbuf, string, buflen);
411+
412+ /* If there's no characters to convert, just go through and don't do anything */
413+ while (*ptr) {
414+ if ((*ptr < 32) || (doreserved && strchr(reserved, *ptr))) {
415+ /* Oops, we need to start working here */
416+ if (!buf) {
417+ buf = outbuf;
418+ out = buf + (ptr - string) ; /* Set output ptr */
419+ }
420+ out += sprintf(out, "%%%02x", (unsigned char) *ptr);
421+ } else if (buf) {
422+ *out = *ptr; /* Continue copying the string */
423+ out++;
424+ }
425+ ptr++;
426+ }
427+ if (buf)
428+ *out = '\0';
429+ return outbuf;
430+}
431+
432+/*! \brief ast_uri_decode: Decode SIP URI, URN, URL (overwrite the string) */
433+void ast_uri_decode(char *s)
434+{
435+ char *o;
436+ unsigned int tmp;
437+
438+ for (o = s; *s; s++, o++) {
439+ if (*s == '%' && strlen(s) > 2 && sscanf(s + 1, "%2x", &tmp) == 1) {
440+ /* have '%', two chars and correct parsing */
441+ *o = tmp;
442+ s += 2; /* Will be incremented once more when we break out */
443+ } else /* all other cases, just copy */
444+ *o = *s;
445+ }
446+ *o = '\0';
447+}
448+
449+/*! \brief ast_inet_ntoa: Recursive thread safe replacement of inet_ntoa */
450+const char *ast_inet_ntoa(struct in_addr ia)
451+{
452+ char *buf;
453+
454+ if (!(buf = ast_threadstorage_get(&inet_ntoa_buf, INET_ADDRSTRLEN)))
455+ return "";
456+
457+ return inet_ntop(AF_INET, &ia, buf, INET_ADDRSTRLEN);
458+}
459+
460+#ifdef HAVE_DEV_URANDOM
461+static int dev_urandom_fd;
462+#endif
463+
464+#ifndef __linux__
465+#undef pthread_create /* For ast_pthread_create function only */
466+#endif /* !__linux__ */
467+
468+#if !defined(LOW_MEMORY)
469+
470+#ifdef DEBUG_THREADS
471+
472+/*! \brief A reasonable maximum number of locks a thread would be holding ... */
473+#define AST_MAX_LOCKS 64
474+
475+/* Allow direct use of pthread_mutex_t and friends */
476+#undef pthread_mutex_t
477+#undef pthread_mutex_lock
478+#undef pthread_mutex_unlock
479+#undef pthread_mutex_init
480+#undef pthread_mutex_destroy
481+
482+/*!
483+ * \brief Keep track of which locks a thread holds
484+ *
485+ * There is an instance of this struct for every active thread
486+ */
487+struct thr_lock_info {
488+ /*! The thread's ID */
489+ pthread_t thread_id;
490+ /*! The thread name which includes where the thread was started */
491+ const char *thread_name;
492+ /*! This is the actual container of info for what locks this thread holds */
493+ struct {
494+ const char *file;
495+ int line_num;
496+ const char *func;
497+ const char *lock_name;
498+ void *lock_addr;
499+ int times_locked;
500+ enum ast_lock_type type;
501+ /*! This thread is waiting on this lock */
502+ int pending:2;
503+#ifdef HAVE_BKTR
504+ struct ast_bt *backtrace;
505+#endif
506+ } locks[AST_MAX_LOCKS];
507+ /*! This is the number of locks currently held by this thread.
508+ * The index (num_locks - 1) has the info on the last one in the
509+ * locks member */
510+ unsigned int num_locks;
511+ /*! Protects the contents of the locks member
512+ * Intentionally not ast_mutex_t */
513+ pthread_mutex_t lock;
514+ AST_LIST_ENTRY(thr_lock_info) entry;
515+};
516+
517+/*!
518+ * \brief Locked when accessing the lock_infos list
519+ */
520+AST_MUTEX_DEFINE_STATIC(lock_infos_lock);
521+/*!
522+ * \brief A list of each thread's lock info
523+ */
524+static AST_LIST_HEAD_NOLOCK_STATIC(lock_infos, thr_lock_info);
525+
526+/*!
527+ * \brief Destroy a thread's lock info
528+ *
529+ * This gets called automatically when the thread stops
530+ */
531+static void lock_info_destroy(void *data)
532+{
533+ struct thr_lock_info *lock_info = data;
534+ int i;
535+
536+ pthread_mutex_lock(&lock_infos_lock.mutex);
537+ AST_LIST_REMOVE(&lock_infos, lock_info, entry);
538+ pthread_mutex_unlock(&lock_infos_lock.mutex);
539+
540+
541+ for (i = 0; i < lock_info->num_locks; i++) {
542+ if (lock_info->locks[i].pending == -1) {
543+ /* This just means that the last lock this thread went for was by
544+ * using trylock, and it failed. This is fine. */
545+ break;
546+ }
547+
548+ ast_log(LOG_ERROR,
549+ "Thread '%s' still has a lock! - '%s' (%p) from '%s' in %s:%d!\n",
550+ lock_info->thread_name,
551+ lock_info->locks[i].lock_name,
552+ lock_info->locks[i].lock_addr,
553+ lock_info->locks[i].func,
554+ lock_info->locks[i].file,
555+ lock_info->locks[i].line_num
556+ );
557+ }
558+
559+ pthread_mutex_destroy(&lock_info->lock);
560+ if (lock_info->thread_name)
561+ free((void *) lock_info->thread_name);
562+ free(lock_info);
563+}
564+
565+/*!
566+ * \brief The thread storage key for per-thread lock info
567+ */
568+AST_THREADSTORAGE_CUSTOM(thread_lock_info, NULL, lock_info_destroy);
569+#ifdef HAVE_BKTR
570+void ast_store_lock_info(enum ast_lock_type type, const char *filename,
571+ int line_num, const char *func, const char *lock_name, void *lock_addr, struct ast_bt *bt)
572+#else
573+void ast_store_lock_info(enum ast_lock_type type, const char *filename,
574+ int line_num, const char *func, const char *lock_name, void *lock_addr)
575+#endif
576+{
577+ struct thr_lock_info *lock_info;
578+ int i;
579+
580+ if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
581+ return;
582+
583+ pthread_mutex_lock(&lock_info->lock);
584+
585+ for (i = 0; i < lock_info->num_locks; i++) {
586+ if (lock_info->locks[i].lock_addr == lock_addr) {
587+ lock_info->locks[i].times_locked++;
588+#ifdef HAVE_BKTR
589+ lock_info->locks[i].backtrace = bt;
590+#endif
591+ pthread_mutex_unlock(&lock_info->lock);
592+ return;
593+ }
594+ }
595+
596+ if (lock_info->num_locks == AST_MAX_LOCKS) {
597+ /* Can't use ast_log here, because it will cause infinite recursion */
598+ fprintf(stderr, "XXX ERROR XXX A thread holds more locks than '%d'."
599+ " Increase AST_MAX_LOCKS!\n", AST_MAX_LOCKS);
600+ pthread_mutex_unlock(&lock_info->lock);
601+ return;
602+ }
603+
604+ if (i && lock_info->locks[i - 1].pending == -1) {
605+ /* The last lock on the list was one that this thread tried to lock but
606+ * failed at doing so. It has now moved on to something else, so remove
607+ * the old lock from the list. */
608+ i--;
609+ lock_info->num_locks--;
610+ memset(&lock_info->locks[i], 0, sizeof(lock_info->locks[0]));
611+ }
612+
613+ lock_info->locks[i].file = filename;
614+ lock_info->locks[i].line_num = line_num;
615+ lock_info->locks[i].func = func;
616+ lock_info->locks[i].lock_name = lock_name;
617+ lock_info->locks[i].lock_addr = lock_addr;
618+ lock_info->locks[i].times_locked = 1;
619+ lock_info->locks[i].type = type;
620+ lock_info->locks[i].pending = 1;
621+#ifdef HAVE_BKTR
622+ lock_info->locks[i].backtrace = bt;
623+#endif
624+ lock_info->num_locks++;
625+
626+ pthread_mutex_unlock(&lock_info->lock);
627+}
628+
629+void ast_mark_lock_acquired(void *lock_addr)
630+{
631+ struct thr_lock_info *lock_info;
632+
633+ if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
634+ return;
635+
636+ pthread_mutex_lock(&lock_info->lock);
637+ if (lock_info->locks[lock_info->num_locks - 1].lock_addr == lock_addr) {
638+ lock_info->locks[lock_info->num_locks - 1].pending = 0;
639+ }
640+ pthread_mutex_unlock(&lock_info->lock);
641+}
642+
643+void ast_mark_lock_failed(void *lock_addr)
644+{
645+ struct thr_lock_info *lock_info;
646+
647+ if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
648+ return;
649+
650+ pthread_mutex_lock(&lock_info->lock);
651+ if (lock_info->locks[lock_info->num_locks - 1].lock_addr == lock_addr) {
652+ lock_info->locks[lock_info->num_locks - 1].pending = -1;
653+ lock_info->locks[lock_info->num_locks - 1].times_locked--;
654+ }
655+ pthread_mutex_unlock(&lock_info->lock);
656+}
657+
658+int ast_find_lock_info(void *lock_addr, char *filename, size_t filename_size, int *lineno, char *func, size_t func_size, char *mutex_name, size_t mutex_name_size)
659+{
660+ struct thr_lock_info *lock_info;
661+ int i = 0;
662+
663+ if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
664+ return -1;
665+
666+ pthread_mutex_lock(&lock_info->lock);
667+
668+ for (i = lock_info->num_locks - 1; i >= 0; i--) {
669+ if (lock_info->locks[i].lock_addr == lock_addr)
670+ break;
671+ }
672+
673+ if (i == -1) {
674+ /* Lock not found :( */
675+ pthread_mutex_unlock(&lock_info->lock);
676+ return -1;
677+ }
678+
679+ ast_copy_string(filename, lock_info->locks[i].file, filename_size);
680+ *lineno = lock_info->locks[i].line_num;
681+ ast_copy_string(func, lock_info->locks[i].func, func_size);
682+ ast_copy_string(mutex_name, lock_info->locks[i].lock_name, mutex_name_size);
683+
684+ pthread_mutex_unlock(&lock_info->lock);
685+
686+ return 0;
687+}
688+
689+#ifdef HAVE_BKTR
690+void ast_remove_lock_info(void *lock_addr, struct ast_bt *bt)
691+#else
692+void ast_remove_lock_info(void *lock_addr)
693+#endif
694+{
695+ struct thr_lock_info *lock_info;
696+ int i = 0;
697+
698+ if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
699+ return;
700+
701+ pthread_mutex_lock(&lock_info->lock);
702+
703+ for (i = lock_info->num_locks - 1; i >= 0; i--) {
704+ if (lock_info->locks[i].lock_addr == lock_addr)
705+ break;
706+ }
707+
708+ if (i == -1) {
709+ /* Lock not found :( */
710+ pthread_mutex_unlock(&lock_info->lock);
711+ return;
712+ }
713+
714+ if (lock_info->locks[i].times_locked > 1) {
715+ lock_info->locks[i].times_locked--;
716+#ifdef HAVE_BKTR
717+ lock_info->locks[i].backtrace = bt;
718+#endif
719+ pthread_mutex_unlock(&lock_info->lock);
720+ return;
721+ }
722+
723+ if (i < lock_info->num_locks - 1) {
724+ /* Not the last one ... *should* be rare! */
725+ memmove(&lock_info->locks[i], &lock_info->locks[i + 1],
726+ (lock_info->num_locks - (i + 1)) * sizeof(lock_info->locks[0]));
727+ }
728+
729+ lock_info->num_locks--;
730+
731+ pthread_mutex_unlock(&lock_info->lock);
732+}
733+
734+static const char *locktype2str(enum ast_lock_type type)
735+{
736+ switch (type) {
737+ case AST_MUTEX:
738+ return "MUTEX";
739+ case AST_RDLOCK:
740+ return "RDLOCK";
741+ case AST_WRLOCK:
742+ return "WRLOCK";
743+ }
744+
745+ return "UNKNOWN";
746+}
747+
748+#ifdef HAVE_BKTR
749+static void append_backtrace_information(struct ast_str **str, struct ast_bt *bt)
750+{
751+ char **symbols;
752+
753+ if (!bt) {
754+ ast_str_append(str, 0, "\tNo backtrace to print\n");
755+ return;
756+ }
757+
758+ if ((symbols = backtrace_symbols(bt->addresses, bt->num_frames))) {
759+ int frame_iterator;
760+
761+ for (frame_iterator = 0; frame_iterator < bt->num_frames; ++frame_iterator) {
762+ ast_str_append(str, 0, "\t%s\n", symbols[frame_iterator]);
763+ }
764+
765+ free(symbols);
766+ } else {
767+ ast_str_append(str, 0, "\tCouldn't retrieve backtrace symbols\n");
768+ }
769+}
770+#endif
771+
772+static void append_lock_information(struct ast_str **str, struct thr_lock_info *lock_info, int i)
773+{
774+ int j;
775+ ast_mutex_t *lock;
776+ struct ast_lock_track *lt;
777+
778+ ast_str_append(str, 0, "=== ---> %sLock #%d (%s): %s %d %s %s %p (%d)\n",
779+ lock_info->locks[i].pending > 0 ? "Waiting for " :
780+ lock_info->locks[i].pending < 0 ? "Tried and failed to get " : "", i,
781+ lock_info->locks[i].file,
782+ locktype2str(lock_info->locks[i].type),
783+ lock_info->locks[i].line_num,
784+ lock_info->locks[i].func, lock_info->locks[i].lock_name,
785+ lock_info->locks[i].lock_addr,
786+ lock_info->locks[i].times_locked);
787+#ifdef HAVE_BKTR
788+ append_backtrace_information(str, lock_info->locks[i].backtrace);
789+#endif
790+
791+ if (!lock_info->locks[i].pending || lock_info->locks[i].pending == -1)
792+ return;
793+
794+ /* We only have further details for mutexes right now */
795+ if (lock_info->locks[i].type != AST_MUTEX)
796+ return;
797+
798+ lock = lock_info->locks[i].lock_addr;
799+ lt = &lock->track;
800+ ast_reentrancy_lock(lt);
801+ for (j = 0; *str && j < lt->reentrancy; j++) {
802+ ast_str_append(str, 0, "=== --- ---> Locked Here: %s line %d (%s)\n",
803+ lt->file[j], lt->lineno[j], lt->func[j]);
804+ }
805+ ast_reentrancy_unlock(lt);
806+}
807+
808+
809+/*! This function can help you find highly temporal locks; locks that happen for a
810+ short time, but at unexpected times, usually at times that create a deadlock,
811+ Why is this thing locked right then? Who is locking it? Who am I fighting
812+ with for this lock?
813+
814+ To answer such questions, just call this routine before you would normally try
815+ to aquire a lock. It doesn't do anything if the lock is not acquired. If the
816+ lock is taken, it will publish a line or two to the console via ast_log().
817+
818+ Sometimes, the lock message is pretty uninformative. For instance, you might
819+ find that the lock is being aquired deep within the astobj2 code; this tells
820+ you little about higher level routines that call the astobj2 routines.
821+ But, using gdb, you can set a break at the ast_log below, and for that
822+ breakpoint, you can set the commands:
823+ where
824+ cont
825+ which will give a stack trace and continue. -- that aught to do the job!
826+
827+*/
828+void log_show_lock(void *this_lock_addr)
829+{
830+ struct thr_lock_info *lock_info;
831+ struct ast_str *str;
832+
833+ if (!(str = ast_str_create(4096))) {
834+ ast_log(LOG_NOTICE,"Could not create str\n");
835+ return;
836+ }
837+
838+
839+ pthread_mutex_lock(&lock_infos_lock.mutex);
840+ AST_LIST_TRAVERSE(&lock_infos, lock_info, entry) {
841+ int i;
842+ pthread_mutex_lock(&lock_info->lock);
843+ for (i = 0; str && i < lock_info->num_locks; i++) {
844+ /* ONLY show info about this particular lock, if
845+ it's acquired... */
846+ if (lock_info->locks[i].lock_addr == this_lock_addr) {
847+ append_lock_information(&str, lock_info, i);
848+ ast_log(LOG_NOTICE, "%s", ast_str_buffer(str));
849+ break;
850+ }
851+ }
852+ pthread_mutex_unlock(&lock_info->lock);
853+ }
854+ pthread_mutex_unlock(&lock_infos_lock.mutex);
855+ ast_free(str);
856+}
857+
858+
859+static char *handle_show_locks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
860+{
861+ struct thr_lock_info *lock_info;
862+ struct ast_str *str;
863+
864+ if (!(str = ast_str_create(4096)))
865+ return CLI_FAILURE;
866+
867+ switch (cmd) {
868+ case CLI_INIT:
869+ e->command = "core show locks";
870+ e->usage =
871+ "Usage: core show locks\n"
872+ " This command is for lock debugging. It prints out which locks\n"
873+ "are owned by each active thread.\n";
874+ return NULL;
875+
876+ case CLI_GENERATE:
877+ return NULL;
878+ }
879+
880+ ast_str_append(&str, 0, "\n"
881+ "=======================================================================\n"
882+ "=== Currently Held Locks ==============================================\n"
883+ "=======================================================================\n"
884+ "===\n"
885+ "=== <pending> <lock#> (<file>): <lock type> <line num> <function> <lock name> <lock addr> (times locked)\n"
886+ "===\n");
887+
888+ if (!str)
889+ return CLI_FAILURE;
890+
891+ pthread_mutex_lock(&lock_infos_lock.mutex);
892+ AST_LIST_TRAVERSE(&lock_infos, lock_info, entry) {
893+ int i;
894+ if (lock_info->num_locks) {
895+ ast_str_append(&str, 0, "=== Thread ID: %ld (%s)\n", (long) lock_info->thread_id,
896+ lock_info->thread_name);
897+ pthread_mutex_lock(&lock_info->lock);
898+ for (i = 0; str && i < lock_info->num_locks; i++) {
899+ append_lock_information(&str, lock_info, i);
900+ }
901+ pthread_mutex_unlock(&lock_info->lock);
902+ if (!str)
903+ break;
904+ ast_str_append(&str, 0, "=== -------------------------------------------------------------------\n"
905+ "===\n");
906+ if (!str)
907+ break;
908+ }
909+ }
910+ pthread_mutex_unlock(&lock_infos_lock.mutex);
911+
912+ if (!str)
913+ return CLI_FAILURE;
914+
915+ ast_str_append(&str, 0, "=======================================================================\n"
916+ "\n");
917+
918+ if (!str)
919+ return CLI_FAILURE;
920+
921+ ast_cli(a->fd, "%s", ast_str_buffer(str));
922+
923+ ast_free(str);
924+
925+ return CLI_SUCCESS;
926+}
927+
928+static struct ast_cli_entry utils_cli[] = {
929+ AST_CLI_DEFINE(handle_show_locks, "Show which locks are held by which thread"),
930+};
931+
932+#endif /* DEBUG_THREADS */
933+
934+/*
935+ * support for 'show threads'. The start routine is wrapped by
936+ * dummy_start(), so that ast_register_thread() and
937+ * ast_unregister_thread() know the thread identifier.
938+ */
939+struct thr_arg {
940+ void *(*start_routine)(void *);
941+ void *data;
942+ char *name;
943+};
944+
945+/*
946+ * on OS/X, pthread_cleanup_push() and pthread_cleanup_pop()
947+ * are odd macros which start and end a block, so they _must_ be
948+ * used in pairs (the latter with a '1' argument to call the
949+ * handler on exit.
950+ * On BSD we don't need this, but we keep it for compatibility.
951+ */
952+static void *dummy_start(void *data)
953+{
954+ void *ret;
955+ struct thr_arg a = *((struct thr_arg *) data); /* make a local copy */
956+#ifdef DEBUG_THREADS
957+ struct thr_lock_info *lock_info;
958+ pthread_mutexattr_t mutex_attr;
959+#endif
960+
961+ /* note that even though data->name is a pointer to allocated memory,
962+ we are not freeing it here because ast_register_thread is going to
963+ keep a copy of the pointer and then ast_unregister_thread will
964+ free the memory
965+ */
966+ ast_free(data);
967+ ast_register_thread(a.name);
968+ pthread_cleanup_push(ast_unregister_thread, (void *) pthread_self());
969+
970+#ifdef DEBUG_THREADS
971+ if (!(lock_info = ast_threadstorage_get(&thread_lock_info, sizeof(*lock_info))))
972+ return NULL;
973+
974+ lock_info->thread_id = pthread_self();
975+ lock_info->thread_name = strdup(a.name);
976+
977+ pthread_mutexattr_init(&mutex_attr);
978+ pthread_mutexattr_settype(&mutex_attr, AST_MUTEX_KIND);
979+ pthread_mutex_init(&lock_info->lock, &mutex_attr);
980+ pthread_mutexattr_destroy(&mutex_attr);
981+
982+ pthread_mutex_lock(&lock_infos_lock.mutex); /* Intentionally not the wrapper */
983+ AST_LIST_INSERT_TAIL(&lock_infos, lock_info, entry);
984+ pthread_mutex_unlock(&lock_infos_lock.mutex); /* Intentionally not the wrapper */
985+#endif /* DEBUG_THREADS */
986+
987+ ret = a.start_routine(a.data);
988+
989+ pthread_cleanup_pop(1);
990+
991+ return ret;
992+}
993+
994+#endif /* !LOW_MEMORY */
995+
996+int ast_pthread_create_stack(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *),
997+ void *data, size_t stacksize, const char *file, const char *caller,
998+ int line, const char *start_fn)
999+{
1000+#if !defined(LOW_MEMORY)
1001+ struct thr_arg *a;
1002+#endif
1003+
1004+ if (!attr) {
1005+ attr = alloca(sizeof(*attr));
1006+ pthread_attr_init(attr);
1007+ }
1008+
1009+#ifdef __linux__
1010+ /* On Linux, pthread_attr_init() defaults to PTHREAD_EXPLICIT_SCHED,
1011+ which is kind of useless. Change this here to
1012+ PTHREAD_INHERIT_SCHED; that way the -p option to set realtime
1013+ priority will propagate down to new threads by default.
1014+ This does mean that callers cannot set a different priority using
1015+ PTHREAD_EXPLICIT_SCHED in the attr argument; instead they must set
1016+ the priority afterwards with pthread_setschedparam(). */
1017+ if ((errno = pthread_attr_setinheritsched(attr, PTHREAD_INHERIT_SCHED)))
1018+ ast_log(LOG_WARNING, "pthread_attr_setinheritsched: %s\n", strerror(errno));
1019+#endif
1020+
1021+ if (!stacksize)
1022+ stacksize = AST_STACKSIZE;
1023+
1024+ if ((errno = pthread_attr_setstacksize(attr, stacksize ? stacksize : AST_STACKSIZE)))
1025+ ast_log(LOG_WARNING, "pthread_attr_setstacksize: %s\n", strerror(errno));
1026+
1027+#if !defined(LOW_MEMORY)
1028+ if ((a = ast_malloc(sizeof(*a)))) {
1029+ a->start_routine = start_routine;
1030+ a->data = data;
1031+ start_routine = dummy_start;
1032+ if (asprintf(&a->name, "%-20s started at [%5d] %s %s()",
1033+ start_fn, line, file, caller) < 0) {
1034+ ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
1035+ a->name = NULL;
1036+ }
1037+ data = a;
1038+ }
1039+#endif /* !LOW_MEMORY */
1040+
1041+ return pthread_create(thread, attr, start_routine, data); /* We're in ast_pthread_create, so it's okay */
1042+}
1043+
1044+
1045+int ast_pthread_create_detached_stack(pthread_t *thread, pthread_attr_t *attr, void *(*start_routine)(void *),
1046+ void *data, size_t stacksize, const char *file, const char *caller,
1047+ int line, const char *start_fn)
1048+{
1049+ unsigned char attr_destroy = 0;
1050+ int res;
1051+
1052+ if (!attr) {
1053+ attr = alloca(sizeof(*attr));
1054+ pthread_attr_init(attr);
1055+ attr_destroy = 1;
1056+ }
1057+
1058+ if ((errno = pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED)))
1059+ ast_log(LOG_WARNING, "pthread_attr_setdetachstate: %s\n", strerror(errno));
1060+
1061+ res = ast_pthread_create_stack(thread, attr, start_routine, data,
1062+ stacksize, file, caller, line, start_fn);
1063+
1064+ if (attr_destroy)
1065+ pthread_attr_destroy(attr);
1066+
1067+ return res;
1068+}
1069+
1070+int ast_wait_for_input(int fd, int ms)
1071+{
1072+ struct pollfd pfd[1];
1073+ memset(pfd, 0, sizeof(pfd));
1074+ pfd[0].fd = fd;
1075+ pfd[0].events = POLLIN|POLLPRI;
1076+ return ast_poll(pfd, 1, ms);
1077+}
1078+
1079+static int ast_wait_for_output(int fd, int timeoutms)
1080+{
1081+ struct pollfd pfd = {
1082+ .fd = fd,
1083+ .events = POLLOUT,
1084+ };
1085+ int res;
1086+ struct timeval start = ast_tvnow();
1087+ int elapsed = 0;
1088+
1089+ /* poll() until the fd is writable without blocking */
1090+ while ((res = ast_poll(&pfd, 1, timeoutms - elapsed)) <= 0) {
1091+ if (res == 0) {
1092+ /* timed out. */
1093+#ifndef STANDALONE
1094+ ast_debug(1, "Timed out trying to write\n");
1095+#endif
1096+ return -1;
1097+ } else if (res == -1) {
1098+ /* poll() returned an error, check to see if it was fatal */
1099+
1100+ if (errno == EINTR || errno == EAGAIN) {
1101+ elapsed = ast_tvdiff_ms(ast_tvnow(), start);
1102+ if (elapsed >= timeoutms) {
1103+ return -1;
1104+ }
1105+ /* This was an acceptable error, go back into poll() */
1106+ continue;
1107+ }
1108+
1109+ /* Fatal error, bail. */
1110+ ast_log(LOG_ERROR, "poll returned error: %s\n", strerror(errno));
1111+
1112+ return -1;
1113+ }
1114+ elapsed = ast_tvdiff_ms(ast_tvnow(), start);
1115+ if (elapsed >= timeoutms) {
1116+ return -1;
1117+ }
1118+ }
1119+
1120+ return 0;
1121+}
1122+
1123+/*!
1124+ * Try to write string, but wait no more than ms milliseconds before timing out.
1125+ *
1126+ * \note The code assumes that the file descriptor has NONBLOCK set,
1127+ * so there is only one system call made to do a write, unless we actually
1128+ * have a need to wait. This way, we get better performance.
1129+ * If the descriptor is blocking, all assumptions on the guaranteed
1130+ * detail do not apply anymore.
1131+ */
1132+int ast_carefulwrite(int fd, char *s, int len, int timeoutms)
1133+{
1134+ struct timeval start = ast_tvnow();
1135+ int res = 0;
1136+ int elapsed = 0;
1137+
1138+ while (len) {
1139+ if (ast_wait_for_output(fd, timeoutms - elapsed)) {
1140+ return -1;
1141+ }
1142+
1143+ res = write(fd, s, len);
1144+
1145+ if (res < 0 && errno != EAGAIN && errno != EINTR) {
1146+ /* fatal error from write() */
1147+ ast_log(LOG_ERROR, "write() returned error: %s\n", strerror(errno));
1148+ return -1;
1149+ }
1150+
1151+ if (res < 0) {
1152+ /* It was an acceptable error */
1153+ res = 0;
1154+ }
1155+
1156+ /* Update how much data we have left to write */
1157+ len -= res;
1158+ s += res;
1159+ res = 0;
1160+
1161+ elapsed = ast_tvdiff_ms(ast_tvnow(), start);
1162+ if (elapsed >= timeoutms) {
1163+ /* We've taken too long to write
1164+ * This is only an error condition if we haven't finished writing. */
1165+ res = len ? -1 : 0;
1166+ break;
1167+ }
1168+ }
1169+
1170+ return res;
1171+}
1172+
1173+int ast_careful_fwrite(FILE *f, int fd, const char *src, size_t len, int timeoutms)
1174+{
1175+ struct timeval start = ast_tvnow();
1176+ int n = 0;
1177+ int elapsed = 0;
1178+
1179+ while (len) {
1180+ if (ast_wait_for_output(fd, timeoutms - elapsed)) {
1181+ /* poll returned a fatal error, so bail out immediately. */
1182+ return -1;
1183+ }
1184+
1185+ /* Clear any errors from a previous write */
1186+ clearerr(f);
1187+
1188+ n = fwrite(src, 1, len, f);
1189+
1190+ if (ferror(f) && errno != EINTR && errno != EAGAIN) {
1191+ /* fatal error from fwrite() */
1192+ if (!feof(f)) {
1193+ /* Don't spam the logs if it was just that the connection is closed. */
1194+ ast_log(LOG_ERROR, "fwrite() returned error: %s\n", strerror(errno));
1195+ }
1196+ n = -1;
1197+ break;
1198+ }
1199+
1200+ /* Update for data already written to the socket */
1201+ len -= n;
1202+ src += n;
1203+
1204+ elapsed = ast_tvdiff_ms(ast_tvnow(), start);
1205+ if (elapsed >= timeoutms) {
1206+ /* We've taken too long to write
1207+ * This is only an error condition if we haven't finished writing. */
1208+ n = len ? -1 : 0;
1209+ break;
1210+ }
1211+ }
1212+
1213+ while (fflush(f)) {
1214+ if (errno == EAGAIN || errno == EINTR) {
1215+ continue;
1216+ }
1217+ if (!feof(f)) {
1218+ /* Don't spam the logs if it was just that the connection is closed. */
1219+ ast_log(LOG_ERROR, "fflush() returned error: %s\n", strerror(errno));
1220+ }
1221+ n = -1;
1222+ break;
1223+ }
1224+
1225+ return n < 0 ? -1 : 0;
1226+}
1227+
1228+char *ast_strip_quoted(char *s, const char *beg_quotes, const char *end_quotes)
1229+{
1230+ char *e;
1231+ char *q;
1232+
1233+ s = ast_strip(s);
1234+ if ((q = strchr(beg_quotes, *s)) && *q != '\0') {
1235+ e = s + strlen(s) - 1;
1236+ if (*e == *(end_quotes + (q - beg_quotes))) {
1237+ s++;
1238+ *e = '\0';
1239+ }
1240+ }
1241+
1242+ return s;
1243+}
1244+
1245+char *ast_unescape_semicolon(char *s)
1246+{
1247+ char *e;
1248+ char *work = s;
1249+
1250+ while ((e = strchr(work, ';'))) {
1251+ if ((e > work) && (*(e-1) == '\\')) {
1252+ memmove(e - 1, e, strlen(e) + 1);
1253+ work = e;
1254+ } else {
1255+ work = e + 1;
1256+ }
1257+ }
1258+
1259+ return s;
1260+}
1261+
1262+/* !\brief unescape some C sequences in place, return pointer to the original string.
1263+ */
1264+char *ast_unescape_c(char *src)
1265+{
1266+ char c, *ret, *dst;
1267+
1268+ if (src == NULL)
1269+ return NULL;
1270+ for (ret = dst = src; (c = *src++); *dst++ = c ) {
1271+ if (c != '\\')
1272+ continue; /* copy char at the end of the loop */
1273+ switch ((c = *src++)) {
1274+ case '\0': /* special, trailing '\' */
1275+ c = '\\';
1276+ break;
1277+ case 'b': /* backspace */
1278+ c = '\b';
1279+ break;
1280+ case 'f': /* form feed */
1281+ c = '\f';
1282+ break;
1283+ case 'n':
1284+ c = '\n';
1285+ break;
1286+ case 'r':
1287+ c = '\r';
1288+ break;
1289+ case 't':
1290+ c = '\t';
1291+ break;
1292+ }
1293+ /* default, use the char literally */
1294+ }
1295+ *dst = '\0';
1296+ return ret;
1297+}
1298+
1299+int ast_build_string_va(char **buffer, size_t *space, const char *fmt, va_list ap)
1300+{
1301+ int result;
1302+
1303+ if (!buffer || !*buffer || !space || !*space)
1304+ return -1;
1305+
1306+ result = vsnprintf(*buffer, *space, fmt, ap);
1307+
1308+ if (result < 0)
1309+ return -1;
1310+ else if (result > *space)
1311+ result = *space;
1312+
1313+ *buffer += result;
1314+ *space -= result;
1315+ return 0;
1316+}
1317+
1318+int ast_build_string(char **buffer, size_t *space, const char *fmt, ...)
1319+{
1320+ va_list ap;
1321+ int result;
1322+
1323+ va_start(ap, fmt);
1324+ result = ast_build_string_va(buffer, space, fmt, ap);
1325+ va_end(ap);
1326+
1327+ return result;
1328+}
1329+
1330+int ast_true(const char *s)
1331+{
1332+ if (ast_strlen_zero(s))
1333+ return 0;
1334+
1335+ /* Determine if this is a true value */
1336+ if (!strcasecmp(s, "yes") ||
1337+ !strcasecmp(s, "true") ||
1338+ !strcasecmp(s, "y") ||
1339+ !strcasecmp(s, "t") ||
1340+ !strcasecmp(s, "1") ||
1341+ !strcasecmp(s, "on"))
1342+ return -1;
1343+
1344+ return 0;
1345+}
1346+
1347+int ast_false(const char *s)
1348+{
1349+ if (ast_strlen_zero(s))
1350+ return 0;
1351+
1352+ /* Determine if this is a false value */
1353+ if (!strcasecmp(s, "no") ||
1354+ !strcasecmp(s, "false") ||
1355+ !strcasecmp(s, "n") ||
1356+ !strcasecmp(s, "f") ||
1357+ !strcasecmp(s, "0") ||
1358+ !strcasecmp(s, "off"))
1359+ return -1;
1360+
1361+ return 0;
1362+}
1363+
1364+#define ONE_MILLION 1000000
1365+/*
1366+ * put timeval in a valid range. usec is 0..999999
1367+ * negative values are not allowed and truncated.
1368+ */
1369+static struct timeval tvfix(struct timeval a)
1370+{
1371+ if (a.tv_usec >= ONE_MILLION) {
1372+ ast_log(LOG_WARNING, "warning too large timestamp %ld.%ld\n",
1373+ (long)a.tv_sec, (long int) a.tv_usec);
1374+ a.tv_sec += a.tv_usec / ONE_MILLION;
1375+ a.tv_usec %= ONE_MILLION;
1376+ } else if (a.tv_usec < 0) {
1377+ ast_log(LOG_WARNING, "warning negative timestamp %ld.%ld\n",
1378+ (long)a.tv_sec, (long int) a.tv_usec);
1379+ a.tv_usec = 0;
1380+ }
1381+ return a;
1382+}
1383+
1384+struct timeval ast_tvadd(struct timeval a, struct timeval b)
1385+{
1386+ /* consistency checks to guarantee usec in 0..999999 */
1387+ a = tvfix(a);
1388+ b = tvfix(b);
1389+ a.tv_sec += b.tv_sec;
1390+ a.tv_usec += b.tv_usec;
1391+ if (a.tv_usec >= ONE_MILLION) {
1392+ a.tv_sec++;
1393+ a.tv_usec -= ONE_MILLION;
1394+ }
1395+ return a;
1396+}
1397+
1398+struct timeval ast_tvsub(struct timeval a, struct timeval b)
1399+{
1400+ /* consistency checks to guarantee usec in 0..999999 */
1401+ a = tvfix(a);
1402+ b = tvfix(b);
1403+ a.tv_sec -= b.tv_sec;
1404+ a.tv_usec -= b.tv_usec;
1405+ if (a.tv_usec < 0) {
1406+ a.tv_sec-- ;
1407+ a.tv_usec += ONE_MILLION;
1408+ }
1409+ return a;
1410+}
1411+#undef ONE_MILLION
1412+
1413+/*! \brief glibc puts a lock inside random(3), so that the results are thread-safe.
1414+ * BSD libc (and others) do not. */
1415+
1416+#ifndef linux
1417+AST_MUTEX_DEFINE_STATIC(randomlock);
1418+#endif
1419+
1420+long int ast_random(void)
1421+{
1422+ long int res;
1423+#ifdef HAVE_DEV_URANDOM
1424+ if (dev_urandom_fd >= 0) {
1425+ int read_res = read(dev_urandom_fd, &res, sizeof(res));
1426+ if (read_res > 0) {
1427+ long int rm = RAND_MAX;
1428+ res = res < 0 ? ~res : res;
1429+ rm++;
1430+ return res % rm;
1431+ }
1432+ }
1433+#endif
1434+#ifdef linux
1435+ res = random();
1436+#else
1437+ ast_mutex_lock(&randomlock);
1438+ res = random();
1439+ ast_mutex_unlock(&randomlock);
1440+#endif
1441+ return res;
1442+}
1443+
1444+char *ast_process_quotes_and_slashes(char *start, char find, char replace_with)
1445+{
1446+ char *dataPut = start;
1447+ int inEscape = 0;
1448+ int inQuotes = 0;
1449+
1450+ for (; *start; start++) {
1451+ if (inEscape) {
1452+ *dataPut++ = *start; /* Always goes verbatim */
1453+ inEscape = 0;
1454+ } else {
1455+ if (*start == '\\') {
1456+ inEscape = 1; /* Do not copy \ into the data */
1457+ } else if (*start == '\'') {
1458+ inQuotes = 1 - inQuotes; /* Do not copy ' into the data */
1459+ } else {
1460+ /* Replace , with |, unless in quotes */
1461+ *dataPut++ = inQuotes ? *start : ((*start == find) ? replace_with : *start);
1462+ }
1463+ }
1464+ }
1465+ if (start != dataPut)
1466+ *dataPut = 0;
1467+ return dataPut;
1468+}
1469+
1470+void ast_join(char *s, size_t len, char * const w[])
1471+{
1472+ int x, ofs = 0;
1473+ const char *src;
1474+
1475+ /* Join words into a string */
1476+ if (!s)
1477+ return;
1478+ for (x = 0; ofs < len && w[x]; x++) {
1479+ if (x > 0)
1480+ s[ofs++] = ' ';
1481+ for (src = w[x]; *src && ofs < len; src++)
1482+ s[ofs++] = *src;
1483+ }
1484+ if (ofs == len)
1485+ ofs--;
1486+ s[ofs] = '\0';
1487+}
1488+
1489+/*
1490+ * stringfields support routines.
1491+ */
1492+
1493+const char __ast_string_field_empty[] = ""; /*!< the empty string */
1494+
1495+/*! \brief add a new block to the pool.
1496+ * We can only allocate from the topmost pool, so the
1497+ * fields in *mgr reflect the size of that only.
1498+ */
1499+static int add_string_pool(struct ast_string_field_mgr *mgr, struct ast_string_field_pool **pool_head,
1500+ size_t size, const char *file, int lineno, const char *func)
1501+{
1502+ struct ast_string_field_pool *pool;
1503+
1504+#if defined(__AST_DEBUG_MALLOC)
1505+ if (!(pool = __ast_calloc(1, sizeof(*pool) + size, file, lineno, func))) {
1506+ return -1;
1507+ }
1508+#else
1509+ if (!(pool = ast_calloc(1, sizeof(*pool) + size))) {
1510+ return -1;
1511+ }
1512+#endif
1513+
1514+ pool->prev = *pool_head;
1515+ *pool_head = pool;
1516+ mgr->size = size;
1517+ mgr->used = 0;
1518+ mgr->last_alloc = NULL;
1519+
1520+ return 0;
1521+}
1522+
1523+/*
1524+ * This is an internal API, code should not use it directly.
1525+ * It initializes all fields as empty, then uses 'size' for 3 functions:
1526+ * size > 0 means initialize the pool list with a pool of given size.
1527+ * This must be called right after allocating the object.
1528+ * size = 0 means release all pools except the most recent one.
1529+ * This is useful to e.g. reset an object to the initial value.
1530+ * size < 0 means release all pools.
1531+ * This must be done before destroying the object.
1532+ */
1533+int __ast_string_field_init(struct ast_string_field_mgr *mgr, struct ast_string_field_pool **pool_head,
1534+ int needed, const char *file, int lineno, const char *func)
1535+{
1536+ const char **p = (const char **) pool_head + 1;
1537+ struct ast_string_field_pool *cur = NULL;
1538+ struct ast_string_field_pool *preserve = NULL;
1539+
1540+ /* clear fields - this is always necessary */
1541+ while ((struct ast_string_field_mgr *) p != mgr)
1542+ *p++ = __ast_string_field_empty;
1543+ mgr->last_alloc = NULL;
1544+#if defined(__AST_DEBUG_MALLOC)
1545+ mgr->owner_file = file;
1546+ mgr->owner_func = func;
1547+ mgr->owner_line = lineno;
1548+#endif
1549+ if (needed > 0) { /* allocate the initial pool */
1550+ *pool_head = NULL;
1551+ return add_string_pool(mgr, pool_head, needed, file, lineno, func);
1552+ }
1553+ if (needed < 0) { /* reset all pools */
1554+ if (*pool_head == NULL) {
1555+ ast_log(LOG_WARNING, "trying to reset empty pool\n");
1556+ return -1;
1557+ }
1558+ cur = *pool_head;
1559+ } else { /* preserve the last pool */
1560+ if (*pool_head == NULL) {
1561+ ast_log(LOG_WARNING, "trying to reset empty pool\n");
1562+ return -1;
1563+ }
1564+ mgr->used = 0;
1565+ preserve = *pool_head;
1566+ cur = preserve->prev;
1567+ }
1568+
1569+ if (preserve) {
1570+ preserve->prev = NULL;
1571+ }
1572+
1573+ while (cur) {
1574+ struct ast_string_field_pool *prev = cur->prev;
1575+
1576+ if (cur != preserve) {
1577+ ast_free(cur);
1578+ }
1579+ cur = prev;
1580+ }
1581+
1582+ *pool_head = preserve;
1583+
1584+ return 0;
1585+}
1586+
1587+ast_string_field __ast_string_field_alloc_space(struct ast_string_field_mgr *mgr,
1588+ struct ast_string_field_pool **pool_head, size_t needed)
1589+{
1590+ char *result = NULL;
1591+ size_t space = mgr->size - mgr->used;
1592+
1593+ if (__builtin_expect(needed > space, 0)) {
1594+ size_t new_size = mgr->size * 2;
1595+
1596+ while (new_size < needed)
1597+ new_size *= 2;
1598+
1599+#if defined(__AST_DEBUG_MALLOC)
1600+ if (add_string_pool(mgr, pool_head, new_size, mgr->owner_file, mgr->owner_line, mgr->owner_func))
1601+ return NULL;
1602+#else
1603+ if (add_string_pool(mgr, pool_head, new_size, __FILE__, __LINE__, __FUNCTION__))
1604+ return NULL;
1605+#endif
1606+ }
1607+
1608+ result = (*pool_head)->base + mgr->used;
1609+ mgr->used += needed;
1610+ mgr->last_alloc = result;
1611+ return result;
1612+}
1613+
1614+int __ast_string_field_ptr_grow(struct ast_string_field_mgr *mgr, size_t needed,
1615+ const ast_string_field *ptr)
1616+{
1617+ int grow = needed - (strlen(*ptr) + 1);
1618+ size_t space = mgr->size - mgr->used;
1619+
1620+ if (grow <= 0) {
1621+ return 0;
1622+ }
1623+
1624+ if (*ptr != mgr->last_alloc) {
1625+ return 1;
1626+ }
1627+
1628+ if (space < grow) {
1629+ return 1;
1630+ }
1631+
1632+ mgr->used += grow;
1633+
1634+ return 0;
1635+}
1636+
1637+void __ast_string_field_ptr_build_va(struct ast_string_field_mgr *mgr,
1638+ struct ast_string_field_pool **pool_head,
1639+ ast_string_field *ptr, const char *format, va_list ap1, va_list ap2)
1640+{
1641+ size_t needed;
1642+ size_t available;
1643+ size_t space = mgr->size - mgr->used;
1644+ char *target;
1645+
1646+ /* if the field already has space allocated, try to reuse it;
1647+ otherwise, use the empty space at the end of the current
1648+ pool
1649+ */
1650+ if ((*ptr)[0] != '\0') {
1651+ target = (char *) *ptr;
1652+ available = strlen(target) + 1;
1653+ } else {
1654+ target = (*pool_head)->base + mgr->used;
1655+ available = space;
1656+ }
1657+
1658+ needed = vsnprintf(target, available, format, ap1) + 1;
1659+
1660+ va_end(ap1);
1661+
1662+ if (needed > available) {
1663+ /* if the space needed can be satisfied by using the current
1664+ pool (which could only occur if we tried to use the field's
1665+ allocated space and failed), then use that space; otherwise
1666+ allocate a new pool
1667+ */
1668+ if (needed > space) {
1669+ size_t new_size = mgr->size * 2;
1670+
1671+ while (new_size < needed)
1672+ new_size *= 2;
1673+
1674+#if defined(__AST_DEBUG_MALLOC)
1675+ if (add_string_pool(mgr, pool_head, new_size, mgr->owner_file, mgr->owner_line, mgr->owner_func))
1676+ return;
1677+#else
1678+ if (add_string_pool(mgr, pool_head, new_size, NULL, 0, NULL))
1679+ return;
1680+#endif
1681+ }
1682+
1683+ target = (*pool_head)->base + mgr->used;
1684+ vsprintf(target, format, ap2);
1685+ }
1686+
1687+ if (*ptr != target) {
1688+ mgr->last_alloc = *ptr = target;
1689+ mgr->used += needed;
1690+ }
1691+}
1692+
1693+void __ast_string_field_ptr_build(struct ast_string_field_mgr *mgr,
1694+ struct ast_string_field_pool **pool_head,
1695+ ast_string_field *ptr, const char *format, ...)
1696+{
1697+ va_list ap1, ap2;
1698+
1699+ va_start(ap1, format);
1700+ va_start(ap2, format); /* va_copy does not exist on FreeBSD */
1701+
1702+ __ast_string_field_ptr_build_va(mgr, pool_head, ptr, format, ap1, ap2);
1703+
1704+ va_end(ap1);
1705+ va_end(ap2);
1706+}
1707+/* end of stringfields support */
1708+
1709+AST_MUTEX_DEFINE_STATIC(fetchadd_m); /* used for all fetc&add ops */
1710+
1711+int ast_atomic_fetchadd_int_slow(volatile int *p, int v)
1712+{
1713+ int ret;
1714+ ast_mutex_lock(&fetchadd_m);
1715+ ret = *p;
1716+ *p += v;
1717+ ast_mutex_unlock(&fetchadd_m);
1718+ return ret;
1719+}
1720+
1721+/*! \brief
1722+ * get values from config variables.
1723+ */
1724+int ast_get_timeval(const char *src, struct timeval *dst, struct timeval _default, int *consumed)
1725+{
1726+ long double dtv = 0.0;
1727+ int scanned;
1728+
1729+ if (dst == NULL)
1730+ return -1;
1731+
1732+ *dst = _default;
1733+
1734+ if (ast_strlen_zero(src))
1735+ return -1;
1736+
1737+ /* only integer at the moment, but one day we could accept more formats */
1738+ if (sscanf(src, "%30Lf%n", &dtv, &scanned) > 0) {
1739+ dst->tv_sec = dtv;
1740+ dst->tv_usec = (dtv - dst->tv_sec) * 1000000.0;
1741+ if (consumed)
1742+ *consumed = scanned;
1743+ return 0;
1744+ } else
1745+ return -1;
1746+}
1747+
1748+/*! \brief
1749+ * get values from config variables.
1750+ */
1751+int ast_get_time_t(const char *src, time_t *dst, time_t _default, int *consumed)
1752+{
1753+ long t;
1754+ int scanned;
1755+
1756+ if (dst == NULL)
1757+ return -1;
1758+
1759+ *dst = _default;
1760+
1761+ if (ast_strlen_zero(src))
1762+ return -1;
1763+
1764+ /* only integer at the moment, but one day we could accept more formats */
1765+ if (sscanf(src, "%30ld%n", &t, &scanned) == 1) {
1766+ *dst = t;
1767+ if (consumed)
1768+ *consumed = scanned;
1769+ return 0;
1770+ } else
1771+ return -1;
1772+}
1773+
1774+void ast_enable_packet_fragmentation(int sock)
1775+{
1776+#if defined(HAVE_IP_MTU_DISCOVER)
1777+ int val = IP_PMTUDISC_DONT;
1778+
1779+ if (setsockopt(sock, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val)))
1780+ ast_log(LOG_WARNING, "Unable to disable PMTU discovery. Large UDP packets may fail to be delivered when sent from this socket.\n");
1781+#endif /* HAVE_IP_MTU_DISCOVER */
1782+}
1783+
1784+int ast_mkdir(const char *path, int mode)
1785+{
1786+ char *ptr;
1787+ int len = strlen(path), count = 0, x, piececount = 0;
1788+ char *tmp = ast_strdupa(path);
1789+ char **pieces;
1790+ char *fullpath = alloca(len + 1);
1791+ int res = 0;
1792+
1793+ for (ptr = tmp; *ptr; ptr++) {
1794+ if (*ptr == '/')
1795+ count++;
1796+ }
1797+
1798+ /* Count the components to the directory path */
1799+ pieces = alloca(count * sizeof(*pieces));
1800+ for (ptr = tmp; *ptr; ptr++) {
1801+ if (*ptr == '/') {
1802+ *ptr = '\0';
1803+ pieces[piececount++] = ptr + 1;
1804+ }
1805+ }
1806+
1807+ *fullpath = '\0';
1808+ for (x = 0; x < piececount; x++) {
1809+ /* This looks funky, but the buffer is always ideally-sized, so it's fine. */
1810+ strcat(fullpath, "/");
1811+ strcat(fullpath, pieces[x]);
1812+ res = mkdir(fullpath, mode);
1813+ if (res && errno != EEXIST)
1814+ return errno;
1815+ }
1816+ return 0;
1817+}
1818+
1819+int ast_utils_init(void)
1820+{
1821+#ifdef HAVE_DEV_URANDOM
1822+ dev_urandom_fd = open("/dev/urandom", O_RDONLY);
1823+#endif
1824+ base64_init();
1825+#ifdef DEBUG_THREADS
1826+#if !defined(LOW_MEMORY)
1827+ ast_cli_register_multiple(utils_cli, ARRAY_LEN(utils_cli));
1828+#endif
1829+#endif
1830+ return 0;
1831+}
1832+
1833+#ifndef __AST_DEBUG_MALLOC
1834+int _ast_asprintf(char **ret, const char *file, int lineno, const char *func, const char *fmt, ...)
1835+{
1836+ int res;
1837+ va_list ap;
1838+
1839+ va_start(ap, fmt);
1840+ if ((res = vasprintf(ret, fmt, ap)) == -1) {
1841+ MALLOC_FAILURE_MSG;
1842+ }
1843+ va_end(ap);
1844+
1845+ return res;
1846+}
1847+#endif
1848
1849=== modified file '.pc/applied-patches'
1850--- .pc/applied-patches 2010-06-23 19:37:50 +0000
1851+++ .pc/applied-patches 2011-01-20 23:45:56 +0000
1852@@ -15,3 +15,4 @@
1853 dahdi_fxs_false_ringing
1854 h323-extra-target
1855 fxs_ports_1626
1856+AST-2011-001-1.6.2
1857
1858=== modified file 'debian/changelog'
1859--- debian/changelog 2010-06-23 19:37:50 +0000
1860+++ debian/changelog 2011-01-20 23:45:56 +0000
1861@@ -1,3 +1,13 @@
1862+asterisk (1:1.6.2.7-1ubuntu1.1) maverick-security; urgency=low
1863+
1864+ * SECURITY UPDATE: Stack buffer overflow in SIP channel driver. (LP: #705014)
1865+ - debian/patches/AST-2011-001-1.6.2: The size of the output buffer passed
1866+ to the ast_uri_encode function is now properly respected in main/utils.c.
1867+ Patch courtesy of upstream.
1868+ - CVE-2011-0495
1869+
1870+ -- Dave Walker (Daviey) <DaveWalker@ubuntu.com> Thu, 20 Jan 2011 23:36:57 +0000
1871+
1872 asterisk (1:1.6.2.7-1ubuntu1) maverick; urgency=low
1873
1874 * Merge from debian unstable (LP: #597792), remaining changes:
1875
1876=== added file 'debian/patches/AST-2011-001-1.6.2'
1877--- debian/patches/AST-2011-001-1.6.2 1970-01-01 00:00:00 +0000
1878+++ debian/patches/AST-2011-001-1.6.2 2011-01-20 23:45:56 +0000
1879@@ -0,0 +1,52 @@
1880+Description: Stack buffer overflow in SIP channel driver.
1881+ Prevent buffer overflows in ast_uri_encode()
1882+Origin: upstream, http://downloads.asterisk.org/pub/security/AST-2011-001.html
1883+Bug-Ubuntu: https://launchpad.net/bugs/705014
1884+Bug-Debian: http://bugs.debian.org/610487
1885+Applied-Upstream: http://svnview.digium.com/svn/asterisk?view=revision&revision=301307
1886+Last-Update: 2011-01-20
1887+
1888+--- a/main/utils.c
1889++++ b/main/utils.c
1890+@@ -385,28 +385,27 @@
1891+ char *reserved = ";/?:@&=+$,# "; /* Reserved chars */
1892+
1893+ const char *ptr = string; /* Start with the string */
1894+- char *out = NULL;
1895+- char *buf = NULL;
1896++ char *out = outbuf;
1897+
1898+- ast_copy_string(outbuf, string, buflen);
1899+-
1900+- /* If there's no characters to convert, just go through and don't do anything */
1901+- while (*ptr) {
1902++ /* If there's no characters to convert, just go through and copy the string */
1903++ while (*ptr && out - outbuf < buflen - 1) {
1904+ if ((*ptr < 32) || (doreserved && strchr(reserved, *ptr))) {
1905+- /* Oops, we need to start working here */
1906+- if (!buf) {
1907+- buf = outbuf;
1908+- out = buf + (ptr - string) ; /* Set output ptr */
1909++ if (out - outbuf >= buflen - 3) {
1910++ break;
1911+ }
1912++
1913+ out += sprintf(out, "%%%02x", (unsigned char) *ptr);
1914+- } else if (buf) {
1915+- *out = *ptr; /* Continue copying the string */
1916++ } else {
1917++ *out = *ptr; /* copy the character */
1918+ out++;
1919+- }
1920++ }
1921+ ptr++;
1922+ }
1923+- if (buf)
1924++
1925++ if (buflen) {
1926+ *out = '\0';
1927++ }
1928++
1929+ return outbuf;
1930+ }
1931+
1932
1933=== modified file 'debian/patches/series'
1934--- debian/patches/series 2010-06-23 19:37:50 +0000
1935+++ debian/patches/series 2011-01-20 23:45:56 +0000
1936@@ -21,3 +21,4 @@
1937 dahdi_fxs_false_ringing
1938 h323-extra-target
1939 fxs_ports_1626
1940+AST-2011-001-1.6.2
1941
1942=== modified file 'main/utils.c'
1943--- main/utils.c 2010-02-16 14:08:54 +0000
1944+++ main/utils.c 2011-01-20 23:45:56 +0000
1945@@ -385,28 +385,27 @@
1946 char *reserved = ";/?:@&=+$,# "; /* Reserved chars */
1947
1948 const char *ptr = string; /* Start with the string */
1949- char *out = NULL;
1950- char *buf = NULL;
1951-
1952- ast_copy_string(outbuf, string, buflen);
1953-
1954- /* If there's no characters to convert, just go through and don't do anything */
1955- while (*ptr) {
1956+ char *out = outbuf;
1957+
1958+ /* If there's no characters to convert, just go through and copy the string */
1959+ while (*ptr && out - outbuf < buflen - 1) {
1960 if ((*ptr < 32) || (doreserved && strchr(reserved, *ptr))) {
1961- /* Oops, we need to start working here */
1962- if (!buf) {
1963- buf = outbuf;
1964- out = buf + (ptr - string) ; /* Set output ptr */
1965+ if (out - outbuf >= buflen - 3) {
1966+ break;
1967 }
1968+
1969 out += sprintf(out, "%%%02x", (unsigned char) *ptr);
1970- } else if (buf) {
1971- *out = *ptr; /* Continue copying the string */
1972+ } else {
1973+ *out = *ptr; /* copy the character */
1974 out++;
1975- }
1976+ }
1977 ptr++;
1978 }
1979- if (buf)
1980+
1981+ if (buflen) {
1982 *out = '\0';
1983+ }
1984+
1985 return outbuf;
1986 }
1987

Subscribers

People subscribed via source and target branches

to all changes: