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