1 /*!
2 * \file sccp_utils.c
3 * \brief SCCP Utils Class
4 * \author Sergio Chersovani <mlists [at] c-net.it>
5 * \note Reworked, but based on chan_sccp code.
6 * The original chan_sccp driver that was made by Zozo which itself was derived from the chan_skinny driver.
7 * Modified by Jan Czmok and Julien Goodwin
8 * \note This program is free software and may be modified and distributed under the terms of the GNU Public License.
9 * See the LICENSE file at the top of the source tree.
10 *
11 */
12
13 #include "config.h"
14 #include "common.h"
15 #include "sccp_channel.h"
16 #include "sccp_device.h"
17 #include "sccp_line.h"
18 #include "sccp_linedevice.h"
19 #include "sccp_session.h"
20 #include "sccp_utils.h"
21 #include "sccp_labels.h"
22
23 SCCP_FILE_VERSION(__FILE__, "");
24 #include <locale.h>
25 #if defined __has_include
26 # if __has_include (<xlocale.h>)
27 # include <xlocale.h>
28 # endif
29 #elif defined(HAVE_XLOCALE_H)
30 # include <xlocale.h>
31 #endif
32 #if defined(DEBUG) && defined(HAVE_EXECINFO_H)
33 # include <execinfo.h>
34 # if defined(HAVE_DLADDR_H) && defined(HAVE_BFD_H)
35 # include <dlfcn.h>
36 # include <bfd.h>
37 # endif
38 # if ASTERISK_VERSION_GROUP >= 112
39 # include <asterisk/backtrace.h>
40 # endif
41 #endif
42 #include <asterisk/ast_version.h> // ast_get_version
43 #ifdef HAVE_PBX_ACL_H // ast_ha, AST_SENSE_ALLOW
44 # include <asterisk/acl.h>
45 #endif
46
47 /*!
48 * \brief Print out a messagebuffer
49 * \param messagebuffer Pointer to Message Buffer as char
50 * \param len Lenght as Int
51 */
sccp_dump_packet(unsigned char * messagebuffer,int len)52 void sccp_dump_packet(unsigned char *messagebuffer, int len)
53 {
54 static const int numcolumns = 16; // number output columns
55
56 if (len <= 0 || !messagebuffer || !sccp_strlen((const char *) messagebuffer)) { // safe quard
57 sccp_log((DEBUGCAT_CORE)) (VERBOSE_PREFIX_1 "SCCP: messagebuffer is not valid. exiting sccp_dump_packet\n");
58 return;
59 }
60 int col = 0;
61 int cur = 0;
62 int hexcolumnlength = 0;
63 const char *hex = "0123456789ABCDEF";
64 char hexout[(numcolumns * 3) + (numcolumns / 8) + 1]; // 3 char per hex value + grouping + endofline
65 char * hexptr = NULL;
66 char chrout[numcolumns + 1];
67 char * chrptr = NULL;
68 pbx_str_t *output_buf = pbx_str_create(DEFAULT_PBX_STR_BUFFERSIZE);
69
70 do {
71 // memset(hexout, 0, sizeof(hexout));
72 memset(hexout, 0, (numcolumns * 3) + (numcolumns / 8) + 1);
73 // memset(chrout, 0, sizeof(chrout));
74 memset(chrout, 0, numcolumns + 1);
75 hexptr = hexout;
76 chrptr = chrout;
77 for (col = 0; col < numcolumns && (cur + col) < len; col++) {
78 *hexptr++ = hex[(*messagebuffer >> 4) & 0xF]; // lookup first part of hex value and add to hexptr
79 *hexptr++ = hex[(*messagebuffer) & 0xF]; // lookup second part of a hex value and add to hexptr
80 *hexptr++ = ' '; // add space to hexptr
81 if ((col + 1) % 8 == 0) {
82 *hexptr++ = ' '; // group by blocks of eight
83 }
84 *chrptr++ = isprint(*messagebuffer) ? *messagebuffer : '.'; // add character or . to chrptr
85 messagebuffer += 1; // instead *messagebuffer++ to circumvent unused warning
86 }
87 hexcolumnlength = (numcolumns * 3) + (numcolumns / 8) - 1; // numcolums + block spaces - 1
88 pbx_str_append(&output_buf, 0, VERBOSE_PREFIX_1 "%08X - %-*.*s - %s\n", cur, hexcolumnlength, hexcolumnlength, hexout, chrout);
89 cur += col;
90 } while (cur < (len - 1));
91 sccp_log((DEBUGCAT_CORE)) (VERBOSE_PREFIX_1 "SCCP: packet hex dump:\n%s", pbx_str_buffer(output_buf));
92 sccp_free(output_buf)
93 }
94
sccp_dump_msg(const sccp_msg_t * const msg)95 void sccp_dump_msg(const sccp_msg_t * const msg)
96 {
97 sccp_dump_packet((unsigned char *) msg, letohl(msg->header.length) + 8);
98 }
99
100 /*!
101 * \brief Clear all Addons from AddOn Linked List
102 * \param d SCCP Device
103 */
sccp_addons_clear(devicePtr d)104 void sccp_addons_clear(devicePtr d)
105 {
106 sccp_addon_t * addon = NULL;
107
108 if (!d) {
109 return;
110 }
111 // while ((AST_LIST_REMOVE_HEAD(&d->addons, list))) ;
112 while ((addon = SCCP_LIST_REMOVE_HEAD(&d->addons, list))) {
113 sccp_free(addon);
114 }
115 d->addons.first = NULL;
116 d->addons.last = NULL;
117 }
118
119 /*!
120 * \brief Put SCCP into Safe Sleep for [num] milli_seconds
121 * \param ms MilliSeconds
122 */
sccp_safe_sleep(int ms)123 void sccp_safe_sleep(int ms)
124 {
125 struct timeval start = pbx_tvnow();
126
127 usleep(1);
128 while (ast_tvdiff_ms(pbx_tvnow(), start) < ms) {
129 usleep(1);
130 }
131 }
132
133 #ifndef HAVE_PBX_STRINGS_H
134 /*!
135 * \brief Asterisk Skip Blanks
136 * \param str as Character
137 * \return String without Blanks
138 */
pbx_skip_blanks(char * str)139 char *pbx_skip_blanks(char *str)
140 {
141 while (*str && *str < 33)
142 str++;
143
144 return str;
145 }
146
147 /*!
148 * \brief Asterisk Trim Blanks
149 * Remove Blanks from the begining and end of a string
150 * \param str as Character
151 * \return String without Beginning or Ending Blanks
152 */
pbx_trim_blanks(char * str)153 char *pbx_trim_blanks(char *str)
154 {
155 char *work = str;
156
157 if (work) {
158 work += strlen(work) - 1;
159 while ((work >= str) && *work < 33)
160 *(work--) = '\0';
161 }
162 return str;
163 }
164
165 /*!
166 * \brief Asterisk Non Blanks
167 * \param str as Character
168 * \return Only the Non Blank Characters
169 */
pbx_skip_nonblanks(char * str)170 char *pbx_skip_nonblanks(char *str)
171 {
172 while (*str && *str > 32)
173 str++;
174
175 return str;
176 }
177
178 /*!
179 * \brief Asterisk Strip
180 * \param s as Character
181 * \return String without all Blanks
182 */
pbx_strip(char * s)183 char *pbx_strip(char *s)
184 {
185 s = pbx_skip_blanks(s);
186 if (s) {
187 pbx_trim_blanks(s);
188 }
189 return s;
190 }
191 #endif
192
193 #ifndef CS_AST_HAS_APP_SEPARATE_ARGS
194
195 /*!
196 * \brief Seperate App Args
197 * \param buf Buffer as Char
198 * \param delim Delimiter as Char
199 * \param array Array as Char Array
200 * \param arraylen Array Length as Int
201 * \return argc Unsigned Int
202 */
sccp_app_separate_args(char * buf,char delim,char ** array,int arraylen)203 unsigned int sccp_app_separate_args(char *buf, char delim, char **array, int arraylen)
204 {
205 int argc = 0;
206 char * scan = NULL;
207 int paren = 0;
208
209 if (!buf || !array || !arraylen) {
210 return 0;
211 }
212 memset(array, 0, arraylen * sizeof(*array));
213
214 scan = buf;
215
216 for (argc = 0; *scan && (argc < arraylen - 1); argc++) {
217 array[argc] = scan;
218 for (; *scan; scan++) {
219 if (*scan == '(') {
220 paren++;
221 } else if (*scan == ')') {
222 if (paren) {
223 paren--;
224 }
225 } else if ((*scan == delim) && !paren) {
226 *scan++ = '\0';
227 break;
228 }
229 }
230 }
231
232 if (*scan) {
233 array[argc++] = scan;
234 }
235 return argc;
236 }
237 #endif
238
239 /*!
240 * \brief Handle Feature Change Event for persistent feature storage
241 * \param event SCCP Event
242 *
243 * \callgraph
244 * \callergraph
245 *
246 * \warning
247 * - device->buttonconfig is not always locked
248 * - line->devices is not always locked
249 */
sccp_util_featureStorageBackend(const sccp_event_t * const event)250 void sccp_util_featureStorageBackend(const sccp_event_t * const event)
251 {
252 char family[25];
253 char cfwdDeviceLineStore[60]; /* backward compatibiliy SCCP/Device/Line */
254 char cfwdLineDeviceStore[60]; /* new format cfwd: SCCP/Line/Device */
255 sccp_linedevice_t * ld = NULL;
256 sccp_device_t * device = NULL;
257
258 if(!event || !(device = event->featureChanged.device)) {
259 return;
260 }
261
262 sccp_log((DEBUGCAT_EVENT + DEBUGCAT_FEATURE)) (VERBOSE_PREFIX_3 "%s: StorageBackend got Feature Change Event: %s(%d)\n", DEV_ID_LOG(device), sccp_feature_type2str(event->featureChanged.featureType), event->featureChanged.featureType);
263 snprintf(family, sizeof(family), "SCCP/%s", device->id);
264
265 switch (event->featureChanged.featureType) {
266 case SCCP_FEATURE_CFWDNONE:
267 case SCCP_FEATURE_CFWDBUSY:
268 case SCCP_FEATURE_CFWDALL:
269 case SCCP_FEATURE_CFWDNOANSWER:
270 if((ld = event->featureChanged.optional_linedevice)) {
271 constLinePtr line = ld->line;
272 uint8_t instance = ld->lineInstance;
273 int res = 0;
274
275 sccp_dev_forward_status(line, instance, device);
276 snprintf(cfwdDeviceLineStore, sizeof(cfwdDeviceLineStore), "SCCP/%s/%s", device->id, line->name);
277 snprintf(cfwdLineDeviceStore, sizeof(cfwdLineDeviceStore), "SCCP/%s/%s", line->name, device->id);
278 if(event->featureChanged.featureType == SCCP_FEATURE_CFWDNONE) {
279 for(uint x = SCCP_CFWD_ALL; x < SCCP_CFWD_SENTINEL; x++) {
280 char cfwdstr[15] = "";
281 snprintf(cfwdstr, 14, "cfwd%s", sccp_cfwd2str((sccp_cfwd_t)x));
282 res |= iPbx.feature_removeFromDatabase(cfwdDeviceLineStore, cfwdstr);
283 res |= iPbx.feature_removeFromDatabase(cfwdLineDeviceStore, cfwdstr);
284 }
285 sccp_log((DEBUGCAT_CORE))(VERBOSE_PREFIX_3 "%s: all cfwd cleared from db (res:%d)\n", DEV_ID_LOG(device), res);
286 } else {
287 sccp_cfwd_t cfwd = sccp_feature2cfwd(event->featureChanged.featureType);
288 // const char * cfwdstr = sccp_cfwd2str(cfwd);
289 char cfwdstr[15] = "";
290 snprintf(cfwdstr, 14, "cfwd%s", sccp_cfwd2str(cfwd));
291 res |= iPbx.feature_removeFromDatabase(cfwdDeviceLineStore, cfwdstr);
292 res |= iPbx.feature_removeFromDatabase(cfwdLineDeviceStore, cfwdstr);
293 sccp_log((DEBUGCAT_CORE))(VERBOSE_PREFIX_3 "%s: db clear %s %s (res:%d))\n", DEV_ID_LOG(device), cfwdDeviceLineStore, cfwdstr, res);
294 if(ld->cfwd[cfwd].enabled) {
295 res |= iPbx.feature_addToDatabase(cfwdDeviceLineStore, cfwdstr, ld->cfwd[cfwd].number);
296 res |= iPbx.feature_addToDatabase(cfwdLineDeviceStore, cfwdstr, ld->cfwd[cfwd].number);
297 sccp_log((DEBUGCAT_CORE))(VERBOSE_PREFIX_3 "%s: db put %s %s (res:%d)\n", DEV_ID_LOG(device), cfwdDeviceLineStore, cfwdstr, res);
298 }
299 }
300 }
301 break;
302 case SCCP_FEATURE_DND:
303 // sccp_log((DEBUGCAT_CORE)) (VERBOSE_PREFIX_3 "%s: change dnd to %s\n", DEV_ID_LOG(device), device->dndFeature.status ? "on" : "off");
304 if (device->dndFeature.previousStatus != device->dndFeature.status) {
305 if (!device->dndFeature.status) {
306 sccp_log((DEBUGCAT_CORE)) (VERBOSE_PREFIX_3 "%s: change dnd to off\n", DEV_ID_LOG(device));
307 iPbx.feature_removeFromDatabase(family, "dnd");
308 } else {
309 if (device->dndFeature.status == SCCP_DNDMODE_SILENT) {
310 sccp_log((DEBUGCAT_CORE)) (VERBOSE_PREFIX_3 "%s: change dnd to silent\n", DEV_ID_LOG(device));
311 iPbx.feature_addToDatabase(family, "dnd", "silent");
312 } else {
313 sccp_log((DEBUGCAT_CORE)) (VERBOSE_PREFIX_3 "%s: change dnd to reject\n", DEV_ID_LOG(device));
314 iPbx.feature_addToDatabase(family, "dnd", "reject");
315 }
316 }
317 device->dndFeature.previousStatus = device->dndFeature.status;
318 }
319 break;
320 case SCCP_FEATURE_PRIVACY:
321 if (device->privacyFeature.previousStatus != device->privacyFeature.status) {
322 if (!device->privacyFeature.status) {
323 iPbx.feature_removeFromDatabase(family, "privacy");
324 } else {
325 char data[256];
326
327 snprintf(data, sizeof(data), "%d", device->privacyFeature.status);
328 iPbx.feature_addToDatabase(family, "privacy", data);
329 }
330 device->privacyFeature.previousStatus = device->privacyFeature.status;
331 }
332 break;
333 case SCCP_FEATURE_MONITOR:
334 if (device->monitorFeature.previousStatus != device->monitorFeature.status) {
335 if (device->monitorFeature.status & SCCP_FEATURE_MONITOR_STATE_REQUESTED) {
336 iPbx.feature_addToDatabase(family, "monitor", "on");
337 } else {
338 iPbx.feature_removeFromDatabase(family, "monitor");
339 }
340 device->monitorFeature.previousStatus = device->monitorFeature.status;
341 }
342 break;
343 default:
344 return;
345 }
346 }
347
348 /*!
349 * \brief Parse Composed ID
350 * \param labelString LabelString as string
351 * \param maxLength Maximum Length as unsigned int
352 * \param subscriptionId SubscriptionId as sccp_subscription_id_t (ByRef) [out]
353 * \param extension char array [SCCP_MAX_EXTENSION] [out]
354 * \return int containing number of matched subcription elements
355 *
356 * \callgraph
357 * \callergraph
358 */
sccp_parseComposedId(const char * labelString,unsigned int maxLength,sccp_subscription_id_t * subscriptionId,char extension[SCCP_MAX_EXTENSION])359 int sccp_parseComposedId(const char *labelString, unsigned int maxLength, sccp_subscription_id_t *subscriptionId, char extension[SCCP_MAX_EXTENSION])
360 {
361 pbx_assert(NULL != labelString && NULL != subscriptionId && NULL != extension);
362 int res = 0;
363 const char *stringIterator = 0;
364 uint32_t i = 0;
365 boolean_t endDetected = FALSE;
366 enum {EXTENSION, ID, CIDNAME, LABEL, AUX} state = EXTENSION;
367 memset(subscriptionId, 0, sizeof(sccp_subscription_id_t));
368
369 for (stringIterator = labelString; stringIterator < labelString + maxLength && !endDetected; stringIterator++) {
370 switch (state) {
371 case EXTENSION: // parsing of main id
372 pbx_assert(i < SCCP_MAX_EXTENSION);
373 switch (*stringIterator) {
374 case '\0':
375 endDetected = TRUE;
376 extension[i] = '\0';
377 res++;
378 break;
379 case '@':
380 extension[i] = '\0';
381 i = 0;
382 state = ID;
383 res++;
384 break;
385 case '!':
386 extension[i] = '\0';
387 i = 0;
388 state = AUX;
389 res++;
390 break;
391 default:
392 extension[i] = *stringIterator;
393 i++;
394 break;
395 }
396 break;
397
398 case ID: // parsing of sub id number
399
400 // button = line, 98099@=98041:cid_name#label !default
401
402 // 98099 is the linename
403 // @ starts a subscriptionid
404 // = replace the cid of the line with the one of the button
405 // 98041 is the replacement subscriptionid... also used the replacement cidnum
406 // cid_name is the new cid_name to use
407 // label is the new label to use
408 // ! starts the options / AUX
409 // default makes this the default line to dial out on
410
411 pbx_assert(i < sizeof(subscriptionId->number));
412 switch (*stringIterator) {
413 case '\0':
414 subscriptionId->number[i] = '\0';
415 endDetected = TRUE;
416 res++;
417 break;
418 case '+':
419 if(i == 0) {
420 subscriptionId->replaceCid = 0;
421 }
422 break;
423 case '=':
424 if(i == 0) {
425 subscriptionId->replaceCid = 1;
426 }
427 break;
428 case ':':
429 subscriptionId->number[i] = '\0'; // assign cidnum
430 i = 0;
431 state = CIDNAME;
432 res++;
433 break;
434 case '#':
435 subscriptionId->name[i] = '\0';
436 i = 0;
437 state = LABEL;
438 res++;
439 break;
440 case '!':
441 subscriptionId->number[i] = '\0';
442 i = 0;
443 state = AUX;
444 res++;
445 break;
446 default:
447 subscriptionId->number[i] = *stringIterator;
448 i++;
449 break;
450 }
451 break;
452
453 case CIDNAME: // parsing of sub id name
454 pbx_assert(i < sizeof(subscriptionId->name));
455 switch (*stringIterator) {
456 case '\0':
457 subscriptionId->name[i] = '\0';
458 endDetected = TRUE;
459 res++;
460 break;
461 case '#':
462 subscriptionId->name[i] = '\0';
463 i = 0;
464 state = LABEL;
465 res++;
466 break;
467 case '!':
468 subscriptionId->name[i] = '\0';
469 i = 0;
470 state = AUX ;
471 res++;
472 break;
473 default:
474 subscriptionId->name[i] = *stringIterator;
475 i++;
476 break;
477 }
478 break;
479
480 case LABEL: // parsing of sub id name
481 pbx_assert(i < sizeof(subscriptionId->label));
482 switch (*stringIterator) {
483 case '\0':
484 subscriptionId->label[i] = '\0';
485 endDetected = TRUE;
486 res++;
487 break;
488 case '!':
489 subscriptionId->label[i] = '\0';
490 i = 0;
491 state = AUX;
492 res++;
493 break;
494 default:
495 subscriptionId->label[i] = *stringIterator;
496 i++;
497 break;
498 }
499 break;
500
501 case AUX: // parsing of auxiliary parameter
502 pbx_assert(i < sizeof(subscriptionId->aux));
503 switch (*stringIterator) {
504 case '\0':
505 subscriptionId->aux[i] = '\0';
506 endDetected = TRUE;
507 res++;
508 break;
509 default:
510 subscriptionId->aux[i] = *stringIterator;
511 i++;
512 break;
513 }
514 break;
515
516 default:
517 pbx_assert(FALSE);
518 res = 0;
519 break;
520 }
521 }
522 return res;
523 }
524
525 /*!
526 * \brief Match Subscription ID
527 * \param channel SCCP Channel
528 * \param subscriptionIdNum Subscription ID Number for ld
529 * \return result as boolean
530 *
531 * \callgraph * \callergraph
532 */
sccp_util_matchSubscriptionId(constChannelPtr channel,const char * subscriptionIdNum)533 boolean_t __PURE__ sccp_util_matchSubscriptionId(constChannelPtr channel, const char * subscriptionIdNum)
534 {
535 boolean_t result = TRUE;
536
537 boolean_t filterPhones = FALSE;
538
539 /* Determine if the phones registered on the shared line shall be filtered at all:
540 only if a non-trivial subscription id is specified with the calling channel,
541 which is not the default subscription id of the shared line denoting all devices,
542 the phones are addressed individually. (-DD) */
543 filterPhones = FALSE; /* set the default to call all phones */
544
545 /* First condition: Non-trivial subscriptionId specified for matching in call. */
546 if (sccp_strlen(channel->subscriptionId.number) != 0) {
547 /* Second condition: SubscriptionId does not match default subscriptionId of line. */
548 if (0 != strncasecmp(channel->subscriptionId.number, channel->line->defaultSubscriptionId.number, sccp_strlen(channel->subscriptionId.number))) {
549 filterPhones = TRUE;
550 }
551 }
552
553 if (FALSE == filterPhones) {
554 /* Accept phone for calling if all phones shall be called. */
555 result = TRUE;
556 } else if (0 != sccp_strlen(subscriptionIdNum) && /* We already know that we won't search for a trivial subscriptionId. */
557 (0 != strncasecmp(channel->subscriptionId.number, subscriptionIdNum, sccp_strlen(channel->subscriptionId.number)))) { /* Do the match! */
558 result = FALSE;
559 }
560 #if 0
561 pbx_log(LOG_NOTICE, "sccp_channel->subscriptionId.number=%s, length=%d\n", channel->subscriptionId.number, sccp_strlen(channel->subscriptionId.number));
562 pbx_log(LOG_NOTICE, "subscriptionIdNum=%s, length=%d\n", subscriptionIdNum ? subscriptionIdNum : "NULL", subscriptionIdNum ? sccp_strlen(subscriptionIdNum) : -1);
563
564 pbx_log(LOG_NOTICE, "sccp_util_matchSubscriptionId: sccp_channel->subscriptionId.number=%s, SubscriptionId=%s\n", (channel->subscriptionId.number) ? channel->subscriptionId.number : "NULL", (subscriptionIdNum) ? subscriptionIdNum : "NULL");
565 pbx_log(LOG_NOTICE, "sccp_util_matchSubscriptionId: result: %d\n", result);
566 #endif
567 return result;
568 }
569
570 /*!
571 * \brief Compare the information of two socket with one another
572 * \param s0 Socket Information
573 * \param s1 Socket Information
574 * \return success as int
575 *
576 * \retval FALSE on diff
577 * \retval TRUE on equal
578 */
sccp_netsock_equals(const struct sockaddr_storage * const s0,const struct sockaddr_storage * const s1)579 gcc_inline boolean_t sccp_netsock_equals(const struct sockaddr_storage * const s0, const struct sockaddr_storage *const s1)
580 {
581 if ((s0->ss_family == s1->ss_family && sccp_netsock_cmp_addr(s0, s1) == 0) && sccp_netsock_cmp_port(s0, s1) == 0) {
582 return TRUE;
583 }
584 return FALSE;
585 }
586
587 /*!
588 * \brief SCCP version of strlen_zero
589 * \param data String to be checked
590 * \return zerolength as boolean
591 *
592 * \retval FALSE on non zero length
593 * \retval TRUE on zero length
594 */
sccp_strlen_zero(const char * data)595 gcc_inline boolean_t sccp_strlen_zero(const char *data)
596 {
597 if (!data || (*data == '\0')) {
598 return TRUE;
599 }
600
601 return FALSE;
602 }
603
604 /*!
605 * \brief SCCP version of strlen
606 * \param data String to be checked
607 * \return length as int
608 */
sccp_strlen(const char * data)609 gcc_inline size_t sccp_strlen(const char *data)
610 {
611 if (!data || (*data == '\0')) {
612 return 0;
613 }
614 return strlen(data);
615 }
616
617 /*!
618 * \brief SCCP version of strequals
619 * \note Takes into account zero length strings, if both strings are zero length returns TRUE
620 * \param data1 String to be checked
621 * \param data2 String to be checked
622 * \return !strcmp as boolean_t
623 *
624 * \retval booleant_t on !strcmp
625 * \retval TRUE on both zero length
626 * \retval FALSE on one of the the parameters being zero length
627 */
sccp_strequals(const char * data1,const char * data2)628 gcc_inline boolean_t sccp_strequals(const char *data1, const char *data2)
629 {
630 if (sccp_strlen_zero(data1) && sccp_strlen_zero(data2)) {
631 return TRUE;
632 } if (!sccp_strlen_zero(data1) && !sccp_strlen_zero(data2) && (sccp_strlen(data1) == sccp_strlen(data2))) {
633 return !strcmp(data1, data2);
634 }
635 return FALSE;
636 }
637
638 /*!
639 * \brief SCCP version of strcaseequals
640 * \note Takes into account zero length strings, if both strings are zero length returns TRUE
641 * \param data1 String to be checked
642 * \param data2 String to be checked
643 * \return !strcasecmp as boolean_t
644 *
645 * \retval boolean_t on strcaseequals
646 * \retval TRUE on both zero length
647 * \retval FALSE on one of the the parameters being zero length
648 */
sccp_strcaseequals(const char * data1,const char * data2)649 gcc_inline boolean_t sccp_strcaseequals(const char *data1, const char *data2)
650 {
651 if (sccp_strlen_zero(data1) && sccp_strlen_zero(data2)) {
652 return TRUE;
653 } if (!sccp_strlen_zero(data1) && !sccp_strlen_zero(data2) && (sccp_strlen(data1) == sccp_strlen(data2))) {
654 return !strcasecmp(data1, data2);
655 }
656 return FALSE;
657 }
658
sccp_strIsNumeric(const char * s)659 int __PURE__ sccp_strIsNumeric(const char *s)
660 {
661 if (*s) {
662 char c = 0;
663
664 while ((c = *s++)) {
665 if (!isdigit(c)) {
666 return 0;
667 }
668 }
669 return 1;
670 }
671 return 0;
672 }
673
674 /*!
675 * \brief Free a list of Host Access Rules
676 * \param ha The head of the list of HAs to free
677 * \retval void
678 */
sccp_free_ha(struct sccp_ha * ha)679 void sccp_free_ha(struct sccp_ha *ha)
680 {
681 struct sccp_ha * hal = NULL;
682
683 while (ha) {
684 hal = ha;
685 ha = ha->next;
686 sccp_free(hal);
687 }
688 }
689
690 /* Helper functions for ipv6 / apply_ha / append_ha */
691 /*!
692 * \brief
693 * Isolate a 32-bit section of an IPv6 address
694 *
695 * An IPv6 address can be divided into 4 32-bit chunks. This gives
696 * easy access to one of these chunks.
697 *
698 * \param sin6 A pointer to a struct sockaddr_in6
699 * \param index Which 32-bit chunk to operate on. Must be in the range 0-3.
700 */
701 #define V6_WORD(sin6, index) ((uint32_t *)&((sin6)->sin6_addr))[(index)]
702
703 /*!
704 * \brief
705 * Apply a netmask to an address and store the result in a separate structure.
706 *
707 * When dealing with IPv6 addresses, one cannot apply a netmask with a simple
708 * logical and operation. Furthermore, the incoming address may be an IPv4 address
709 * and need to be mapped properly before attempting to apply a rule.
710 *
711 * \param netaddr The IP address to apply the mask to.
712 * \param netmask The netmask configured in the host access rule.
713 * \param result [out] The resultant address after applying the netmask to the given address
714 *
715 * \retval 0 Successfully applied netmask
716 * \retval -1 Failed to apply netmask
717 */
apply_netmask(const struct sockaddr_storage * netaddr,const struct sockaddr_storage * netmask,struct sockaddr_storage * result)718 static int apply_netmask(const struct sockaddr_storage *netaddr, const struct sockaddr_storage *netmask, struct sockaddr_storage *result)
719 {
720 int res = 0;
721
722 char *straddr = pbx_strdupa(sccp_netsock_stringify_addr(netaddr));
723 char *strmask = pbx_strdupa(sccp_netsock_stringify_addr(netmask));
724
725 sccp_log(DEBUGCAT_HIGH) (VERBOSE_PREFIX_2 "SCCP: (apply_netmask) applying netmask to %s/%s\n", straddr, strmask);
726
727 if (netaddr->ss_family == AF_INET) {
728 struct sockaddr_in result4 = { 0, };
729 struct sockaddr_in *addr4 = (struct sockaddr_in *) netaddr;
730 struct sockaddr_in *mask4 = (struct sockaddr_in *) netmask;
731
732 result4.sin_family = AF_INET;
733 result4.sin_addr.s_addr = addr4->sin_addr.s_addr & mask4->sin_addr.s_addr;
734 memcpy(result, &result4, sizeof(result4));
735 } else if (netaddr->ss_family == AF_INET6) {
736 struct sockaddr_in6 result6 = { 0, };
737 struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) netaddr;
738 struct sockaddr_in6 *mask6 = (struct sockaddr_in6 *) netmask;
739 int i = 0;
740
741 result6.sin6_family = AF_INET6;
742 for (i = 0; i < 4; ++i) {
743 V6_WORD(&result6, i) = V6_WORD(addr6, i) & V6_WORD(mask6, i);
744 }
745 memcpy(result, &result6, sizeof(result6));
746 } else {
747 pbx_log(LOG_NOTICE, "SCCP: (apply_netmask) Unsupported address scheme\n");
748 /* Unsupported address scheme */
749 res = -1;
750 }
751 sccp_log(DEBUGCAT_HIGH) (VERBOSE_PREFIX_2 "SCCP: (apply_netmask) result applied netmask %s\n", sccp_netsock_stringify_addr(result));
752
753 return res;
754 }
755
756 /*!
757 * \brief Apply a set of rules to a given IP address
758 *
759 * \param ha The head of the list of host access rules to follow
760 * \param addr A sockaddr_in whose address is considered when matching rules
761 * \retval AST_SENSE_ALLOW The IP address passes our ACL
762 * \retval AST_SENSE_DENY The IP address fails our ACL
763 */
sccp_apply_ha(const struct sccp_ha * ha,const struct sockaddr_storage * addr)764 int sccp_apply_ha(const struct sccp_ha *ha, const struct sockaddr_storage *addr)
765 {
766 return sccp_apply_ha_default(ha, addr, AST_SENSE_ALLOW);
767 }
768
769 /*!
770 * \brief Apply a set of rules to a given IP address
771 *
772 * \details
773 * The list of host access rules is traversed, beginning with the
774 * input rule. If the IP address given matches a rule, the "sense"
775 * of that rule is used as the return value. Note that if an IP
776 * address matches multiple rules that the last one matched will be
777 * the one whose sense will be returned.
778 *
779 * \param ha The head of the list of host access rules to follow
780 * \param addr An sockaddr_storage whose address is considered when matching rules
781 * \param defaultValue int value
782 * \retval AST_SENSE_ALLOW The IP address passes our ACL
783 * \retval AST_SENSE_DENY The IP address fails our ACL
784 */
sccp_apply_ha_default(const struct sccp_ha * ha,const struct sockaddr_storage * addr,int defaultValue)785 int sccp_apply_ha_default(const struct sccp_ha *ha, const struct sockaddr_storage *addr, int defaultValue)
786 {
787 /* Start optimistic */
788 int res = defaultValue;
789 const struct sccp_ha * current_ha = NULL;
790
791 for (current_ha = ha; current_ha; current_ha = current_ha->next) {
792
793 struct sockaddr_storage result;
794 struct sockaddr_storage mapped_addr;
795 const struct sockaddr_storage * addr_to_use = NULL;
796
797 if (sccp_netsock_is_IPv4(&ha->netaddr)) {
798 if (sccp_netsock_is_IPv6(addr)) {
799 if (sccp_netsock_is_mapped_IPv4(addr)) {
800 if (!sccp_netsock_ipv4_mapped(addr, &mapped_addr)) {
801 pbx_log(LOG_ERROR, "%s provided to ast_sockaddr_ipv4_mapped could not be converted. That shouldn't be possible\n", sccp_netsock_stringify_addr(addr));
802 continue;
803 }
804 addr_to_use = &mapped_addr;
805 } else {
806 /* An IPv4 ACL does not apply to an IPv6 address */
807 continue;
808 }
809 } else {
810 /* Address is IPv4 and ACL is IPv4. No biggie */
811 addr_to_use = addr;
812 }
813 } else {
814 if (sccp_netsock_is_IPv6(addr) && !sccp_netsock_is_mapped_IPv4(addr)) {
815 addr_to_use = addr;
816 } else {
817 /* Address is IPv4 or IPv4 mapped but ACL is IPv6. Skip */
818 continue;
819 }
820 }
821 // char *straddr = pbx_strdupa(sccp_netsock_stringify_addr(¤t_ha->netaddr));
822 // char *strmask = pbx_strdupa(sccp_netsock_stringify_addr(¤t_ha->netmask));
823 // sccp_log(DEBUGCAT_HIGH)(VERBOSE_PREFIX_3 "%s:%s/%s\n", AST_SENSE_DENY == current_ha->sense ? "deny" : "permit", straddr, strmask);
824
825 /* For each rule, if this address and the netmask = the net address
826 apply the current rule */
827 if (apply_netmask(addr_to_use, ¤t_ha->netmask, &result)) {
828 /* Unlikely to happen since we know the address to be IPv4 or IPv6 */
829 continue;
830 }
831 if (sccp_netsock_cmp_addr(&result, ¤t_ha->netaddr) == 0) {
832 //sccp_log(DEBUGCAT_HIGH)(VERBOSE_PREFIX_3 "SCCP: apply_ha_default: result: %s\n", sccp_netsock_stringify_addr(&result));
833 //sccp_log(DEBUGCAT_HIGH)(VERBOSE_PREFIX_3 "SCCP: apply_ha_default: current_ha->netaddr: %s\n", sccp_netsock_stringify_addr(¤t_ha->netaddr));
834 res = current_ha->sense;
835 }
836 }
837 return res;
838 }
839
840 /*!
841 * \brief
842 * Parse an IPv4 or IPv6 address string.
843 *
844 * \details
845 * Parses a string containing an IPv4 or IPv6 address followed by an optional
846 * port (separated by a colon) into a struct ast_sockaddr. The allowed formats
847 * are the following:
848 *
849 * a.b.c.d
850 * a.b.c.d:port
851 * a:b:c:...:d
852 * [a:b:c:...:d]
853 * [a:b:c:...:d]:port
854 *
855 * Host names are NOT allowed.
856 *
857 * \param[out] addr The resulting ast_sockaddr. This MAY be NULL from
858 * functions that are performing validity checks only, e.g. ast_parse_arg().
859 * \param str The string to parse
860 * \param flags If set to zero, a port MAY be present. If set to
861 * PARSE_PORT_IGNORE, a port MAY be present but will be ignored. If set to
862 * PARSE_PORT_REQUIRE, a port MUST be present. If set to PARSE_PORT_FORBID, a
863 * port MUST NOT be present.
864 *
865 * \retval 1 Success
866 * \retval 0 Failure
867 */
sccp_sockaddr_storage_parse(struct sockaddr_storage * addr,const char * str,int flags)868 int sccp_sockaddr_storage_parse(struct sockaddr_storage *addr, const char *str, int flags)
869 {
870 struct addrinfo hints;
871 struct addrinfo * res = NULL;
872 char * s = NULL;
873 char * host = NULL;
874 char * port = NULL;
875 int e = 0;
876
877 s = pbx_strdupa(str);
878 if (!sccp_netsock_split_hostport(s, &host, &port, flags)) {
879 return 0;
880 }
881
882 memset(&hints, 0, sizeof(hints));
883 /* Hint to get only one entry from getaddrinfo */
884 hints.ai_socktype = SOCK_DGRAM;
885
886 #ifdef AI_NUMERICSERV
887 hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV;
888 #else
889 hints.ai_flags = AI_NUMERICHOST;
890 #endif
891 if ((e = getaddrinfo(host, port, &hints, &res))) {
892 if (e != EAI_NONAME) { /* if this was just a host name rather than a ip address, don't print error */
893 pbx_log(LOG_ERROR, "getaddrinfo(\"%s\", \"%s\", ...): %s\n", host, S_OR(port, "(null)"), gai_strerror(e));
894 }
895 return 0;
896 }
897
898 /*
899 * I don't see how this could be possible since we're not resolving host
900 * names. But let's be careful...
901 */
902 if (res->ai_next != NULL) {
903 pbx_log(LOG_WARNING, "getaddrinfo() returned multiple " "addresses. Ignoring all but the first.\n");
904 }
905
906 if (addr) {
907 memcpy(addr, res->ai_addr, (res->ai_family == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
908 sccp_log(DEBUGCAT_HIGH) (VERBOSE_PREFIX_2 "SCCP: (sccp_sockaddr_storage_parse) addr:%s\n", sccp_netsock_stringify_addr(addr));
909 }
910
911 freeaddrinfo(res);
912 return 1;
913 }
914
915 /*!
916 * \brief
917 * Parse a netmask in CIDR notation
918 *
919 * \details
920 * For a mask of an IPv4 address, this should be a number between 0 and 32. For
921 * a mask of an IPv6 address, this should be a number between 0 and 128. This
922 * function creates an IPv6 sockaddr_storage from the given netmask. For masks of
923 * IPv4 addresses, this is accomplished by adding 96 to the original netmask.
924 *
925 * \param[out] addr The sockaddr_stroage produced from the CIDR netmask
926 * \param is_v4 Tells if the address we are masking is IPv4.
927 * \param mask_str The CIDR mask to convert
928 * \retval -1 Failure
929 * \retval 0 Success
930 */
parse_cidr_mask(struct sockaddr_storage * addr,int is_v4,const char * mask_str)931 static int parse_cidr_mask(struct sockaddr_storage *addr, int is_v4, const char *mask_str)
932 {
933 int mask = 0;
934
935 if (sscanf(mask_str, "%30d", &mask) != 1) {
936 return -1;
937 }
938 if (is_v4) {
939 struct sockaddr_in sin = { 0, };
940 if (mask < 0 || mask > 32) {
941 return -1;
942 }
943 sin.sin_family = AF_INET;
944 /* If mask is 0, then we already have the
945 * appropriate all 0s address in sin from
946 * the above memset.
947 */
948 if (mask != 0) {
949 sin.sin_addr.s_addr = htonl(0xFFFFFFFF << (32 - mask));
950 }
951 memcpy(addr, &sin, sizeof(sin));
952 } else {
953 struct sockaddr_in6 sin6 = { 0, };
954 int i = 0;
955
956 if (mask < 0 || mask > 128) {
957 return -1;
958 }
959 sin6.sin6_family = AF_INET6;
960 for (i = 0; i < 4; ++i) {
961 /* Once mask reaches 0, we don't have
962 * to explicitly set anything anymore
963 * since sin6 was zeroed out already
964 */
965 if (mask > 0) {
966 V6_WORD(&sin6, i) = htonl(0xFFFFFFFF << (mask < 32 ? (32 - mask) : 0));
967 mask -= mask < 32 ? mask : 32;
968 }
969 }
970 memcpy(addr, &sin6, sizeof(sin6));
971 }
972 return 0;
973 }
974
975 /*!
976 * \brief Add a new rule to a list of HAs
977 *
978 * \param sense Either "permit" or "deny" (Actually any 'p' word will result
979 * in permission, and any other word will result in denial)
980 * \param stuff The IP address and subnet mask, separated with a '/'. The subnet
981 * mask can either be in dotted-decimal format or in CIDR notation (i.e. 0-32).
982 * \param path The head of the HA list to which we wish to append our new rule. If
983 * NULL is passed, then the new rule will become the head of the list
984 * \param[out] error The integer error points to will be set non-zero if an error occurs
985 * \return The head of the HA list
986 */
sccp_append_ha(const char * sense,const char * stuff,struct sccp_ha * path,int * error)987 struct sccp_ha *sccp_append_ha(const char *sense, const char *stuff, struct sccp_ha *path, int *error)
988 {
989 struct sccp_ha * ha = NULL;
990 struct sccp_ha * prev = NULL;
991 struct sccp_ha * ret = NULL;
992 char *tmp = pbx_strdupa(stuff);
993 char * address = NULL;
994
995 char * mask = NULL;
996 int addr_is_v4 = 0;
997
998 ret = path;
999 while (path) {
1000 prev = path;
1001 path = path->next;
1002 }
1003
1004 if (!(ha = (struct sccp_ha *)sccp_calloc(sizeof *ha, 1))) {
1005 pbx_log(LOG_ERROR, SS_Memory_Allocation_Error, "SCCP");
1006 if (error) {
1007 *error = 1;
1008 }
1009 return ret;
1010 }
1011
1012 address = strsep(&tmp, "/");
1013 if (!address) {
1014 address = tmp;
1015 } else {
1016 mask = tmp;
1017 }
1018 if (!sccp_sockaddr_storage_parse(&ha->netaddr, address, PARSE_PORT_FORBID)) {
1019 pbx_log(LOG_WARNING, "Invalid IP address: %s\n", address);
1020 sccp_free_ha(ha);
1021 if (error) {
1022 *error = 1;
1023 }
1024 return ret;
1025 }
1026 /*
1027 sccp_log(DEBUGCAT_HIGH)(VERBOSE_PREFIX_2 "SCCP: (sccp_append_ha) netaddr:%s\n", sccp_netsock_stringify_addr(&ha->netaddr));
1028 */
1029 /* If someone specifies an IPv4-mapped IPv6 address,
1030 * we just convert this to an IPv4 ACL
1031 */
1032 if (sccp_netsock_ipv4_mapped(&ha->netaddr, &ha->netaddr)) {
1033 pbx_log(LOG_NOTICE, "IPv4-mapped ACL network address specified. " "Converting to an IPv4 ACL network address.\n");
1034 }
1035
1036 addr_is_v4 = sccp_netsock_is_IPv4(&ha->netaddr);
1037
1038 if (!mask) {
1039 parse_cidr_mask(&ha->netmask, addr_is_v4, addr_is_v4 ? "32" : "128");
1040 } else if (strchr(mask, ':') || strchr(mask, '.')) {
1041 int mask_is_v4 = 0;
1042
1043 /* Mask is of x.x.x.x or x:x:x:x:x:x:x:x variety */
1044 sccp_log(DEBUGCAT_HIGH) (VERBOSE_PREFIX_2 "SCCP: (sccp_append_ha) mask:%s\n", mask);
1045 if (!sccp_sockaddr_storage_parse(&ha->netmask, mask, PARSE_PORT_FORBID)) {
1046 pbx_log(LOG_WARNING, "Invalid netmask: %s\n", mask);
1047 sccp_free_ha(ha);
1048 if (error) {
1049 *error = 1;
1050 }
1051 return ret;
1052 }
1053 sccp_log(DEBUGCAT_HIGH) (VERBOSE_PREFIX_2 "SCCP: (sccp_append_ha) strmask:%s, netmask:%s\n", mask, sccp_netsock_stringify_addr(&ha->netmask));
1054 /* If someone specifies an IPv4-mapped IPv6 netmask,
1055 * we just convert this to an IPv4 ACL
1056 */
1057 if (sccp_netsock_ipv4_mapped(&ha->netmask, &ha->netmask)) {
1058 ast_log(LOG_NOTICE, "IPv4-mapped ACL netmask specified. " "Converting to an IPv4 ACL netmask.\n");
1059 }
1060 mask_is_v4 = sccp_netsock_is_IPv4(&ha->netmask);
1061 if (addr_is_v4 ^ mask_is_v4) {
1062 pbx_log(LOG_WARNING, "Address and mask are not using same address scheme (%d / %d)\n", addr_is_v4, mask_is_v4);
1063 sccp_free_ha(ha);
1064 if (error) {
1065 *error = 1;
1066 }
1067 return ret;
1068 }
1069 } else if (parse_cidr_mask(&ha->netmask, addr_is_v4, mask)) {
1070 pbx_log(LOG_WARNING, "Invalid CIDR netmask: %s\n", mask);
1071 sccp_free_ha(ha);
1072 if (error) {
1073 *error = 1;
1074 }
1075 return ret;
1076 }
1077 if (apply_netmask(&ha->netaddr, &ha->netmask, &ha->netaddr)) {
1078 /* This shouldn't happen because ast_sockaddr_parse would
1079 * have failed much earlier on an unsupported address scheme
1080 */
1081 char *failaddr = pbx_strdupa(sccp_netsock_stringify_addr(&ha->netaddr));
1082 char *failmask = pbx_strdupa(sccp_netsock_stringify_addr(&ha->netmask));
1083
1084 pbx_log(LOG_WARNING, "Unable to apply netmask %s to address %s\n", failaddr, failmask);
1085 sccp_free_ha(ha);
1086 if (error) {
1087 *error = 1;
1088 }
1089 return ret;
1090 }
1091
1092 ha->sense = strncasecmp(sense, "p", 1) ? AST_SENSE_DENY : AST_SENSE_ALLOW;
1093
1094 ha->next = NULL;
1095 if (prev) {
1096 prev->next = ha;
1097 } else {
1098 ret = ha;
1099 }
1100
1101 sccp_log (DEBUGCAT_HIGH) (VERBOSE_PREFIX_2 "%s/%s sense %d appended to acl for peer\n", sccp_netsock_stringify_addr (&ha->netaddr), sccp_netsock_stringify_addr (&ha->netmask), ha->sense);
1102
1103 return ret;
1104 }
1105
sccp_print_ha(struct ast_str * buf,int buflen,struct sccp_ha * path)1106 void sccp_print_ha(struct ast_str *buf, int buflen, struct sccp_ha *path)
1107 {
1108 while (path) {
1109 pbx_str_append (&buf, buflen, "%s:%s/%s,", AST_SENSE_DENY == path->sense ? "deny" : "permit", sccp_netsock_stringify_addr (&path->netaddr), sccp_netsock_stringify_addr (&path->netmask));
1110 path = path->next;
1111 }
1112 }
1113
1114 #if CS_TEST_FRAMEWORK
1115 #include <asterisk/test.h>
AST_TEST_DEFINE(chan_sccp_acl_tests)1116 AST_TEST_DEFINE(chan_sccp_acl_tests)
1117 {
1118 struct sccp_ha *ha = NULL;
1119 struct sockaddr_storage sas10;
1120
1121 struct sockaddr_storage sas1015;
1122
1123 struct sockaddr_storage sas172;
1124
1125 struct sockaddr_storage sas200;
1126
1127 struct sockaddr_storage sasff;
1128
1129 struct sockaddr_storage sasffff;
1130 int error = 0;
1131
1132 switch (cmd) {
1133 case TEST_INIT:
1134 info->name = "permit_deny";
1135 info->category = "/channels/chan_sccp/acl/";
1136 info->summary = "chan-sccp-b ha / permit / deny test";
1137 info->description = "chan-sccp-b ha / permit / deny parsing tests";
1138 return AST_TEST_NOT_RUN;
1139 case TEST_EXECUTE:
1140 break;
1141 }
1142
1143 pbx_test_status_update(test, "Executing chan-sccp-b ha path tests...\n");
1144
1145 pbx_test_status_update(test, "Setting up sockaddr_storage...\n");
1146 sccp_sockaddr_storage_parse(&sas10, "10.0.0.1", PARSE_PORT_FORBID);
1147 pbx_test_validate(test, sccp_netsock_is_IPv4(&sas10));
1148
1149 sccp_sockaddr_storage_parse(&sas1015, "10.15.15.1", PARSE_PORT_FORBID);
1150 pbx_test_validate(test, sccp_netsock_is_IPv4(&sas1015));
1151
1152 sccp_sockaddr_storage_parse(&sas172, "172.16.0.1", PARSE_PORT_FORBID);
1153 pbx_test_validate(test, sccp_netsock_is_IPv4(&sas172));
1154
1155 sccp_sockaddr_storage_parse(&sas200, "200.200.100.100", PARSE_PORT_FORBID);
1156 pbx_test_validate(test, sccp_netsock_is_IPv4(&sas200));
1157
1158 sccp_sockaddr_storage_parse(&sasff, "fe80::ffff:0:0:0", PARSE_PORT_FORBID);
1159 pbx_test_validate(test, sccp_netsock_is_IPv6(&sasff));
1160
1161 sccp_sockaddr_storage_parse(&sasffff, "fe80::ffff:0:ffff:0", PARSE_PORT_FORBID);
1162 pbx_test_validate(test, sccp_netsock_is_IPv6(&sasffff));
1163
1164 // test 1
1165 pbx_test_status_update(test, "test 1: ha deny all\n");
1166 ha = sccp_append_ha("deny", "0.0.0.0/0.0.0.0", ha, &error);
1167 pbx_test_validate(test, error == 0);
1168 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas10) == AST_SENSE_DENY);
1169 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas1015) == AST_SENSE_DENY);
1170 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas172) == AST_SENSE_DENY);
1171 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas200) == AST_SENSE_DENY);
1172
1173 // test 2
1174 pbx_test_status_update(test, "test 2: previous + permit 10.15.15.0/255.255.255.0\n");
1175 ha = sccp_append_ha("permit", "10.15.15.0/255.255.255.0", ha, &error);
1176 pbx_test_validate(test, error == 0);
1177 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas10) == AST_SENSE_DENY);
1178 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas1015) != AST_SENSE_DENY);
1179 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas172) == AST_SENSE_DENY);
1180 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas200) == AST_SENSE_DENY);
1181
1182 // test 3
1183 pbx_test_status_update(test, "test 3: previous + second permit 10.15.15.0/255.255.255.0\n");
1184 ha = sccp_append_ha("permit", "10.15.15.0/255.255.255.0", ha, &error);
1185 pbx_test_validate(test, error == 0);
1186 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas10) == AST_SENSE_DENY);
1187 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas1015) != AST_SENSE_DENY);
1188 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas172) == AST_SENSE_DENY);
1189 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas200) == AST_SENSE_DENY);
1190 sccp_free_ha(ha);
1191 ha = NULL;
1192
1193 // test 4
1194 pbx_test_status_update(test, "test 4: deny all + permit 10.0.0.0/255.255.255.0\n");
1195 ha = sccp_append_ha("deny", "0.0.0.0/0.0.0.0", ha, &error);
1196 pbx_test_validate(test, error == 0);
1197 ha = sccp_append_ha("permit", "10.0.0.0/255.0.0.0", ha, &error);
1198 pbx_test_validate(test, error == 0);
1199 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas10) != AST_SENSE_DENY);
1200 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas1015) != AST_SENSE_DENY);
1201 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas172) == AST_SENSE_DENY);
1202 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas200) == AST_SENSE_DENY);
1203
1204 // test 5
1205 pbx_test_status_update(test, "test 5: previous + 172.16.0.0/255.255.0.0\n");
1206 ha = sccp_append_ha("permit", "172.16.0.0/255.0.0.0", ha, &error);
1207 pbx_test_validate(test, error == 0);
1208 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas10) != AST_SENSE_DENY);
1209 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas1015) != AST_SENSE_DENY);
1210 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas172) != AST_SENSE_DENY);
1211 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas200) == AST_SENSE_DENY);
1212
1213 // test 6
1214 pbx_test_status_update(test, "test 6: previous + deny_all at the end\n");
1215 ha = sccp_append_ha("deny", "0.0.0.0/0.0.0.0", ha, &error);
1216 pbx_test_validate(test, error == 0);
1217 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas10) == AST_SENSE_DENY);
1218 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas1015) == AST_SENSE_DENY);
1219 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas172) == AST_SENSE_DENY);
1220 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas200) == AST_SENSE_DENY);
1221 sccp_free_ha(ha);
1222 ha = NULL;
1223
1224 // test 7: ipv6
1225 pbx_test_status_update(test, "test 7: IPv6: deny 0.0.0.0/0.0.0.0,::,::/0::\n");
1226 ha = sccp_append_ha("deny", "0.0.0.0/0.0.0.0", ha, &error);
1227 pbx_test_validate(test, error == 0);
1228 ha = sccp_append_ha("deny", "::", ha, &error);
1229 pbx_test_validate(test, error == 0);
1230 ha = sccp_append_ha("deny", "::/0", ha, &error);
1231 pbx_test_validate(test, error == 0);
1232 //pbx_test_status_update(test, "test 7: deny !fe80::/64\n"); /* we cannot parse this format yes (asterisk-13) */
1233 //ha = sccp_append_ha("deny", "!fe80::/64", ha, &error);
1234 //pbx_test_validate(test, error == 0);
1235 pbx_test_status_update(test, " : previous + permit fe80::ffff:0:0:0/80\n");
1236 ha = sccp_append_ha("permit", "fe80::ffff:0:0:0/80", ha, &error);
1237 pbx_test_validate(test, error == 0);
1238 pbx_test_status_update(test, " : previous + permit fe80::ffff:0:ffff:0/112\n");
1239 ha = sccp_append_ha("permit", "fe80::ffff:0:ffff:0/112", ha, &error);
1240 pbx_test_validate(test, error == 0);
1241 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas10) == AST_SENSE_DENY);
1242 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sasff) != AST_SENSE_DENY);
1243 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sasffff) != AST_SENSE_DENY);
1244 pbx_test_validate(test, sccp_apply_ha(ha, (struct sockaddr_storage *) &sas200) == AST_SENSE_DENY);
1245 sccp_free_ha(ha);
1246 ha = NULL;
1247
1248 return AST_TEST_PASS;
1249 }
1250
AST_TEST_DEFINE(chan_sccp_acl_invalid_tests)1251 AST_TEST_DEFINE(chan_sccp_acl_invalid_tests)
1252 {
1253 struct sccp_ha *ha = NULL;
1254 enum ast_test_result_state res = AST_TEST_PASS;
1255
1256 switch (cmd) {
1257 case TEST_INIT:
1258 info->name = "invalid";
1259 info->category = "/channels/chan_sccp/acl/";
1260 info->summary = "Invalid ACL unit test";
1261 info->description = "Ensures that garbage ACL values are not accepted";
1262 return AST_TEST_NOT_RUN;
1263 case TEST_EXECUTE:
1264 break;
1265 }
1266
1267 pbx_test_status_update(test, "Executing invalid acl test tests...\n");
1268
1269 // test invalid
1270 const char * invalid_acls[] = {
1271 /* Negative netmask */
1272 "1.3.3.7/-1",
1273 /* Netmask too large */
1274 "1.3.3.7/33",
1275 /* Netmask waaaay too large */
1276 "1.3.3.7/92342348927389492307420",
1277 /* Netmask non-numeric */
1278 "1.3.3.7/California",
1279 /* Too many octets in Netmask */
1280 "1.3.3.7/255.255.255.255.255",
1281 /* Octets in IP address exceed 255 */
1282 "57.60.278.900/31",
1283 /* Octets in IP address exceed 255 and are negative */
1284 "400.32.201029.-6/24",
1285 /* Invalidly formatted IP address */
1286 "EGGSOFDEATH/4000",
1287 /* Too many octets in IP address */
1288 "33.4.7.8.3/300030",
1289 /* Too many octets in Netmask */
1290 "1.2.3.4/6.7.8.9.0",
1291 /* Too many octets in IP address */
1292 "3.1.4.1.5.9/3",
1293 /* IPv6 address has multiple double colons */
1294 "ff::ff::ff/3",
1295 /* IPv6 address is too long */
1296 "1234:5678:90ab:cdef:1234:5678:90ab:cdef:1234/56",
1297 /* IPv6 netmask is too large */
1298 "::ffff/129",
1299 /* IPv4-mapped IPv6 address has too few octets */
1300 "::ffff:255.255.255/128",
1301 /* Leading and trailing colons for IPv6 address */
1302 ":1234:/15",
1303 /* IPv6 address and IPv4 netmask */
1304 "fe80::1234/255.255.255.0",
1305 };
1306 uint8_t i = 0;
1307 for (i = 0; i < ARRAY_LEN(invalid_acls); ++i) {
1308 int error = 0;
1309 ha = sccp_append_ha("permit", invalid_acls[i], ha, &error);
1310 if (ha || !error) {
1311 pbx_test_status_update(test, "ACL %s accepted even though it is total garbage.\n", invalid_acls[i]);
1312 if (ha) {
1313 sccp_free_ha(ha);
1314 }
1315 res = AST_TEST_FAIL;
1316 }
1317 }
1318 sccp_free_ha(ha);
1319 ha = NULL;
1320
1321 return res;
1322 }
1323 #endif
1324
1325 /*!
1326 * \brief Print Group
1327 * \param buf Buf as char
1328 * \param buflen Buffer Lendth as int
1329 * \param group Group as sccp_group_t
1330 * \return Result as char
1331 */
sccp_print_group(struct ast_str * buf,int buflen,sccp_group_t group)1332 void sccp_print_group(struct ast_str *buf, int buflen, sccp_group_t group)
1333 {
1334 unsigned int i = 0;
1335 int first = 1;
1336 uint8_t max = (sizeof(sccp_group_t) * 8) - 1;
1337
1338 if (!group) {
1339 return;
1340 }
1341 for (i = 0; i <= max; i++) {
1342 if (group & ((sccp_group_t) 1 << i)) {
1343 if (!first) {
1344 pbx_str_append(&buf, buflen, ",");
1345 } else {
1346 first = 0;
1347 }
1348 pbx_str_append(&buf, buflen, "%d", i);
1349 }
1350 }
1351 }
1352
1353 #if 0
1354 /*!
1355 * \brief Compare two socket addressed with each other
1356 *
1357 * \note not used
1358 */
1359 int sockaddr_cmp_addr(struct sockaddr_storage *addr1, socklen_t len1, struct sockaddr_storage *addr2, socklen_t len2)
1360 {
1361 struct sockaddr_in *p1_in = (struct sockaddr_in *) addr1;
1362 struct sockaddr_in *p2_in = (struct sockaddr_in *) addr2;
1363 struct sockaddr_in6 *p1_in6 = (struct sockaddr_in6 *) addr1;
1364 struct sockaddr_in6 *p2_in6 = (struct sockaddr_in6 *) addr2;
1365
1366 if (len1 < len2) {
1367 return -1;
1368 }
1369 if (len1 > len2) {
1370 return 1;
1371 }
1372 if (p1_in->sin_family < p2_in->sin_family) {
1373 return -1;
1374 }
1375 if (p1_in->sin_family > p2_in->sin_family) {
1376 return 1;
1377 }
1378 /* compare ip4 */
1379 if (p1_in->sin_family == AF_INET) {
1380 return memcmp(&p1_in->sin_addr, &p2_in->sin_addr, sizeof(p1_in->sin_addr));
1381 } else if (p1_in6->sin6_family == AF_INET6) {
1382 return memcmp(&p1_in6->sin6_addr, &p2_in6->sin6_addr, sizeof(p1_in6->sin6_addr));
1383 } else {
1384 /* unknown type, compare for sanity. */
1385 return memcmp(addr1, addr2, len1);
1386 }
1387 }
1388 #endif
1389
sccp_strversioncmp(const char * s1,const char * s2)1390 int __PURE__ sccp_strversioncmp(const char *s1, const char *s2)
1391 {
1392 static const char *digits = "0123456789";
1393 int ret = 0;
1394
1395 int lz1 = 0;
1396
1397 int lz2 = 0;
1398 size_t p1 = 0;
1399
1400 size_t p2 = 0;
1401
1402 p1 = strcspn(s1, digits);
1403 p2 = strcspn(s2, digits);
1404 while (p1 == p2 && s1[p1] != '\0' && s2[p2] != '\0') {
1405 /* Different prefix */
1406 ret = strncmp(s1, s2, p1);
1407 if(ret != 0) {
1408 return ret;
1409 }
1410 s1 += p1;
1411 s2 += p2;
1412
1413 lz1 = lz2 = 0;
1414 if (*s1 == '0') {
1415 lz1 = 1;
1416 }
1417 if (*s2 == '0') {
1418 lz2 = 1;
1419 }
1420 if (lz1 > lz2) {
1421 return -1;
1422 }
1423 if (lz1 < lz2) {
1424 return 1;
1425 }
1426 if (lz1 == 1) {
1427 /*
1428 * If the common prefix for s1 and s2 consists only of zeros, then the
1429 * "longer" number has to compare less. Otherwise the comparison needs
1430 * to be numerical (just fallthrough). See
1431 */
1432 while (*s1 == '0' && *s2 == '0') {
1433 ++s1;
1434 ++s2;
1435 }
1436
1437 p1 = strspn(s1, digits);
1438 p2 = strspn(s2, digits);
1439
1440 /* Catch empty strings */
1441 if (p1 == 0 && p2 > 0) {
1442 return 1;
1443 } if (p2 == 0 && p1 > 0) {
1444 return -1;
1445 }
1446 /* Prefixes are not same */
1447 if (*s1 != *s2 && *s1 != '0' && *s2 != '0') {
1448 if (p1 < p2) {
1449 return 1;
1450 }
1451 if (p1 > p2) {
1452 return -1;
1453 }
1454 } else {
1455 if (p1 < p2) {
1456 ret = strncmp(s1, s2, p1);
1457 } else if (p1 > p2) {
1458 ret = strncmp(s1, s2, p2);
1459 }
1460 if (ret != 0) {
1461 return ret;
1462 }
1463 }
1464 }
1465
1466 p1 = strspn(s1, digits);
1467 p2 = strspn(s2, digits);
1468
1469 if (p1 < p2) {
1470 return -1;
1471 }
1472 if (p1 > p2) {
1473 return 1;
1474 }
1475 ret = strncmp(s1, s2, p1);
1476 if(ret != 0) {
1477 return ret;
1478 }
1479 /* Numbers are equal or not present, try with next ones. */
1480 s1 += p1;
1481 s2 += p2;
1482 p1 = strcspn(s1, digits);
1483 p2 = strcspn(s2, digits);
1484 }
1485
1486 return strcmp(s1, s2);
1487 }
1488
sccp_dec2binstr(char * buf,size_t size,int value)1489 char *sccp_dec2binstr(char *buf, size_t size, int value)
1490 {
1491 char b[33] = { 0 };
1492 int pos = 0;
1493 long long z = 0;
1494
1495 for (z = 1LL << 31, pos = 0; z > 0; z >>= 1, pos++) {
1496 b[pos] = (((value & z) == z) ? '1' : '0');
1497 }
1498 snprintf(buf, size, "%s", b);
1499 return buf;
1500 }
1501
sccp_copy_string(char * dst,const char * src,size_t size)1502 gcc_inline void sccp_copy_string(char *dst, const char *src, size_t size)
1503 {
1504 pbx_assert(NULL != dst && NULL != src);
1505 if (do_expect(size != 0)) {
1506 while (do_expect(--size != 0)) {
1507 if (+(*dst++ = *src++) == '\0') {
1508 break;
1509 }
1510 }
1511 }
1512 *dst = '\0';
1513 }
1514
1515 /*
1516 * \brief trim white space from beginning and ending of string
1517 * \note This function returns a pointer to a substring of the original string.
1518 * If the given string was allocated dynamically, the caller must not overwrite
1519 * that pointer with the returned value, since the original pointer must be
1520 * deallocated using the same allocator with which it was allocated. The return
1521 * value must NOT be deallocated using free() etc.
1522 */
sccp_trimwhitespace(char * str)1523 char *sccp_trimwhitespace(char *str)
1524 {
1525 char * end = NULL;
1526
1527 // Trim leading space
1528 while (isspace(*str)) {
1529 str++;
1530 }
1531 if (*str == 0) { // All spaces
1532 return str;
1533 }
1534 // Trim trailing space
1535 end = str + sccp_strlen(str) - 1;
1536 while (end > str && isspace(*end)) {
1537 end--;
1538 }
1539 // Write new null terminator
1540 *(end + 1) = 0;
1541 return str;
1542 }
1543
sccp_atoi(const char * const buf,size_t buflen)1544 gcc_inline int sccp_atoi(const char * const buf, size_t buflen)
1545 {
1546 int result = 0;
1547 if (buf && buflen > 0) {
1548 errno = 0;
1549 char *end = NULL;
1550 long temp = strtol (buf, &end, 10);
1551 if (end != buf && errno != ERANGE && temp >= INT_MIN && temp <= INT_MAX) {
1552 result = (int)temp;
1553 }
1554 }
1555 return result;
1556 }
1557
sccp_random(void)1558 int sccp_random(void)
1559 {
1560 /* potentially replace with our own implementation */
1561 return (int)pbx_random();
1562 }
1563
sccp_retrieve_str_variable_byKey(PBX_VARIABLE_TYPE * params,const char * key)1564 const char * sccp_retrieve_str_variable_byKey(PBX_VARIABLE_TYPE *params, const char *key)
1565 {
1566 PBX_VARIABLE_TYPE * param = NULL;
1567 for(param = params;param;param = param->next) {
1568 if(strcasecmp(key, param->name) == 0) {
1569 return param->value;
1570 break;
1571 }
1572 }
1573 return NULL;
1574 }
1575
sccp_retrieve_int_variable_byKey(PBX_VARIABLE_TYPE * params,const char * key)1576 int sccp_retrieve_int_variable_byKey(PBX_VARIABLE_TYPE *params, const char *key)
1577 {
1578 const char *value = sccp_retrieve_str_variable_byKey(params, key);
1579 if (value) {
1580 return sccp_atoi(value, strlen(value));
1581 }
1582 return -1;
1583 }
1584
sccp_append_variable(PBX_VARIABLE_TYPE * params,const char * key,const char * value)1585 boolean_t sccp_append_variable(PBX_VARIABLE_TYPE *params, const char *key, const char *value)
1586 {
1587 boolean_t res = FALSE;
1588 PBX_VARIABLE_TYPE * newvar = NULL;
1589 if ((newvar = pbx_variable_new(key, value, ""))) {
1590 if (params) {
1591 while(params->next) {
1592 params = params->next;
1593 }
1594 params->next = newvar;
1595 } else {
1596 params = newvar;
1597 }
1598 res = TRUE;
1599 } else {
1600 pbx_log(LOG_ERROR, "SCCP: (append_variable) Error while creating newvar structure\n");
1601 }
1602 return res;
1603 }
1604
1605
sccp_utf8_columnwidth(int width,const char * const ms)1606 gcc_inline int sccp_utf8_columnwidth(int width, const char *const ms)
1607 {
1608 // don't use setlocale() as that is global to the process
1609 int res = 0;
1610 locale_t locale = newlocale(LC_ALL_MASK, "", NULL);
1611 locale_t old_locale = uselocale(locale);
1612
1613 if(ms) {
1614 res = width + (strlen(ms) - mbstowcs(NULL, ms, width));
1615 }
1616
1617 uselocale(old_locale);
1618 if(locale != (locale_t)0) {
1619 freelocale(locale);
1620 }
1621
1622 return res;
1623 }
1624
sccp_always_false(void)1625 gcc_inline boolean_t sccp_always_false(void)
1626 {
1627 return FALSE;
1628 }
1629
sccp_always_true(void)1630 gcc_inline boolean_t sccp_always_true(void)
1631 {
1632 return TRUE;
1633 }
1634
sccp_cfwd2feature(const sccp_cfwd_t type)1635 gcc_inline sccp_feature_type_t sccp_cfwd2feature(const sccp_cfwd_t type)
1636 {
1637 switch(type) {
1638 case SCCP_CFWD_ALL:
1639 return SCCP_FEATURE_CFWDALL;
1640 case SCCP_CFWD_BUSY:
1641 return SCCP_FEATURE_CFWDBUSY;
1642 case SCCP_CFWD_NOANSWER:
1643 return SCCP_FEATURE_CFWDNOANSWER;
1644 case SCCP_CFWD_NONE:
1645 return SCCP_FEATURE_CFWDNONE;
1646 default:
1647 return SCCP_FEATURE_TYPE_SENTINEL;
1648 }
1649 }
1650
sccp_feature2cfwd(const sccp_feature_type_t type)1651 gcc_inline sccp_cfwd_t sccp_feature2cfwd(const sccp_feature_type_t type)
1652 {
1653 switch(type) {
1654 case SCCP_FEATURE_CFWDALL:
1655 return SCCP_CFWD_ALL;
1656 case SCCP_FEATURE_CFWDBUSY:
1657 return SCCP_CFWD_BUSY;
1658 case SCCP_FEATURE_CFWDNOANSWER:
1659 return SCCP_CFWD_NOANSWER;
1660 case SCCP_FEATURE_CFWDNONE:
1661 return SCCP_CFWD_NONE;
1662 default:
1663 return SCCP_CFWD_SENTINEL;
1664 }
1665 }
1666
sccp_cfwd2stimulus(const sccp_cfwd_t type)1667 gcc_inline skinny_stimulus_t sccp_cfwd2stimulus(const sccp_cfwd_t type)
1668 {
1669 switch(type) {
1670 case SCCP_CFWD_ALL:
1671 return SKINNY_STIMULUS_FORWARDALL;
1672 case SCCP_CFWD_BUSY:
1673 return SKINNY_STIMULUS_FORWARDBUSY;
1674 case SCCP_CFWD_NOANSWER:
1675 return SKINNY_STIMULUS_FORWARDNOANSWER;
1676 case SCCP_CFWD_NONE:
1677 default:
1678 return SKINNY_STIMULUS_SENTINEL;
1679 }
1680 }
1681
sccp_cfwd2disp(const sccp_cfwd_t type)1682 gcc_inline const char * const sccp_cfwd2disp(const sccp_cfwd_t type)
1683 {
1684 switch(type) {
1685 case SCCP_CFWD_ALL:
1686 return SKINNY_DISP_CFWDALL;
1687 case SCCP_CFWD_BUSY:
1688 return SKINNY_DISP_CFWDBUSY;
1689 case SCCP_CFWD_NOANSWER:
1690 return SKINNY_DISP_NOANSWER;
1691 case SCCP_CFWD_NONE:
1692 case SCCP_CFWD_SENTINEL:
1693 default:
1694 return "";
1695 }
1696 }
1697
1698 #if CS_TEST_FRAMEWORK
sccp_register_tests(void)1699 static void __attribute__((constructor)) sccp_register_tests(void)
1700 {
1701 AST_TEST_REGISTER(chan_sccp_acl_tests);
1702 AST_TEST_REGISTER(chan_sccp_acl_invalid_tests);
1703 }
1704
sccp_unregister_tests(void)1705 static void __attribute__((destructor)) sccp_unregister_tests(void)
1706 {
1707 AST_TEST_UNREGISTER(chan_sccp_acl_tests);
1708 AST_TEST_UNREGISTER(chan_sccp_acl_invalid_tests);
1709 }
1710 #endif
1711
1712 #ifdef DEBUG
1713 #if ASTERISK_VERSION_GROUP < 112
1714 #if HAVE_EXECINFO_H
__sccp_bt_get_symbols(void ** addresses,size_t num_frames)1715 static char **__sccp_bt_get_symbols(void **addresses, size_t num_frames)
1716 {
1717 char ** strings = NULL;
1718 # if defined(HAVE_DLADDR_H) && defined(HAVE_BFD_H)
1719 size_t stackfr;
1720 bfd * bfdobj = NULL; /* bfd.h */
1721 Dl_info dli; /* dlfcn.h */
1722 long allocsize;
1723 asymbol **syms = NULL; /* bfd.h */
1724 bfd_vma offset; /* bfd.h */
1725 const char * lastslash = NULL;
1726 asection * section = NULL;
1727 const char *file, *func;
1728 unsigned int line;
1729 char address_str[128];
1730 char msg[1024];
1731 size_t strings_size;
1732 size_t * eachlen = NULL;
1733
1734 strings_size = num_frames * sizeof(*strings);
1735
1736 eachlen = (size_t *) sccp_calloc(sizeof *eachlen, num_frames);
1737 strings = (char **) sccp_calloc(sizeof *strings, num_frames);
1738 if (!eachlen || !strings) {
1739 pbx_log(LOG_ERROR, SS_Memory_Allocation_Error, "SCCP");
1740 sccp_free(eachlen);
1741 sccp_free(strings);
1742 return NULL;
1743 }
1744
1745 for (stackfr = 0; stackfr < num_frames; stackfr++) {
1746 int found = 0, symbolcount;
1747
1748 msg[0] = '\0';
1749
1750 if (!dladdr(addresses[stackfr], &dli)) {
1751 continue;
1752 }
1753
1754 if (strcmp(dli.dli_fname, "asterisk") == 0) {
1755 char asteriskpath[256];
1756
1757 if (!(dli.dli_fname = ast_utils_which("asterisk", asteriskpath, sizeof(asteriskpath)))) {
1758 /* This will fail to find symbols */
1759 dli.dli_fname = "asterisk";
1760 }
1761 }
1762
1763 lastslash = strrchr(dli.dli_fname, '/');
1764 if ((bfdobj = bfd_openr(dli.dli_fname, NULL)) &&
1765 bfd_check_format(bfdobj, bfd_object) &&
1766 (allocsize = bfd_get_symtab_upper_bound(bfdobj)) > 0 &&
1767 (syms = (asymbol **)sccp_malloc(allocsize)) &&
1768 (symbolcount = bfd_canonicalize_symtab(bfdobj, syms))) {
1769
1770 if (bfdobj->flags & DYNAMIC) {
1771 offset = addresses[stackfr] - dli.dli_fbase;
1772 } else {
1773 offset = addresses[stackfr] - (void *) 0;
1774 }
1775
1776 for (section = bfdobj->sections; section; section = section->next) {
1777 if (!(bfd_get_section_flags(bfdobj, section) & SEC_ALLOC) ||
1778 section->vma > offset ||
1779 section->size + section->vma < offset) {
1780 continue;
1781 }
1782
1783 if (!bfd_find_nearest_line(bfdobj, section, syms, offset - section->vma, &file, &func, &line)) {
1784 continue;
1785 }
1786
1787 /* file can possibly be null even with a success result from bfd_find_nearest_line */
1788 file = file ? file : "";
1789
1790 /* Stack trace output */
1791 found++;
1792 if ((lastslash = strrchr(file, '/'))) {
1793 const char * prevslash = NULL;
1794
1795 for (prevslash = lastslash - 1; *prevslash != '/' && prevslash >= file; prevslash--) {
1796 }
1797 if (prevslash >= file) {
1798 lastslash = prevslash;
1799 }
1800 }
1801 if (dli.dli_saddr == NULL) {
1802 address_str[0] = '\0';
1803 } else {
1804 snprintf(address_str, sizeof(address_str), " (%p+%lX)",
1805 dli.dli_saddr,
1806 (unsigned long) (addresses[stackfr] - dli.dli_saddr));
1807 }
1808 snprintf(msg, sizeof(msg), "%s:%u %s()%s",
1809 lastslash ? lastslash + 1 : file, line,
1810 S_OR(func, "???"),
1811 address_str);
1812
1813 break; /* out of section iteration */
1814 }
1815 }
1816 if (bfdobj) {
1817 bfd_close(bfdobj);
1818 }
1819 if (syms) {
1820 sccp_free(syms);
1821 }
1822
1823 /* Default output, if we cannot find the information within BFD */
1824 if (!found) {
1825 if (dli.dli_saddr == NULL) {
1826 address_str[0] = '\0';
1827 } else {
1828 snprintf(address_str, sizeof(address_str), " (%p+%lX)",
1829 dli.dli_saddr,
1830 (unsigned long) (addresses[stackfr] - dli.dli_saddr));
1831 }
1832 snprintf(msg, sizeof(msg), "%s %s()%s",
1833 lastslash ? lastslash + 1 : dli.dli_fname,
1834 S_OR(dli.dli_sname, "<unknown>"),
1835 address_str);
1836 }
1837
1838 if (!ast_strlen_zero(msg)) {
1839 char ** tmp = NULL;
1840
1841 eachlen[stackfr] = strlen(msg) + 1;
1842 if (!(tmp = (char **)sccp_realloc(strings, strings_size + eachlen[stackfr]))) {
1843 pbx_log(LOG_ERROR, SS_Memory_Allocation_Error, "SCCP");
1844 sccp_free(strings);
1845 strings = NULL;
1846 break; /* out of stack frame iteration */
1847 }
1848 strings = tmp;
1849 strings[stackfr] = (char *) strings + strings_size;
1850 //__strcpy(strings[stackfr], msg);/* Safe since we just allocated the room. */
1851 sccp_copy_string(strings[stackfr], msg, strings_size + eachlen[stackfr]);
1852 strings_size += eachlen[stackfr];
1853 }
1854 }
1855
1856 if (strings) {
1857 /* Recalculate the offset pointers because of the reallocs. */
1858 strings[0] = (char *) strings + num_frames * sizeof(*strings);
1859 for (stackfr = 1; stackfr < num_frames; stackfr++) {
1860 strings[stackfr] = strings[stackfr - 1] + eachlen[stackfr - 1];
1861 }
1862 }
1863 sccp_free(eachlen);
1864 #else
1865 strings = backtrace_symbols(addresses, num_frames);
1866 #endif // defined(HAVE_DLADDR_H) && defined(HAVE_BFD_H)
1867 return strings;
1868 }
1869 #endif // HAVE_EXECINFO_H
1870 #endif // ASTERISK_VERSION_GROUP
1871
sccp_do_backtrace()1872 void sccp_do_backtrace()
1873 {
1874 pbx_rwlock_rdlock(&GLOB(lock));
1875 boolean_t running = GLOB(module_running);
1876 pbx_rwlock_unlock(&GLOB(lock));
1877 if (!running) {
1878 return;
1879 }
1880
1881 #if defined(HAVE_EXECINFO_H) && defined(HAVE_BKTR)
1882 void *addresses[SCCP_BACKTRACE_SIZE];
1883 size_t size = 0;
1884
1885 size_t i = 0;
1886 bt_string_t * strings = NULL;
1887 struct ast_str * btbuf = NULL;
1888 if (!(btbuf = pbx_str_alloca(DEFAULT_PBX_STR_BUFFERSIZE * 2))) {
1889 return;
1890 }
1891
1892 pbx_str_append(&btbuf, DEFAULT_PBX_STR_BUFFERSIZE, "================================================================================\n");
1893 pbx_str_append(&btbuf, DEFAULT_PBX_STR_BUFFERSIZE, "OPERATING SYSTEM: %s, ARCHITECTURE: %s, KERNEL: %s\nASTERISK: %s\nCHAN-SCCP-b: %s\n", BUILD_OS, BUILD_MACHINE, BUILD_KERNEL, pbx_get_version(), SCCP_VERSIONSTR);
1894 #if !defined(HAVE_DLADDR_H) || !defined(HAVE_BFD_H)
1895 pbx_str_append(&btbuf, DEFAULT_PBX_STR_BUFFERSIZE, "To get a better backtrace you would need to install libbfd (package binutils devel package)\n");
1896 #endif
1897 pbx_str_append(&btbuf, DEFAULT_PBX_STR_BUFFERSIZE, "--------------------------------------------------------------------------(bt)--\n");
1898 size = backtrace(addresses, SCCP_BACKTRACE_SIZE);
1899 #if ASTERISK_VERSION_GROUP >= 112
1900 strings = ast_bt_get_symbols(addresses, size);
1901 #else
1902 strings = __sccp_bt_get_symbols(addresses, size);
1903 #endif
1904
1905 if (strings) {
1906 for (i = 1; i < size; i++) {
1907 #ifdef CS_AST_BACKTRACE_VECTOR_STRING
1908 //struct ast_vector_string * strings;
1909 pbx_str_append(&btbuf, DEFAULT_PBX_STR_BUFFERSIZE, " (bt) > %s\n", AST_VECTOR_GET(strings, i));
1910 #else
1911 pbx_str_append(&btbuf, DEFAULT_PBX_STR_BUFFERSIZE, " (bt) > %s\n", strings[i]);
1912 #endif
1913 }
1914 bt_free(strings);
1915
1916 pbx_str_append(&btbuf, DEFAULT_PBX_STR_BUFFERSIZE, "================================================================================\n");
1917 pbx_log(LOG_WARNING, "SCCP: (backtrace) \n%s\n", pbx_str_buffer(btbuf));
1918 }
1919 #endif // HAVE_EXECINFO_H
1920 }
1921 #endif // DEBUG
1922 // kate: indent-width 8; replace-tabs off; indent-mode cstyle; auto-insert-doxygen on; line-numbers on; tab-indents on; keep-extra-spaces off; auto-brackets off;
1923