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