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