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