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