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