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