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