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