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(&current_ha->netaddr));
822 		// char *strmask = pbx_strdupa(sccp_netsock_stringify_addr(&current_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, &current_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, &current_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(&current_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