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