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