00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #include "asterisk.h"
00029
00030 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
00031
00032 #include <stdlib.h>
00033 #include <stdio.h>
00034 #include <unistd.h>
00035 #include <netinet/in.h>
00036 #include <arpa/inet.h>
00037 #include <sys/socket.h>
00038 #include <string.h>
00039 #include <errno.h>
00040 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(SOLARIS) || defined(__Darwin__)
00041 #include <sys/types.h>
00042 #include <netinet/in_systm.h>
00043 #endif
00044 #include <netinet/ip.h>
00045 #include <sys/ioctl.h>
00046 #include <netinet/in.h>
00047 #include <net/if.h>
00048 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__)
00049 #include <net/if_dl.h>
00050 #include <ifaddrs.h>
00051 #endif
00052 #include <zlib.h>
00053 #include <sys/signal.h>
00054 #include <pthread.h>
00055
00056 #include "asterisk/file.h"
00057 #include "asterisk/logger.h"
00058 #include "asterisk/channel.h"
00059 #include "asterisk/config.h"
00060 #include "asterisk/options.h"
00061 #include "asterisk/pbx.h"
00062 #include "asterisk/module.h"
00063 #include "asterisk/frame.h"
00064 #include "asterisk/file.h"
00065 #include "asterisk/cli.h"
00066 #include "asterisk/lock.h"
00067 #include "asterisk/md5.h"
00068 #include "asterisk/dundi.h"
00069 #include "asterisk/sched.h"
00070 #include "asterisk/io.h"
00071 #include "asterisk/utils.h"
00072 #include "asterisk/crypto.h"
00073 #include "asterisk/astdb.h"
00074 #include "asterisk/acl.h"
00075 #include "asterisk/aes.h"
00076
00077 #include "dundi-parser.h"
00078
00079 #define MAX_RESULTS 64
00080
00081 #define MAX_PACKET_SIZE 8192
00082
00083 #define DUNDI_MODEL_INBOUND (1 << 0)
00084 #define DUNDI_MODEL_OUTBOUND (1 << 1)
00085 #define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND)
00086
00087
00088 #define DUNDI_TIMING_HISTORY 10
00089
00090 #define FLAG_ISREG (1 << 0)
00091 #define FLAG_DEAD (1 << 1)
00092 #define FLAG_FINAL (1 << 2)
00093 #define FLAG_ISQUAL (1 << 3)
00094 #define FLAG_ENCRYPT (1 << 4)
00095 #define FLAG_SENDFULLKEY (1 << 5)
00096 #define FLAG_STOREHIST (1 << 6)
00097
00098 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17)
00099
00100 #if 0
00101 #define DUNDI_SECRET_TIME 15
00102 #else
00103 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME
00104 #endif
00105
00106 #define KEY_OUT 0
00107 #define KEY_IN 1
00108
00109 static struct io_context *io;
00110 static struct sched_context *sched;
00111 static int netsocket = -1;
00112 static pthread_t netthreadid = AST_PTHREADT_NULL;
00113 static pthread_t precachethreadid = AST_PTHREADT_NULL;
00114 static int tos = 0;
00115 static int dundidebug = 0;
00116 static int authdebug = 0;
00117 static int dundi_ttl = DUNDI_DEFAULT_TTL;
00118 static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE;
00119 static int dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
00120 static int global_autokilltimeout = 0;
00121 static dundi_eid global_eid;
00122 static int default_expiration = 60;
00123 static int global_storehistory = 0;
00124 static char dept[80];
00125 static char org[80];
00126 static char locality[80];
00127 static char stateprov[80];
00128 static char country[80];
00129 static char email[80];
00130 static char phone[80];
00131 static char secretpath[80];
00132 static char cursecret[80];
00133 static char ipaddr[80];
00134 static time_t rotatetime;
00135 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } };
00136 static int dundi_shutdown = 0;
00137
00138 struct permission {
00139 AST_LIST_ENTRY(permission) list;
00140 int allow;
00141 char name[0];
00142 };
00143
00144 struct dundi_packet {
00145 AST_LIST_ENTRY(dundi_packet) list;
00146 struct dundi_hdr *h;
00147 int datalen;
00148 struct dundi_transaction *parent;
00149 int retransid;
00150 int retrans;
00151 unsigned char data[0];
00152 };
00153
00154 struct dundi_hint_metadata {
00155 unsigned short flags;
00156 char exten[AST_MAX_EXTENSION];
00157 };
00158
00159 struct dundi_precache_queue {
00160 AST_LIST_ENTRY(dundi_precache_queue) list;
00161 char *context;
00162 time_t expiration;
00163 char number[0];
00164 };
00165
00166 struct dundi_request;
00167
00168 struct dundi_transaction {
00169 struct sockaddr_in addr;
00170 struct timeval start;
00171 dundi_eid eids[DUNDI_MAX_STACK + 1];
00172 int eidcount;
00173 dundi_eid us_eid;
00174 dundi_eid them_eid;
00175 aes_encrypt_ctx ecx;
00176 aes_decrypt_ctx dcx;
00177 unsigned int flags;
00178 int ttl;
00179 int thread;
00180 int retranstimer;
00181 int autokillid;
00182 int autokilltimeout;
00183 unsigned short strans;
00184 unsigned short dtrans;
00185 unsigned char iseqno;
00186 unsigned char oiseqno;
00187 unsigned char oseqno;
00188 unsigned char aseqno;
00189 AST_LIST_HEAD_NOLOCK(packetlist, dundi_packet) packets;
00190 struct packetlist lasttrans;
00191 struct dundi_request *parent;
00192 AST_LIST_ENTRY(dundi_transaction) parentlist;
00193 AST_LIST_ENTRY(dundi_transaction) all;
00194 };
00195
00196 struct dundi_request {
00197 char dcontext[AST_MAX_EXTENSION];
00198 char number[AST_MAX_EXTENSION];
00199 dundi_eid query_eid;
00200 dundi_eid root_eid;
00201 struct dundi_result *dr;
00202 struct dundi_entity_info *dei;
00203 struct dundi_hint_metadata *hmd;
00204 int maxcount;
00205 int respcount;
00206 int expiration;
00207 int cbypass;
00208 int pfds[2];
00209 unsigned long crc32;
00210 AST_LIST_HEAD_NOLOCK(, dundi_transaction) trans;
00211 AST_LIST_ENTRY(dundi_request) list;
00212 };
00213
00214 struct dundi_mapping {
00215 char dcontext[AST_MAX_EXTENSION];
00216 char lcontext[AST_MAX_EXTENSION];
00217 int weight;
00218 int options;
00219 int tech;
00220 int dead;
00221 char dest[AST_MAX_EXTENSION];
00222 AST_LIST_ENTRY(dundi_mapping) list;
00223 };
00224
00225 struct dundi_peer {
00226 dundi_eid eid;
00227 struct sockaddr_in addr;
00228 AST_LIST_HEAD_NOLOCK(permissionlist, permission) permit;
00229 struct permissionlist include;
00230 dundi_eid us_eid;
00231 char inkey[80];
00232 char outkey[80];
00233 int dead;
00234 int registerid;
00235 int qualifyid;
00236 int sentfullkey;
00237 int order;
00238 unsigned char txenckey[256];
00239 unsigned char rxenckey[256];
00240 unsigned long us_keycrc32;
00241 aes_encrypt_ctx us_ecx;
00242 aes_decrypt_ctx us_dcx;
00243 unsigned long them_keycrc32;
00244 aes_encrypt_ctx them_ecx;
00245 aes_decrypt_ctx them_dcx;
00246 time_t keyexpire;
00247 int registerexpire;
00248 int lookuptimes[DUNDI_TIMING_HISTORY];
00249 char *lookups[DUNDI_TIMING_HISTORY];
00250 int avgms;
00251 struct dundi_transaction *regtrans;
00252 struct dundi_transaction *qualtrans;
00253 int model;
00254 int pcmodel;
00255
00256 unsigned int dynamic:1;
00257 int lastms;
00258 int maxms;
00259 struct timeval qualtx;
00260 AST_LIST_ENTRY(dundi_peer) list;
00261 };
00262
00263 static AST_LIST_HEAD_STATIC(peers, dundi_peer);
00264 static AST_LIST_HEAD_STATIC(pcq, dundi_precache_queue);
00265 static AST_LIST_HEAD_NOLOCK_STATIC(mappings, dundi_mapping);
00266 static AST_LIST_HEAD_NOLOCK_STATIC(requests, dundi_request);
00267 static AST_LIST_HEAD_NOLOCK_STATIC(alltrans, dundi_transaction);
00268
00269
00270
00271
00272
00273
00274 static struct dundi_peer *any_peer;
00275
00276 static int dundi_xmit(struct dundi_packet *pack);
00277
00278 static void dundi_debug_output(const char *data)
00279 {
00280 if (dundidebug)
00281 ast_verbose("%s", data);
00282 }
00283
00284 static void dundi_error_output(const char *data)
00285 {
00286 ast_log(LOG_WARNING, "%s", data);
00287 }
00288
00289 static int has_permission(struct permissionlist *permlist, char *cont)
00290 {
00291 struct permission *perm;
00292 int res = 0;
00293
00294 AST_LIST_TRAVERSE(permlist, perm, list) {
00295 if (!strcasecmp(perm->name, "all") || !strcasecmp(perm->name, cont))
00296 res = perm->allow;
00297 }
00298
00299 return res;
00300 }
00301
00302 static char *tech2str(int tech)
00303 {
00304 switch(tech) {
00305 case DUNDI_PROTO_NONE:
00306 return "None";
00307 case DUNDI_PROTO_IAX:
00308 return "IAX2";
00309 case DUNDI_PROTO_SIP:
00310 return "SIP";
00311 case DUNDI_PROTO_H323:
00312 return "H323";
00313 default:
00314 return "Unknown";
00315 }
00316 }
00317
00318 static int str2tech(char *str)
00319 {
00320 if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2"))
00321 return DUNDI_PROTO_IAX;
00322 else if (!strcasecmp(str, "SIP"))
00323 return DUNDI_PROTO_SIP;
00324 else if (!strcasecmp(str, "H323"))
00325 return DUNDI_PROTO_H323;
00326 else
00327 return -1;
00328 }
00329
00330 static int dundi_lookup_internal(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int ttl, int blockempty, struct dundi_hint_metadata *md, int *expiration, int cybpass, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int direct[]);
00331 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]);
00332 static struct dundi_transaction *create_transaction(struct dundi_peer *p);
00333 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin)
00334 {
00335 struct dundi_transaction *trans;
00336
00337
00338 AST_LIST_TRAVERSE(&alltrans, trans, all) {
00339 if (!inaddrcmp(&trans->addr, sin) &&
00340 ((trans->strans == (ntohs(hdr->dtrans) & 32767)) ||
00341 ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) ) {
00342 if (hdr->strans)
00343 trans->dtrans = ntohs(hdr->strans) & 32767;
00344 break;
00345 }
00346 }
00347 if (!trans) {
00348 switch(hdr->cmdresp & 0x7f) {
00349 case DUNDI_COMMAND_DPDISCOVER:
00350 case DUNDI_COMMAND_EIDQUERY:
00351 case DUNDI_COMMAND_PRECACHERQ:
00352 case DUNDI_COMMAND_REGREQ:
00353 case DUNDI_COMMAND_NULL:
00354 case DUNDI_COMMAND_ENCRYPT:
00355 if (hdr->strans) {
00356
00357 trans = create_transaction(NULL);
00358 if (trans) {
00359 memcpy(&trans->addr, sin, sizeof(trans->addr));
00360 trans->dtrans = ntohs(hdr->strans) & 32767;
00361 } else
00362 ast_log(LOG_WARNING, "Out of memory!\n");
00363 }
00364 break;
00365 default:
00366 break;
00367 }
00368 }
00369 return trans;
00370 }
00371
00372 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied);
00373
00374 static int dundi_ack(struct dundi_transaction *trans, int final)
00375 {
00376 return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL);
00377 }
00378 static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin)
00379 {
00380 struct {
00381 struct dundi_packet pack;
00382 struct dundi_hdr hdr;
00383 } tmp;
00384 struct dundi_transaction trans;
00385
00386 if (h->cmdresp == DUNDI_COMMAND_INVALID)
00387 return;
00388 memset(&tmp, 0, sizeof(tmp));
00389 memset(&trans, 0, sizeof(trans));
00390 memcpy(&trans.addr, sin, sizeof(trans.addr));
00391 tmp.hdr.strans = h->dtrans;
00392 tmp.hdr.dtrans = h->strans;
00393 tmp.hdr.iseqno = h->oseqno;
00394 tmp.hdr.oseqno = h->iseqno;
00395 tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID;
00396 tmp.hdr.cmdflags = 0;
00397 tmp.pack.h = (struct dundi_hdr *)tmp.pack.data;
00398 tmp.pack.datalen = sizeof(struct dundi_hdr);
00399 tmp.pack.parent = &trans;
00400 dundi_xmit(&tmp.pack);
00401 }
00402
00403 static void reset_global_eid(void)
00404 {
00405 #if defined(SIOCGIFHWADDR)
00406 int x,s;
00407 char eid_str[20];
00408 struct ifreq ifr;
00409
00410 s = socket(AF_INET, SOCK_STREAM, 0);
00411 if (s > 0) {
00412 x = 0;
00413 for(x=0;x<10;x++) {
00414 memset(&ifr, 0, sizeof(ifr));
00415 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x);
00416 if (!ioctl(s, SIOCGIFHWADDR, &ifr)) {
00417 memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid));
00418 ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s' using 'siocgifhwaddr'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifr.ifr_name);
00419 close(s);
00420 return;
00421 }
00422 }
00423 close(s);
00424 }
00425 #else
00426 #if defined(ifa_broadaddr) && !defined(SOLARIS)
00427 char eid_str[20];
00428 struct ifaddrs *ifap;
00429
00430 if (getifaddrs(&ifap) == 0) {
00431 struct ifaddrs *p;
00432 for (p = ifap; p; p = p->ifa_next) {
00433 if ((p->ifa_addr->sa_family == AF_LINK) && !(p->ifa_flags & IFF_LOOPBACK) && (p->ifa_flags & IFF_RUNNING)) {
00434 struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr;
00435 memcpy(&(global_eid.eid), sdp->sdl_data + sdp->sdl_nlen, 6);
00436 ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s' using 'getifaddrs'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), p->ifa_name);
00437 freeifaddrs(ifap);
00438 return;
00439 }
00440 }
00441 freeifaddrs(ifap);
00442 }
00443 #endif
00444 #endif
00445 ast_log(LOG_NOTICE, "No ethernet interface found for seeding global EID. You will have to set it manually.\n");
00446 }
00447
00448 static int get_trans_id(void)
00449 {
00450 struct dundi_transaction *t;
00451 int stid = (ast_random() % 32766) + 1;
00452 int tid = stid;
00453
00454 do {
00455 AST_LIST_TRAVERSE(&alltrans, t, all) {
00456 if (t->strans == tid)
00457 break;
00458 }
00459 if (!t)
00460 return tid;
00461 tid = (tid % 32766) + 1;
00462 } while (tid != stid);
00463
00464 return 0;
00465 }
00466
00467 static int reset_transaction(struct dundi_transaction *trans)
00468 {
00469 int tid;
00470 tid = get_trans_id();
00471 if (tid < 1)
00472 return -1;
00473 trans->strans = tid;
00474 trans->dtrans = 0;
00475 trans->iseqno = 0;
00476 trans->oiseqno = 0;
00477 trans->oseqno = 0;
00478 trans->aseqno = 0;
00479 ast_clear_flag(trans, FLAG_FINAL);
00480 return 0;
00481 }
00482
00483 static struct dundi_peer *find_peer(dundi_eid *eid)
00484 {
00485 struct dundi_peer *cur = NULL;
00486
00487 if (!eid)
00488 eid = &empty_eid;
00489
00490 AST_LIST_TRAVERSE(&peers, cur, list) {
00491 if (!dundi_eid_cmp(&cur->eid,eid))
00492 break;
00493 }
00494
00495 if (!cur && any_peer)
00496 cur = any_peer;
00497
00498 return cur;
00499 }
00500
00501 static void build_iv(unsigned char *iv)
00502 {
00503
00504 unsigned int *fluffy;
00505 int x;
00506 fluffy = (unsigned int *)(iv);
00507 for (x=0;x<4;x++)
00508 fluffy[x] = ast_random();
00509 }
00510
00511 struct dundi_query_state {
00512 dundi_eid *eids[DUNDI_MAX_STACK + 1];
00513 int directs[DUNDI_MAX_STACK + 1];
00514 dundi_eid reqeid;
00515 char called_context[AST_MAX_EXTENSION];
00516 char called_number[AST_MAX_EXTENSION];
00517 struct dundi_mapping *maps;
00518 int nummaps;
00519 int nocache;
00520 struct dundi_transaction *trans;
00521 void *chal;
00522 int challen;
00523 int ttl;
00524 char fluffy[0];
00525 };
00526
00527 static int dundi_lookup_local(struct dundi_result *dr, struct dundi_mapping *map, char *called_number, dundi_eid *us_eid, int anscnt, struct dundi_hint_metadata *hmd)
00528 {
00529 struct ast_flags flags = {0};
00530 int x;
00531 if (!ast_strlen_zero(map->lcontext)) {
00532 if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL))
00533 ast_set_flag(&flags, DUNDI_FLAG_EXISTS);
00534 if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL))
00535 ast_set_flag(&flags, DUNDI_FLAG_CANMATCH);
00536 if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL))
00537 ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE);
00538 if (ast_ignore_pattern(map->lcontext, called_number))
00539 ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT);
00540
00541
00542 if (ast_test_flag(&flags, AST_FLAGS_ALL))
00543 ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK);
00544
00545 if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) {
00546
00547 ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH);
00548 }
00549 if (ast_test_flag(&flags, AST_FLAGS_ALL)) {
00550 struct varshead headp;
00551 struct ast_var_t *newvariable;
00552 ast_set_flag(&flags, map->options & 0xffff);
00553 ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL);
00554 dr[anscnt].techint = map->tech;
00555 dr[anscnt].weight = map->weight;
00556 dr[anscnt].expiration = dundi_cache_time;
00557 ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech));
00558 dr[anscnt].eid = *us_eid;
00559 dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid);
00560 if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) {
00561 AST_LIST_HEAD_INIT_NOLOCK(&headp);
00562 newvariable = ast_var_assign("NUMBER", called_number);
00563 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
00564 newvariable = ast_var_assign("EID", dr[anscnt].eid_str);
00565 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
00566 newvariable = ast_var_assign("SECRET", cursecret);
00567 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
00568 newvariable = ast_var_assign("IPADDR", ipaddr);
00569 AST_LIST_INSERT_HEAD(&headp, newvariable, entries);
00570 pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest));
00571 while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries)))
00572 ast_var_delete(newvariable);
00573 } else
00574 dr[anscnt].dest[0] = '\0';
00575 anscnt++;
00576 } else {
00577
00578
00579 char tmp[AST_MAX_EXTENSION + 1] = "";
00580 for (x = 0; x < (sizeof(tmp) - 1); x++) {
00581 tmp[x] = called_number[x];
00582 if (!tmp[x])
00583 break;
00584 if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) {
00585
00586
00587 if (strlen(tmp) > strlen(hmd->exten)) {
00588 ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten));
00589 }
00590 break;
00591 }
00592 }
00593 }
00594 }
00595 return anscnt;
00596 }
00597
00598 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout);
00599
00600 static void *dundi_lookup_thread(void *data)
00601 {
00602 struct dundi_query_state *st = data;
00603 struct dundi_result dr[MAX_RESULTS];
00604 struct dundi_ie_data ied;
00605 struct dundi_hint_metadata hmd;
00606 char eid_str[20];
00607 int res, x;
00608 int ouranswers=0;
00609 int max = 999999;
00610 int expiration = dundi_cache_time;
00611
00612 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
00613 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
00614 memset(&ied, 0, sizeof(ied));
00615 memset(&dr, 0, sizeof(dr));
00616 memset(&hmd, 0, sizeof(hmd));
00617
00618 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
00619 for (x=0;x<st->nummaps;x++)
00620 ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd);
00621 if (ouranswers < 0)
00622 ouranswers = 0;
00623 for (x=0;x<ouranswers;x++) {
00624 if (dr[x].weight < max)
00625 max = dr[x].weight;
00626 }
00627
00628 if (max) {
00629
00630 res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, st->called_context, st->called_number, st->ttl, 1, &hmd, &expiration, st->nocache, 0, NULL, st->eids, st->directs);
00631 if (res > 0) {
00632
00633 ouranswers += res;
00634 } else {
00635 if ((res < -1) && (!ouranswers))
00636 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending");
00637 }
00638 }
00639 AST_LIST_LOCK(&peers);
00640
00641 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
00642 hmd.exten[0] = '\0';
00643 if (ast_test_flag(st->trans, FLAG_DEAD)) {
00644 ast_log(LOG_DEBUG, "Our transaction went away!\n");
00645 st->trans->thread = 0;
00646 destroy_trans(st->trans, 0);
00647 } else {
00648 for (x=0;x<ouranswers;x++) {
00649
00650 if (dr[x].expiration && (expiration > dr[x].expiration))
00651 expiration = dr[x].expiration;
00652 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
00653 }
00654 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
00655 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
00656 dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
00657 st->trans->thread = 0;
00658 }
00659 AST_LIST_UNLOCK(&peers);
00660 free(st);
00661 return NULL;
00662 }
00663
00664 static void *dundi_precache_thread(void *data)
00665 {
00666 struct dundi_query_state *st = data;
00667 struct dundi_ie_data ied;
00668 struct dundi_hint_metadata hmd;
00669 char eid_str[20];
00670
00671 ast_log(LOG_DEBUG, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context,
00672 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
00673 memset(&ied, 0, sizeof(ied));
00674
00675
00676 dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids);
00677
00678 AST_LIST_LOCK(&peers);
00679
00680 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK))
00681 hmd.exten[0] = '\0';
00682 if (ast_test_flag(st->trans, FLAG_DEAD)) {
00683 ast_log(LOG_DEBUG, "Our transaction went away!\n");
00684 st->trans->thread = 0;
00685 destroy_trans(st->trans, 0);
00686 } else {
00687 dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
00688 st->trans->thread = 0;
00689 }
00690 AST_LIST_UNLOCK(&peers);
00691 free(st);
00692 return NULL;
00693 }
00694
00695 static int dundi_query_eid_internal(struct dundi_entity_info *dei, const char *dcontext, dundi_eid *eid, struct dundi_hint_metadata *hmd, int ttl, int blockempty, dundi_eid *avoid[]);
00696
00697 static void *dundi_query_thread(void *data)
00698 {
00699 struct dundi_query_state *st = data;
00700 struct dundi_entity_info dei;
00701 struct dundi_ie_data ied;
00702 struct dundi_hint_metadata hmd;
00703 char eid_str[20];
00704 int res;
00705 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context,
00706 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves");
00707 memset(&ied, 0, sizeof(ied));
00708 memset(&dei, 0, sizeof(dei));
00709 memset(&hmd, 0, sizeof(hmd));
00710 if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) {
00711
00712 ast_log(LOG_DEBUG, "Neat, someone look for us!\n");
00713 ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit));
00714 ast_copy_string(dei.org, org, sizeof(dei.org));
00715 ast_copy_string(dei.locality, locality, sizeof(dei.locality));
00716 ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov));
00717 ast_copy_string(dei.country, country, sizeof(dei.country));
00718 ast_copy_string(dei.email, email, sizeof(dei.email));
00719 ast_copy_string(dei.phone, phone, sizeof(dei.phone));
00720 res = 1;
00721 } else {
00722
00723 res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids);
00724 }
00725 AST_LIST_LOCK(&peers);
00726 if (ast_test_flag(st->trans, FLAG_DEAD)) {
00727 ast_log(LOG_DEBUG, "Our transaction went away!\n");
00728 st->trans->thread = 0;
00729 destroy_trans(st->trans, 0);
00730 } else {
00731 if (res) {
00732 dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit);
00733 dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org);
00734 dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality);
00735 dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov);
00736 dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country);
00737 dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email);
00738 dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone);
00739 if (!ast_strlen_zero(dei.ipaddr))
00740 dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr);
00741 }
00742 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
00743 dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
00744 st->trans->thread = 0;
00745 }
00746 AST_LIST_UNLOCK(&peers);
00747 free(st);
00748 return NULL;
00749 }
00750
00751 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
00752 {
00753 struct dundi_query_state *st;
00754 int totallen;
00755 int x;
00756 int skipfirst=0;
00757 struct dundi_ie_data ied;
00758 char eid_str[20];
00759 char *s;
00760 pthread_t lookupthread;
00761 pthread_attr_t attr;
00762 if (ies->eidcount > 1) {
00763
00764
00765
00766
00767 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
00768 skipfirst = 1;
00769 }
00770 totallen = sizeof(struct dundi_query_state);
00771 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
00772 st = ast_calloc(1, totallen);
00773 if (st) {
00774 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
00775 memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid));
00776 st->trans = trans;
00777 st->ttl = ies->ttl - 1;
00778 if (st->ttl < 0)
00779 st->ttl = 0;
00780 s = st->fluffy;
00781 for (x=skipfirst;ies->eids[x];x++) {
00782 st->eids[x-skipfirst] = (dundi_eid *)s;
00783 *st->eids[x-skipfirst] = *ies->eids[x];
00784 s += sizeof(dundi_eid);
00785 }
00786 ast_log(LOG_DEBUG, "Answering EID query for '%s@%s'!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), ies->reqeid), ies->called_context);
00787 pthread_attr_init(&attr);
00788 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
00789 trans->thread = 1;
00790 if (ast_pthread_create(&lookupthread, &attr, dundi_query_thread, st)) {
00791 trans->thread = 0;
00792 ast_log(LOG_WARNING, "Unable to create thread!\n");
00793 free(st);
00794 memset(&ied, 0, sizeof(ied));
00795 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
00796 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
00797 pthread_attr_destroy(&attr);
00798 return -1;
00799 }
00800 pthread_attr_destroy(&attr);
00801 } else {
00802 ast_log(LOG_WARNING, "Out of memory!\n");
00803 memset(&ied, 0, sizeof(ied));
00804 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
00805 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied);
00806 return -1;
00807 }
00808 return 0;
00809 }
00810
00811 static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration)
00812 {
00813 int unaffected;
00814 char key1[256];
00815 char key2[256];
00816 char eidpeer_str[20];
00817 char eidroot_str[20];
00818 char data[80];
00819 time_t timeout;
00820
00821 if (expiration < 0)
00822 expiration = dundi_cache_time;
00823
00824
00825 if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK)))
00826 return 0;
00827
00828 unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED));
00829
00830 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
00831 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
00832 snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32);
00833 snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str);
00834
00835 time(&timeout);
00836 timeout += expiration;
00837 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
00838
00839 ast_db_put("dundi/cache", key1, data);
00840 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key1);
00841 ast_db_put("dundi/cache", key2, data);
00842 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key2);
00843 return 0;
00844 }
00845
00846 static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push)
00847 {
00848 int x;
00849 char key1[256];
00850 char key2[256];
00851 char data[1024];
00852 char eidpeer_str[20];
00853 char eidroot_str[20];
00854 time_t timeout;
00855
00856 if (expiration < 1)
00857 expiration = dundi_cache_time;
00858
00859
00860 if (push)
00861 expiration += 10;
00862 else
00863 expiration -= 10;
00864 if (expiration < 1)
00865 expiration = 1;
00866 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer);
00867 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
00868 snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32);
00869 snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str);
00870
00871 time(&timeout);
00872 timeout += expiration;
00873 snprintf(data, sizeof(data), "%ld|", (long)(timeout));
00874 for (x=start;x<req->respcount;x++) {
00875
00876 if (strchr(req->dr[x].dest, '|'))
00877 continue;
00878 snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|",
00879 req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest,
00880 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid));
00881 }
00882 ast_db_put("dundi/cache", key1, data);
00883 ast_db_put("dundi/cache", key2, data);
00884 return 0;
00885 }
00886
00887 static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
00888 {
00889 struct dundi_query_state *st;
00890 int totallen;
00891 int x,z;
00892 struct dundi_ie_data ied;
00893 char *s;
00894 struct dundi_result dr2[MAX_RESULTS];
00895 struct dundi_request dr;
00896 struct dundi_hint_metadata hmd;
00897
00898 struct dundi_mapping *cur;
00899 int mapcount;
00900 int skipfirst = 0;
00901
00902 pthread_t lookupthread;
00903 pthread_attr_t attr;
00904
00905 memset(&dr2, 0, sizeof(dr2));
00906 memset(&dr, 0, sizeof(dr));
00907 memset(&hmd, 0, sizeof(hmd));
00908
00909
00910 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
00911 dr.dr = dr2;
00912 dr.maxcount = MAX_RESULTS;
00913 dr.expiration = dundi_cache_time;
00914 dr.hmd = &hmd;
00915 dr.pfds[0] = dr.pfds[1] = -1;
00916 trans->parent = &dr;
00917 ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext));
00918 ast_copy_string(dr.number, ies->called_number, sizeof(dr.number));
00919
00920 for (x=0;x<ies->anscount;x++) {
00921 if (trans->parent->respcount < trans->parent->maxcount) {
00922
00923 for (z=0;z<trans->parent->respcount;z++) {
00924 if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) &&
00925 !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data))
00926 break;
00927 }
00928 if (z == trans->parent->respcount) {
00929
00930 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags);
00931 trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol;
00932 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight);
00933 trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid;
00934 if (ies->expiration > 0)
00935 trans->parent->dr[trans->parent->respcount].expiration = ies->expiration;
00936 else
00937 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
00938 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
00939 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
00940 &ies->answers[x]->eid);
00941 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data,
00942 sizeof(trans->parent->dr[trans->parent->respcount].dest));
00943 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol),
00944 sizeof(trans->parent->dr[trans->parent->respcount].tech));
00945 trans->parent->respcount++;
00946 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
00947 } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) {
00948
00949 trans->parent->dr[z].weight = ies->answers[x]->weight;
00950 }
00951 } else
00952 ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n",
00953 trans->parent->number, trans->parent->dcontext);
00954
00955 }
00956
00957 cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1);
00958 if (ies->hint)
00959 cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration);
00960
00961 totallen = sizeof(struct dundi_query_state);
00962
00963 mapcount = 0;
00964 AST_LIST_TRAVERSE(&mappings, cur, list) {
00965 if (!strcasecmp(cur->dcontext, ccontext))
00966 mapcount++;
00967 }
00968
00969
00970 if (!mapcount)
00971 return -1;
00972
00973 if (ies->eidcount > 1) {
00974
00975
00976
00977
00978 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
00979 skipfirst = 1;
00980 }
00981
00982
00983 totallen += mapcount * sizeof(struct dundi_mapping);
00984 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
00985 st = ast_calloc(1, totallen);
00986 if (st) {
00987 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
00988 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
00989 st->trans = trans;
00990 st->ttl = ies->ttl - 1;
00991 st->nocache = ies->cbypass;
00992 if (st->ttl < 0)
00993 st->ttl = 0;
00994 s = st->fluffy;
00995 for (x=skipfirst;ies->eids[x];x++) {
00996 st->eids[x-skipfirst] = (dundi_eid *)s;
00997 *st->eids[x-skipfirst] = *ies->eids[x];
00998 st->directs[x-skipfirst] = ies->eid_direct[x];
00999 s += sizeof(dundi_eid);
01000 }
01001
01002 x = 0;
01003 st->maps = (struct dundi_mapping *)s;
01004 AST_LIST_TRAVERSE(&mappings, cur, list) {
01005 if (!strcasecmp(cur->dcontext, ccontext)) {
01006 if (x < mapcount) {
01007 st->maps[x] = *cur;
01008 st->maps[x].list.next = NULL;
01009 x++;
01010 }
01011 }
01012 }
01013 st->nummaps = mapcount;
01014 ast_log(LOG_DEBUG, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context);
01015 pthread_attr_init(&attr);
01016 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
01017 trans->thread = 1;
01018 if (ast_pthread_create(&lookupthread, &attr, dundi_precache_thread, st)) {
01019 trans->thread = 0;
01020 ast_log(LOG_WARNING, "Unable to create thread!\n");
01021 free(st);
01022 memset(&ied, 0, sizeof(ied));
01023 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
01024 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
01025 pthread_attr_destroy(&attr);
01026 return -1;
01027 }
01028 pthread_attr_destroy(&attr);
01029 } else {
01030 ast_log(LOG_WARNING, "Out of memory!\n");
01031 memset(&ied, 0, sizeof(ied));
01032 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
01033 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied);
01034 return -1;
01035 }
01036 return 0;
01037 }
01038
01039 static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext)
01040 {
01041 struct dundi_query_state *st;
01042 int totallen;
01043 int x;
01044 struct dundi_ie_data ied;
01045 char *s;
01046 struct dundi_mapping *cur;
01047 int mapcount = 0;
01048 int skipfirst = 0;
01049
01050 pthread_t lookupthread;
01051 pthread_attr_t attr;
01052 totallen = sizeof(struct dundi_query_state);
01053
01054 AST_LIST_TRAVERSE(&mappings, cur, list) {
01055 if (!strcasecmp(cur->dcontext, ccontext))
01056 mapcount++;
01057 }
01058
01059 if (!mapcount)
01060 return -1;
01061
01062 if (ies->eidcount > 1) {
01063
01064
01065
01066
01067 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1]))
01068 skipfirst = 1;
01069 }
01070
01071 totallen += mapcount * sizeof(struct dundi_mapping);
01072 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid);
01073 st = ast_calloc(1, totallen);
01074 if (st) {
01075 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context));
01076 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number));
01077 st->trans = trans;
01078 st->ttl = ies->ttl - 1;
01079 st->nocache = ies->cbypass;
01080 if (st->ttl < 0)
01081 st->ttl = 0;
01082 s = st->fluffy;
01083 for (x=skipfirst;ies->eids[x];x++) {
01084 st->eids[x-skipfirst] = (dundi_eid *)s;
01085 *st->eids[x-skipfirst] = *ies->eids[x];
01086 st->directs[x-skipfirst] = ies->eid_direct[x];
01087 s += sizeof(dundi_eid);
01088 }
01089
01090 x = 0;
01091 st->maps = (struct dundi_mapping *)s;
01092 AST_LIST_TRAVERSE(&mappings, cur, list) {
01093 if (!strcasecmp(cur->dcontext, ccontext)) {
01094 if (x < mapcount) {
01095 st->maps[x] = *cur;
01096 st->maps[x].list.next = NULL;
01097 x++;
01098 }
01099 }
01100 }
01101 st->nummaps = mapcount;
01102 ast_log(LOG_DEBUG, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context);
01103 pthread_attr_init(&attr);
01104 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
01105 trans->thread = 1;
01106 if (ast_pthread_create(&lookupthread, &attr, dundi_lookup_thread, st)) {
01107 trans->thread = 0;
01108 ast_log(LOG_WARNING, "Unable to create thread!\n");
01109 free(st);
01110 memset(&ied, 0, sizeof(ied));
01111 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads");
01112 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
01113 pthread_attr_destroy(&attr);
01114 return -1;
01115 }
01116 pthread_attr_destroy(&attr);
01117 } else {
01118 ast_log(LOG_WARNING, "Out of memory!\n");
01119 memset(&ied, 0, sizeof(ied));
01120 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory");
01121 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied);
01122 return -1;
01123 }
01124 return 0;
01125 }
01126
01127 static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration)
01128 {
01129 char data[1024];
01130 char *ptr, *term, *src;
01131 int tech;
01132 struct ast_flags flags;
01133 int weight;
01134 int length;
01135 int z;
01136 char fs[256];
01137
01138
01139 if (!ast_db_get("dundi/cache", key, data, sizeof(data))) {
01140 time_t timeout;
01141 ptr = data;
01142 if (!ast_get_time_t(ptr, &timeout, 0, &length)) {
01143 int expiration = timeout - now;
01144 if (expiration > 0) {
01145 ast_log(LOG_DEBUG, "Found cache expiring in %d seconds!\n", expiration);
01146 ptr += length + 1;
01147 while((sscanf(ptr, "%d/%d/%d/%n", &(flags.flags), &weight, &tech, &length) == 3)) {
01148 ptr += length;
01149 term = strchr(ptr, '|');
01150 if (term) {
01151 *term = '\0';
01152 src = strrchr(ptr, '/');
01153 if (src) {
01154 *src = '\0';
01155 src++;
01156 } else
01157 src = "";
01158 ast_log(LOG_DEBUG, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n",
01159 tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full);
01160
01161 for (z=0;z<req->respcount;z++) {
01162 if ((req->dr[z].techint == tech) &&
01163 !strcmp(req->dr[z].dest, ptr))
01164 break;
01165 }
01166 if (z == req->respcount) {
01167
01168 ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL);
01169 req->dr[req->respcount].weight = weight;
01170 req->dr[req->respcount].techint = tech;
01171 req->dr[req->respcount].expiration = expiration;
01172 dundi_str_short_to_eid(&req->dr[req->respcount].eid, src);
01173 dundi_eid_to_str(req->dr[req->respcount].eid_str,
01174 sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid);
01175 ast_copy_string(req->dr[req->respcount].dest, ptr,
01176 sizeof(req->dr[req->respcount].dest));
01177 ast_copy_string(req->dr[req->respcount].tech, tech2str(tech),
01178 sizeof(req->dr[req->respcount].tech));
01179 req->respcount++;
01180 ast_clear_flag_nonstd(req->hmd, DUNDI_HINT_DONT_ASK);
01181 } else if (req->dr[z].weight > weight)
01182 req->dr[z].weight = weight;
01183 ptr = term + 1;
01184 }
01185 }
01186
01187 if (expiration < *lowexpiration)
01188 *lowexpiration = expiration;
01189 return 1;
01190 } else
01191 ast_db_del("dundi/cache", key);
01192 } else
01193 ast_db_del("dundi/cache", key);
01194 }
01195
01196 return 0;
01197 }
01198
01199 static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration)
01200 {
01201 char key[256];
01202 char eid_str[20];
01203 char eidroot_str[20];
01204 time_t now;
01205 int res=0;
01206 int res2=0;
01207 char eid_str_full[20];
01208 char tmp[256]="";
01209 int x;
01210
01211 time(&now);
01212 dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid);
01213 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid);
01214 dundi_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid);
01215 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32);
01216 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01217 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L);
01218 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01219 snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str);
01220 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01221 x = 0;
01222 if (!req->respcount) {
01223 while(!res2) {
01224
01225
01226 if (!(tmp[x] = req->number[x]))
01227 break;
01228 x++;
01229
01230 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32);
01231 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01232 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L);
01233 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01234 snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str);
01235 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration);
01236 if (res2) {
01237 if (strlen(tmp) > strlen(req->hmd->exten)) {
01238
01239 ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten));
01240 }
01241 }
01242 }
01243 res |= res2;
01244 }
01245
01246 return res;
01247 }
01248
01249 static void qualify_peer(struct dundi_peer *peer, int schedonly);
01250
01251 static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p)
01252 {
01253 if (!trans->addr.sin_addr.s_addr)
01254 memcpy(&trans->addr, &p->addr, sizeof(trans->addr));
01255 trans->us_eid = p->us_eid;
01256 trans->them_eid = p->eid;
01257
01258 if (!ast_strlen_zero(p->inkey))
01259 ast_set_flag(trans, FLAG_ENCRYPT);
01260 if (p->maxms) {
01261 trans->autokilltimeout = p->maxms;
01262 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
01263 if (p->lastms > 1) {
01264 trans->retranstimer = p->lastms * 2;
01265
01266 if (trans->retranstimer < 150)
01267 trans->retranstimer = 150;
01268 }
01269 if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER)
01270 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
01271 } else
01272 trans->autokilltimeout = global_autokilltimeout;
01273 }
01274
01275
01276 static int do_register_expire(const void *data)
01277 {
01278 struct dundi_peer *peer = (struct dundi_peer *)data;
01279 char eid_str[20];
01280 ast_log(LOG_DEBUG, "Register expired for '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01281 peer->registerexpire = -1;
01282 peer->lastms = 0;
01283 memset(&peer->addr, 0, sizeof(peer->addr));
01284 return 0;
01285 }
01286
01287 static int update_key(struct dundi_peer *peer)
01288 {
01289 unsigned char key[16];
01290 struct ast_key *ekey, *skey;
01291 char eid_str[20];
01292 int res;
01293 if (!peer->keyexpire || (peer->keyexpire < time(NULL))) {
01294 build_iv(key);
01295 aes_encrypt_key128(key, &peer->us_ecx);
01296 aes_decrypt_key128(key, &peer->us_dcx);
01297 ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
01298 if (!ekey) {
01299 ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n",
01300 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01301 return -1;
01302 }
01303 skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
01304 if (!skey) {
01305 ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n",
01306 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01307 return -1;
01308 }
01309 if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) {
01310 ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128);
01311 return -1;
01312 }
01313 if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) {
01314 ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res);
01315 return -1;
01316 }
01317 peer->us_keycrc32 = crc32(0L, peer->txenckey, 128);
01318 peer->sentfullkey = 0;
01319
01320 time(&peer->keyexpire);
01321 peer->keyexpire += dundi_key_ttl;
01322 }
01323 return 0;
01324 }
01325
01326 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_encrypt_ctx *ecx)
01327 {
01328 unsigned char curblock[16];
01329 int x;
01330 memcpy(curblock, iv, sizeof(curblock));
01331 while(len > 0) {
01332 for (x=0;x<16;x++)
01333 curblock[x] ^= src[x];
01334 aes_encrypt(curblock, dst, ecx);
01335 memcpy(curblock, dst, sizeof(curblock));
01336 dst += 16;
01337 src += 16;
01338 len -= 16;
01339 }
01340 return 0;
01341 }
01342 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_decrypt_ctx *dcx)
01343 {
01344 unsigned char lastblock[16];
01345 int x;
01346 memcpy(lastblock, iv, sizeof(lastblock));
01347 while(len > 0) {
01348 aes_decrypt(src, dst, dcx);
01349 for (x=0;x<16;x++)
01350 dst[x] ^= lastblock[x];
01351 memcpy(lastblock, src, sizeof(lastblock));
01352 dst += 16;
01353 src += 16;
01354 len -= 16;
01355 }
01356 return 0;
01357 }
01358
01359 static struct dundi_hdr *dundi_decrypt(struct dundi_transaction *trans, unsigned char *dst, int *dstlen, struct dundi_hdr *ohdr, struct dundi_encblock *src, int srclen)
01360 {
01361 int space = *dstlen;
01362 unsigned long bytes;
01363 struct dundi_hdr *h;
01364 unsigned char *decrypt_space;
01365 decrypt_space = alloca(srclen);
01366 if (!decrypt_space)
01367 return NULL;
01368 decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx);
01369
01370 h = (struct dundi_hdr *)dst;
01371 *h = *ohdr;
01372 bytes = space - 6;
01373 if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) {
01374 ast_log(LOG_DEBUG, "Ouch, uncompress failed :(\n");
01375 return NULL;
01376 }
01377
01378 *dstlen = bytes + 6;
01379
01380 return h;
01381 }
01382
01383 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack)
01384 {
01385 unsigned char *compress_space;
01386 int len;
01387 int res;
01388 unsigned long bytes;
01389 struct dundi_ie_data ied;
01390 struct dundi_peer *peer;
01391 unsigned char iv[16];
01392 len = pack->datalen + pack->datalen / 100 + 42;
01393 compress_space = alloca(len);
01394 if (compress_space) {
01395 memset(compress_space, 0, len);
01396
01397 bytes = len;
01398 res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6);
01399 if (res != Z_OK) {
01400 ast_log(LOG_DEBUG, "Ouch, compression failed!\n");
01401 return -1;
01402 }
01403 memset(&ied, 0, sizeof(ied));
01404
01405 if (!pack->h->iseqno && !pack->h->oseqno) {
01406
01407 if (!(peer = find_peer(&trans->them_eid)))
01408 return -1;
01409 if (update_key(peer))
01410 return -1;
01411 if (!peer->sentfullkey)
01412 ast_set_flag(trans, FLAG_SENDFULLKEY);
01413
01414 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
01415 if (ast_test_flag(trans, FLAG_SENDFULLKEY)) {
01416 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
01417 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
01418 } else {
01419 dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32);
01420 }
01421
01422 trans->ecx = peer->us_ecx;
01423 trans->dcx = peer->us_dcx;
01424
01425
01426 peer->sentfullkey = 1;
01427 }
01428
01429 build_iv(iv);
01430
01431 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16);
01432
01433 if ((ied.pos + bytes) >= sizeof(ied.buf)) {
01434 ast_log(LOG_NOTICE, "Final packet too large!\n");
01435 return -1;
01436 }
01437 encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx);
01438 ied.pos += ((bytes + 15) / 16) * 16;
01439
01440 pack->datalen = sizeof(struct dundi_hdr);
01441 pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT;
01442 pack->h->cmdflags = 0;
01443 memcpy(pack->h->ies, ied.buf, ied.pos);
01444 pack->datalen += ied.pos;
01445 return 0;
01446 }
01447 return -1;
01448 }
01449
01450 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32)
01451 {
01452 unsigned char dst[128];
01453 int res;
01454 struct ast_key *key, *skey;
01455 char eid_str[20];
01456 if (option_debug)
01457 ast_log(LOG_DEBUG, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32);
01458 if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) {
01459
01460 return 1;
01461 } else if (!newkey || !newsig)
01462 return 0;
01463 if (!memcmp(peer->rxenckey, newkey, 128) &&
01464 !memcmp(peer->rxenckey + 128, newsig, 128)) {
01465
01466 return 1;
01467 }
01468
01469 key = ast_key_get(peer->outkey, AST_KEY_PRIVATE);
01470 if (!key) {
01471 ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n",
01472 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01473 return -1;
01474 }
01475
01476 skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC);
01477 if (!skey) {
01478 ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n",
01479 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
01480 return -1;
01481 }
01482
01483
01484 res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig);
01485 if (res)
01486 return 0;
01487
01488 res = ast_decrypt_bin(dst, newkey, sizeof(dst), key);
01489 if (res != 16) {
01490 if (res >= 0)
01491 ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res);
01492 return 0;
01493 }
01494
01495 ast_log(LOG_DEBUG, "Wow, new key combo passed signature and decrypt!\n");
01496 memcpy(peer->rxenckey, newkey, 128);
01497 memcpy(peer->rxenckey + 128, newsig, 128);
01498 peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128);
01499 aes_decrypt_key128(dst, &peer->them_dcx);
01500 aes_encrypt_key128(dst, &peer->them_ecx);
01501 return 1;
01502 }
01503
01504 static void deep_copy_peer(struct dundi_peer *peer_dst, const struct dundi_peer *peer_src)
01505 {
01506 struct permission *cur, *perm;
01507
01508 memcpy(peer_dst, peer_src, sizeof(*peer_dst));
01509
01510 memset(&peer_dst->permit, 0, sizeof(peer_dst->permit));
01511 memset(&peer_dst->include, 0, sizeof(peer_dst->permit));
01512
01513 AST_LIST_TRAVERSE(&peer_src->permit, cur, list) {
01514 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
01515 continue;
01516
01517 perm->allow = cur->allow;
01518 strcpy(perm->name, cur->name);
01519
01520 AST_LIST_INSERT_HEAD(&peer_dst->permit, perm, list);
01521 }
01522
01523 AST_LIST_TRAVERSE(&peer_src->include, cur, list) {
01524 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(cur->name) + 1)))
01525 continue;
01526
01527 perm->allow = cur->allow;
01528 strcpy(perm->name, cur->name);
01529
01530 AST_LIST_INSERT_HEAD(&peer_dst->include, perm, list);
01531 }
01532 }
01533
01534 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted)
01535 {
01536
01537 int final = hdr->cmdresp & 0x80;
01538 int cmd = hdr->cmdresp & 0x7f;
01539 int x,y,z;
01540 int resp;
01541 int res;
01542 int authpass=0;
01543 unsigned char *bufcpy;
01544 struct dundi_ie_data ied;
01545 struct dundi_ies ies;
01546 struct dundi_peer *peer = NULL;
01547 char eid_str[20];
01548 char eid_str2[20];
01549 memset(&ied, 0, sizeof(ied));
01550 memset(&ies, 0, sizeof(ies));
01551 if (datalen) {
01552 bufcpy = alloca(datalen);
01553 if (!bufcpy)
01554 return -1;
01555
01556 memcpy(bufcpy, hdr->ies, datalen);
01557 ast_log(LOG_DEBUG, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : "");
01558 if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) {
01559 ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n");
01560 return -1;
01561 }
01562 }
01563 switch(cmd) {
01564 case DUNDI_COMMAND_DPDISCOVER:
01565 case DUNDI_COMMAND_EIDQUERY:
01566 case DUNDI_COMMAND_PRECACHERQ:
01567 if (cmd == DUNDI_COMMAND_EIDQUERY)
01568 resp = DUNDI_COMMAND_EIDRESPONSE;
01569 else if (cmd == DUNDI_COMMAND_PRECACHERQ)
01570 resp = DUNDI_COMMAND_PRECACHERP;
01571 else
01572 resp = DUNDI_COMMAND_DPRESPONSE;
01573
01574 peer = find_peer(ies.eids[0]);
01575 if (!peer) {
01576 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
01577 dundi_send(trans, resp, 0, 1, &ied);
01578 } else {
01579 int hasauth = 0;
01580 trans->us_eid = peer->us_eid;
01581 if (strlen(peer->inkey)) {
01582 hasauth = encrypted;
01583 } else
01584 hasauth = 1;
01585 if (hasauth) {
01586
01587 if (!ies.called_context)
01588 ies.called_context = "e164";
01589 if (cmd == DUNDI_COMMAND_EIDQUERY) {
01590 res = dundi_answer_entity(trans, &ies, ies.called_context);
01591 } else {
01592 if (ast_strlen_zero(ies.called_number)) {
01593
01594 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity");
01595 dundi_send(trans, resp, 0, 1, &ied);
01596 } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) &&
01597 (peer->model & DUNDI_MODEL_INBOUND) &&
01598 has_permission(&peer->permit, ies.called_context)) {
01599 res = dundi_answer_query(trans, &ies, ies.called_context);
01600 if (res < 0) {
01601
01602 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
01603 dundi_send(trans, resp, 0, 1, &ied);
01604 }
01605 } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) &&
01606 (peer->pcmodel & DUNDI_MODEL_INBOUND) &&
01607 has_permission(&peer->include, ies.called_context)) {
01608 res = dundi_prop_precache(trans, &ies, ies.called_context);
01609 if (res < 0) {
01610
01611 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context");
01612 dundi_send(trans, resp, 0, 1, &ied);
01613 }
01614 } else {
01615
01616 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied");
01617 dundi_send(trans, resp, 0, 1, &ied);
01618 }
01619 }
01620 } else {
01621
01622 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted");
01623 dundi_send(trans, resp, 0, 1, &ied);
01624 }
01625 }
01626 break;
01627 case DUNDI_COMMAND_REGREQ:
01628
01629 peer = find_peer(ies.eids[0]);
01630
01631
01632 if (any_peer && peer == any_peer) {
01633
01634 peer = ast_calloc(1, sizeof(*peer));
01635 if (peer) {
01636 deep_copy_peer(peer, any_peer);
01637
01638
01639 peer->eid = *ies.eids[0];
01640
01641 AST_LIST_LOCK(&peers);
01642 AST_LIST_INSERT_HEAD(&peers, peer, list);
01643 AST_LIST_UNLOCK(&peers);
01644 }
01645 }
01646
01647 if (!peer || !peer->dynamic) {
01648 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL);
01649 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
01650 } else {
01651 int hasauth = 0;
01652 trans->us_eid = peer->us_eid;
01653 if (!ast_strlen_zero(peer->inkey)) {
01654 hasauth = encrypted;
01655 } else
01656 hasauth = 1;
01657 if (hasauth) {
01658 int expire = default_expiration;
01659 char data[256];
01660 int needqual = 0;
01661 AST_SCHED_DEL(sched, peer->registerexpire);
01662 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
01663 snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(trans->addr.sin_addr),
01664 ntohs(trans->addr.sin_port), expire);
01665 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data);
01666 if (inaddrcmp(&peer->addr, &trans->addr)) {
01667 if (option_verbose > 2) {
01668 ast_verbose(VERBOSE_PREFIX_3 "Registered DUNDi peer '%s' at '%s:%d'\n",
01669 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
01670 ast_inet_ntoa(trans->addr.sin_addr), ntohs(trans->addr.sin_port));
01671 }
01672 needqual = 1;
01673 }
01674
01675 memcpy(&peer->addr, &trans->addr, sizeof(peer->addr));
01676 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
01677 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied);
01678 if (needqual)
01679 qualify_peer(peer, 1);
01680 }
01681 }
01682 break;
01683 case DUNDI_COMMAND_DPRESPONSE:
01684
01685 if (ies.cause < 1) {
01686
01687 ast_log(LOG_DEBUG, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount);
01688 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
01689 authpass = encrypted;
01690 } else
01691 authpass = 1;
01692 if (authpass) {
01693
01694 if (trans->parent && trans->parent->dr) {
01695 y = trans->parent->respcount;
01696 for (x=0;x<ies.anscount;x++) {
01697 if (trans->parent->respcount < trans->parent->maxcount) {
01698
01699 for (z=0;z<trans->parent->respcount;z++) {
01700 if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) &&
01701 !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data))
01702 break;
01703 }
01704 if (z == trans->parent->respcount) {
01705
01706 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags);
01707 trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol;
01708 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight);
01709 trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid;
01710 if (ies.expiration > 0)
01711 trans->parent->dr[trans->parent->respcount].expiration = ies.expiration;
01712 else
01713 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time;
01714 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str,
01715 sizeof(trans->parent->dr[trans->parent->respcount].eid_str),
01716 &ies.answers[x]->eid);
01717 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data,
01718 sizeof(trans->parent->dr[trans->parent->respcount].dest));
01719 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol),
01720 sizeof(trans->parent->dr[trans->parent->respcount].tech));
01721 trans->parent->respcount++;
01722 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
01723 } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) {
01724
01725 trans->parent->dr[z].weight = ies.answers[x]->weight;
01726 }
01727 } else
01728 ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n",
01729 trans->parent->number, trans->parent->dcontext);
01730 }
01731
01732
01733 cache_save(&trans->them_eid, trans->parent, y,
01734 ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0);
01735 if (ies.hint) {
01736 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration);
01737 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
01738 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
01739 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) {
01740 if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) {
01741 ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data,
01742 sizeof(trans->parent->hmd->exten));
01743 }
01744 } else {
01745 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK);
01746 }
01747 }
01748 if (ies.expiration > 0) {
01749 if (trans->parent->expiration > ies.expiration) {
01750 trans->parent->expiration = ies.expiration;
01751 }
01752 }
01753 }
01754
01755 if (!final)
01756 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01757 }
01758
01759 } else {
01760
01761 if (!final) {
01762
01763 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01764 }
01765 }
01766 break;
01767 case DUNDI_COMMAND_EIDRESPONSE:
01768
01769 if (ies.cause < 1) {
01770
01771 ast_log(LOG_DEBUG, "Looks like success of some sort (%d)\n", ies.cause);
01772 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
01773 authpass = encrypted;
01774 } else
01775 authpass = 1;
01776 if (authpass) {
01777
01778 if (trans->parent && trans->parent->dei && ies.q_org) {
01779 if (!trans->parent->respcount) {
01780 trans->parent->respcount++;
01781 if (ies.q_dept)
01782 ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit));
01783 if (ies.q_org)
01784 ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org));
01785 if (ies.q_locality)
01786 ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality));
01787 if (ies.q_stateprov)
01788 ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov));
01789 if (ies.q_country)
01790 ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country));
01791 if (ies.q_email)
01792 ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email));
01793 if (ies.q_phone)
01794 ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone));
01795 if (ies.q_ipaddr)
01796 ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr));
01797 if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) {
01798
01799 ast_copy_string(trans->parent->dei->ipaddr, ast_inet_ntoa(trans->addr.sin_addr), sizeof(trans->parent->dei->ipaddr));
01800 }
01801 }
01802 if (ies.hint) {
01803 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED)))
01804 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED);
01805 }
01806 }
01807
01808 if (!final)
01809 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01810 }
01811
01812 } else {
01813
01814 if (!final) {
01815
01816 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01817 }
01818 }
01819 break;
01820 case DUNDI_COMMAND_REGRESPONSE:
01821
01822 if (ies.cause < 1) {
01823 int hasauth;
01824
01825 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
01826 hasauth = encrypted;
01827 } else
01828 hasauth = 1;
01829
01830 if (!hasauth) {
01831 ast_log(LOG_NOTICE, "Reponse to register not authorized!\n");
01832 if (!final) {
01833 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer");
01834 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied);
01835 }
01836 } else {
01837 ast_log(LOG_DEBUG, "Yay, we've registered as '%s' to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->us_eid),
01838 dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid));
01839
01840 if (!final)
01841 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01842 }
01843 } else {
01844
01845 if (!final) {
01846 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01847 }
01848 }
01849 break;
01850 case DUNDI_COMMAND_INVALID:
01851 case DUNDI_COMMAND_NULL:
01852 case DUNDI_COMMAND_PRECACHERP:
01853
01854 if (!final)
01855 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01856 break;
01857 case DUNDI_COMMAND_ENCREJ:
01858 if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || AST_LIST_EMPTY(&trans->lasttrans) || !(peer = find_peer(&trans->them_eid))) {
01859
01860 if (!final)
01861 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
01862 } else {
01863
01864 ast_set_flag(trans, FLAG_SENDFULLKEY);
01865 if (final) {
01866
01867 dundi_ack(trans, hdr->cmdresp & 0x80);
01868 trans->aseqno = trans->iseqno;
01869
01870 if (!reset_transaction(trans)) {
01871
01872 hdr->cmdresp &= 0x7f;
01873
01874 memset(&ies, 0, sizeof(ies));
01875 dundi_parse_ies(&ies, (AST_LIST_FIRST(&trans->lasttrans))->h->ies, (AST_LIST_FIRST(&trans->lasttrans))->datalen - sizeof(struct dundi_hdr));
01876
01877 memset(&ied, 0, sizeof(ied));
01878 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
01879 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128);
01880 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128);
01881 if (ies.encblock)
01882 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen);
01883 dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, (AST_LIST_FIRST(&trans->lasttrans))->h->cmdresp & 0x80, &ied);
01884 peer->sentfullkey = 1;
01885 }
01886 }
01887 }
01888 break;
01889 case DUNDI_COMMAND_ENCRYPT:
01890 if (!encrypted) {
01891
01892 if ((trans->iseqno == 1) && !trans->oseqno) {
01893 if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) ||
01894 ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) ||
01895 (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) {
01896 if (!final) {
01897 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
01898 }
01899 break;
01900 }
01901 apply_peer(trans, peer);
01902
01903 trans->ecx = peer->them_ecx;
01904 trans->dcx = peer->them_dcx;
01905 }
01906 if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) {
01907 struct dundi_hdr *dhdr;
01908 unsigned char decoded[MAX_PACKET_SIZE];
01909 int ddatalen;
01910 ddatalen = sizeof(decoded);
01911 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen);
01912 if (dhdr) {
01913
01914 if (dundidebug)
01915 dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr));
01916 handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1);
01917
01918 hdr->cmdresp |= dhdr->cmdresp & 0x80;
01919 break;
01920 } else
01921 ast_log(LOG_DEBUG, "Ouch, decrypt failed :(\n");
01922 }
01923 }
01924 if (!final) {
01925
01926 ast_clear_flag(trans, FLAG_ENCRYPT);
01927 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL);
01928 }
01929 break;
01930 default:
01931
01932
01933 if (!final) {
01934 dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd);
01935 dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied);
01936 }
01937 }
01938 return 0;
01939 }
01940
01941 static void destroy_packet(struct dundi_packet *pack, int needfree);
01942 static void destroy_packets(struct packetlist *p)
01943 {
01944 struct dundi_packet *pack;
01945
01946 while ((pack = AST_LIST_REMOVE_HEAD(p, list))) {
01947 AST_SCHED_DEL(sched, pack->retransid);
01948 free(pack);
01949 }
01950 }
01951
01952
01953 static int ack_trans(struct dundi_transaction *trans, int iseqno)
01954 {
01955 struct dundi_packet *pack;
01956
01957
01958 AST_LIST_TRAVERSE(&trans->packets, pack, list) {
01959 if ((pack->h->oseqno + 1) % 255 == iseqno) {
01960 destroy_packet(pack, 0);
01961 if (!AST_LIST_EMPTY(&trans->lasttrans)) {
01962 ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n");
01963 destroy_packets(&trans->lasttrans);
01964 }
01965 AST_LIST_INSERT_HEAD(&trans->lasttrans, pack, list);
01966 AST_SCHED_DEL(sched, trans->autokillid);
01967 return 1;
01968 }
01969 }
01970
01971 return 0;
01972 }
01973
01974 static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen)
01975 {
01976 struct dundi_transaction *trans;
01977 trans = find_transaction(h, sin);
01978 if (!trans) {
01979 dundi_reject(h, sin);
01980 return 0;
01981 }
01982
01983 if (h->oseqno == trans->iseqno) {
01984
01985 if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) {
01986
01987 destroy_trans(trans, 0);
01988 return 0;
01989 }
01990 if (h->cmdresp != DUNDI_COMMAND_ACK) {
01991 trans->oiseqno = trans->iseqno;
01992 trans->iseqno++;
01993 handle_command_response(trans, h, datalen, 0);
01994 }
01995 if (trans->aseqno != trans->iseqno) {
01996 dundi_ack(trans, h->cmdresp & 0x80);
01997 trans->aseqno = trans->iseqno;
01998 }
01999
02000 destroy_packets(&trans->lasttrans);
02001 if (h->cmdresp & 0x80) {
02002
02003 destroy_trans(trans, 0);
02004 }
02005 } else if (h->oseqno == trans->oiseqno) {
02006
02007 dundi_ack(trans, 0);
02008 } else {
02009
02010 ast_log(LOG_DEBUG, "Dropping packet out of window!\n");
02011 }
02012 return 0;
02013 }
02014
02015 static int socket_read(int *id, int fd, short events, void *cbdata)
02016 {
02017 struct sockaddr_in sin;
02018 int res;
02019 struct dundi_hdr *h;
02020 char buf[MAX_PACKET_SIZE];
02021 socklen_t len;
02022 len = sizeof(sin);
02023 res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len);
02024 if (res < 0) {
02025 if (errno != ECONNREFUSED)
02026 ast_log(LOG_WARNING, "Error: %s\n", strerror(errno));
02027 return 1;
02028 }
02029 if (res < sizeof(struct dundi_hdr)) {
02030 ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr));
02031 return 1;
02032 }
02033 buf[res] = '\0';
02034 h = (struct dundi_hdr *)buf;
02035 if (dundidebug)
02036 dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr));
02037 AST_LIST_LOCK(&peers);
02038 handle_frame(h, &sin, res - sizeof(struct dundi_hdr));
02039 AST_LIST_UNLOCK(&peers);
02040 return 1;
02041 }
02042
02043 static void build_secret(char *secret, int seclen)
02044 {
02045 unsigned char tmp[16];
02046 char *s;
02047 build_iv(tmp);
02048 secret[0] = '\0';
02049 ast_base64encode(secret, tmp, sizeof(tmp), seclen);
02050
02051 while((s = strchr(secret, ';'))) *s = '+';
02052 while((s = strchr(secret, '/'))) *s = '+';
02053 while((s = strchr(secret, ':'))) *s = '+';
02054 while((s = strchr(secret, '@'))) *s = '+';
02055 }
02056
02057
02058 static void save_secret(const char *newkey, const char *oldkey)
02059 {
02060 char tmp[256];
02061 if (oldkey)
02062 snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey);
02063 else
02064 snprintf(tmp, sizeof(tmp), "%s", newkey);
02065 rotatetime = time(NULL) + DUNDI_SECRET_TIME;
02066 ast_db_put(secretpath, "secret", tmp);
02067 snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime);
02068 ast_db_put(secretpath, "secretexpiry", tmp);
02069 }
02070
02071 static void load_password(void)
02072 {
02073 char *current=NULL;
02074 char *last=NULL;
02075 char tmp[256];
02076 time_t expired;
02077
02078 ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp));
02079 if (!ast_get_time_t(tmp, &expired, 0, NULL)) {
02080 ast_db_get(secretpath, "secret", tmp, sizeof(tmp));
02081 current = strchr(tmp, ';');
02082 if (!current)
02083 current = tmp;
02084 else {
02085 *current = '\0';
02086 current++;
02087 };
02088 if ((time(NULL) - expired) < 0) {
02089 if ((expired - time(NULL)) > DUNDI_SECRET_TIME)
02090 expired = time(NULL) + DUNDI_SECRET_TIME;
02091 } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) {
02092 last = current;
02093 current = NULL;
02094 } else {
02095 last = NULL;
02096 current = NULL;
02097 }
02098 }
02099 if (current) {
02100
02101 ast_copy_string(cursecret, current, sizeof(cursecret));
02102 rotatetime = expired;
02103 } else {
02104
02105 build_secret(cursecret, sizeof(cursecret));
02106 save_secret(cursecret, last);
02107 }
02108 }
02109
02110 static void check_password(void)
02111 {
02112 char oldsecret[80];
02113 time_t now;
02114
02115 time(&now);
02116 #if 0
02117 printf("%ld/%ld\n", now, rotatetime);
02118 #endif
02119 if ((now - rotatetime) >= 0) {
02120
02121 ast_copy_string(oldsecret, cursecret, sizeof(oldsecret));
02122 build_secret(cursecret, sizeof(cursecret));
02123 save_secret(cursecret, oldsecret);
02124 }
02125 }
02126
02127 static void *network_thread(void *ignore)
02128 {
02129
02130
02131 int res;
02132
02133 ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL);
02134
02135 while (!dundi_shutdown) {
02136 res = ast_sched_wait(sched);
02137 if ((res > 1000) || (res < 0))
02138 res = 1000;
02139 res = ast_io_wait(io, res);
02140 if (res >= 0) {
02141 AST_LIST_LOCK(&peers);
02142 ast_sched_runq(sched);
02143 AST_LIST_UNLOCK(&peers);
02144 }
02145 check_password();
02146 }
02147
02148 netthreadid = AST_PTHREADT_NULL;
02149
02150 return NULL;
02151 }
02152
02153 static void *process_precache(void *ign)
02154 {
02155 struct dundi_precache_queue *qe;
02156 time_t now;
02157 char context[256];
02158 char number[256];
02159 int run;
02160
02161 while (!dundi_shutdown) {
02162 time(&now);
02163 run = 0;
02164 AST_LIST_LOCK(&pcq);
02165 if ((qe = AST_LIST_FIRST(&pcq))) {
02166 if (!qe->expiration) {
02167
02168 AST_LIST_REMOVE_HEAD(&pcq, list);
02169 free(qe);
02170 } else if (qe->expiration < now) {
02171
02172 qe->expiration = 0;
02173 ast_copy_string(context, qe->context, sizeof(context));
02174 ast_copy_string(number, qe->number, sizeof(number));
02175 run = 1;
02176 }
02177 }
02178 AST_LIST_UNLOCK(&pcq);
02179 if (run) {
02180 dundi_precache(context, number);
02181 } else
02182 sleep(1);
02183 }
02184
02185 precachethreadid = AST_PTHREADT_NULL;
02186
02187 return NULL;
02188 }
02189
02190 static int start_network_thread(void)
02191 {
02192 ast_pthread_create_background(&netthreadid, NULL, network_thread, NULL);
02193 ast_pthread_create_background(&precachethreadid, NULL, process_precache, NULL);
02194 return 0;
02195 }
02196
02197 static int dundi_do_debug(int fd, int argc, char *argv[])
02198 {
02199 if (argc != 2)
02200 return RESULT_SHOWUSAGE;
02201 dundidebug = 1;
02202 ast_cli(fd, "DUNDi Debugging Enabled\n");
02203 return RESULT_SUCCESS;
02204 }
02205
02206 static int dundi_do_store_history(int fd, int argc, char *argv[])
02207 {
02208 if (argc != 3)
02209 return RESULT_SHOWUSAGE;
02210 global_storehistory = 1;
02211 ast_cli(fd, "DUNDi History Storage Enabled\n");
02212 return RESULT_SUCCESS;
02213 }
02214
02215 static int dundi_flush(int fd, int argc, char *argv[])
02216 {
02217 int stats = 0;
02218 if ((argc < 2) || (argc > 3))
02219 return RESULT_SHOWUSAGE;
02220 if (argc > 2) {
02221 if (!strcasecmp(argv[2], "stats"))
02222 stats = 1;
02223 else
02224 return RESULT_SHOWUSAGE;
02225 }
02226 if (stats) {
02227
02228 struct dundi_peer *p;
02229 int x;
02230 AST_LIST_LOCK(&peers);
02231 AST_LIST_TRAVERSE(&peers, p, list) {
02232 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
02233 if (p->lookups[x])
02234 free(p->lookups[x]);
02235 p->lookups[x] = NULL;
02236 p->lookuptimes[x] = 0;
02237 }
02238 p->avgms = 0;
02239 }
02240 AST_LIST_UNLOCK(&peers);
02241 } else {
02242 ast_db_deltree("dundi/cache", NULL);
02243 ast_cli(fd, "DUNDi Cache Flushed\n");
02244 }
02245 return RESULT_SUCCESS;
02246 }
02247
02248 static int dundi_no_debug(int fd, int argc, char *argv[])
02249 {
02250 if (argc != 3)
02251 return RESULT_SHOWUSAGE;
02252 dundidebug = 0;
02253 ast_cli(fd, "DUNDi Debugging Disabled\n");
02254 return RESULT_SUCCESS;
02255 }
02256
02257 static int dundi_no_store_history(int fd, int argc, char *argv[])
02258 {
02259 if (argc != 4)
02260 return RESULT_SHOWUSAGE;
02261 global_storehistory = 0;
02262 ast_cli(fd, "DUNDi History Storage Disabled\n");
02263 return RESULT_SUCCESS;
02264 }
02265
02266 static char *model2str(int model)
02267 {
02268 switch(model) {
02269 case DUNDI_MODEL_INBOUND:
02270 return "Inbound";
02271 case DUNDI_MODEL_OUTBOUND:
02272 return "Outbound";
02273 case DUNDI_MODEL_SYMMETRIC:
02274 return "Symmetric";
02275 default:
02276 return "Unknown";
02277 }
02278 }
02279
02280 static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos)
02281 {
02282 int which=0, len;
02283 char *ret = NULL;
02284 struct dundi_peer *p;
02285 char eid_str[20];
02286
02287 if (pos != rpos)
02288 return NULL;
02289 AST_LIST_LOCK(&peers);
02290 len = strlen(word);
02291 AST_LIST_TRAVERSE(&peers, p, list) {
02292 const char *s = dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid);
02293 if (!strncasecmp(word, s, len) && ++which > state) {
02294 ret = ast_strdup(s);
02295 break;
02296 }
02297 }
02298 AST_LIST_UNLOCK(&peers);
02299 return ret;
02300 }
02301
02302 static char *complete_peer_4(const char *line, const char *word, int pos, int state)
02303 {
02304 return complete_peer_helper(line, word, pos, state, 3);
02305 }
02306
02307 static int rescomp(const void *a, const void *b)
02308 {
02309 const struct dundi_result *resa, *resb;
02310 resa = a;
02311 resb = b;
02312 if (resa->weight < resb->weight)
02313 return -1;
02314 if (resa->weight > resb->weight)
02315 return 1;
02316 return 0;
02317 }
02318
02319 static void sort_results(struct dundi_result *results, int count)
02320 {
02321 qsort(results, count, sizeof(results[0]), rescomp);
02322 }
02323
02324 static int dundi_do_lookup(int fd, int argc, char *argv[])
02325 {
02326 int res;
02327 char tmp[256];
02328 char fs[80] = "";
02329 char *context;
02330 int x;
02331 int bypass = 0;
02332 struct dundi_result dr[MAX_RESULTS];
02333 struct timeval start;
02334 if ((argc < 3) || (argc > 4))
02335 return RESULT_SHOWUSAGE;
02336 if (argc > 3) {
02337 if (!strcasecmp(argv[3], "bypass"))
02338 bypass=1;
02339 else
02340 return RESULT_SHOWUSAGE;
02341 }
02342 ast_copy_string(tmp, argv[2], sizeof(tmp));
02343 context = strchr(tmp, '@');
02344 if (context) {
02345 *context = '\0';
02346 context++;
02347 }
02348 start = ast_tvnow();
02349 res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass);
02350
02351 if (res < 0)
02352 ast_cli(fd, "DUNDi lookup returned error.\n");
02353 else if (!res)
02354 ast_cli(fd, "DUNDi lookup returned no results.\n");
02355 else
02356 sort_results(dr, res);
02357 for (x=0;x<res;x++) {
02358 ast_cli(fd, "%3d. %5d %s/%s (%s)\n", x + 1, dr[x].weight, dr[x].tech, dr[x].dest, dundi_flags2str(fs, sizeof(fs), dr[x].flags));
02359 ast_cli(fd, " from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration);
02360 }
02361 ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
02362 return RESULT_SUCCESS;
02363 }
02364
02365 static int dundi_do_precache(int fd, int argc, char *argv[])
02366 {
02367 int res;
02368 char tmp[256];
02369 char *context;
02370 struct timeval start;
02371 if ((argc < 3) || (argc > 3))
02372 return RESULT_SHOWUSAGE;
02373 ast_copy_string(tmp, argv[2], sizeof(tmp));
02374 context = strchr(tmp, '@');
02375 if (context) {
02376 *context = '\0';
02377 context++;
02378 }
02379 start = ast_tvnow();
02380 res = dundi_precache(context, tmp);
02381
02382 if (res < 0)
02383 ast_cli(fd, "DUNDi precache returned error.\n");
02384 else if (!res)
02385 ast_cli(fd, "DUNDi precache returned no error.\n");
02386 ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start));
02387 return RESULT_SUCCESS;
02388 }
02389
02390 static int dundi_do_query(int fd, int argc, char *argv[])
02391 {
02392 int res;
02393 char tmp[256];
02394 char *context;
02395 dundi_eid eid;
02396 struct dundi_entity_info dei;
02397 if ((argc < 3) || (argc > 3))
02398 return RESULT_SHOWUSAGE;
02399 if (dundi_str_to_eid(&eid, argv[2])) {
02400 ast_cli(fd, "'%s' is not a valid EID!\n", argv[2]);
02401 return RESULT_SHOWUSAGE;
02402 }
02403 ast_copy_string(tmp, argv[2], sizeof(tmp));
02404 context = strchr(tmp, '@');
02405 if (context) {
02406 *context = '\0';
02407 context++;
02408 }
02409 res = dundi_query_eid(&dei, context, eid);
02410 if (res < 0)
02411 ast_cli(fd, "DUNDi Query EID returned error.\n");
02412 else if (!res)
02413 ast_cli(fd, "DUNDi Query EID returned no results.\n");
02414 else {
02415 ast_cli(fd, "DUNDi Query EID succeeded:\n");
02416 ast_cli(fd, "Department: %s\n", dei.orgunit);
02417 ast_cli(fd, "Organization: %s\n", dei.org);
02418 ast_cli(fd, "City/Locality: %s\n", dei.locality);
02419 ast_cli(fd, "State/Province: %s\n", dei.stateprov);
02420 ast_cli(fd, "Country: %s\n", dei.country);
02421 ast_cli(fd, "E-mail: %s\n", dei.email);
02422 ast_cli(fd, "Phone: %s\n", dei.phone);
02423 ast_cli(fd, "IP Address: %s\n", dei.ipaddr);
02424 }
02425 return RESULT_SUCCESS;
02426 }
02427
02428 static int dundi_show_peer(int fd, int argc, char *argv[])
02429 {
02430 struct dundi_peer *peer;
02431 struct permission *p;
02432 char *order;
02433 char eid_str[20];
02434 int x, cnt;
02435
02436 if (argc != 4)
02437 return RESULT_SHOWUSAGE;
02438 AST_LIST_LOCK(&peers);
02439 AST_LIST_TRAVERSE(&peers, peer, list) {
02440 if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), argv[3]))
02441 break;
02442 }
02443 if (peer) {
02444 switch(peer->order) {
02445 case 0:
02446 order = "Primary";
02447 break;
02448 case 1:
02449 order = "Secondary";
02450 break;
02451 case 2:
02452 order = "Tertiary";
02453 break;
02454 case 3:
02455 order = "Quartiary";
02456 break;
02457 default:
02458 order = "Unknown";
02459 }
02460 ast_cli(fd, "Peer: %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
02461 ast_cli(fd, "Model: %s\n", model2str(peer->model));
02462 ast_cli(fd, "Host: %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "<Unspecified>");
02463 ast_cli(fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no");
02464 ast_cli(fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes");
02465 ast_cli(fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey);
02466 ast_cli(fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey);
02467 if (!AST_LIST_EMPTY(&peer->include))
02468 ast_cli(fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)");
02469 AST_LIST_TRAVERSE(&peer->include, p, list)
02470 ast_cli(fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name);
02471 if (!AST_LIST_EMPTY(&peer->permit))
02472 ast_cli(fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)");
02473 AST_LIST_TRAVERSE(&peer->permit, p, list)
02474 ast_cli(fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name);
02475 cnt = 0;
02476 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) {
02477 if (peer->lookups[x]) {
02478 if (!cnt)
02479 ast_cli(fd, "Last few query times:\n");
02480 ast_cli(fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]);
02481 cnt++;
02482 }
02483 }
02484 if (cnt)
02485 ast_cli(fd, "Average query time: %d ms\n", peer->avgms);
02486 } else
02487 ast_cli(fd, "No such peer '%s'\n", argv[3]);
02488 AST_LIST_UNLOCK(&peers);
02489 return RESULT_SUCCESS;
02490 }
02491
02492 static int dundi_show_peers(int fd, int argc, char *argv[])
02493 {
02494 #define FORMAT2 "%-20.20s %-15.15s %-10.10s %-8.8s %-15.15s\n"
02495 #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n"
02496 struct dundi_peer *peer;
02497 int registeredonly=0;
02498 char avgms[20];
02499 char eid_str[20];
02500 int online_peers = 0;
02501 int offline_peers = 0;
02502 int unmonitored_peers = 0;
02503 int total_peers = 0;
02504
02505 if ((argc != 3) && (argc != 4) && (argc != 5))
02506 return RESULT_SHOWUSAGE;
02507 if ((argc == 4)) {
02508 if (!strcasecmp(argv[3], "registered")) {
02509 registeredonly = 1;
02510 } else
02511 return RESULT_SHOWUSAGE;
02512 }
02513 AST_LIST_LOCK(&peers);
02514 ast_cli(fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status");
02515 AST_LIST_TRAVERSE(&peers, peer, list) {
02516 char status[20];
02517 int print_line = -1;
02518 char srch[2000];
02519 total_peers++;
02520 if (registeredonly && !peer->addr.sin_addr.s_addr)
02521 continue;
02522 if (peer->maxms) {
02523 if (peer->lastms < 0) {
02524 strcpy(status, "UNREACHABLE");
02525 offline_peers++;
02526 }
02527 else if (peer->lastms > peer->maxms) {
02528 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms);
02529 offline_peers++;
02530 }
02531 else if (peer->lastms) {
02532 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms);
02533 online_peers++;
02534 }
02535 else {
02536 strcpy(status, "UNKNOWN");
02537 offline_peers++;
02538 }
02539 } else {
02540 strcpy(status, "Unmonitored");
02541 unmonitored_peers++;
02542 }
02543 if (peer->avgms)
02544 snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms);
02545 else
02546 strcpy(avgms, "Unavail");
02547 snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
02548 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
02549 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
02550
02551 if (argc == 5) {
02552 if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) {
02553 print_line = -1;
02554 } else if (!strcasecmp(argv[3],"exclude") && !strstr(srch,argv[4])) {
02555 print_line = 1;
02556 } else if (!strcasecmp(argv[3],"begin") && !strncasecmp(srch,argv[4],strlen(argv[4]))) {
02557 print_line = -1;
02558 } else {
02559 print_line = 0;
02560 }
02561 }
02562
02563 if (print_line) {
02564 ast_cli(fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid),
02565 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)",
02566 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status);
02567 }
02568 }
02569 ast_cli(fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers);
02570 AST_LIST_UNLOCK(&peers);
02571 return RESULT_SUCCESS;
02572 #undef FORMAT
02573 #undef FORMAT2
02574 }
02575
02576 static int dundi_show_trans(int fd, int argc, char *argv[])
02577 {
02578 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n"
02579 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n"
02580 struct dundi_transaction *trans;
02581 if (argc != 3)
02582 return RESULT_SHOWUSAGE;
02583 AST_LIST_LOCK(&peers);
02584 ast_cli(fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack");
02585 AST_LIST_TRAVERSE(&alltrans, trans, all) {
02586 ast_cli(fd, FORMAT, ast_inet_ntoa(trans->addr.sin_addr),
02587 ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno);
02588 }
02589 AST_LIST_UNLOCK(&peers);
02590 return RESULT_SUCCESS;
02591 #undef FORMAT
02592 #undef FORMAT2
02593 }
02594
02595 static int dundi_show_entityid(int fd, int argc, char *argv[])
02596 {
02597 char eid_str[20];
02598 if (argc != 3)
02599 return RESULT_SHOWUSAGE;
02600 AST_LIST_LOCK(&peers);
02601 dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid);
02602 AST_LIST_UNLOCK(&peers);
02603 ast_cli(fd, "Global EID for this system is '%s'\n", eid_str);
02604 return RESULT_SUCCESS;
02605 }
02606
02607 static int dundi_show_requests(int fd, int argc, char *argv[])
02608 {
02609 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n"
02610 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n"
02611 struct dundi_request *req;
02612 char eidstr[20];
02613 if (argc != 3)
02614 return RESULT_SHOWUSAGE;
02615 AST_LIST_LOCK(&peers);
02616 ast_cli(fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp");
02617 AST_LIST_TRAVERSE(&requests, req, list) {
02618 ast_cli(fd, FORMAT, req->number, req->dcontext,
02619 dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount);
02620 }
02621 AST_LIST_UNLOCK(&peers);
02622 return RESULT_SUCCESS;
02623 #undef FORMAT
02624 #undef FORMAT2
02625 }
02626
02627
02628
02629 static int dundi_show_mappings(int fd, int argc, char *argv[])
02630 {
02631 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n"
02632 #define FORMAT "%-12.12s %-7d %-12.12s %-10.10s %-5.5s %-25.25s\n"
02633 struct dundi_mapping *map;
02634 char fs[256];
02635 if (argc != 3)
02636 return RESULT_SHOWUSAGE;
02637 AST_LIST_LOCK(&peers);
02638 ast_cli(fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination");
02639 AST_LIST_TRAVERSE(&mappings, map, list) {
02640 ast_cli(fd, FORMAT, map->dcontext, map->weight,
02641 ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext,
02642 dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest);
02643 }
02644 AST_LIST_UNLOCK(&peers);
02645 return RESULT_SUCCESS;
02646 #undef FORMAT
02647 #undef FORMAT2
02648 }
02649
02650 static int dundi_show_precache(int fd, int argc, char *argv[])
02651 {
02652 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n"
02653 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n"
02654 struct dundi_precache_queue *qe;
02655 int h,m,s;
02656 time_t now;
02657
02658 if (argc != 3)
02659 return RESULT_SHOWUSAGE;
02660 time(&now);
02661 ast_cli(fd, FORMAT2, "Number", "Context", "Expiration");
02662 AST_LIST_LOCK(&pcq);
02663 AST_LIST_TRAVERSE(&pcq, qe, list) {
02664 s = qe->expiration - now;
02665 h = s / 3600;
02666 s = s % 3600;
02667 m = s / 60;
02668 s = s % 60;
02669 ast_cli(fd, FORMAT, qe->number, qe->context, h,m,s);
02670 }
02671 AST_LIST_UNLOCK(&pcq);
02672
02673 return RESULT_SUCCESS;
02674 #undef FORMAT
02675 #undef FORMAT2
02676 }
02677
02678 static char debug_usage[] =
02679 "Usage: dundi debug\n"
02680 " Enables dumping of DUNDi packets for debugging purposes\n";
02681
02682 static char no_debug_usage[] =
02683 "Usage: dundi no debug\n"
02684 " Disables dumping of DUNDi packets for debugging purposes\n";
02685
02686 static char store_history_usage[] =
02687 "Usage: dundi store history\n"
02688 " Enables storing of DUNDi requests and times for debugging\n"
02689 "purposes\n";
02690
02691 static char no_store_history_usage[] =
02692 "Usage: dundi no store history\n"
02693 " Disables storing of DUNDi requests and times for debugging\n"
02694 "purposes\n";
02695
02696 static char show_peers_usage[] =
02697 "Usage: dundi show peers\n"
02698 " Lists all known DUNDi peers.\n";
02699
02700 static char show_trans_usage[] =
02701 "Usage: dundi show trans\n"
02702 " Lists all known DUNDi transactions.\n";
02703
02704 static char show_mappings_usage[] =
02705 "Usage: dundi show mappings\n"
02706 " Lists all known DUNDi mappings.\n";
02707
02708 static char show_precache_usage[] =
02709 "Usage: dundi show precache\n"
02710 " Lists all known DUNDi scheduled precache updates.\n";
02711
02712 static char show_entityid_usage[] =
02713 "Usage: dundi show entityid\n"
02714 " Displays the global entityid for this host.\n";
02715
02716 static char show_peer_usage[] =
02717 "Usage: dundi show peer [peer]\n"
02718 " Provide a detailed description of a specifid DUNDi peer.\n";
02719
02720 static char show_requests_usage[] =
02721 "Usage: dundi show requests\n"
02722 " Lists all known pending DUNDi requests.\n";
02723
02724 static char lookup_usage[] =
02725 "Usage: dundi lookup <number>[@context] [bypass]\n"
02726 " Lookup the given number within the given DUNDi context\n"
02727 "(or e164 if none is specified). Bypasses cache if 'bypass'\n"
02728 "keyword is specified.\n";
02729
02730 static char precache_usage[] =
02731 "Usage: dundi precache <number>[@context]\n"
02732 " Lookup the given number within the given DUNDi context\n"
02733 "(or e164 if none is specified) and precaches the results to any\n"
02734 "upstream DUNDi push servers.\n";
02735
02736 static char query_usage[] =
02737 "Usage: dundi query <entity>[@context]\n"
02738 " Attempts to retrieve contact information for a specific\n"
02739 "DUNDi entity identifier (EID) within a given DUNDi context (or\n"
02740 "e164 if none is specified).\n";
02741
02742 static char flush_usage[] =
02743 "Usage: dundi flush [stats]\n"
02744 " Flushes DUNDi answer cache, used primarily for debug. If\n"
02745 "'stats' is present, clears timer statistics instead of normal\n"
02746 "operation.\n";
02747
02748 static struct ast_cli_entry cli_dundi[] = {
02749 { { "dundi", "debug", NULL },
02750 dundi_do_debug, "Enable DUNDi debugging",
02751 debug_usage },
02752
02753 { { "dundi", "store", "history", NULL },
02754 dundi_do_store_history, "Enable DUNDi historic records",
02755 store_history_usage },
02756
02757 { { "dundi", "no", "store", "history", NULL },
02758 dundi_no_store_history, "Disable DUNDi historic records",
02759 no_store_history_usage },
02760
02761 { { "dundi", "flush", NULL },
02762 dundi_flush, "Flush DUNDi cache",
02763 flush_usage },
02764
02765 { { "dundi", "no", "debug", NULL },
02766 dundi_no_debug, "Disable DUNDi debugging",
02767 no_debug_usage },
02768
02769 { { "dundi", "show", "peers", NULL },
02770 dundi_show_peers, "Show defined DUNDi peers",
02771 show_peers_usage },
02772
02773 { { "dundi", "show", "trans", NULL },
02774 dundi_show_trans, "Show active DUNDi transactions",
02775 show_trans_usage },
02776
02777 { { "dundi", "show", "entityid", NULL },
02778 dundi_show_entityid, "Display Global Entity ID",
02779 show_entityid_usage },
02780
02781 { { "dundi", "show", "mappings", NULL },
02782 dundi_show_mappings, "Show DUNDi mappings",
02783 show_mappings_usage },
02784
02785 { { "dundi", "show", "precache", NULL },
02786 dundi_show_precache, "Show DUNDi precache",
02787 show_precache_usage },
02788
02789 { { "dundi", "show", "requests", NULL },
02790 dundi_show_requests, "Show DUNDi requests",
02791 show_requests_usage },
02792
02793 { { "dundi", "show", "peer", NULL },
02794 dundi_show_peer, "Show info on a specific DUNDi peer",
02795 show_peer_usage, complete_peer_4 },
02796
02797 { { "dundi", "lookup", NULL },
02798 dundi_do_lookup, "Lookup a number in DUNDi",
02799 lookup_usage },
02800
02801 { { "dundi", "precache", NULL },
02802 dundi_do_precache, "Precache a number in DUNDi",
02803 precache_usage },
02804
02805 { { "dundi", "query", NULL },
02806 dundi_do_query, "Query a DUNDi EID",
02807 query_usage },
02808 };
02809
02810 static struct dundi_transaction *create_transaction(struct dundi_peer *p)
02811 {
02812 struct dundi_transaction *trans;
02813 int tid;
02814
02815
02816 if (p && !p->addr.sin_addr.s_addr)
02817 return NULL;
02818 tid = get_trans_id();
02819 if (tid < 1)
02820 return NULL;
02821 trans = ast_calloc(1, sizeof(*trans));
02822 if (trans) {
02823 if (global_storehistory) {
02824 trans->start = ast_tvnow();
02825 ast_set_flag(trans, FLAG_STOREHIST);
02826 }
02827 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER;
02828 trans->autokillid = -1;
02829 if (p) {
02830 apply_peer(trans, p);
02831 if (!p->sentfullkey)
02832 ast_set_flag(trans, FLAG_SENDFULLKEY);
02833 }
02834 trans->strans = tid;
02835 AST_LIST_INSERT_HEAD(&alltrans, trans, all);
02836 }
02837 return trans;
02838 }
02839
02840 static int dundi_xmit(struct dundi_packet *pack)
02841 {
02842 int res;
02843 if (dundidebug)
02844 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr));
02845 res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr));
02846 if (res < 0) {
02847 ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n",
02848 ast_inet_ntoa(pack->parent->addr.sin_addr),
02849 ntohs(pack->parent->addr.sin_port), strerror(errno));
02850 }
02851 if (res > 0)
02852 res = 0;
02853 return res;
02854 }
02855
02856 static void destroy_packet(struct dundi_packet *pack, int needfree)
02857 {
02858 if (pack->parent)
02859 AST_LIST_REMOVE(&pack->parent->packets, pack, list);
02860 AST_SCHED_DEL(sched, pack->retransid);
02861 if (needfree)
02862 free(pack);
02863 }
02864
02865 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout)
02866 {
02867 struct dundi_peer *peer;
02868 int ms;
02869 int x;
02870 int cnt;
02871 char eid_str[20];
02872 if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) {
02873 AST_LIST_TRAVERSE(&peers, peer, list) {
02874 if (peer->regtrans == trans)
02875 peer->regtrans = NULL;
02876 if (peer->qualtrans == trans) {
02877 if (fromtimeout) {
02878 if (peer->lastms > -1)
02879 ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
02880 peer->lastms = -1;
02881 } else {
02882 ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx);
02883 if (ms < 1)
02884 ms = 1;
02885 if (ms < peer->maxms) {
02886 if ((peer->lastms >= peer->maxms) || (peer->lastms < 0))
02887 ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
02888 } else if (peer->lastms < peer->maxms) {
02889 ast_log(LOG_NOTICE, "Peer '%s' has become TOO LAGGED (%d ms)\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), ms);
02890 }
02891 peer->lastms = ms;
02892 }
02893 peer->qualtrans = NULL;
02894 }
02895 if (ast_test_flag(trans, FLAG_STOREHIST)) {
02896 if (trans->parent && !ast_strlen_zero(trans->parent->number)) {
02897 if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) {
02898 peer->avgms = 0;
02899 cnt = 0;
02900 if (peer->lookups[DUNDI_TIMING_HISTORY-1])
02901 free(peer->lookups[DUNDI_TIMING_HISTORY-1]);
02902 for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) {
02903 peer->lookuptimes[x] = peer->lookuptimes[x-1];
02904 peer->lookups[x] = peer->lookups[x-1];
02905 if (peer->lookups[x]) {
02906 peer->avgms += peer->lookuptimes[x];
02907 cnt++;
02908 }
02909 }
02910 peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start);
02911 peer->lookups[0] = ast_malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2);
02912 if (peer->lookups[0]) {
02913 sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext);
02914 peer->avgms += peer->lookuptimes[0];
02915 cnt++;
02916 }
02917 if (cnt)
02918 peer->avgms /= cnt;
02919 }
02920 }
02921 }
02922 }
02923 }
02924 if (trans->parent) {
02925
02926 AST_LIST_REMOVE(&trans->parent->trans, trans, parentlist);
02927 if (AST_LIST_EMPTY(&trans->parent->trans)) {
02928
02929 if (trans->parent->pfds[1] > -1) {
02930 write(trans->parent->pfds[1], "killa!", 6);
02931 }
02932 }
02933 }
02934
02935 AST_LIST_REMOVE(&alltrans, trans, all);
02936 destroy_packets(&trans->packets);
02937 destroy_packets(&trans->lasttrans);
02938 AST_SCHED_DEL(sched, trans->autokillid);
02939 if (trans->thread) {
02940
02941 ast_set_flag(trans, FLAG_DEAD);
02942 } else
02943 free(trans);
02944 }
02945
02946 static int dundi_rexmit(const void *data)
02947 {
02948 struct dundi_packet *pack = (struct dundi_packet *)data;
02949 int res;
02950 AST_LIST_LOCK(&peers);
02951 if (pack->retrans < 1) {
02952 pack->retransid = -1;
02953 if (!ast_test_flag(pack->parent, FLAG_ISQUAL))
02954 ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n",
02955 ast_inet_ntoa(pack->parent->addr.sin_addr),
02956 ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans));
02957 destroy_trans(pack->parent, 1);
02958 res = 0;
02959 } else {
02960
02961 pack->retrans--;
02962 dundi_xmit(pack);
02963 res = 1;
02964 }
02965 AST_LIST_UNLOCK(&peers);
02966 return res;
02967 }
02968
02969 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied)
02970 {
02971 struct dundi_packet *pack;
02972 int res;
02973 int len;
02974 char eid_str[20];
02975 len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0);
02976
02977 if (ast_test_flag(trans, FLAG_ENCRYPT))
02978 len += 384;
02979 pack = ast_calloc(1, len);
02980 if (pack) {
02981 pack->h = (struct dundi_hdr *)(pack->data);
02982 if (cmdresp != DUNDI_COMMAND_ACK) {
02983 pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack);
02984 pack->retrans = DUNDI_DEFAULT_RETRANS - 1;
02985 AST_LIST_INSERT_HEAD(&trans->packets, pack, list);
02986 }
02987 pack->parent = trans;
02988 pack->h->strans = htons(trans->strans);
02989 pack->h->dtrans = htons(trans->dtrans);
02990 pack->h->iseqno = trans->iseqno;
02991 pack->h->oseqno = trans->oseqno;
02992 pack->h->cmdresp = cmdresp;
02993 pack->datalen = sizeof(struct dundi_hdr);
02994 if (ied) {
02995 memcpy(pack->h->ies, ied->buf, ied->pos);
02996 pack->datalen += ied->pos;
02997 }
02998 if (final) {
02999 pack->h->cmdresp |= DUNDI_COMMAND_FINAL;
03000 ast_set_flag(trans, FLAG_FINAL);
03001 }
03002 pack->h->cmdflags = flags;
03003 if (cmdresp != DUNDI_COMMAND_ACK) {
03004 trans->oseqno++;
03005 trans->oseqno = trans->oseqno % 256;
03006 }
03007 trans->aseqno = trans->iseqno;
03008
03009 if (ast_test_flag(trans, FLAG_ENCRYPT)) {
03010 switch(cmdresp) {
03011 case DUNDI_COMMAND_REGREQ:
03012 case DUNDI_COMMAND_REGRESPONSE:
03013 case DUNDI_COMMAND_DPDISCOVER:
03014 case DUNDI_COMMAND_DPRESPONSE:
03015 case DUNDI_COMMAND_EIDQUERY:
03016 case DUNDI_COMMAND_EIDRESPONSE:
03017 case DUNDI_COMMAND_PRECACHERQ:
03018 case DUNDI_COMMAND_PRECACHERP:
03019 if (dundidebug)
03020 dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr));
03021 res = dundi_encrypt(trans, pack);
03022 break;
03023 default:
03024 res = 0;
03025 }
03026 } else
03027 res = 0;
03028 if (!res)
03029 res = dundi_xmit(pack);
03030 if (res)
03031 ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
03032
03033 if (cmdresp == DUNDI_COMMAND_ACK)
03034 free(pack);
03035 return res;
03036 }
03037 return -1;
03038 }
03039
03040 static int do_autokill(const void *data)
03041 {
03042 struct dundi_transaction *trans = (struct dundi_transaction *)data;
03043 char eid_str[20];
03044 ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n",
03045 dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid));
03046 trans->autokillid = -1;
03047 destroy_trans(trans, 0);
03048 return 0;
03049 }
03050
03051 static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us)
03052 {
03053 struct dundi_peer *p;
03054 if (!dundi_eid_cmp(eid, us)) {
03055 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
03056 return;
03057 }
03058 AST_LIST_LOCK(&peers);
03059 AST_LIST_TRAVERSE(&peers, p, list) {
03060 if (!dundi_eid_cmp(&p->eid, eid)) {
03061 if (has_permission(&p->include, context))
03062 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid);
03063 else
03064 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
03065 break;
03066 }
03067 }
03068 if (!p)
03069 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid);
03070 AST_LIST_UNLOCK(&peers);
03071 }
03072
03073 static int dundi_discover(struct dundi_transaction *trans)
03074 {
03075 struct dundi_ie_data ied;
03076 int x;
03077 if (!trans->parent) {
03078 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
03079 return -1;
03080 }
03081 memset(&ied, 0, sizeof(ied));
03082 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
03083 if (!dundi_eid_zero(&trans->us_eid))
03084 dundi_ie_append_eid(&ied, DUNDI_IE_EID_DIRECT, &trans->us_eid);
03085 for (x=0;x<trans->eidcount;x++)
03086 dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid);
03087 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
03088 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
03089 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
03090 if (trans->parent->cbypass)
03091 dundi_ie_append(&ied, DUNDI_IE_CACHEBYPASS);
03092 if (trans->autokilltimeout)
03093 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
03094 return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied);
03095 }
03096
03097 static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers)
03098 {
03099 struct dundi_ie_data ied;
03100 int x, res;
03101 int max = 999999;
03102 int expiration = dundi_cache_time;
03103 int ouranswers=0;
03104 dundi_eid *avoid[1] = { NULL, };
03105 int direct[1] = { 0, };
03106 struct dundi_result dr[MAX_RESULTS];
03107 struct dundi_hint_metadata hmd;
03108 if (!trans->parent) {
03109 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n");
03110 return -1;
03111 }
03112 memset(&hmd, 0, sizeof(hmd));
03113 memset(&dr, 0, sizeof(dr));
03114
03115 for (x=0;x<mapcount;x++)
03116 ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd);
03117 if (ouranswers < 0)
03118 ouranswers = 0;
03119 for (x=0;x<ouranswers;x++) {
03120 if (dr[x].weight < max)
03121 max = dr[x].weight;
03122 }
03123 if (max) {
03124
03125 res = dundi_lookup_internal(dr + ouranswers, MAX_RESULTS - ouranswers, NULL, trans->parent->dcontext, trans->parent->number, trans->ttl, 1, &hmd, &expiration, 0, 1, &trans->them_eid, avoid, direct);
03126 if (res > 0) {
03127
03128 ouranswers += res;
03129 }
03130 }
03131
03132 if (ouranswers > 0) {
03133 *foundanswers += ouranswers;
03134 memset(&ied, 0, sizeof(ied));
03135 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
03136 if (!dundi_eid_zero(&trans->us_eid))
03137 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
03138 for (x=0;x<trans->eidcount;x++)
03139 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
03140 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number);
03141 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
03142 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
03143 for (x=0;x<ouranswers;x++) {
03144
03145 if (dr[x].expiration && (expiration > dr[x].expiration))
03146 expiration = dr[x].expiration;
03147 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest);
03148 }
03149 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten);
03150 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration);
03151 if (trans->autokilltimeout)
03152 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
03153 if (expiration < *minexp)
03154 *minexp = expiration;
03155 return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied);
03156 } else {
03157
03158 destroy_trans(trans, 0);
03159 return 0;
03160 }
03161 }
03162
03163 static int dundi_query(struct dundi_transaction *trans)
03164 {
03165 struct dundi_ie_data ied;
03166 int x;
03167 if (!trans->parent) {
03168 ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n");
03169 return -1;
03170 }
03171 memset(&ied, 0, sizeof(ied));
03172 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
03173 if (!dundi_eid_zero(&trans->us_eid))
03174 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid);
03175 for (x=0;x<trans->eidcount;x++)
03176 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]);
03177 dundi_ie_append_eid(&ied, DUNDI_IE_REQEID, &trans->parent->query_eid);
03178 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext);
03179 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl);
03180 if (trans->autokilltimeout)
03181 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans);
03182 return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied);
03183 }
03184
03185 static int discover_transactions(struct dundi_request *dr)
03186 {
03187 struct dundi_transaction *trans;
03188 AST_LIST_LOCK(&peers);
03189 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
03190 dundi_discover(trans);
03191 }
03192 AST_LIST_UNLOCK(&peers);
03193 return 0;
03194 }
03195
03196 static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers)
03197 {
03198 struct dundi_transaction *trans;
03199
03200
03201 AST_LIST_LOCK(&peers);
03202 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
03203 if (trans->thread)
03204 ast_log(LOG_WARNING, "This shouldn't happen, really...\n");
03205 trans->thread = 1;
03206 }
03207 AST_LIST_UNLOCK(&peers);
03208
03209 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
03210 if (!ast_test_flag(trans, FLAG_DEAD))
03211 precache_trans(trans, maps, mapcount, expiration, foundanswers);
03212 }
03213
03214
03215 AST_LIST_LOCK(&peers);
03216 AST_LIST_TRAVERSE_SAFE_BEGIN(&dr->trans, trans, parentlist) {
03217 trans->thread = 0;
03218 if (ast_test_flag(trans, FLAG_DEAD)) {
03219 ast_log(LOG_DEBUG, "Our transaction went away!\n");
03220
03221
03222 destroy_trans(trans, 0);
03223 }
03224 }
03225 AST_LIST_TRAVERSE_SAFE_END
03226 AST_LIST_UNLOCK(&peers);
03227
03228 return 0;
03229 }
03230
03231 static int query_transactions(struct dundi_request *dr)
03232 {
03233 struct dundi_transaction *trans;
03234
03235 AST_LIST_LOCK(&peers);
03236 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
03237 dundi_query(trans);
03238 }
03239 AST_LIST_UNLOCK(&peers);
03240
03241 return 0;
03242 }
03243
03244 static int optimize_transactions(struct dundi_request *dr, int order)
03245 {
03246
03247
03248 struct dundi_transaction *trans;
03249 struct dundi_peer *peer;
03250 dundi_eid tmp;
03251 int x;
03252 int needpush;
03253
03254 AST_LIST_LOCK(&peers);
03255 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) {
03256
03257 if (trans->eidcount) {
03258 tmp = trans->eids[--trans->eidcount];
03259 needpush = 1;
03260 } else {
03261 tmp = trans->us_eid;
03262 needpush = 0;
03263 }
03264
03265 AST_LIST_TRAVERSE(&peers, peer, list) {
03266 if (has_permission(&peer->include, dr->dcontext) &&
03267 dundi_eid_cmp(&peer->eid, &trans->them_eid) &&
03268 (peer->order <= order)) {
03269
03270
03271
03272 if (!dundi_eid_cmp(&tmp, &peer->eid))
03273 x = -1;
03274 else {
03275 for (x=0;x<trans->eidcount;x++) {
03276 if (!dundi_eid_cmp(&trans->eids[x], &peer->eid))
03277 break;
03278 }
03279 }
03280 if (x == trans->eidcount) {
03281
03282 if (trans->eidcount < DUNDI_MAX_STACK - needpush) {
03283 trans->eids[trans->eidcount++] = peer->eid;
03284
03285
03286 needpush = 1;
03287 }
03288 }
03289 }
03290 }
03291
03292 if (needpush)
03293 trans->eids[trans->eidcount++] = tmp;
03294 }
03295 AST_LIST_UNLOCK(&peers);
03296
03297 return 0;
03298 }
03299
03300 static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[])
03301 {
03302 struct dundi_transaction *trans;
03303 int x;
03304 char eid_str[20];
03305 char eid_str2[20];
03306
03307
03308 if (!p->addr.sin_addr.s_addr)
03309 return 0;
03310 if (p->maxms && ((p->lastms < 0) || (p->lastms >= p->maxms)))
03311 return 0;
03312 if (ast_strlen_zero(dr->number))
03313 ast_log(LOG_DEBUG, "Will query peer '%s' for '%s' (context '%s')\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dundi_eid_to_str(eid_str2, sizeof(eid_str2), &dr->query_eid), dr->dcontext);
03314 else
03315 ast_log(LOG_DEBUG, "Will query peer '%s' for '%s@%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dr->number, dr->dcontext);
03316 trans = create_transaction(p);
03317 if (!trans)
03318 return -1;
03319 trans->parent = dr;
03320 trans->ttl = ttl;
03321 for (x = 0; avoid[x] && (x < DUNDI_MAX_STACK); x++)
03322 trans->eids[x] = *avoid[x];
03323 trans->eidcount = x;
03324 AST_LIST_INSERT_HEAD(&dr->trans, trans, parentlist);
03325
03326 return 0;
03327 }
03328
03329 static void cancel_request(struct dundi_request *dr)
03330 {
03331 struct dundi_transaction *trans;
03332
03333 AST_LIST_LOCK(&peers);
03334 while ((trans = AST_LIST_REMOVE_HEAD(&dr->trans, parentlist))) {
03335
03336 trans->parent = NULL;
03337
03338 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL);
03339 }
03340 AST_LIST_UNLOCK(&peers);
03341 }
03342
03343 static void abort_request(struct dundi_request *dr)
03344 {
03345 struct dundi_transaction *trans;
03346
03347 AST_LIST_LOCK(&peers);
03348 while ((trans = AST_LIST_FIRST(&dr->trans))) {
03349
03350 destroy_trans(trans, 0);
03351 }
03352 AST_LIST_UNLOCK(&peers);
03353 }
03354
03355 static void build_transactions(struct dundi_request *dr, int ttl, int order, int *foundcache, int *skipped, int blockempty, int nocache, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int directs[])
03356 {
03357 struct dundi_peer *p;
03358 int x;
03359 int res;
03360 int pass;
03361 int allowconnect;
03362 char eid_str[20];
03363 AST_LIST_LOCK(&peers);
03364 AST_LIST_TRAVERSE(&peers, p, list) {
03365 if (modeselect == 1) {
03366
03367 pass = has_permission(&p->permit, dr->dcontext) && (p->pcmodel & DUNDI_MODEL_OUTBOUND);
03368 allowconnect = 1;
03369 } else {
03370
03371 pass = has_permission(&p->include, dr->dcontext);
03372 allowconnect = p->model & DUNDI_MODEL_OUTBOUND;
03373 }
03374 if (skip) {
03375 if (!dundi_eid_cmp(skip, &p->eid))
03376 pass = 0;
03377 }
03378 if (pass) {
03379 if (p->order <= order) {
03380
03381
03382
03383 if((nocache || !(res = cache_lookup(dr, &p->eid, dr->crc32, &dr->expiration)))) {
03384 res = 0;
03385
03386
03387 for (x=0;avoid[x];x++) {
03388 if (!dundi_eid_cmp(avoid[x], &p->eid) || !dundi_eid_cmp(avoid[x], &p->us_eid)) {
03389
03390 if (directs && !directs[x])
03391 ast_clear_flag_nonstd(dr->hmd, DUNDI_HINT_UNAFFECTED);
03392 break;
03393 }
03394 }
03395
03396 if (allowconnect) {
03397 if (!avoid[x] && (!blockempty || !dundi_eid_zero(&p->us_eid))) {
03398
03399 append_transaction(dr, p, ttl, avoid);
03400 } else
03401 ast_log(LOG_DEBUG, "Avoiding '%s' in transaction\n", dundi_eid_to_str(eid_str, sizeof(eid_str), avoid[x]));
03402 }
03403 }
03404 *foundcache |= res;
03405 } else if (!*skipped || (p->order < *skipped))
03406 *skipped = p->order;
03407 }
03408 }
03409 AST_LIST_UNLOCK(&peers);
03410 }
03411
03412 static int register_request(struct dundi_request *dr, struct dundi_request **pending)
03413 {
03414 struct dundi_request *cur;
03415 int res=0;
03416 char eid_str[20];
03417 AST_LIST_LOCK(&peers);
03418 AST_LIST_TRAVERSE(&requests, cur, list) {
03419 if (option_debug)
03420 ast_log(LOG_DEBUG, "Checking '%s@%s' vs '%s@%s'\n", cur->dcontext, cur->number,
03421 dr->dcontext, dr->number);
03422 if (!strcasecmp(cur->dcontext, dr->dcontext) &&
03423 !strcasecmp(cur->number, dr->number) &&
03424 (!dundi_eid_cmp(&cur->root_eid, &dr->root_eid) || (cur->crc32 == dr->crc32))) {
03425 ast_log(LOG_DEBUG, "Found existing query for '%s@%s' for '%s' crc '%08lx'\n",
03426 cur->dcontext, cur->number, dundi_eid_to_str(eid_str, sizeof(eid_str), &cur->root_eid), cur->crc32);
03427 *pending = cur;
03428 res = 1;
03429 break;
03430 }
03431 }
03432 if (!res) {
03433 ast_log(LOG_DEBUG, "Registering request for '%s@%s' on behalf of '%s' crc '%08lx'\n",
03434 dr->number, dr->dcontext, dundi_eid_to_str(eid_str, sizeof(eid_str), &dr->root_eid), dr->crc32);
03435
03436 AST_LIST_INSERT_HEAD(&requests, dr, list);
03437 *pending = NULL;
03438 }
03439 AST_LIST_UNLOCK(&peers);
03440 return res;
03441 }
03442
03443 static void unregister_request(struct dundi_request *dr)
03444 {
03445 AST_LIST_LOCK(&peers);
03446 AST_LIST_REMOVE(&requests, dr, list);
03447 AST_LIST_UNLOCK(&peers);
03448 }
03449
03450 static int check_request(struct dundi_request *dr)
03451 {
03452 struct dundi_request *cur;
03453
03454 AST_LIST_LOCK(&peers);
03455 AST_LIST_TRAVERSE(&requests, cur, list) {
03456 if (cur == dr)
03457 break;
03458 }
03459 AST_LIST_UNLOCK(&peers);
03460
03461 return cur ? 1 : 0;
03462 }
03463
03464 static unsigned long avoid_crc32(dundi_eid *avoid[])
03465 {
03466
03467
03468 unsigned long acrc32 = 0;
03469 int x;
03470 for (x=0;avoid[x];x++) {
03471
03472 if (avoid[x+1]) {
03473 acrc32 ^= crc32(0L, (unsigned char *)avoid[x], sizeof(dundi_eid));
03474 }
03475 }
03476 return acrc32;
03477 }
03478
03479 static int dundi_lookup_internal(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int ttl, int blockempty, struct dundi_hint_metadata *hmd, int *expiration, int cbypass, int modeselect, dundi_eid *skip, dundi_eid *avoid[], int direct[])
03480 {
03481 int res;
03482 struct dundi_request dr, *pending;
03483 dundi_eid *rooteid=NULL;
03484 int x;
03485 int ttlms;
03486 int ms;
03487 int foundcache;
03488 int skipped=0;
03489 int order=0;
03490 char eid_str[20];
03491 struct timeval start;
03492
03493
03494 if (chan && chan->_softhangup)
03495 return 0;
03496
03497 ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
03498
03499 for (x=0;avoid[x];x++)
03500 rooteid = avoid[x];
03501
03502 memset(&dr, 0, sizeof(dr));
03503 if (pipe(dr.pfds)) {
03504 ast_log(LOG_WARNING, "pipe failed: %s\n" , strerror(errno));
03505 return -1;
03506 }
03507 dr.dr = result;
03508 dr.hmd = hmd;
03509 dr.maxcount = maxret;
03510 dr.expiration = *expiration;
03511 dr.cbypass = cbypass;
03512 dr.crc32 = avoid_crc32(avoid);
03513 ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext));
03514 ast_copy_string(dr.number, number, sizeof(dr.number));
03515 if (rooteid)
03516 dr.root_eid = *rooteid;
03517 res = register_request(&dr, &pending);
03518 if (res) {
03519
03520 if (rooteid && !dundi_eid_cmp(&dr.root_eid, &pending->root_eid)) {
03521
03522
03523 ast_log(LOG_DEBUG, "Oooh, duplicate request for '%s@%s' for '%s'\n",
03524 dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &dr.root_eid));
03525 close(dr.pfds[0]);
03526 close(dr.pfds[1]);
03527 return -2;
03528 } else {
03529
03530 ast_log(LOG_DEBUG, "Waiting for similar request for '%s@%s' for '%s'\n",
03531 dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &pending->root_eid));
03532 start = ast_tvnow();
03533 while(check_request(pending) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !chan->_softhangup)) {
03534
03535
03536 usleep(1);
03537 }
03538
03539 }
03540 }
03541
03542 do {
03543 order = skipped;
03544 skipped = 0;
03545 foundcache = 0;
03546 build_transactions(&dr, ttl, order, &foundcache, &skipped, blockempty, cbypass, modeselect, skip, avoid, direct);
03547 } while (skipped && !foundcache && AST_LIST_EMPTY(&dr.trans));
03548
03549
03550
03551 if (!ttl) {
03552 ast_set_flag_nonstd(hmd, DUNDI_HINT_TTL_EXPIRED);
03553 abort_request(&dr);
03554 unregister_request(&dr);
03555 close(dr.pfds[0]);
03556 close(dr.pfds[1]);
03557 return 0;
03558 }
03559
03560
03561 optimize_transactions(&dr, order);
03562
03563 discover_transactions(&dr);
03564
03565 start = ast_tvnow();
03566 while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !chan->_softhangup)) {
03567 ms = 100;
03568 ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
03569 }
03570 if (chan && chan->_softhangup)
03571 ast_log(LOG_DEBUG, "Hrm, '%s' hungup before their query for %s@%s finished\n", chan->name, dr.number, dr.dcontext);
03572 cancel_request(&dr);
03573 unregister_request(&dr);
03574 res = dr.respcount;
03575 *expiration = dr.expiration;
03576 close(dr.pfds[0]);
03577 close(dr.pfds[1]);
03578 return res;
03579 }
03580
03581 int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int cbypass)
03582 {
03583 struct dundi_hint_metadata hmd;
03584 dundi_eid *avoid[1] = { NULL, };
03585 int direct[1] = { 0, };
03586 int expiration = dundi_cache_time;
03587 memset(&hmd, 0, sizeof(hmd));
03588 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED;
03589 return dundi_lookup_internal(result, maxret, chan, dcontext, number, dundi_ttl, 0, &hmd, &expiration, cbypass, 0, NULL, avoid, direct);
03590 }
03591
03592 static void reschedule_precache(const char *number, const char *context, int expiration)
03593 {
03594 int len;
03595 struct dundi_precache_queue *qe, *prev;
03596
03597 AST_LIST_LOCK(&pcq);
03598 AST_LIST_TRAVERSE_SAFE_BEGIN(&pcq, qe, list) {
03599 if (!strcmp(number, qe->number) && !strcasecmp(context, qe->context)) {
03600 AST_LIST_REMOVE_CURRENT(&pcq, list);
03601 break;
03602 }
03603 }
03604 AST_LIST_TRAVERSE_SAFE_END
03605 if (!qe) {
03606 len = sizeof(*qe);
03607 len += strlen(number) + 1;
03608 len += strlen(context) + 1;
03609 if (!(qe = ast_calloc(1, len))) {
03610 AST_LIST_UNLOCK(&pcq);
03611 return;
03612 }
03613 strcpy(qe->number, number);
03614 qe->context = qe->number + strlen(number) + 1;
03615 strcpy(qe->context, context);
03616 }
03617 time(&qe->expiration);
03618 qe->expiration += expiration;
03619 if ((prev = AST_LIST_FIRST(&pcq))) {
03620 while (AST_LIST_NEXT(prev, list) && ((AST_LIST_NEXT(prev, list))->expiration <= qe->expiration))
03621 prev = AST_LIST_NEXT(prev, list);
03622 AST_LIST_INSERT_AFTER(&pcq, prev, qe, list);
03623 } else
03624 AST_LIST_INSERT_HEAD(&pcq, qe, list);
03625 AST_LIST_UNLOCK(&pcq);
03626 }
03627
03628 static void dundi_precache_full(void)
03629 {
03630 struct dundi_mapping *cur;
03631 struct ast_context *con;
03632 struct ast_exten *e;
03633
03634 AST_LIST_TRAVERSE(&mappings, cur, list) {
03635 ast_log(LOG_NOTICE, "Should precache context '%s'\n", cur->dcontext);
03636 ast_rdlock_contexts();
03637 con = ast_walk_contexts(NULL);
03638 while (con) {
03639 if (!strcasecmp(cur->lcontext, ast_get_context_name(con))) {
03640
03641 ast_lock_context(con);
03642 e = ast_walk_context_extensions(con, NULL);
03643 while (e) {
03644 reschedule_precache(ast_get_extension_name(e), cur->dcontext, 0);
03645 e = ast_walk_context_extensions(con, e);
03646 }
03647 ast_unlock_context(con);
03648 }
03649 con = ast_walk_contexts(con);
03650 }
03651 ast_unlock_contexts();
03652 }
03653 }
03654
03655 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[])
03656 {
03657 struct dundi_request dr;
03658 struct dundi_hint_metadata hmd;
03659 struct dundi_result dr2[MAX_RESULTS];
03660 struct timeval start;
03661 struct dundi_mapping *maps = NULL, *cur;
03662 int nummaps = 0;
03663 int foundanswers;
03664 int foundcache, skipped, ttlms, ms;
03665 if (!context)
03666 context = "e164";
03667 ast_log(LOG_DEBUG, "Precache internal (%s@%s)!\n", number, context);
03668
03669 AST_LIST_LOCK(&peers);
03670 AST_LIST_TRAVERSE(&mappings, cur, list) {
03671 if (!strcasecmp(cur->dcontext, context))
03672 nummaps++;
03673 }
03674 if (nummaps) {
03675 maps = alloca(nummaps * sizeof(*maps));
03676 nummaps = 0;
03677 if (maps) {
03678 AST_LIST_TRAVERSE(&mappings, cur, list) {
03679 if (!strcasecmp(cur->dcontext, context))
03680 maps[nummaps++] = *cur;
03681 }
03682 }
03683 }
03684 AST_LIST_UNLOCK(&peers);
03685 if (!nummaps || !maps)
03686 return -1;
03687 ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
03688 memset(&dr2, 0, sizeof(dr2));
03689 memset(&dr, 0, sizeof(dr));
03690 memset(&hmd, 0, sizeof(hmd));
03691 dr.dr = dr2;
03692 ast_copy_string(dr.number, number, sizeof(dr.number));
03693 ast_copy_string(dr.dcontext, context ? context : "e164", sizeof(dr.dcontext));
03694 dr.maxcount = MAX_RESULTS;
03695 dr.expiration = dundi_cache_time;
03696 dr.hmd = &hmd;
03697 dr.pfds[0] = dr.pfds[1] = -1;
03698 pipe(dr.pfds);
03699 build_transactions(&dr, ttl, 0, &foundcache, &skipped, 0, 1, 1, NULL, avoids, NULL);
03700 optimize_transactions(&dr, 0);
03701 foundanswers = 0;
03702 precache_transactions(&dr, maps, nummaps, &dr.expiration, &foundanswers);
03703 if (foundanswers) {
03704 if (dr.expiration > 0)
03705 reschedule_precache(dr.number, dr.dcontext, dr.expiration);
03706 else
03707 ast_log(LOG_NOTICE, "Weird, expiration = %d, but need to precache for %s@%s?!\n", dr.expiration, dr.number, dr.dcontext);
03708 }
03709 start = ast_tvnow();
03710 while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms)) {
03711 if (dr.pfds[0] > -1) {
03712 ms = 100;
03713 ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL);
03714 } else
03715 usleep(1);
03716 }
03717 cancel_request(&dr);
03718 if (dr.pfds[0] > -1) {
03719 close(dr.pfds[0]);
03720 close(dr.pfds[1]);
03721 }
03722 return 0;
03723 }
03724
03725 int dundi_precache(const char *context, const char *number)
03726 {
03727 dundi_eid *avoid[1] = { NULL, };
03728 return dundi_precache_internal(context, number, dundi_ttl, avoid);
03729 }
03730
03731 static int dundi_query_eid_internal(struct dundi_entity_info *dei, const char *dcontext, dundi_eid *eid, struct dundi_hint_metadata *hmd, int ttl, int blockempty, dundi_eid *avoid[])
03732 {
03733 int res;
03734 struct dundi_request dr;
03735 dundi_eid *rooteid=NULL;
03736 int x;
03737 int ttlms;
03738 int skipped=0;
03739 int foundcache=0;
03740 struct timeval start;
03741
03742 ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME;
03743
03744 for (x=0;avoid[x];x++)
03745 rooteid = avoid[x];
03746
03747 memset(&dr, 0, sizeof(dr));
03748 dr.hmd = hmd;
03749 dr.dei = dei;
03750 dr.pfds[0] = dr.pfds[1] = -1;
03751 ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext));
03752 memcpy(&dr.query_eid, eid, sizeof(dr.query_eid));
03753 if (rooteid)
03754 dr.root_eid = *rooteid;
03755
03756 build_transactions(&dr, ttl, 9999, &foundcache, &skipped, blockempty, 0, 0, NULL, avoid, NULL);
03757
03758
03759
03760
03761 if (!ttl) {
03762 ast_set_flag_nonstd(hmd, DUNDI_HINT_TTL_EXPIRED);
03763 return 0;
03764 }
03765
03766
03767 optimize_transactions(&dr, 9999);
03768
03769 query_transactions(&dr);
03770
03771 start = ast_tvnow();
03772 while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms))
03773 usleep(1);
03774 res = dr.respcount;
03775 return res;
03776 }
03777
03778 int dundi_query_eid(struct dundi_entity_info *dei, const char *dcontext, dundi_eid eid)
03779 {
03780 dundi_eid *avoid[1] = { NULL, };
03781 struct dundi_hint_metadata hmd;
03782 memset(&hmd, 0, sizeof(hmd));
03783 return dundi_query_eid_internal(dei, dcontext, &eid, &hmd, dundi_ttl, 0, avoid);
03784 }
03785
03786 static int dundifunc_read(struct ast_channel *chan, char *cmd, char *num, char *buf, size_t len)
03787 {
03788 char *context;
03789 char *opts;
03790 int results;
03791 int x;
03792 int bypass = 0;
03793 struct ast_module_user *u;
03794 struct dundi_result dr[MAX_RESULTS];
03795
03796 buf[0] = '\0';
03797
03798 if (ast_strlen_zero(num)) {
03799 ast_log(LOG_WARNING, "DUNDILOOKUP requires an argument (number)\n");
03800 return -1;
03801 }
03802
03803 u = ast_module_user_add(chan);
03804
03805 context = strchr(num, '|');
03806 if (context) {
03807 *context++ = '\0';
03808 opts = strchr(context, '|');
03809 if (opts) {
03810 *opts++ = '\0';
03811 if (strchr(opts, 'b'))
03812 bypass = 1;
03813 }
03814 }
03815
03816 if (ast_strlen_zero(context))
03817 context = "e164";
03818
03819 results = dundi_lookup(dr, MAX_RESULTS, NULL, context, num, bypass);
03820 if (results > 0) {
03821 sort_results(dr, results);
03822 for (x = 0; x < results; x++) {
03823 if (ast_test_flag(dr + x, DUNDI_FLAG_EXISTS)) {
03824 snprintf(buf, len, "%s/%s", dr[x].tech, dr[x].dest);
03825 break;
03826 }
03827 }
03828 }
03829
03830 ast_module_user_remove(u);
03831
03832 return 0;
03833 }
03834
03835
03836
03837
03838
03839 static struct ast_custom_function dundi_function = {
03840 .name = "DUNDILOOKUP",
03841 .synopsis = "Do a DUNDi lookup of a phone number.",
03842 .syntax = "DUNDILOOKUP(number[|context[|options]])",
03843 .desc = "This will do a DUNDi lookup of the given phone number.\n"
03844 "If no context is given, the default will be e164. The result of\n"
03845 "this function will the Technology/Resource found in the DUNDi\n"
03846 "lookup. If no results were found, the result will be blank.\n"
03847 "If the 'b' option is specified, the internal DUNDi cache will\n"
03848 "be bypassed.\n",
03849 .read = dundifunc_read,
03850 };
03851
03852 static void mark_peers(void)
03853 {
03854 struct dundi_peer *peer;
03855 AST_LIST_LOCK(&peers);
03856 AST_LIST_TRAVERSE(&peers, peer, list) {
03857 peer->dead = 1;
03858 }
03859 AST_LIST_UNLOCK(&peers);
03860 }
03861
03862 static void mark_mappings(void)
03863 {
03864 struct dundi_mapping *map;
03865
03866 AST_LIST_LOCK(&peers);
03867 AST_LIST_TRAVERSE(&mappings, map, list) {
03868 map->dead = 1;
03869 }
03870 AST_LIST_UNLOCK(&peers);
03871 }
03872
03873 static void destroy_permissions(struct permissionlist *permlist)
03874 {
03875 struct permission *perm;
03876
03877 while ((perm = AST_LIST_REMOVE_HEAD(permlist, list)))
03878 free(perm);
03879 }
03880
03881 static void destroy_peer(struct dundi_peer *peer)
03882 {
03883 AST_SCHED_DEL(sched, peer->registerid);
03884 if (peer->regtrans)
03885 destroy_trans(peer->regtrans, 0);
03886 AST_SCHED_DEL(sched, peer->qualifyid);
03887 destroy_permissions(&peer->permit);
03888 destroy_permissions(&peer->include);
03889 free(peer);
03890 }
03891
03892 static void destroy_map(struct dundi_mapping *map)
03893 {
03894 free(map);
03895 }
03896
03897 static void prune_peers(void)
03898 {
03899 struct dundi_peer *peer;
03900
03901 AST_LIST_LOCK(&peers);
03902 AST_LIST_TRAVERSE_SAFE_BEGIN(&peers, peer, list) {
03903 if (peer->dead) {
03904 AST_LIST_REMOVE_CURRENT(&peers, list);
03905 destroy_peer(peer);
03906 }
03907 }
03908 AST_LIST_TRAVERSE_SAFE_END
03909 AST_LIST_UNLOCK(&peers);
03910 }
03911
03912 static void prune_mappings(void)
03913 {
03914 struct dundi_mapping *map;
03915
03916 AST_LIST_LOCK(&peers);
03917 AST_LIST_TRAVERSE_SAFE_BEGIN(&mappings, map, list) {
03918 if (map->dead) {
03919 AST_LIST_REMOVE_CURRENT(&mappings, list);
03920 destroy_map(map);
03921 }
03922 }
03923 AST_LIST_TRAVERSE_SAFE_END
03924 AST_LIST_UNLOCK(&peers);
03925 }
03926
03927 static void append_permission(struct permissionlist *permlist, char *s, int allow)
03928 {
03929 struct permission *perm;
03930
03931 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(s) + 1)))
03932 return;
03933
03934 strcpy(perm->name, s);
03935 perm->allow = allow;
03936
03937 AST_LIST_INSERT_TAIL(permlist, perm, list);
03938 }
03939
03940 #define MAX_OPTS 128
03941
03942 static void build_mapping(char *name, char *value)
03943 {
03944 char *t, *fields[MAX_OPTS];
03945 struct dundi_mapping *map;
03946 int x;
03947 int y;
03948
03949 t = ast_strdupa(value);
03950
03951 AST_LIST_TRAVERSE(&mappings, map, list) {
03952
03953 if (!strcasecmp(map->dcontext, name) &&
03954 (!strncasecmp(map->lcontext, value, strlen(map->lcontext)) &&
03955 (!value[strlen(map->lcontext)] ||
03956 (value[strlen(map->lcontext)] == ','))))
03957 break;
03958 }
03959 if (!map) {
03960 if (!(map = ast_calloc(1, sizeof(*map))))
03961 return;
03962 AST_LIST_INSERT_HEAD(&mappings, map, list);
03963 map->dead = 1;
03964 }
03965 map->options = 0;
03966 memset(fields, 0, sizeof(fields));
03967 x = 0;
03968 while (t && x < MAX_OPTS) {
03969 fields[x++] = t;
03970 t = strchr(t, ',');
03971 if (t) {
03972 *t = '\0';
03973 t++;
03974 }
03975 }
03976 if ((x == 1) && ast_strlen_zero(fields[0])) {
03977
03978 ast_copy_string(map->dcontext, name, sizeof(map->dcontext));
03979 map->dead = 0;
03980 } else if (x >= 4) {
03981 ast_copy_string(map->dcontext, name, sizeof(map->dcontext));
03982 ast_copy_string(map->lcontext, fields[0], sizeof(map->lcontext));
03983 if ((sscanf(fields[1], "%d", &map->weight) == 1) && (map->weight >= 0) && (map->weight < 60000)) {
03984 ast_copy_string(map->dest, fields[3], sizeof(map->dest));
03985 if ((map->tech = str2tech(fields[2]))) {
03986 map->dead = 0;
03987 }
03988 } else {
03989 ast_log(LOG_WARNING, "Invalid weight '%s' specified, deleting entry '%s/%s'\n", fields[1], map->dcontext, map->lcontext);
03990 }
03991 for (y = 4;y < x; y++) {
03992 if (!strcasecmp(fields[y], "nounsolicited"))
03993 map->options |= DUNDI_FLAG_NOUNSOLICITED;
03994 else if (!strcasecmp(fields[y], "nocomunsolicit"))
03995 map->options |= DUNDI_FLAG_NOCOMUNSOLICIT;
03996 else if (!strcasecmp(fields[y], "residential"))
03997 map->options |= DUNDI_FLAG_RESIDENTIAL;
03998 else if (!strcasecmp(fields[y], "commercial"))
03999 map->options |= DUNDI_FLAG_COMMERCIAL;
04000 else if (!strcasecmp(fields[y], "mobile"))
04001 map->options |= DUNDI_FLAG_MOBILE;
04002 else if (!strcasecmp(fields[y], "nopartial"))
04003 map->options |= DUNDI_FLAG_INTERNAL_NOPARTIAL;
04004 else
04005 ast_log(LOG_WARNING, "Don't know anything about option '%s'\n", fields[y]);
04006 }
04007 } else
04008 ast_log(LOG_WARNING, "Expected at least %d arguments in map, but got only %d\n", 4, x);
04009 }
04010
04011
04012 static int do_register(const void *data)
04013 {
04014 struct dundi_ie_data ied;
04015 struct dundi_peer *peer = (struct dundi_peer *)data;
04016 char eid_str[20];
04017 char eid_str2[20];
04018 ast_log(LOG_DEBUG, "Register us as '%s' to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->us_eid), dundi_eid_to_str(eid_str2, sizeof(eid_str2), &peer->eid));
04019 peer->registerid = ast_sched_add(sched, default_expiration * 1000, do_register, data);
04020
04021 if (peer->regtrans)
04022 destroy_trans(peer->regtrans, 0);
04023 peer->regtrans = create_transaction(peer);
04024 if (peer->regtrans) {
04025 ast_set_flag(peer->regtrans, FLAG_ISREG);
04026 memset(&ied, 0, sizeof(ied));
04027 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
04028 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &peer->regtrans->us_eid);
04029 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
04030 dundi_send(peer->regtrans, DUNDI_COMMAND_REGREQ, 0, 0, &ied);
04031
04032 } else
04033 ast_log(LOG_NOTICE, "Unable to create new transaction for registering to '%s'!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04034
04035 return 0;
04036 }
04037
04038 static int do_qualify(const void *data)
04039 {
04040 struct dundi_peer *peer = (struct dundi_peer *)data;
04041 peer->qualifyid = -1;
04042 qualify_peer(peer, 0);
04043 return 0;
04044 }
04045
04046 static void qualify_peer(struct dundi_peer *peer, int schedonly)
04047 {
04048 int when;
04049 AST_SCHED_DEL(sched, peer->qualifyid);
04050 if (peer->qualtrans)
04051 destroy_trans(peer->qualtrans, 0);
04052 peer->qualtrans = NULL;
04053 if (peer->maxms > 0) {
04054 when = 60000;
04055 if (peer->lastms < 0)
04056 when = 10000;
04057 if (schedonly)
04058 when = 5000;
04059 peer->qualifyid = ast_sched_add(sched, when, do_qualify, peer);
04060 if (!schedonly)
04061 peer->qualtrans = create_transaction(peer);
04062 if (peer->qualtrans) {
04063 peer->qualtx = ast_tvnow();
04064 ast_set_flag(peer->qualtrans, FLAG_ISQUAL);
04065 dundi_send(peer->qualtrans, DUNDI_COMMAND_NULL, 0, 1, NULL);
04066 }
04067 }
04068 }
04069 static void populate_addr(struct dundi_peer *peer, dundi_eid *eid)
04070 {
04071 char data[256];
04072 char *c;
04073 int port, expire;
04074 char eid_str[20];
04075 dundi_eid_to_str(eid_str, sizeof(eid_str), eid);
04076 if (!ast_db_get("dundi/dpeers", eid_str, data, sizeof(data))) {
04077 c = strchr(data, ':');
04078 if (c) {
04079 *c = '\0';
04080 c++;
04081 if (sscanf(c, "%d:%d", &port, &expire) == 2) {
04082
04083 inet_aton(data, &peer->addr.sin_addr);
04084 peer->addr.sin_family = AF_INET;
04085 peer->addr.sin_port = htons(port);
04086 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
04087 }
04088 }
04089 }
04090 }
04091
04092
04093 static void build_peer(dundi_eid *eid, struct ast_variable *v, int *globalpcmode)
04094 {
04095 struct dundi_peer *peer;
04096 struct ast_hostent he;
04097 struct hostent *hp;
04098 dundi_eid testeid;
04099 int needregister=0;
04100 char eid_str[20];
04101
04102 AST_LIST_LOCK(&peers);
04103 AST_LIST_TRAVERSE(&peers, peer, list) {
04104 if (!dundi_eid_cmp(&peer->eid, eid)) {
04105 break;
04106 }
04107 }
04108 if (!peer) {
04109
04110 if (!(peer = ast_calloc(1, sizeof(*peer)))) {
04111 AST_LIST_UNLOCK(&peers);
04112 return;
04113 }
04114 peer->registerid = -1;
04115 peer->registerexpire = -1;
04116 peer->qualifyid = -1;
04117 peer->addr.sin_family = AF_INET;
04118 peer->addr.sin_port = htons(DUNDI_PORT);
04119 populate_addr(peer, eid);
04120 AST_LIST_INSERT_HEAD(&peers, peer, list);
04121 }
04122 peer->dead = 0;
04123 peer->eid = *eid;
04124 peer->us_eid = global_eid;
04125 destroy_permissions(&peer->permit);
04126 destroy_permissions(&peer->include);
04127 AST_SCHED_DEL(sched, peer->registerid);
04128 for (; v; v = v->next) {
04129 if (!strcasecmp(v->name, "inkey")) {
04130 ast_copy_string(peer->inkey, v->value, sizeof(peer->inkey));
04131 } else if (!strcasecmp(v->name, "outkey")) {
04132 ast_copy_string(peer->outkey, v->value, sizeof(peer->outkey));
04133 } else if (!strcasecmp(v->name, "host")) {
04134 if (!strcasecmp(v->value, "dynamic")) {
04135 peer->dynamic = 1;
04136 } else {
04137 hp = ast_gethostbyname(v->value, &he);
04138 if (hp) {
04139 memcpy(&peer->addr.sin_addr, hp->h_addr, sizeof(peer->addr.sin_addr));
04140 peer->dynamic = 0;
04141 } else {
04142 ast_log(LOG_WARNING, "Unable to find host '%s' at line %d\n", v->value, v->lineno);
04143 peer->dead = 1;
04144 }
04145 }
04146 } else if (!strcasecmp(v->name, "ustothem")) {
04147 if (!dundi_str_to_eid(&testeid, v->value))
04148 peer->us_eid = testeid;
04149 else
04150 ast_log(LOG_WARNING, "'%s' is not a valid DUNDi Entity Identifier at line %d\n", v->value, v->lineno);
04151 } else if (!strcasecmp(v->name, "include")) {
04152 append_permission(&peer->include, v->value, 1);
04153 } else if (!strcasecmp(v->name, "permit")) {
04154 append_permission(&peer->permit, v->value, 1);
04155 } else if (!strcasecmp(v->name, "noinclude")) {
04156 append_permission(&peer->include, v->value, 0);
04157 } else if (!strcasecmp(v->name, "deny")) {
04158 append_permission(&peer->permit, v->value, 0);
04159 } else if (!strcasecmp(v->name, "register")) {
04160 needregister = ast_true(v->value);
04161 } else if (!strcasecmp(v->name, "order")) {
04162 if (!strcasecmp(v->value, "primary"))
04163 peer->order = 0;
04164 else if (!strcasecmp(v->value, "secondary"))
04165 peer->order = 1;
04166 else if (!strcasecmp(v->value, "tertiary"))
04167 peer->order = 2;
04168 else if (!strcasecmp(v->value, "quartiary"))
04169 peer->order = 3;
04170 else {
04171 ast_log(LOG_WARNING, "'%s' is not a valid order, should be primary, secondary, tertiary or quartiary at line %d\n", v->value, v->lineno);
04172 }
04173 } else if (!strcasecmp(v->name, "qualify")) {
04174 if (!strcasecmp(v->value, "no")) {
04175 peer->maxms = 0;
04176 } else if (!strcasecmp(v->value, "yes")) {
04177 peer->maxms = DEFAULT_MAXMS;
04178 } else if (sscanf(v->value, "%d", &peer->maxms) != 1) {
04179 ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of dundi.conf\n",
04180 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), v->lineno);
04181 peer->maxms = 0;
04182 }
04183 } else if (!strcasecmp(v->name, "model")) {
04184 if (!strcasecmp(v->value, "inbound"))
04185 peer->model = DUNDI_MODEL_INBOUND;
04186 else if (!strcasecmp(v->value, "outbound"))
04187 peer->model = DUNDI_MODEL_OUTBOUND;
04188 else if (!strcasecmp(v->value, "symmetric"))
04189 peer->model = DUNDI_MODEL_SYMMETRIC;
04190 else if (!strcasecmp(v->value, "none"))
04191 peer->model = 0;
04192 else {
04193 ast_log(LOG_WARNING, "Unknown model '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
04194 v->value, v->lineno);
04195 }
04196 } else if (!strcasecmp(v->name, "precache")) {
04197 if (!strcasecmp(v->value, "inbound"))
04198 peer->pcmodel = DUNDI_MODEL_INBOUND;
04199 else if (!strcasecmp(v->value, "outbound"))
04200 peer->pcmodel = DUNDI_MODEL_OUTBOUND;
04201 else if (!strcasecmp(v->value, "symmetric"))
04202 peer->pcmodel = DUNDI_MODEL_SYMMETRIC;
04203 else if (!strcasecmp(v->value, "none"))
04204 peer->pcmodel = 0;
04205 else {
04206 ast_log(LOG_WARNING, "Unknown pcmodel '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
04207 v->value, v->lineno);
04208 }
04209 }
04210 }
04211 (*globalpcmode) |= peer->pcmodel;
04212 if (!peer->model && !peer->pcmodel) {
04213 ast_log(LOG_WARNING, "Peer '%s' lacks a model or pcmodel, discarding!\n",
04214 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04215 peer->dead = 1;
04216 } else if ((peer->model & DUNDI_MODEL_INBOUND) && (peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
04217 ast_log(LOG_WARNING, "Peer '%s' may not be both inbound/symmetric model and outbound/symmetric precache model, discarding!\n",
04218 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04219 peer->dead = 1;
04220 } else if ((peer->model & DUNDI_MODEL_OUTBOUND) && (peer->pcmodel & DUNDI_MODEL_INBOUND)) {
04221 ast_log(LOG_WARNING, "Peer '%s' may not be both outbound/symmetric model and inbound/symmetric precache model, discarding!\n",
04222 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04223 peer->dead = 1;
04224 } else if (!AST_LIST_EMPTY(&peer->include) && !(peer->model & DUNDI_MODEL_OUTBOUND) && !(peer->pcmodel & DUNDI_MODEL_INBOUND)) {
04225 ast_log(LOG_WARNING, "Peer '%s' is supposed to be included in outbound searches but isn't an outbound peer or inbound precache!\n",
04226 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04227 } else if (!AST_LIST_EMPTY(&peer->permit) && !(peer->model & DUNDI_MODEL_INBOUND) && !(peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
04228 ast_log(LOG_WARNING, "Peer '%s' is supposed to have permission for some inbound searches but isn't an inbound peer or outbound precache!\n",
04229 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04230 } else {
04231 if (needregister) {
04232 peer->registerid = ast_sched_add(sched, 2000, do_register, peer);
04233 }
04234 qualify_peer(peer, 1);
04235 }
04236 AST_LIST_UNLOCK(&peers);
04237 }
04238
04239 static int dundi_helper(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *data, int flag)
04240 {
04241 struct dundi_result results[MAX_RESULTS];
04242 int res;
04243 int x;
04244 int found = 0;
04245 if (!strncasecmp(context, "macro-", 6)) {
04246 if (!chan) {
04247 ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
04248 return -1;
04249 }
04250
04251 if (!strcasecmp(exten, "s")) {
04252 exten = pbx_builtin_getvar_helper(chan, "ARG1");
04253 if (ast_strlen_zero(exten))
04254 exten = chan->macroexten;
04255 if (ast_strlen_zero(exten))
04256 exten = chan->exten;
04257 if (ast_strlen_zero(exten)) {
04258 ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
04259 return -1;
04260 }
04261 }
04262 if (ast_strlen_zero(data))
04263 data = "e164";
04264 } else {
04265 if (ast_strlen_zero(data))
04266 data = context;
04267 }
04268 res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
04269 for (x=0;x<res;x++) {
04270 if (ast_test_flag(results + x, flag))
04271 found++;
04272 }
04273 if (found >= priority)
04274 return 1;
04275 return 0;
04276 }
04277
04278 static int dundi_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
04279 {
04280 return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_EXISTS);
04281 }
04282
04283 static int dundi_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
04284 {
04285 return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_CANMATCH);
04286 }
04287
04288 static int dundi_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
04289 {
04290 struct dundi_result results[MAX_RESULTS];
04291 int res;
04292 int x=0;
04293 char req[1024];
04294 struct ast_app *dial;
04295
04296 if (!strncasecmp(context, "macro-", 6)) {
04297 if (!chan) {
04298 ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
04299 return -1;
04300 }
04301
04302 if (!strcasecmp(exten, "s")) {
04303 exten = pbx_builtin_getvar_helper(chan, "ARG1");
04304 if (ast_strlen_zero(exten))
04305 exten = chan->macroexten;
04306 if (ast_strlen_zero(exten))
04307 exten = chan->exten;
04308 if (ast_strlen_zero(exten)) {
04309 ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
04310 return -1;
04311 }
04312 }
04313 if (ast_strlen_zero(data))
04314 data = "e164";
04315 } else {
04316 if (ast_strlen_zero(data))
04317 data = context;
04318 }
04319 res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
04320 if (res > 0) {
04321 sort_results(results, res);
04322 for (x=0;x<res;x++) {
04323 if (ast_test_flag(results + x, DUNDI_FLAG_EXISTS)) {
04324 if (!--priority)
04325 break;
04326 }
04327 }
04328 }
04329 if (x < res) {
04330
04331 snprintf(req, sizeof(req), "%s/%s", results[x].tech, results[x].dest);
04332 dial = pbx_findapp("Dial");
04333 if (dial)
04334 res = pbx_exec(chan, dial, req);
04335 } else
04336 res = -1;
04337 return res;
04338 }
04339
04340 static int dundi_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
04341 {
04342 return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_MATCHMORE);
04343 }
04344
04345 static struct ast_switch dundi_switch =
04346 {
04347 name: "DUNDi",
04348 description: "DUNDi Discovered Dialplan Switch",
04349 exists: dundi_exists,
04350 canmatch: dundi_canmatch,
04351 exec: dundi_exec,
04352 matchmore: dundi_matchmore,
04353 };
04354
04355 static int set_config(char *config_file, struct sockaddr_in* sin)
04356 {
04357 struct ast_config *cfg;
04358 struct ast_variable *v;
04359 char *cat;
04360 int format;
04361 int x;
04362 char hn[MAXHOSTNAMELEN] = "";
04363 struct ast_hostent he;
04364 struct hostent *hp;
04365 struct sockaddr_in sin2;
04366 static int last_port = 0;
04367 int globalpcmodel = 0;
04368 dundi_eid testeid;
04369
04370 dundi_ttl = DUNDI_DEFAULT_TTL;
04371 dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
04372 any_peer = NULL;
04373
04374 cfg = ast_config_load(config_file);
04375
04376 if (!cfg) {
04377 ast_log(LOG_ERROR, "Unable to load config %s\n", config_file);
04378 return -1;
04379 }
04380 ipaddr[0] = '\0';
04381 if (!gethostname(hn, sizeof(hn)-1)) {
04382 hp = ast_gethostbyname(hn, &he);
04383 if (hp) {
04384 memcpy(&sin2.sin_addr, hp->h_addr, sizeof(sin2.sin_addr));
04385 ast_copy_string(ipaddr, ast_inet_ntoa(sin2.sin_addr), sizeof(ipaddr));
04386 } else
04387 ast_log(LOG_WARNING, "Unable to look up host '%s'\n", hn);
04388 } else
04389 ast_log(LOG_WARNING, "Unable to get host name!\n");
04390 AST_LIST_LOCK(&peers);
04391 reset_global_eid();
04392 global_storehistory = 0;
04393 ast_copy_string(secretpath, "dundi", sizeof(secretpath));
04394 v = ast_variable_browse(cfg, "general");
04395 while(v) {
04396 if (!strcasecmp(v->name, "port")){
04397 sin->sin_port = ntohs(atoi(v->value));
04398 if(last_port==0){
04399 last_port=sin->sin_port;
04400 } else if(sin->sin_port != last_port)
04401 ast_log(LOG_WARNING, "change to port ignored until next asterisk re-start\n");
04402 } else if (!strcasecmp(v->name, "bindaddr")) {
04403 struct hostent *hp;
04404 struct ast_hostent he;
04405 hp = ast_gethostbyname(v->value, &he);
04406 if (hp) {
04407 memcpy(&sin->sin_addr, hp->h_addr, sizeof(sin->sin_addr));
04408 } else
04409 ast_log(LOG_WARNING, "Invalid host/IP '%s'\n", v->value);
04410 } else if (!strcasecmp(v->name, "authdebug")) {
04411 authdebug = ast_true(v->value);
04412 } else if (!strcasecmp(v->name, "ttl")) {
04413 if ((sscanf(v->value, "%d", &x) == 1) && (x > 0) && (x < DUNDI_DEFAULT_TTL)) {
04414 dundi_ttl = x;
04415 } else {
04416 ast_log(LOG_WARNING, "'%s' is not a valid TTL at line %d, must be number from 1 to %d\n",
04417 v->value, v->lineno, DUNDI_DEFAULT_TTL);
04418 }
04419 } else if (!strcasecmp(v->name, "autokill")) {
04420 if (sscanf(v->value, "%d", &x) == 1) {
04421 if (x >= 0)
04422 global_autokilltimeout = x;
04423 else
04424 ast_log(LOG_NOTICE, "Nice try, but autokill has to be >0 or 'yes' or 'no' at line %d\n", v->lineno);
04425 } else if (ast_true(v->value)) {
04426 global_autokilltimeout = DEFAULT_MAXMS;
04427 } else {
04428 global_autokilltimeout = 0;
04429 }
04430 } else if (!strcasecmp(v->name, "entityid")) {
04431 if (!dundi_str_to_eid(&testeid, v->value))
04432 global_eid = testeid;
04433 else
04434 ast_log(LOG_WARNING, "Invalid global endpoint identifier '%s' at line %d\n", v->value, v->lineno);
04435 } else if (!strcasecmp(v->name, "tos")) {
04436 if (sscanf(v->value, "%d", &format) == 1)
04437 tos = format & 0xff;
04438 else if (!strcasecmp(v->value, "lowdelay"))
04439 tos = IPTOS_LOWDELAY;
04440 else if (!strcasecmp(v->value, "throughput"))
04441 tos = IPTOS_THROUGHPUT;
04442 else if (!strcasecmp(v->value, "reliability"))
04443 tos = IPTOS_RELIABILITY;
04444 #if !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(SOLARIS)
04445 else if (!strcasecmp(v->value, "mincost"))
04446 tos = IPTOS_MINCOST;
04447 #endif
04448 else if (!strcasecmp(v->value, "none"))
04449 tos = 0;
04450 else
04451 #if !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(SOLARIS)
04452 ast_log(LOG_WARNING, "Invalid tos value at line %d, should be 'lowdelay', 'throughput', 'reliability', 'mincost', or 'none'\n", v->lineno);
04453 #else
04454 ast_log(LOG_WARNING, "Invalid tos value at line %d, should be 'lowdelay', 'throughput', 'reliability', or 'none'\n", v->lineno);
04455 #endif
04456 } else if (!strcasecmp(v->name, "department")) {
04457 ast_copy_string(dept, v->value, sizeof(dept));
04458 } else if (!strcasecmp(v->name, "organization")) {
04459 ast_copy_string(org, v->value, sizeof(org));
04460 } else if (!strcasecmp(v->name, "locality")) {
04461 ast_copy_string(locality, v->value, sizeof(locality));
04462 } else if (!strcasecmp(v->name, "stateprov")) {
04463 ast_copy_string(stateprov, v->value, sizeof(stateprov));
04464 } else if (!strcasecmp(v->name, "country")) {
04465 ast_copy_string(country, v->value, sizeof(country));
04466 } else if (!strcasecmp(v->name, "email")) {
04467 ast_copy_string(email, v->value, sizeof(email));
04468 } else if (!strcasecmp(v->name, "phone")) {
04469 ast_copy_string(phone, v->value, sizeof(phone));
04470 } else if (!strcasecmp(v->name, "storehistory")) {
04471 global_storehistory = ast_true(v->value);
04472 } else if (!strcasecmp(v->name, "cachetime")) {
04473 if ((sscanf(v->value, "%d", &x) == 1)) {
04474 dundi_cache_time = x;
04475 } else {
04476 ast_log(LOG_WARNING, "'%s' is not a valid cache time at line %d. Using default value '%d'.\n",
04477 v->value, v->lineno, DUNDI_DEFAULT_CACHE_TIME);
04478 }
04479 }
04480 v = v->next;
04481 }
04482 AST_LIST_UNLOCK(&peers);
04483 mark_mappings();
04484 v = ast_variable_browse(cfg, "mappings");
04485 while(v) {
04486 build_mapping(v->name, v->value);
04487 v = v->next;
04488 }
04489 prune_mappings();
04490 mark_peers();
04491 cat = ast_category_browse(cfg, NULL);
04492 while(cat) {
04493 if (strcasecmp(cat, "general") && strcasecmp(cat, "mappings")) {
04494
04495 if (!dundi_str_to_eid(&testeid, cat))
04496 build_peer(&testeid, ast_variable_browse(cfg, cat), &globalpcmodel);
04497 else if (!strcasecmp(cat, "*")) {
04498 build_peer(&empty_eid, ast_variable_browse(cfg, cat), &globalpcmodel);
04499 any_peer = find_peer(NULL);
04500 } else
04501 ast_log(LOG_NOTICE, "Ignoring invalid EID entry '%s'\n", cat);
04502 }
04503 cat = ast_category_browse(cfg, cat);
04504 }
04505 prune_peers();
04506 ast_config_destroy(cfg);
04507 load_password();
04508 if (globalpcmodel & DUNDI_MODEL_OUTBOUND)
04509 dundi_precache_full();
04510 return 0;
04511 }
04512
04513 static int unload_module(void)
04514 {
04515 pthread_t previous_netthreadid = netthreadid, previous_precachethreadid = precachethreadid;
04516 ast_module_user_hangup_all();
04517
04518
04519 dundi_shutdown = 1;
04520 if (previous_netthreadid != AST_PTHREADT_NULL) {
04521 pthread_kill(previous_netthreadid, SIGURG);
04522 pthread_join(previous_netthreadid, NULL);
04523 }
04524 if (previous_precachethreadid != AST_PTHREADT_NULL) {
04525 pthread_kill(previous_precachethreadid, SIGURG);
04526 pthread_join(previous_precachethreadid, NULL);
04527 }
04528
04529 ast_cli_unregister_multiple(cli_dundi, sizeof(cli_dundi) / sizeof(struct ast_cli_entry));
04530 ast_unregister_switch(&dundi_switch);
04531 ast_custom_function_unregister(&dundi_function);
04532 close(netsocket);
04533 io_context_destroy(io);
04534 sched_context_destroy(sched);
04535
04536 mark_mappings();
04537 prune_mappings();
04538 mark_peers();
04539 prune_peers();
04540
04541 return 0;
04542 }
04543
04544 static int reload(void)
04545 {
04546 struct sockaddr_in sin;
04547 set_config("dundi.conf",&sin);
04548 return 0;
04549 }
04550
04551 static int load_module(void)
04552 {
04553 int res = 0;
04554 struct sockaddr_in sin;
04555
04556 if(set_config("dundi.conf",&sin))
04557 return AST_MODULE_LOAD_DECLINE;
04558
04559 dundi_set_output(dundi_debug_output);
04560 dundi_set_error(dundi_error_output);
04561
04562 sin.sin_family = AF_INET;
04563 sin.sin_port = ntohs(DUNDI_PORT);
04564 sin.sin_addr.s_addr = INADDR_ANY;
04565
04566
04567 io = io_context_create();
04568 sched = sched_context_create();
04569
04570 if (!io || !sched) {
04571 ast_log(LOG_ERROR, "Out of memory\n");
04572 return -1;
04573 }
04574
04575 netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
04576
04577 if (netsocket < 0) {
04578 ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
04579 return -1;
04580 }
04581 if (bind(netsocket,(struct sockaddr *)&sin, sizeof(sin))) {
04582 ast_log(LOG_ERROR, "Unable to bind to %s port %d: %s\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), strerror(errno));
04583 return -1;
04584 }
04585
04586 if (option_verbose > 1)
04587 ast_verbose(VERBOSE_PREFIX_2 "Using TOS bits %d\n", tos);
04588
04589 if (setsockopt(netsocket, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)))
04590 ast_log(LOG_WARNING, "Unable to set TOS to %d\n", tos);
04591
04592 res = start_network_thread();
04593 if (res) {
04594 ast_log(LOG_ERROR, "Unable to start network thread\n");
04595 close(netsocket);
04596 return -1;
04597 }
04598
04599 if (option_verbose > 1)
04600 ast_verbose(VERBOSE_PREFIX_2 "DUNDi Ready and Listening on %s port %d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
04601
04602 ast_cli_register_multiple(cli_dundi, sizeof(cli_dundi) / sizeof(struct ast_cli_entry));
04603 if (ast_register_switch(&dundi_switch))
04604 ast_log(LOG_ERROR, "Unable to register DUNDi switch\n");
04605 ast_custom_function_register(&dundi_function);
04606
04607 return res;
04608 }
04609
04610 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Distributed Universal Number Discovery (DUNDi)",
04611 .load = load_module,
04612 .unload = unload_module,
04613 .reload = reload,
04614 );
04615