1 /*
2  * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3  * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
4  *
5  * Version: MPL 1.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18  *
19  * The Initial Developer of the Original Code is
20  * Anthony Minessale II <anthm@freeswitch.org>
21  * Portions created by the Initial Developer are Copyright (C)
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *
26  * Anthony Minessale II <anthm@freeswitch.org>
27  * Jay Binks <jaybinks@gmail.com>
28  *
29  * mod_enum.c -- ENUM
30  *
31  */
32 
33 #include <switch.h>
34 #ifdef _MSC_VER
35 #define ssize_t int
36 #endif
37 #include <ldns/ldns.h>
38 
39 #define ENUM_MAXNAMESERVERS	10	/* max nameservers that will be used */
40 
41 SWITCH_MODULE_LOAD_FUNCTION(mod_enum_load);
42 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_enum_shutdown);
43 SWITCH_MODULE_DEFINITION(mod_enum, mod_enum_load, mod_enum_shutdown, NULL);
44 
45 static switch_mutex_t *MUTEX = NULL;
46 
47 struct enum_record {
48 	int order;
49 	int preference;
50 	char *service;
51 	char *route;
52 	int supported;
53 	struct enum_record *next;
54 	struct enum_record *tail;
55 };
56 typedef struct enum_record enum_record_t;
57 
58 struct route {
59 	char *service;
60 	char *regex;
61 	char *replace;
62 	struct route *next;
63 };
64 typedef struct route enum_route_t;
65 
66 static switch_event_node_t *NODE = NULL;
67 
68 static struct {
69 	char *root;
70 	char *isn_root;
71 	enum_route_t *route_order;
72 	switch_memory_pool_t *pool;
73 	int auto_reload;
74 	int timeout;
75 	int retries;
76 	int random;
77 	char *nameserver[ENUM_MAXNAMESERVERS];
78 #ifdef _MSC_VER
79 	char *nameserver_buf;
80 #endif
81 } globals;
82 
83 SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_root, globals.root);
84 SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_isn_root, globals.isn_root);
85 
add_route(char * service,char * regex,char * replace)86 static void add_route(char *service, char *regex, char *replace)
87 {
88 	enum_route_t *route, *rp;
89 
90 	route = switch_core_alloc(globals.pool, sizeof(*route));
91 
92 
93 	route->service = switch_core_strdup(globals.pool, service);
94 	route->regex = switch_core_strdup(globals.pool, regex);
95 	route->replace = switch_core_strdup(globals.pool, replace);
96 
97 	switch_mutex_lock(MUTEX);
98 	if (!globals.route_order) {
99 		globals.route_order = route;
100 	} else {
101 		for (rp = globals.route_order; rp && rp->next; rp = rp->next);
102 		rp->next = route;
103 	}
104 	switch_mutex_unlock(MUTEX);
105 }
106 
load_config(void)107 static switch_status_t load_config(void)
108 {
109 	char *cf = "enum.conf";
110 	int inameserver = 0;
111 	switch_xml_t cfg, xml = NULL, param, settings, route, routes;
112 	switch_status_t status = SWITCH_STATUS_SUCCESS;
113 
114 	if (!(xml = switch_xml_open_cfg(cf, &cfg, NULL))) {
115 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Open of %s failed\n", cf);
116 		status = SWITCH_STATUS_FALSE;
117 		goto done;
118 	}
119 
120 	globals.timeout = 5000;
121 	globals.retries = 3;
122 	globals.random  = 0;
123 
124 	if ((settings = switch_xml_child(cfg, "settings"))) {
125 		for (param = switch_xml_child(settings, "param"); param; param = param->next) {
126 			const char *var = switch_xml_attr_soft(param, "name");
127 			const char *val = switch_xml_attr_soft(param, "value");
128 			if (!strcasecmp(var, "default-root")) {
129 				set_global_root(val);
130 			} else if (!strcasecmp(var, "auto-reload")) {
131 				globals.auto_reload = switch_true(val);
132 			} else if (!strcasecmp(var, "query-timeout")) {
133 				globals.timeout = atoi(val) * 1000;
134 			} else if (!strcasecmp(var, "query-timeout-ms")) {
135 				globals.timeout = atoi(val);
136 			} else if (!strcasecmp(var, "query-timeout-retry")) {
137 				globals.retries = atoi(val);
138 			} else if (!strcasecmp(var, "random-nameserver")) {
139 				globals.random = switch_true(val);
140 			} else if (!strcasecmp(var, "default-isn-root")) {
141 				set_global_isn_root(val);
142 			} else if (!strcasecmp(var, "nameserver") || !strcasecmp(var, "use-server")) {
143 				if ( inameserver < ENUM_MAXNAMESERVERS ) {
144 					globals.nameserver[inameserver] = (char *) val;
145 					inameserver++;
146 				}
147 			}
148 		}
149 	}
150 
151 	if ((routes = switch_xml_child(cfg, "routes"))) {
152 		for (route = switch_xml_child(routes, "route"); route; route = route->next) {
153 			char *service = (char *) switch_xml_attr_soft(route, "service");
154 			char *regex = (char *) switch_xml_attr_soft(route, "regex");
155 			char *replace = (char *) switch_xml_attr_soft(route, "replace");
156 
157 			if (service && regex && replace) {
158 				add_route(service, regex, replace);
159 			} else {
160 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Route!\n");
161 			}
162 		}
163 	}
164 
165   done:
166 #ifdef _MSC_VER
167 	if (!globals.nameserver[0]) {
168 		HKEY hKey;
169 		DWORD data_sz;
170 		RegOpenKeyEx(HKEY_LOCAL_MACHINE,
171 			"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters",
172 			0, KEY_QUERY_VALUE, &hKey);
173 
174 		if (hKey) {
175 			RegQueryValueEx(hKey, "DhcpNameServer", NULL, NULL, NULL, &data_sz);
176 			if (data_sz) {
177 				globals.nameserver_buf = (char*)malloc(data_sz + 1);
178 
179 				RegQueryValueEx(hKey, "DhcpNameServer", NULL, NULL, (LPBYTE)globals.nameserver_buf, &data_sz);
180 
181 				if(globals.nameserver_buf[data_sz - 1] != 0) {
182 					globals.nameserver_buf[data_sz] = 0;
183 				}
184 				switch_replace_char(globals.nameserver_buf, ' ', 0, SWITCH_FALSE); /* only use the first entry ex "192.168.1.1 192.168.1.2" */
185 				globals.nameserver[0] = globals.nameserver_buf;
186 			}
187 
188 			RegCloseKey(hKey);
189 		}
190 	}
191 #endif
192 
193 
194 	if (xml) {
195 		switch_xml_free(xml);
196 	}
197 
198 	if (!globals.root) {
199 		set_global_root("e164.org");
200 	}
201 
202 	if (!globals.isn_root) {
203 		set_global_isn_root("freenum.org");
204 	}
205 
206 	return status;
207 }
208 
reverse_number(const char * in,const char * root)209 static char *reverse_number(const char *in, const char *root)
210 {
211 	switch_size_t len;
212 	char *out = NULL;
213 	const char *y;
214 	char *z;
215 
216 	if (!(in && root)) {
217 		return NULL;
218 	}
219 
220 	len = (strlen(in) * 2) + strlen(root) + 1;
221 	if ((out = malloc(len))) {
222 		memset(out, 0, len);
223 
224 		z = out;
225 		for (y = in + (strlen(in) - 1); y; y--) {
226 			if (*y > 47 && *y < 58) {
227 				*z++ = *y;
228 				*z++ = '.';
229 			}
230 			if (y == in) {
231 				break;
232 			}
233 		}
234 		strcat(z, root);
235 	}
236 
237 	return out;
238 }
239 
240 
add_result(enum_record_t ** results,int order,int preference,char * service,char * route,int supported)241 static void add_result(enum_record_t **results, int order, int preference, char *service, char *route, int supported)
242 {
243 	enum_record_t *new_result;
244 
245 	new_result = malloc(sizeof(*new_result));
246 	switch_assert(new_result);
247 
248 	memset(new_result, 0, sizeof(*new_result));
249 	new_result->order = order;
250 	new_result->preference = preference;
251 	new_result->service = strdup(service);
252 	new_result->route = strdup(route);
253 	new_result->supported = supported;
254 
255 
256 	if (!*results) {
257 		*results = new_result;
258 		(*results)->tail = new_result;
259 	} else {
260 		(*results)->tail->next = new_result;
261 		(*results)->tail = new_result;
262 	}
263 
264 }
265 
266 
free_results(enum_record_t ** results)267 static void free_results(enum_record_t ** results)
268 {
269 	enum_record_t *fp, *rp;
270 
271 	for (rp = *results; rp;) {
272 		fp = rp;
273 		rp = rp->next;
274 		switch_safe_free(fp->service);
275 		switch_safe_free(fp->route);
276 		switch_safe_free(fp);
277 	}
278 	*results = NULL;
279 }
280 
281 
ldns_rdf_new_addr_frm_str(const char * str)282 static ldns_rdf *ldns_rdf_new_addr_frm_str(const char *str)
283 {
284 	ldns_rdf *a = NULL;
285 
286 	ldns_str2rdf_a(&a, str);
287 
288 	if (!a) {
289 		/* maybe ip6 */
290 		ldns_str2rdf_aaaa(&a, str);
291 		if (!a) {
292 			return NULL;
293 		}
294 	}
295 	return a;
296 }
297 
298 #define strip_quotes(_s) if (*_s == '"') _s++; if (end_of(_s) == '"') end_of(_s) = '\0'
299 
parse_naptr(const ldns_rr * naptr,const char * number,enum_record_t ** results)300 static void parse_naptr(const ldns_rr *naptr, const char *number, enum_record_t **results)
301 {
302 	char *str = ldns_rr2str(naptr);
303 	char *argv[11] = { 0 };
304 	int i, argc;
305 	char *pack[4] = { 0 };
306 	int packc;
307 
308 	char *p;
309 	int order = 10;
310 	int preference = 100;
311 	char *service = NULL;
312 	char *packstr;
313 
314 	char *regex, *replace;
315 
316 	if (zstr(str)) {
317 		if (str != NULL) {
318 			/* In this case ldns_rr2str returned a malloc'd null terminated string */
319 			switch_safe_free(str);
320 		}
321 		return;
322 	}
323 
324 	for (p = str; p && *p; p++) {
325 		if (*p == '\t') *p = ' ';
326 		if (*p == ' ' && *(p+1) == '.') *p = '\0';
327 	}
328 
329 
330 	argc = switch_split(str, ' ', argv);
331 
332 	for (i = 0; i < argc; i++) {
333 		if (i > 0) {
334 			strip_quotes(argv[i]);
335 		}
336 	}
337 
338 	service = argv[7];
339 	packstr = argv[8];
340 
341 	if (zstr(service) || zstr(packstr)) {
342 		goto end;
343 	}
344 
345 	if (!zstr(argv[4])) {
346 		order = atoi(argv[4]);
347 	}
348 
349 	if (!zstr(argv[5])) {
350 		preference = atoi(argv[5]);
351 	}
352 
353 
354 	if ((packc = switch_split(packstr, '!', pack))) {
355 		regex = pack[1];
356 		replace = pack[2];
357 	} else {
358 		goto end;
359 	}
360 
361 	for (p = replace; p && *p; p++) {
362 		if (*p == '\\') {
363 			*p = '$';
364 		}
365 	}
366 
367 	if (service && regex && replace) {
368 		switch_regex_t *re = NULL, *re2 = NULL;
369 		int proceed = 0, ovector[30];
370 		char *substituted = NULL;
371 		char *substituted_2 = NULL;
372 		char *orig_uri;
373 		char *uri_expanded = NULL;
374 		enum_route_t *route;
375 		int supported = 0;
376 		uint32_t len = 0;
377 
378 		if ((proceed = switch_regex_perform(number, regex, &re, ovector, sizeof(ovector) / sizeof(ovector[0])))) {
379 			if (strchr(regex, '(')) {
380 				len = (uint32_t) (strlen(number) + strlen(replace) + 10) * proceed;
381 				if (!(substituted = malloc(len))) {
382 					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n");
383 					switch_regex_safe_free(re);
384 					goto end;
385 				}
386 				memset(substituted, 0, len);
387 
388 				switch_perform_substitution(re, proceed, replace, number, substituted, len, ovector);
389 				orig_uri = substituted;
390 			} else {
391 				orig_uri = replace;
392 			}
393 
394 			switch_mutex_lock(MUTEX);
395 			for (route = globals.route_order; route; route = route->next) {
396 				char *uri = orig_uri;
397 
398 				if (strcasecmp(service, route->service)) {
399 					continue;
400 				}
401 
402 				if ((proceed = switch_regex_perform(uri, route->regex, &re2, ovector, sizeof(ovector) / sizeof(ovector[0])))) {
403 					switch_event_t *event = NULL;
404 
405 					if (strchr(route->regex, '(')) {
406 						len = (uint32_t) (strlen(uri) + strlen(route->replace) + 10) * proceed;
407 						if (!(substituted_2 = malloc(len))) {
408 							switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n");
409 							switch_safe_free(substituted);
410 							switch_regex_safe_free(re);
411 							switch_regex_safe_free(re2);
412 							switch_mutex_unlock(MUTEX);
413 							goto end;
414 						}
415 						memset(substituted_2, 0, len);
416 
417 						switch_perform_substitution(re2, proceed, route->replace, uri, substituted_2, len, ovector);
418 						uri = substituted_2;
419 					} else {
420 						uri = route->replace;
421 					}
422 					switch_event_create(&event, SWITCH_EVENT_REQUEST_PARAMS);
423 					uri_expanded = switch_event_expand_headers(event, uri);
424 					switch_event_destroy(&event);
425 
426 					if (uri_expanded == uri) {
427 						uri_expanded = NULL;
428 					} else {
429 						uri = uri_expanded;
430 					}
431 
432 					supported++;
433 					add_result(results, order, preference, service, uri, supported);
434 
435 				}
436 				switch_safe_free(uri_expanded);
437 				switch_safe_free(substituted_2);
438 				switch_regex_safe_free(re2);
439 			}
440 			switch_mutex_unlock(MUTEX);
441 
442 			if (!supported) {
443 				add_result(results, order, preference, service, orig_uri, 0);
444 			}
445 
446 			switch_safe_free(substituted);
447 			switch_regex_safe_free(re);
448 		}
449 	}
450 
451  end:
452 
453 	switch_safe_free(str);
454 
455 	return;
456 }
457 
ldns_lookup(const char * number,const char * root,char * server_name[ENUM_MAXNAMESERVERS],enum_record_t ** results)458 switch_status_t ldns_lookup(const char *number, const char *root, char *server_name[ENUM_MAXNAMESERVERS] , enum_record_t **results)
459 {
460 	ldns_resolver *res = NULL;
461 	ldns_rdf *domain = NULL;
462 	ldns_pkt *p = NULL;
463 	ldns_rr_list *naptr = NULL;
464 	ldns_status s = LDNS_STATUS_ERR;
465 	ldns_rdf *serv_rdf;
466 	switch_status_t status = SWITCH_STATUS_FALSE;
467 	char *name = NULL;
468 	struct timeval to = { 0, 0};
469 	int inameserver = 0;
470 	int added_server = 0;
471 
472 	if (!(name = reverse_number(number, root))) {
473 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Parse Error!\n");
474 		goto end;
475 	}
476 
477 	if (!(domain = ldns_dname_new_frm_str(name))) {
478 		goto end;
479 	}
480 
481 	if (server_name) {
482 		res = ldns_resolver_new();
483 		switch_assert(res);
484 
485 		for(inameserver=0; inameserver<ENUM_MAXNAMESERVERS; inameserver++) {
486 			if ( server_name[inameserver] != NULL ) {
487 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Adding Nameserver [%s]\n", server_name[inameserver]);
488 				if ((serv_rdf = ldns_rdf_new_addr_frm_str( server_name[inameserver] ))) {
489 					s = ldns_resolver_push_nameserver(res, serv_rdf);
490 					ldns_rdf_deep_free(serv_rdf);
491 					added_server = 1;
492 				}
493 			}
494 		}
495 	}
496 	if (!added_server) {
497 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "No Nameservers specified, using host default\n");
498 		/* create a new resolver from /etc/resolv.conf */
499 		s = ldns_resolver_new_frm_file(&res, NULL);
500 	}
501 
502 	if (s != LDNS_STATUS_OK) {
503 		goto end;
504 	}
505 
506 	to.tv_sec = globals.timeout / 1000;
507 	to.tv_usec = (globals.timeout % 1000) * 1000;
508 
509 	ldns_resolver_set_timeout(res, to);
510 	ldns_resolver_set_retry(res, (uint8_t)globals.retries);
511 	ldns_resolver_set_random(res, globals.random);
512 
513 	if ((p = ldns_resolver_query(res,
514 								 domain,
515 								 LDNS_RR_TYPE_NAPTR,
516 								 LDNS_RR_CLASS_IN,
517 								 LDNS_RD))) {
518 		/* retrieve the NAPTR records from the answer section of that
519 		 * packet
520 		 */
521 
522 		if ((naptr = ldns_pkt_rr_list_by_type(p, LDNS_RR_TYPE_NAPTR, LDNS_SECTION_ANSWER))) {
523 			size_t i;
524 
525 			ldns_rr_list_sort(naptr);
526 
527 			for (i = 0; i < ldns_rr_list_rr_count(naptr); i++) {
528 				parse_naptr(ldns_rr_list_rr(naptr, i), number, results);
529 			}
530 
531 			//ldns_rr_list_print(stdout, naptr);
532 			ldns_rr_list_deep_free(naptr);
533 			status = SWITCH_STATUS_SUCCESS;
534 		}
535 	}
536 
537  end:
538 
539 	switch_safe_free(name);
540 
541 	if (domain) {
542 		ldns_rdf_deep_free(domain);
543 	}
544 
545 	if (p) {
546 		ldns_pkt_free(p);
547 	}
548 
549 	if (res) {
550 		ldns_resolver_deep_free(res);
551 	}
552 
553 	return status;
554 }
555 
enum_lookup(char * root,char * in,enum_record_t ** results,switch_channel_t * channel,switch_core_session_t * session)556 static switch_status_t enum_lookup(char *root, char *in, enum_record_t **results, switch_channel_t *channel, switch_core_session_t *session)
557 {
558 	switch_status_t sstatus = SWITCH_STATUS_SUCCESS;
559 	char *mnum = NULL, *mroot = NULL, *p;
560 	char *server[ENUM_MAXNAMESERVERS];
561 	int inameserver = 0;
562 	char *argv[ ENUM_MAXNAMESERVERS ] = { 0 };
563 	int argc;
564 	int x = 0;
565 	char *enum_nameserver_dup;
566 	const char *enum_nameserver = NULL;
567 
568 	*results = NULL;
569 
570 	mnum = switch_mprintf("%s%s", *in == '+' ? "" : "+", in);
571 
572 	if ((p = strchr(mnum, '*'))) {
573 		*p++ = '\0';
574 		mroot = switch_mprintf("%s.%s", p, root ? root : globals.isn_root);
575 		root = mroot;
576 	}
577 
578 	if (zstr(root)) {
579 		root = globals.root;
580 	}
581 
582 	/* Empty the server array */
583 	for(inameserver=0; inameserver<ENUM_MAXNAMESERVERS; inameserver++) {
584 		server[inameserver] = NULL;
585 	}
586 
587 	inameserver = 0;
588 
589 	/* check for enum_nameserver channel var */
590 
591 	if (channel) {
592 		enum_nameserver = switch_channel_get_variable(channel, "enum_nameserver");
593 	}
594 
595 	if (zstr(enum_nameserver)) {
596 		enum_nameserver = switch_core_get_variable("enum-server");
597 	}
598 
599 	if (!zstr(enum_nameserver)) {
600 		/* Blank the server array */
601 		for(inameserver=0; inameserver<ENUM_MAXNAMESERVERS; inameserver++) {
602 			server[inameserver] = NULL;
603 		}
604 
605 		enum_nameserver_dup = switch_core_session_strdup(session, enum_nameserver);
606 		argc = switch_separate_string(enum_nameserver_dup, ',', argv, (sizeof(argv) / sizeof(argv[0])));
607 
608 		inameserver = 0;
609 		for (x = 0; x < argc; x++) {
610 			server[inameserver] = argv[x];
611 			inameserver++;
612 		}
613 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Enum nameserver override : %s\n", enum_nameserver);
614 	}
615 
616 	if (!inameserver) {
617 		/* use config param "nameserver" ( can be up to ENUM_MAXNAMESERVERS ) */
618 		for(inameserver = 0; inameserver<ENUM_MAXNAMESERVERS; inameserver++) {
619 			server[inameserver] = NULL;
620 			if ( globals.nameserver[inameserver] != NULL ) {
621 				server[inameserver] = globals.nameserver[inameserver];
622 			}
623 		}
624 	}
625 
626 	ldns_lookup(mnum, root, server, results);
627 
628 	switch_safe_free(mnum);
629 	switch_safe_free(mroot);
630 
631 	return sstatus;
632 }
633 
SWITCH_STANDARD_DIALPLAN(enum_dialplan_hunt)634 SWITCH_STANDARD_DIALPLAN(enum_dialplan_hunt)
635 {
636 	switch_caller_extension_t *extension = NULL;
637 	enum_record_t *results = NULL, *rp;
638 	switch_channel_t *channel = switch_core_session_get_channel(session);
639 	char *dp = (char *) arg;
640 
641 	if (!caller_profile) {
642 		caller_profile = switch_channel_get_caller_profile(channel);
643 	}
644 
645 	switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "ENUM Lookup on %s\n", caller_profile->destination_number);
646 
647 	if (enum_lookup(dp, caller_profile->destination_number, &results, channel, session) == SWITCH_STATUS_SUCCESS) {
648 		if ((extension = switch_caller_extension_new(session, caller_profile->destination_number, caller_profile->destination_number)) == 0) {
649 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n");
650 			free_results(&results);
651 			return NULL;
652 		}
653 		switch_channel_set_variable(channel, SWITCH_HANGUP_AFTER_BRIDGE_VARIABLE, "true");
654 
655 
656 		for (rp = results; rp; rp = rp->next) {
657 			if (!rp->supported) {
658 				continue;
659 			}
660 
661 			switch_caller_extension_add_application(session, extension, "bridge", rp->route);
662 		}
663 
664 
665 		free_results(&results);
666 	}
667 
668 	return extension;
669 }
670 
SWITCH_STANDARD_APP(enum_app_function)671 SWITCH_STANDARD_APP(enum_app_function)
672 {
673 	int argc = 0;
674 	char *argv[4] = { 0 };
675 	char *mydata = NULL;
676 	char *dest = NULL, *root = NULL;
677 	enum_record_t *results, *rp;
678 	char rbuf[1024] = "";
679 	char vbuf[1024] = "";
680 	char *rbp = rbuf;
681 	switch_size_t l = 0, rbl = sizeof(rbuf);
682 	uint32_t cnt = 1;
683 	switch_channel_t *channel = switch_core_session_get_channel(session);
684 	int last_order = -1, last_pref = -2;
685 	char *last_delim = "|";
686 
687 	if (!(mydata = switch_core_session_strdup(session, data))) {
688 		return;
689 	}
690 
691 	if ((argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
692 		dest = argv[0];
693 		root = argv[1];
694 		if (enum_lookup(root, dest, &results, channel, session) == SWITCH_STATUS_SUCCESS) {
695 			switch_event_t *vars;
696 
697 			if (switch_channel_get_variables(channel, &vars) == SWITCH_STATUS_SUCCESS) {
698 				switch_event_header_t *hi;
699 				for (hi = vars->headers; hi; hi = hi->next) {
700 					char *vvar = hi->name;
701 					if (vvar && !strncmp(vvar, "enum_", 5)) {
702 						switch_channel_set_variable(channel, (char *) vvar, NULL);
703 					}
704 				}
705 				switch_event_destroy(&vars);
706 			}
707 
708 			for (rp = results; rp; rp = rp->next) {
709 				if (!rp->supported) {
710 					continue;
711 				}
712 				switch_snprintf(vbuf, sizeof(vbuf), "enum_route_%d", cnt++);
713 				switch_channel_set_variable_var_check(channel, vbuf, rp->route, SWITCH_FALSE);
714 				if (rp->preference == last_pref && rp->order == last_order) {
715 					*last_delim = ',';
716 				}
717 				switch_snprintf(rbp, rbl, "%s|", rp->route);
718 				last_delim = end_of_p(rbp);
719 				last_order = rp->order;
720 				last_pref = rp->preference;
721 				l = strlen(rp->route) + 1;
722 				rbp += l;
723 				rbl -= l;
724 			}
725 
726 			switch_snprintf(vbuf, sizeof(vbuf), "%d", cnt - 1);
727 			switch_channel_set_variable_var_check(channel, "enum_route_count", vbuf, SWITCH_FALSE);
728 			*(rbuf + strlen(rbuf) - 1) = '\0';
729 			switch_channel_set_variable_var_check(channel, "enum_auto_route", rbuf, SWITCH_FALSE);
730 			free_results(&results);
731 		}
732 	}
733 }
734 
SWITCH_STANDARD_API(enum_api)735 SWITCH_STANDARD_API(enum_api)
736 {
737 	int argc = 0;
738 	char *argv[4] = { 0 };
739 	char *mydata = NULL;
740 	char *dest = NULL, *root = NULL;
741 	enum_record_t *results, *rp;
742 	char rbuf[1024] = "";
743 	char *rbp = rbuf;
744 	switch_size_t l = 0, rbl = sizeof(rbuf);
745 	int last_order = -1, last_pref = -2;
746 	char *last_delim = "|";
747 	int ok = 0;
748 
749 	if (zstr(cmd)) {
750 		stream->write_function(stream, "%s", "none");
751 		return SWITCH_STATUS_SUCCESS;
752 	}
753 
754 	if (!(mydata = strdup(cmd))) {
755 		abort();
756 	}
757 
758 	if ((argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
759 		dest = argv[0];
760 		root = argv[1];
761 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Looking up %s@%s\n", dest, root);
762 		if (enum_lookup(root, dest, &results, NULL, session) == SWITCH_STATUS_SUCCESS) {
763 			for (rp = results; rp; rp = rp->next) {
764 				if (!rp->supported) {
765 					continue;
766 				}
767 				if (rp->preference == last_pref && rp->order == last_order) {
768 					*last_delim = ',';
769 				}
770 				switch_snprintf(rbp, rbl, "%s|", rp->route);
771 				last_delim = end_of_p(rbp);
772 				last_order = rp->order;
773 				last_pref = rp->preference;
774 				l = strlen(rp->route) + 1;
775 				rbp += l;
776 				rbl -= l;
777 
778 			}
779 			*(rbuf + strlen(rbuf) - 1) = '\0';
780 			stream->write_function(stream, "%s", rbuf);
781 			free_results(&results);
782 			ok++;
783 		}
784 	}
785 
786 	switch_safe_free(mydata);
787 
788 	if (!ok) {
789 		stream->write_function(stream, "%s", "none");
790 	}
791 
792 	return SWITCH_STATUS_SUCCESS;
793 }
794 
do_load(void)795 static void do_load(void)
796 {
797 	switch_mutex_lock(MUTEX);
798 	if (globals.pool) {
799 		switch_core_destroy_memory_pool(&globals.pool);
800 	}
801 
802 	switch_safe_free(globals.root);
803 	switch_safe_free(globals.isn_root);
804 	memset(&globals, 0, sizeof(globals));
805 	switch_core_new_memory_pool(&globals.pool);
806 	globals.timeout = 10;
807 	load_config();
808 	switch_mutex_unlock(MUTEX);
809 
810 }
811 
SWITCH_STANDARD_API(enum_function)812 SWITCH_STANDARD_API(enum_function)
813 {
814 	int argc = 0;
815 	char *argv[4] = { 0 };
816 	enum_record_t *results, *rp;
817 	char *mydata = NULL;
818 	char *dest = NULL, *root = NULL;
819 
820 	if (session) {
821 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "This function cannot be called from the dialplan.\n");
822 		return SWITCH_STATUS_FALSE;
823 	}
824 
825 	if (!cmd || !(mydata = strdup(cmd))) {
826 		stream->write_function(stream, "Usage: enum [reload | <number> [<root>] ]\n");
827 		return SWITCH_STATUS_SUCCESS;
828 	}
829 
830 	if ((argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
831 		dest = argv[0];
832 		root = argv[1];
833 		switch_assert(dest);
834 
835 		if (!strcasecmp(dest, "reload")) {
836 			do_load();
837 			stream->write_function(stream, "+OK ENUM Reloaded.\n");
838 			switch_safe_free(mydata);
839 			return SWITCH_STATUS_SUCCESS;
840 
841 		}
842 
843 		if (enum_lookup(root, dest, &results, NULL, session) != SWITCH_STATUS_SUCCESS) {
844 			stream->write_function(stream, "No Match!\n");
845 			switch_safe_free(mydata);
846 			return SWITCH_STATUS_SUCCESS;
847 		}
848 
849 		stream->write_function(stream,
850 							   "\nOffered Routes:\n"
851 							   "Order\tPref\tService   \tRoute\n" "==============================================================================\n");
852 
853 		for (rp = results; rp; rp = rp->next) {
854 			stream->write_function(stream, "%d\t%d\t%-10s\t%s\n", rp->order, rp->preference, rp->service, rp->route);
855 		}
856 
857 
858 		stream->write_function(stream,
859 							   "\nSupported Routes:\n"
860 							   "Order\tPref\tService   \tRoute\n" "==============================================================================\n");
861 
862 
863 		for (rp = results; rp; rp = rp->next) {
864 			if (rp->supported) {
865 				stream->write_function(stream, "%d\t%d\t%-10s\t%s\n", rp->order, rp->preference, rp->service, rp->route);
866 			}
867 		}
868 
869 		free_results(&results);
870 	} else {
871 		stream->write_function(stream, "Invalid Input!\n");
872 	}
873 	switch_safe_free(mydata);
874 
875 	return SWITCH_STATUS_SUCCESS;
876 }
877 
event_handler(switch_event_t * event)878 static void event_handler(switch_event_t *event)
879 {
880 	if (globals.auto_reload) {
881 		switch_mutex_lock(MUTEX);
882 		do_load();
883 		switch_mutex_unlock(MUTEX);
884 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "ENUM Reloaded\n");
885 	}
886 }
887 
SWITCH_MODULE_LOAD_FUNCTION(mod_enum_load)888 SWITCH_MODULE_LOAD_FUNCTION(mod_enum_load)
889 {
890 	switch_api_interface_t *api_interface;
891 	switch_application_interface_t *app_interface;
892 	switch_dialplan_interface_t *dp_interface;
893 
894 
895 	switch_mutex_init(&MUTEX, SWITCH_MUTEX_NESTED, pool);
896 
897 	if ((switch_event_bind_removable(modname, SWITCH_EVENT_RELOADXML, NULL, event_handler, NULL, &NODE) != SWITCH_STATUS_SUCCESS)) {
898 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't bind!\n");
899 		return SWITCH_STATUS_TERM;
900 	}
901 
902 
903 	memset(&globals, 0, sizeof(globals));
904 	do_load();
905 
906 	/* connect my internal structure to the blank pointer passed to me */
907 	*module_interface = switch_loadable_module_create_module_interface(pool, modname);
908 	SWITCH_ADD_API(api_interface, "enum", "ENUM", enum_function, "");
909 	SWITCH_ADD_API(api_interface, "enum_auto", "ENUM", enum_api, "");
910 	SWITCH_ADD_APP(app_interface, "enum", "Perform an ENUM lookup", "Perform an ENUM lookup", enum_app_function, "[reload | <number> [<root>]]",
911 				   SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC);
912 	SWITCH_ADD_DIALPLAN(dp_interface, "enum", enum_dialplan_hunt);
913 
914 
915 	/* indicate that the module should continue to be loaded */
916 	return SWITCH_STATUS_SUCCESS;
917 }
918 
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_enum_shutdown)919 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_enum_shutdown)
920 {
921 	switch_event_unbind(&NODE);
922 
923 	if (globals.pool) {
924 		switch_core_destroy_memory_pool(&globals.pool);
925 	}
926 
927 	switch_safe_free(globals.root);
928 	switch_safe_free(globals.isn_root);
929 #ifdef _MSC_VER
930 	switch_safe_free(globals.nameserver_buf);
931 #endif
932 
933 	return SWITCH_STATUS_UNLOAD;
934 }
935 
936 /* For Emacs:
937  * Local Variables:
938  * mode:c
939  * indent-tabs-mode:t
940  * tab-width:4
941  * c-basic-offset:4
942  * End:
943  * For VIM:
944  * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
945  */
946