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