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