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: 213977 $")
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, "%30d", &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, "%30d/%30d/%30d/%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 enum {
03858 OPT_BYPASS_CACHE = (1 << 0),
03859 };
03860
03861 AST_APP_OPTIONS(dundi_query_opts, BEGIN_OPTIONS
03862 AST_APP_OPTION('b', OPT_BYPASS_CACHE),
03863 END_OPTIONS );
03864
03865 static int dundifunc_read(struct ast_channel *chan, const char *cmd, char *num, char *buf, size_t len)
03866 {
03867 int results;
03868 int x;
03869 struct ast_module_user *u;
03870 struct dundi_result dr[MAX_RESULTS];
03871 AST_DECLARE_APP_ARGS(args,
03872 AST_APP_ARG(number);
03873 AST_APP_ARG(context);
03874 AST_APP_ARG(options);
03875 );
03876 char *parse;
03877 struct ast_flags opts = { 0, };
03878
03879 buf[0] = '\0';
03880
03881 if (ast_strlen_zero(num)) {
03882 ast_log(LOG_WARNING, "DUNDILOOKUP requires an argument (number)\n");
03883 return -1;
03884 }
03885
03886 u = ast_module_user_add(chan);
03887
03888 parse = ast_strdupa(num);
03889
03890 AST_STANDARD_APP_ARGS(args, parse);
03891
03892 if (!ast_strlen_zero(args.options)) {
03893 ast_app_parse_options(dundi_query_opts, &opts, NULL, args.options);
03894 }
03895 if (ast_strlen_zero(args.context)) {
03896 args.context = "e164";
03897 }
03898
03899 results = dundi_lookup(dr, MAX_RESULTS, NULL, args.context, args.number, ast_test_flag(&opts, OPT_BYPASS_CACHE));
03900 if (results > 0) {
03901 sort_results(dr, results);
03902 for (x = 0; x < results; x++) {
03903 if (ast_test_flag(dr + x, DUNDI_FLAG_EXISTS)) {
03904 snprintf(buf, len, "%s/%s", dr[x].tech, dr[x].dest);
03905 break;
03906 }
03907 }
03908 }
03909
03910 ast_module_user_remove(u);
03911
03912 return 0;
03913 }
03914
03915
03916
03917
03918
03919 static struct ast_custom_function dundi_function = {
03920 .name = "DUNDILOOKUP",
03921 .synopsis = "Do a DUNDi lookup of a phone number.",
03922 .syntax = "DUNDILOOKUP(number[,context[,options]])",
03923 .desc = "This will do a DUNDi lookup of the given phone number.\n"
03924 "If no context is given, the default will be e164. The result of\n"
03925 "this function will return the Technology/Resource found in the first result\n"
03926 "in the DUNDi lookup. If no results were found, the result will be blank.\n"
03927 "If the 'b' option is specified, the internal DUNDi cache will\n"
03928 "be bypassed.\n",
03929 .read = dundifunc_read,
03930 };
03931
03932 unsigned int dundi_result_id;
03933
03934 struct dundi_result_datastore {
03935 struct dundi_result results[MAX_RESULTS];
03936 unsigned int num_results;
03937 unsigned int id;
03938 };
03939
03940 static void drds_destroy(struct dundi_result_datastore *drds)
03941 {
03942 ast_free(drds);
03943 }
03944
03945 static void drds_destroy_cb(void *data)
03946 {
03947 struct dundi_result_datastore *drds = data;
03948 drds_destroy(drds);
03949 }
03950
03951 static const struct ast_datastore_info dundi_result_datastore_info = {
03952 .type = "DUNDIQUERY",
03953 .destroy = drds_destroy_cb,
03954 };
03955
03956 static int dundi_query_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
03957 {
03958 struct ast_module_user *u;
03959 AST_DECLARE_APP_ARGS(args,
03960 AST_APP_ARG(number);
03961 AST_APP_ARG(context);
03962 AST_APP_ARG(options);
03963 );
03964 struct ast_flags opts = { 0, };
03965 char *parse;
03966 struct dundi_result_datastore *drds;
03967 struct ast_datastore *datastore;
03968
03969 u = ast_module_user_add(chan);
03970
03971 if (ast_strlen_zero(data)) {
03972 ast_log(LOG_WARNING, "DUNDIQUERY requires an argument (number)\n");
03973 ast_module_user_remove(u);
03974 return -1;
03975 }
03976
03977 if (!chan) {
03978 ast_log(LOG_ERROR, "DUNDIQUERY can not be used without a channel!\n");
03979 ast_module_user_remove(u);
03980 return -1;
03981 }
03982
03983 parse = ast_strdupa(data);
03984
03985 AST_STANDARD_APP_ARGS(args, parse);
03986
03987 if (!ast_strlen_zero(args.options))
03988 ast_app_parse_options(dundi_query_opts, &opts, NULL, args.options);
03989
03990 if (ast_strlen_zero(args.context))
03991 args.context = "e164";
03992
03993 if (!(drds = ast_calloc(1, sizeof(*drds)))) {
03994 ast_module_user_remove(u);
03995 return -1;
03996 }
03997
03998 drds->id = ast_atomic_fetchadd_int((int *) &dundi_result_id, 1);
03999 snprintf(buf, len, "%u", drds->id);
04000
04001 if (!(datastore = ast_datastore_alloc(&dundi_result_datastore_info, buf))) {
04002 drds_destroy(drds);
04003 ast_module_user_remove(u);
04004 return -1;
04005 }
04006
04007 datastore->data = drds;
04008
04009 drds->num_results = dundi_lookup(drds->results, ARRAY_LEN(drds->results), NULL, args.context,
04010 args.number, ast_test_flag(&opts, OPT_BYPASS_CACHE));
04011
04012 if (drds->num_results > 0)
04013 sort_results(drds->results, drds->num_results);
04014
04015 ast_channel_lock(chan);
04016 ast_channel_datastore_add(chan, datastore);
04017 ast_channel_unlock(chan);
04018
04019 ast_module_user_remove(u);
04020
04021 return 0;
04022 }
04023
04024 static struct ast_custom_function dundi_query_function = {
04025 .name = "DUNDIQUERY",
04026 .synopsis = "Initiate a DUNDi query.",
04027 .syntax = "DUNDIQUERY(number[|context[|options]])",
04028 .desc = "This will do a DUNDi lookup of the given phone number.\n"
04029 "If no context is given, the default will be e164. The result of\n"
04030 "this function will be a numeric ID that can be used to retrieve\n"
04031 "the results with the DUNDIRESULT function. If the 'b' option is\n"
04032 "is specified, the internal DUNDi cache will be bypassed.\n",
04033 .read = dundi_query_read,
04034 };
04035
04036 static int dundi_result_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
04037 {
04038 struct ast_module_user *u;
04039 AST_DECLARE_APP_ARGS(args,
04040 AST_APP_ARG(id);
04041 AST_APP_ARG(resultnum);
04042 );
04043 char *parse;
04044 unsigned int num;
04045 struct dundi_result_datastore *drds;
04046 struct ast_datastore *datastore;
04047 int res = -1;
04048
04049 u = ast_module_user_add(chan);
04050
04051 if (ast_strlen_zero(data)) {
04052 ast_log(LOG_WARNING, "DUNDIRESULT requires an argument (id and resultnum)\n");
04053 goto finish;
04054 }
04055
04056 if (!chan) {
04057 ast_log(LOG_ERROR, "DUNDRESULT can not be used without a channel!\n");
04058 goto finish;
04059 }
04060
04061 parse = ast_strdupa(data);
04062
04063 AST_STANDARD_APP_ARGS(args, parse);
04064
04065 if (ast_strlen_zero(args.id)) {
04066 ast_log(LOG_ERROR, "A result ID must be provided to DUNDIRESULT\n");
04067 goto finish;
04068 }
04069
04070 if (ast_strlen_zero(args.resultnum)) {
04071 ast_log(LOG_ERROR, "A result number must be given to DUNDIRESULT!\n");
04072 goto finish;
04073 }
04074
04075 ast_channel_lock(chan);
04076 datastore = ast_channel_datastore_find(chan, &dundi_result_datastore_info, args.id);
04077 ast_channel_unlock(chan);
04078
04079 if (!datastore) {
04080 ast_log(LOG_WARNING, "No DUNDi results found for query ID '%s'\n", args.id);
04081 goto finish;
04082 }
04083
04084 drds = datastore->data;
04085
04086 if (!strcasecmp(args.resultnum, "getnum")) {
04087 snprintf(buf, len, "%u", drds->num_results);
04088 res = 0;
04089 goto finish;
04090 }
04091
04092 if (sscanf(args.resultnum, "%30u", &num) != 1) {
04093 ast_log(LOG_ERROR, "Invalid value '%s' for resultnum to DUNDIRESULT!\n",
04094 args.resultnum);
04095 goto finish;
04096 }
04097
04098 if (num && num <= drds->num_results) {
04099 snprintf(buf, len, "%s/%s", drds->results[num - 1].tech, drds->results[num - 1].dest);
04100 res = 0;
04101 } else
04102 ast_log(LOG_WARNING, "Result number %u is not valid for DUNDi query results for ID %s!\n", num, args.id);
04103
04104 finish:
04105 ast_module_user_remove(u);
04106
04107 return res;
04108 }
04109
04110 static struct ast_custom_function dundi_result_function = {
04111 .name = "DUNDIRESULT",
04112 .synopsis = "Retrieve results from a DUNDIQUERY",
04113 .syntax = "DUNDIRESULT(id|resultnum)",
04114 .desc = "This function will retrieve results from a previous use\n"
04115 "of the DUNDIQUERY function.\n"
04116 " id - This argument is the identifier returned by the DUNDIQUERY function.\n"
04117 " resultnum - This is the number of the result that you want to retrieve.\n"
04118 " Results start at 1. If this argument is specified as \"getnum\",\n"
04119 " then it will return the total number of results that are available.\n",
04120 .read = dundi_result_read,
04121 };
04122
04123 static void mark_peers(void)
04124 {
04125 struct dundi_peer *peer;
04126 AST_LIST_LOCK(&peers);
04127 AST_LIST_TRAVERSE(&peers, peer, list) {
04128 peer->dead = 1;
04129 }
04130 AST_LIST_UNLOCK(&peers);
04131 }
04132
04133 static void mark_mappings(void)
04134 {
04135 struct dundi_mapping *map;
04136
04137 AST_LIST_LOCK(&peers);
04138 AST_LIST_TRAVERSE(&mappings, map, list) {
04139 map->dead = 1;
04140 }
04141 AST_LIST_UNLOCK(&peers);
04142 }
04143
04144 static void destroy_permissions(struct permissionlist *permlist)
04145 {
04146 struct permission *perm;
04147
04148 while ((perm = AST_LIST_REMOVE_HEAD(permlist, list)))
04149 ast_free(perm);
04150 }
04151
04152 static void destroy_peer(struct dundi_peer *peer)
04153 {
04154 AST_SCHED_DEL(sched, peer->registerid);
04155 if (peer->regtrans)
04156 destroy_trans(peer->regtrans, 0);
04157 AST_SCHED_DEL(sched, peer->qualifyid);
04158 destroy_permissions(&peer->permit);
04159 destroy_permissions(&peer->include);
04160 ast_free(peer);
04161 }
04162
04163 static void destroy_map(struct dundi_mapping *map)
04164 {
04165 if (map->weightstr)
04166 ast_free(map->weightstr);
04167 ast_free(map);
04168 }
04169
04170 static void prune_peers(void)
04171 {
04172 struct dundi_peer *peer;
04173
04174 AST_LIST_LOCK(&peers);
04175 AST_LIST_TRAVERSE_SAFE_BEGIN(&peers, peer, list) {
04176 if (peer->dead) {
04177 AST_LIST_REMOVE_CURRENT(list);
04178 destroy_peer(peer);
04179 }
04180 }
04181 AST_LIST_TRAVERSE_SAFE_END;
04182 AST_LIST_UNLOCK(&peers);
04183 }
04184
04185 static void prune_mappings(void)
04186 {
04187 struct dundi_mapping *map;
04188
04189 AST_LIST_LOCK(&peers);
04190 AST_LIST_TRAVERSE_SAFE_BEGIN(&mappings, map, list) {
04191 if (map->dead) {
04192 AST_LIST_REMOVE_CURRENT(list);
04193 destroy_map(map);
04194 }
04195 }
04196 AST_LIST_TRAVERSE_SAFE_END;
04197 AST_LIST_UNLOCK(&peers);
04198 }
04199
04200 static void append_permission(struct permissionlist *permlist, const char *s, int allow)
04201 {
04202 struct permission *perm;
04203
04204 if (!(perm = ast_calloc(1, sizeof(*perm) + strlen(s) + 1)))
04205 return;
04206
04207 strcpy(perm->name, s);
04208 perm->allow = allow;
04209
04210 AST_LIST_INSERT_TAIL(permlist, perm, list);
04211 }
04212
04213 #define MAX_OPTS 128
04214
04215 static void build_mapping(const char *name, const char *value)
04216 {
04217 char *t, *fields[MAX_OPTS];
04218 struct dundi_mapping *map;
04219 int x;
04220 int y;
04221
04222 t = ast_strdupa(value);
04223
04224 AST_LIST_TRAVERSE(&mappings, map, list) {
04225
04226 if (!strcasecmp(map->dcontext, name) &&
04227 (!strncasecmp(map->lcontext, value, strlen(map->lcontext)) &&
04228 (!value[strlen(map->lcontext)] ||
04229 (value[strlen(map->lcontext)] == ','))))
04230 break;
04231 }
04232 if (!map) {
04233 if (!(map = ast_calloc(1, sizeof(*map))))
04234 return;
04235 AST_LIST_INSERT_HEAD(&mappings, map, list);
04236 map->dead = 1;
04237 }
04238 map->options = 0;
04239 memset(fields, 0, sizeof(fields));
04240 x = 0;
04241 while (t && x < MAX_OPTS) {
04242 fields[x++] = t;
04243 t = strchr(t, ',');
04244 if (t) {
04245 *t = '\0';
04246 t++;
04247 }
04248 }
04249 if ((x == 1) && ast_strlen_zero(fields[0])) {
04250
04251 ast_copy_string(map->dcontext, name, sizeof(map->dcontext));
04252 map->dead = 0;
04253 } else if (x >= 4) {
04254 ast_copy_string(map->dcontext, name, sizeof(map->dcontext));
04255 ast_copy_string(map->lcontext, fields[0], sizeof(map->lcontext));
04256 if ((sscanf(fields[1], "%30d", &map->_weight) == 1) && (map->_weight >= 0) && (map->_weight <= MAX_WEIGHT)) {
04257 ast_copy_string(map->dest, fields[3], sizeof(map->dest));
04258 if ((map->tech = str2tech(fields[2])))
04259 map->dead = 0;
04260 } else if (!strncmp(fields[1], "${", 2) && fields[1][strlen(fields[1]) - 1] == '}') {
04261 map->weightstr = ast_strdup(fields[1]);
04262 ast_copy_string(map->dest, fields[3], sizeof(map->dest));
04263 if ((map->tech = str2tech(fields[2])))
04264 map->dead = 0;
04265 } else {
04266 ast_log(LOG_WARNING, "Invalid weight '%s' specified, deleting entry '%s/%s'\n", fields[1], map->dcontext, map->lcontext);
04267 }
04268 for (y = 4;y < x; y++) {
04269 if (!strcasecmp(fields[y], "nounsolicited"))
04270 map->options |= DUNDI_FLAG_NOUNSOLICITED;
04271 else if (!strcasecmp(fields[y], "nocomunsolicit"))
04272 map->options |= DUNDI_FLAG_NOCOMUNSOLICIT;
04273 else if (!strcasecmp(fields[y], "residential"))
04274 map->options |= DUNDI_FLAG_RESIDENTIAL;
04275 else if (!strcasecmp(fields[y], "commercial"))
04276 map->options |= DUNDI_FLAG_COMMERCIAL;
04277 else if (!strcasecmp(fields[y], "mobile"))
04278 map->options |= DUNDI_FLAG_MOBILE;
04279 else if (!strcasecmp(fields[y], "nopartial"))
04280 map->options |= DUNDI_FLAG_INTERNAL_NOPARTIAL;
04281 else
04282 ast_log(LOG_WARNING, "Don't know anything about option '%s'\n", fields[y]);
04283 }
04284 } else
04285 ast_log(LOG_WARNING, "Expected at least %d arguments in map, but got only %d\n", 4, x);
04286 }
04287
04288
04289 static int do_register(const void *data)
04290 {
04291 struct dundi_ie_data ied;
04292 struct dundi_peer *peer = (struct dundi_peer *)data;
04293 char eid_str[20];
04294 char eid_str2[20];
04295 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));
04296 peer->registerid = ast_sched_add(sched, default_expiration * 1000, do_register, data);
04297
04298 if (peer->regtrans)
04299 destroy_trans(peer->regtrans, 0);
04300 peer->regtrans = create_transaction(peer);
04301 if (peer->regtrans) {
04302 ast_set_flag(peer->regtrans, FLAG_ISREG);
04303 memset(&ied, 0, sizeof(ied));
04304 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION);
04305 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &peer->regtrans->us_eid);
04306 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration);
04307 dundi_send(peer->regtrans, DUNDI_COMMAND_REGREQ, 0, 0, &ied);
04308
04309 } else
04310 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));
04311
04312 return 0;
04313 }
04314
04315 static int do_qualify(const void *data)
04316 {
04317 struct dundi_peer *peer = (struct dundi_peer *)data;
04318 peer->qualifyid = -1;
04319 qualify_peer(peer, 0);
04320 return 0;
04321 }
04322
04323 static void qualify_peer(struct dundi_peer *peer, int schedonly)
04324 {
04325 int when;
04326 AST_SCHED_DEL(sched, peer->qualifyid);
04327 if (peer->qualtrans)
04328 destroy_trans(peer->qualtrans, 0);
04329 peer->qualtrans = NULL;
04330 if (peer->maxms > 0) {
04331 when = 60000;
04332 if (peer->lastms < 0)
04333 when = 10000;
04334 if (schedonly)
04335 when = 5000;
04336 peer->qualifyid = ast_sched_add(sched, when, do_qualify, peer);
04337 if (!schedonly)
04338 peer->qualtrans = create_transaction(peer);
04339 if (peer->qualtrans) {
04340 peer->qualtx = ast_tvnow();
04341 ast_set_flag(peer->qualtrans, FLAG_ISQUAL);
04342 dundi_send(peer->qualtrans, DUNDI_COMMAND_NULL, 0, 1, NULL);
04343 }
04344 }
04345 }
04346 static void populate_addr(struct dundi_peer *peer, dundi_eid *eid)
04347 {
04348 char data[256];
04349 char *c;
04350 int port, expire;
04351 char eid_str[20];
04352 ast_eid_to_str(eid_str, sizeof(eid_str), eid);
04353 if (!ast_db_get("dundi/dpeers", eid_str, data, sizeof(data))) {
04354 c = strchr(data, ':');
04355 if (c) {
04356 *c = '\0';
04357 c++;
04358 if (sscanf(c, "%5d:%30d", &port, &expire) == 2) {
04359
04360 inet_aton(data, &peer->addr.sin_addr);
04361 peer->addr.sin_family = AF_INET;
04362 peer->addr.sin_port = htons(port);
04363 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer);
04364 }
04365 }
04366 }
04367 }
04368
04369
04370 static void build_peer(dundi_eid *eid, struct ast_variable *v, int *globalpcmode)
04371 {
04372 struct dundi_peer *peer;
04373 struct ast_hostent he;
04374 struct hostent *hp;
04375 dundi_eid testeid;
04376 int needregister=0;
04377 char eid_str[20];
04378
04379 AST_LIST_LOCK(&peers);
04380 AST_LIST_TRAVERSE(&peers, peer, list) {
04381 if (!ast_eid_cmp(&peer->eid, eid)) {
04382 break;
04383 }
04384 }
04385 if (!peer) {
04386
04387 if (!(peer = ast_calloc(1, sizeof(*peer)))) {
04388 AST_LIST_UNLOCK(&peers);
04389 return;
04390 }
04391 peer->registerid = -1;
04392 peer->registerexpire = -1;
04393 peer->qualifyid = -1;
04394 peer->addr.sin_family = AF_INET;
04395 peer->addr.sin_port = htons(DUNDI_PORT);
04396 populate_addr(peer, eid);
04397 AST_LIST_INSERT_HEAD(&peers, peer, list);
04398 }
04399 peer->dead = 0;
04400 peer->eid = *eid;
04401 peer->us_eid = global_eid;
04402 destroy_permissions(&peer->permit);
04403 destroy_permissions(&peer->include);
04404 AST_SCHED_DEL(sched, peer->registerid);
04405 for (; v; v = v->next) {
04406 if (!strcasecmp(v->name, "inkey")) {
04407 ast_copy_string(peer->inkey, v->value, sizeof(peer->inkey));
04408 } else if (!strcasecmp(v->name, "outkey")) {
04409 ast_copy_string(peer->outkey, v->value, sizeof(peer->outkey));
04410 } else if (!strcasecmp(v->name, "host")) {
04411 if (!strcasecmp(v->value, "dynamic")) {
04412 peer->dynamic = 1;
04413 } else {
04414 hp = ast_gethostbyname(v->value, &he);
04415 if (hp) {
04416 memcpy(&peer->addr.sin_addr, hp->h_addr, sizeof(peer->addr.sin_addr));
04417 peer->dynamic = 0;
04418 } else {
04419 ast_log(LOG_WARNING, "Unable to find host '%s' at line %d\n", v->value, v->lineno);
04420 peer->dead = 1;
04421 }
04422 }
04423 } else if (!strcasecmp(v->name, "ustothem")) {
04424 if (!ast_str_to_eid(&testeid, v->value))
04425 peer->us_eid = testeid;
04426 else
04427 ast_log(LOG_WARNING, "'%s' is not a valid DUNDi Entity Identifier at line %d\n", v->value, v->lineno);
04428 } else if (!strcasecmp(v->name, "include")) {
04429 append_permission(&peer->include, v->value, 1);
04430 } else if (!strcasecmp(v->name, "permit")) {
04431 append_permission(&peer->permit, v->value, 1);
04432 } else if (!strcasecmp(v->name, "noinclude")) {
04433 append_permission(&peer->include, v->value, 0);
04434 } else if (!strcasecmp(v->name, "deny")) {
04435 append_permission(&peer->permit, v->value, 0);
04436 } else if (!strcasecmp(v->name, "register")) {
04437 needregister = ast_true(v->value);
04438 } else if (!strcasecmp(v->name, "order")) {
04439 if (!strcasecmp(v->value, "primary"))
04440 peer->order = 0;
04441 else if (!strcasecmp(v->value, "secondary"))
04442 peer->order = 1;
04443 else if (!strcasecmp(v->value, "tertiary"))
04444 peer->order = 2;
04445 else if (!strcasecmp(v->value, "quartiary"))
04446 peer->order = 3;
04447 else {
04448 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);
04449 }
04450 } else if (!strcasecmp(v->name, "qualify")) {
04451 if (!strcasecmp(v->value, "no")) {
04452 peer->maxms = 0;
04453 } else if (!strcasecmp(v->value, "yes")) {
04454 peer->maxms = DEFAULT_MAXMS;
04455 } else if (sscanf(v->value, "%30d", &peer->maxms) != 1) {
04456 ast_log(LOG_WARNING, "Qualification of peer '%s' should be 'yes', 'no', or a number of milliseconds at line %d of dundi.conf\n",
04457 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), v->lineno);
04458 peer->maxms = 0;
04459 }
04460 } else if (!strcasecmp(v->name, "model")) {
04461 if (!strcasecmp(v->value, "inbound"))
04462 peer->model = DUNDI_MODEL_INBOUND;
04463 else if (!strcasecmp(v->value, "outbound"))
04464 peer->model = DUNDI_MODEL_OUTBOUND;
04465 else if (!strcasecmp(v->value, "symmetric"))
04466 peer->model = DUNDI_MODEL_SYMMETRIC;
04467 else if (!strcasecmp(v->value, "none"))
04468 peer->model = 0;
04469 else {
04470 ast_log(LOG_WARNING, "Unknown model '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
04471 v->value, v->lineno);
04472 }
04473 } else if (!strcasecmp(v->name, "precache")) {
04474 if (!strcasecmp(v->value, "inbound"))
04475 peer->pcmodel = DUNDI_MODEL_INBOUND;
04476 else if (!strcasecmp(v->value, "outbound"))
04477 peer->pcmodel = DUNDI_MODEL_OUTBOUND;
04478 else if (!strcasecmp(v->value, "symmetric"))
04479 peer->pcmodel = DUNDI_MODEL_SYMMETRIC;
04480 else if (!strcasecmp(v->value, "none"))
04481 peer->pcmodel = 0;
04482 else {
04483 ast_log(LOG_WARNING, "Unknown pcmodel '%s', should be 'none', 'outbound', 'inbound', or 'symmetric' at line %d\n",
04484 v->value, v->lineno);
04485 }
04486 }
04487 }
04488 (*globalpcmode) |= peer->pcmodel;
04489 if (!peer->model && !peer->pcmodel) {
04490 ast_log(LOG_WARNING, "Peer '%s' lacks a model or pcmodel, discarding!\n",
04491 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04492 peer->dead = 1;
04493 } else if ((peer->model & DUNDI_MODEL_INBOUND) && (peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
04494 ast_log(LOG_WARNING, "Peer '%s' may not be both inbound/symmetric model and outbound/symmetric precache model, discarding!\n",
04495 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04496 peer->dead = 1;
04497 } else if ((peer->model & DUNDI_MODEL_OUTBOUND) && (peer->pcmodel & DUNDI_MODEL_INBOUND)) {
04498 ast_log(LOG_WARNING, "Peer '%s' may not be both outbound/symmetric model and inbound/symmetric precache model, discarding!\n",
04499 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04500 peer->dead = 1;
04501 } else if (!AST_LIST_EMPTY(&peer->include) && !(peer->model & DUNDI_MODEL_OUTBOUND) && !(peer->pcmodel & DUNDI_MODEL_INBOUND)) {
04502 ast_log(LOG_WARNING, "Peer '%s' is supposed to be included in outbound searches but isn't an outbound peer or inbound precache!\n",
04503 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04504 } else if (!AST_LIST_EMPTY(&peer->permit) && !(peer->model & DUNDI_MODEL_INBOUND) && !(peer->pcmodel & DUNDI_MODEL_OUTBOUND)) {
04505 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",
04506 ast_eid_to_str(eid_str, sizeof(eid_str), &peer->eid));
04507 } else {
04508 if (needregister) {
04509 peer->registerid = ast_sched_add(sched, 2000, do_register, peer);
04510 }
04511 qualify_peer(peer, 1);
04512 }
04513 AST_LIST_UNLOCK(&peers);
04514 }
04515
04516 static int dundi_helper(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *data, int flag)
04517 {
04518 struct dundi_result results[MAX_RESULTS];
04519 int res;
04520 int x;
04521 int found = 0;
04522 if (!strncasecmp(context, "macro-", 6)) {
04523 if (!chan) {
04524 ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
04525 return -1;
04526 }
04527
04528 if (!strcasecmp(exten, "s")) {
04529 exten = pbx_builtin_getvar_helper(chan, "ARG1");
04530 if (ast_strlen_zero(exten))
04531 exten = chan->macroexten;
04532 if (ast_strlen_zero(exten))
04533 exten = chan->exten;
04534 if (ast_strlen_zero(exten)) {
04535 ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
04536 return -1;
04537 }
04538 }
04539 if (ast_strlen_zero(data))
04540 data = "e164";
04541 } else {
04542 if (ast_strlen_zero(data))
04543 data = context;
04544 }
04545 res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
04546 for (x=0;x<res;x++) {
04547 if (ast_test_flag(results + x, flag))
04548 found++;
04549 }
04550 if (found >= priority)
04551 return 1;
04552 return 0;
04553 }
04554
04555 static int dundi_exists(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
04556 {
04557 return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_EXISTS);
04558 }
04559
04560 static int dundi_canmatch(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
04561 {
04562 return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_CANMATCH);
04563 }
04564
04565 static int dundi_exec(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
04566 {
04567 struct dundi_result results[MAX_RESULTS];
04568 int res;
04569 int x=0;
04570 char req[1024];
04571 const char *dundiargs;
04572 struct ast_app *dial;
04573
04574 if (!strncasecmp(context, "macro-", 6)) {
04575 if (!chan) {
04576 ast_log(LOG_NOTICE, "Can't use macro mode without a channel!\n");
04577 return -1;
04578 }
04579
04580 if (!strcasecmp(exten, "s")) {
04581 exten = pbx_builtin_getvar_helper(chan, "ARG1");
04582 if (ast_strlen_zero(exten))
04583 exten = chan->macroexten;
04584 if (ast_strlen_zero(exten))
04585 exten = chan->exten;
04586 if (ast_strlen_zero(exten)) {
04587 ast_log(LOG_WARNING, "Called in Macro mode with no ARG1 or MACRO_EXTEN?\n");
04588 return -1;
04589 }
04590 }
04591 if (ast_strlen_zero(data))
04592 data = "e164";
04593 } else {
04594 if (ast_strlen_zero(data))
04595 data = context;
04596 }
04597 res = dundi_lookup(results, MAX_RESULTS, chan, data, exten, 0);
04598 if (res > 0) {
04599 sort_results(results, res);
04600 for (x=0;x<res;x++) {
04601 if (ast_test_flag(results + x, DUNDI_FLAG_EXISTS)) {
04602 if (!--priority)
04603 break;
04604 }
04605 }
04606 }
04607 if (x < res) {
04608
04609 dundiargs = pbx_builtin_getvar_helper(chan, "DUNDIDIALARGS");
04610 snprintf(req, sizeof(req), "%s/%s,,%s", results[x].tech, results[x].dest,
04611 S_OR(dundiargs, ""));
04612 dial = pbx_findapp("Dial");
04613 if (dial)
04614 res = pbx_exec(chan, dial, req);
04615 } else
04616 res = -1;
04617 return res;
04618 }
04619
04620 static int dundi_matchmore(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *callerid, const char *data)
04621 {
04622 return dundi_helper(chan, context, exten, priority, data, DUNDI_FLAG_MATCHMORE);
04623 }
04624
04625 static struct ast_switch dundi_switch =
04626 {
04627 name: "DUNDi",
04628 description: "DUNDi Discovered Dialplan Switch",
04629 exists: dundi_exists,
04630 canmatch: dundi_canmatch,
04631 exec: dundi_exec,
04632 matchmore: dundi_matchmore,
04633 };
04634
04635 static int set_config(char *config_file, struct sockaddr_in* sin, int reload)
04636 {
04637 struct ast_config *cfg;
04638 struct ast_variable *v;
04639 char *cat;
04640 int x;
04641 struct ast_flags config_flags = { 0 };
04642 char hn[MAXHOSTNAMELEN] = "";
04643 struct ast_hostent he;
04644 struct hostent *hp;
04645 struct sockaddr_in sin2;
04646 static int last_port = 0;
04647 int globalpcmodel = 0;
04648 dundi_eid testeid;
04649
04650 if (!(cfg = ast_config_load(config_file, config_flags))) {
04651 ast_log(LOG_ERROR, "Unable to load config %s\n", config_file);
04652 return -1;
04653 }
04654
04655 dundi_ttl = DUNDI_DEFAULT_TTL;
04656 dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME;
04657 any_peer = NULL;
04658
04659 ipaddr[0] = '\0';
04660 if (!gethostname(hn, sizeof(hn)-1)) {
04661 hp = ast_gethostbyname(hn, &he);
04662 if (hp) {
04663 memcpy(&sin2.sin_addr, hp->h_addr, sizeof(sin2.sin_addr));
04664 ast_copy_string(ipaddr, ast_inet_ntoa(sin2.sin_addr), sizeof(ipaddr));
04665 } else
04666 ast_log(LOG_WARNING, "Unable to look up host '%s'\n", hn);
04667 } else
04668 ast_log(LOG_WARNING, "Unable to get host name!\n");
04669 AST_LIST_LOCK(&peers);
04670
04671 memcpy(&global_eid, &ast_eid_default, sizeof(global_eid));
04672
04673 global_storehistory = 0;
04674 ast_copy_string(secretpath, "dundi", sizeof(secretpath));
04675 v = ast_variable_browse(cfg, "general");
04676 while(v) {
04677 if (!strcasecmp(v->name, "port")){
04678 sin->sin_port = ntohs(atoi(v->value));
04679 if(last_port==0){
04680 last_port=sin->sin_port;
04681 } else if(sin->sin_port != last_port)
04682 ast_log(LOG_WARNING, "change to port ignored until next asterisk re-start\n");
04683 } else if (!strcasecmp(v->name, "bindaddr")) {
04684 struct hostent *hep;
04685 struct ast_hostent hent;
04686 hep = ast_gethostbyname(v->value, &hent);
04687 if (hep) {
04688 memcpy(&sin->sin_addr, hep->h_addr, sizeof(sin->sin_addr));
04689 } else
04690 ast_log(LOG_WARNING, "Invalid host/IP '%s'\n", v->value);
04691 } else if (!strcasecmp(v->name, "authdebug")) {
04692 authdebug = ast_true(v->value);
04693 } else if (!strcasecmp(v->name, "ttl")) {
04694 if ((sscanf(v->value, "%30d", &x) == 1) && (x > 0) && (x < DUNDI_DEFAULT_TTL)) {
04695 dundi_ttl = x;
04696 } else {
04697 ast_log(LOG_WARNING, "'%s' is not a valid TTL at line %d, must be number from 1 to %d\n",
04698 v->value, v->lineno, DUNDI_DEFAULT_TTL);
04699 }
04700 } else if (!strcasecmp(v->name, "autokill")) {
04701 if (sscanf(v->value, "%30d", &x) == 1) {
04702 if (x >= 0)
04703 global_autokilltimeout = x;
04704 else
04705 ast_log(LOG_NOTICE, "Nice try, but autokill has to be >0 or 'yes' or 'no' at line %d\n", v->lineno);
04706 } else if (ast_true(v->value)) {
04707 global_autokilltimeout = DEFAULT_MAXMS;
04708 } else {
04709 global_autokilltimeout = 0;
04710 }
04711 } else if (!strcasecmp(v->name, "entityid")) {
04712 if (!ast_str_to_eid(&testeid, v->value))
04713 global_eid = testeid;
04714 else
04715 ast_log(LOG_WARNING, "Invalid global endpoint identifier '%s' at line %d\n", v->value, v->lineno);
04716 } else if (!strcasecmp(v->name, "tos")) {
04717 if (ast_str2tos(v->value, &tos))
04718 ast_log(LOG_WARNING, "Invalid tos value at line %d, refer to QoS documentation\n", v->lineno);
04719 } else if (!strcasecmp(v->name, "department")) {
04720 ast_copy_string(dept, v->value, sizeof(dept));
04721 } else if (!strcasecmp(v->name, "organization")) {
04722 ast_copy_string(org, v->value, sizeof(org));
04723 } else if (!strcasecmp(v->name, "locality")) {
04724 ast_copy_string(locality, v->value, sizeof(locality));
04725 } else if (!strcasecmp(v->name, "stateprov")) {
04726 ast_copy_string(stateprov, v->value, sizeof(stateprov));
04727 } else if (!strcasecmp(v->name, "country")) {
04728 ast_copy_string(country, v->value, sizeof(country));
04729 } else if (!strcasecmp(v->name, "email")) {
04730 ast_copy_string(email, v->value, sizeof(email));
04731 } else if (!strcasecmp(v->name, "phone")) {
04732 ast_copy_string(phone, v->value, sizeof(phone));
04733 } else if (!strcasecmp(v->name, "storehistory")) {
04734 global_storehistory = ast_true(v->value);
04735 } else if (!strcasecmp(v->name, "cachetime")) {
04736 if ((sscanf(v->value, "%30d", &x) == 1)) {
04737 dundi_cache_time = x;
04738 } else {
04739 ast_log(LOG_WARNING, "'%s' is not a valid cache time at line %d. Using default value '%d'.\n",
04740 v->value, v->lineno, DUNDI_DEFAULT_CACHE_TIME);
04741 }
04742 }
04743 v = v->next;
04744 }
04745 AST_LIST_UNLOCK(&peers);
04746 mark_mappings();
04747 v = ast_variable_browse(cfg, "mappings");
04748 while(v) {
04749 build_mapping(v->name, v->value);
04750 v = v->next;
04751 }
04752 prune_mappings();
04753 mark_peers();
04754 cat = ast_category_browse(cfg, NULL);
04755 while(cat) {
04756 if (strcasecmp(cat, "general") && strcasecmp(cat, "mappings")) {
04757
04758 if (!ast_str_to_eid(&testeid, cat))
04759 build_peer(&testeid, ast_variable_browse(cfg, cat), &globalpcmodel);
04760 else if (!strcasecmp(cat, "*")) {
04761 build_peer(&empty_eid, ast_variable_browse(cfg, cat), &globalpcmodel);
04762 any_peer = find_peer(NULL);
04763 } else
04764 ast_log(LOG_NOTICE, "Ignoring invalid EID entry '%s'\n", cat);
04765 }
04766 cat = ast_category_browse(cfg, cat);
04767 }
04768 prune_peers();
04769 ast_config_destroy(cfg);
04770 load_password();
04771 if (globalpcmodel & DUNDI_MODEL_OUTBOUND)
04772 dundi_precache_full();
04773 return 0;
04774 }
04775
04776 static int unload_module(void)
04777 {
04778 pthread_t previous_netthreadid = netthreadid, previous_precachethreadid = precachethreadid, previous_clearcachethreadid = clearcachethreadid;
04779 ast_module_user_hangup_all();
04780
04781
04782 dundi_shutdown = 1;
04783 if (previous_netthreadid != AST_PTHREADT_NULL) {
04784 pthread_kill(previous_netthreadid, SIGURG);
04785 pthread_join(previous_netthreadid, NULL);
04786 }
04787 if (previous_precachethreadid != AST_PTHREADT_NULL) {
04788 pthread_kill(previous_precachethreadid, SIGURG);
04789 pthread_join(previous_precachethreadid, NULL);
04790 }
04791 if (previous_clearcachethreadid != AST_PTHREADT_NULL) {
04792 pthread_cancel(previous_clearcachethreadid);
04793 pthread_join(previous_clearcachethreadid, NULL);
04794 }
04795
04796 ast_cli_unregister_multiple(cli_dundi, sizeof(cli_dundi) / sizeof(struct ast_cli_entry));
04797 ast_unregister_switch(&dundi_switch);
04798 ast_custom_function_unregister(&dundi_function);
04799 ast_custom_function_unregister(&dundi_query_function);
04800 ast_custom_function_unregister(&dundi_result_function);
04801 close(netsocket);
04802 io_context_destroy(io);
04803 sched_context_destroy(sched);
04804
04805 mark_mappings();
04806 prune_mappings();
04807 mark_peers();
04808 prune_peers();
04809
04810 return 0;
04811 }
04812
04813 static int reload(void)
04814 {
04815 struct sockaddr_in sin;
04816
04817 if (set_config("dundi.conf", &sin, 1))
04818 return AST_MODULE_LOAD_FAILURE;
04819
04820 return AST_MODULE_LOAD_SUCCESS;
04821 }
04822
04823 static int load_module(void)
04824 {
04825 struct sockaddr_in sin;
04826
04827 dundi_set_output(dundi_debug_output);
04828 dundi_set_error(dundi_error_output);
04829
04830 sin.sin_family = AF_INET;
04831 sin.sin_port = ntohs(DUNDI_PORT);
04832 sin.sin_addr.s_addr = INADDR_ANY;
04833
04834
04835 io = io_context_create();
04836 sched = sched_context_create();
04837
04838 if (!io || !sched)
04839 return AST_MODULE_LOAD_FAILURE;
04840
04841 if (set_config("dundi.conf", &sin, 0))
04842 return AST_MODULE_LOAD_DECLINE;
04843
04844 netsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
04845
04846 if (netsocket < 0) {
04847 ast_log(LOG_ERROR, "Unable to create network socket: %s\n", strerror(errno));
04848 return AST_MODULE_LOAD_FAILURE;
04849 }
04850 if (bind(netsocket, (struct sockaddr *) &sin, sizeof(sin))) {
04851 ast_log(LOG_ERROR, "Unable to bind to %s port %d: %s\n",
04852 ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), strerror(errno));
04853 return AST_MODULE_LOAD_FAILURE;
04854 }
04855
04856 ast_netsock_set_qos(netsocket, tos, 0, "DUNDi");
04857
04858 if (start_network_thread()) {
04859 ast_log(LOG_ERROR, "Unable to start network thread\n");
04860 close(netsocket);
04861 return AST_MODULE_LOAD_FAILURE;
04862 }
04863
04864 ast_cli_register_multiple(cli_dundi, sizeof(cli_dundi) / sizeof(*cli_dundi));
04865 if (ast_register_switch(&dundi_switch))
04866 ast_log(LOG_ERROR, "Unable to register DUNDi switch\n");
04867 ast_custom_function_register(&dundi_function);
04868 ast_custom_function_register(&dundi_query_function);
04869 ast_custom_function_register(&dundi_result_function);
04870
04871 ast_verb(2, "DUNDi Ready and Listening on %s port %d\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
04872
04873 return AST_MODULE_LOAD_SUCCESS;
04874 }
04875
04876 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Distributed Universal Number Discovery (DUNDi)",
04877 .load = load_module,
04878 .unload = unload_module,
04879 .reload = reload,
04880 );
04881