![]() |
Home page |
Mailing list |
Docs
Asterisk developer's documentation :: Codename Pineapple
pbx_dundi.c
Go to the documentation of this file.
00001 /* 00002 * Asterisk -- An open source telephony toolkit. 00003 * 00004 * Copyright (C) 1999 - 2006, Digium, Inc. 00005 * 00006 * Mark Spencer <markster@digium.com> 00007 * 00008 * See http://www.asterisk.org for more information about 00009 * the Asterisk project. Please do not directly contact 00010 * any of the maintainers of this project for assistance; 00011 * the project provides a web site, mailing lists and IRC 00012 * channels for your use. 00013 * 00014 * This program is free software, distributed under the terms of 00015 * the GNU General Public License Version 2. See the LICENSE file 00016 * at the top of the source tree. 00017 */ 00018 00019 /*! \file 00020 * 00021 * \brief Distributed Universal Number Discovery (DUNDi) 00022 * 00023 */ 00024 00025 /*** MODULEINFO 00026 <depend>zlib</depend> 00027 ***/ 00028 00029 #include "asterisk.h" 00030 00031 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 49726 $") 00032 00033 #include <stdlib.h> 00034 #include <stdio.h> 00035 #include <unistd.h> 00036 #include <netinet/in.h> 00037 #include <arpa/inet.h> 00038 #include <sys/socket.h> 00039 #include <string.h> 00040 #include <errno.h> 00041 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(SOLARIS) || defined(__Darwin__) 00042 #include <sys/types.h> 00043 #include <netinet/in_systm.h> 00044 #endif 00045 #include <netinet/ip.h> 00046 #include <sys/ioctl.h> 00047 #include <netinet/in.h> 00048 #include <net/if.h> 00049 #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__Darwin__) 00050 #include <net/if_dl.h> 00051 #include <ifaddrs.h> 00052 #endif 00053 #include <zlib.h> 00054 00055 #include "asterisk/file.h" 00056 #include "asterisk/logger.h" 00057 #include "asterisk/channel.h" 00058 #include "asterisk/config.h" 00059 #include "asterisk/options.h" 00060 #include "asterisk/pbx.h" 00061 #include "asterisk/module.h" 00062 #include "asterisk/frame.h" 00063 #include "asterisk/file.h" 00064 #include "asterisk/cli.h" 00065 #include "asterisk/lock.h" 00066 #include "asterisk/md5.h" 00067 #include "asterisk/dundi.h" 00068 #include "asterisk/sched.h" 00069 #include "asterisk/io.h" 00070 #include "asterisk/utils.h" 00071 #include "asterisk/crypto.h" 00072 #include "asterisk/astdb.h" 00073 #include "asterisk/acl.h" 00074 #include "asterisk/aes.h" 00075 00076 #include "dundi-parser.h" 00077 00078 #define MAX_RESULTS 64 00079 00080 #define MAX_PACKET_SIZE 8192 00081 00082 #define DUNDI_MODEL_INBOUND (1 << 0) 00083 #define DUNDI_MODEL_OUTBOUND (1 << 1) 00084 #define DUNDI_MODEL_SYMMETRIC (DUNDI_MODEL_INBOUND | DUNDI_MODEL_OUTBOUND) 00085 00086 /*! Keep times of last 10 lookups */ 00087 #define DUNDI_TIMING_HISTORY 10 00088 00089 enum { 00090 FLAG_ISREG = (1 << 0), /*!< Transaction is register request */ 00091 FLAG_DEAD = (1 << 1), /*!< Transaction is dead */ 00092 FLAG_FINAL = (1 << 2), /*!< Transaction has final message sent */ 00093 FLAG_ISQUAL = (1 << 3), /*!< Transaction is a qualification */ 00094 FLAG_ENCRYPT = (1 << 4), /*!< Transaction is encrypted wiht ECX/DCX */ 00095 FLAG_SENDFULLKEY = (1 << 5), /*!< Send full key on transaction */ 00096 FLAG_STOREHIST = (1 << 6), /*!< Record historic performance */ 00097 }; 00098 00099 #define DUNDI_FLAG_INTERNAL_NOPARTIAL (1 << 17) 00100 00101 #if 0 00102 #define DUNDI_SECRET_TIME 15 /* Testing only */ 00103 #else 00104 #define DUNDI_SECRET_TIME DUNDI_DEFAULT_CACHE_TIME 00105 #endif 00106 00107 static struct io_context *io; 00108 static struct sched_context *sched; 00109 static int netsocket = -1; 00110 static pthread_t netthreadid = AST_PTHREADT_NULL; 00111 static pthread_t precachethreadid = AST_PTHREADT_NULL; 00112 static int tos = 0; 00113 static int dundidebug = 0; 00114 static int authdebug = 0; 00115 static int dundi_ttl = DUNDI_DEFAULT_TTL; 00116 static int dundi_key_ttl = DUNDI_DEFAULT_KEY_EXPIRE; 00117 static int dundi_cache_time = DUNDI_DEFAULT_CACHE_TIME; 00118 static int global_autokilltimeout = 0; 00119 static dundi_eid global_eid; 00120 static int default_expiration = 60; 00121 static int global_storehistory = 0; 00122 static char dept[80]; 00123 static char org[80]; 00124 static char locality[80]; 00125 static char stateprov[80]; 00126 static char country[80]; 00127 static char email[80]; 00128 static char phone[80]; 00129 static char secretpath[80]; 00130 static char cursecret[80]; 00131 static char ipaddr[80]; 00132 static time_t rotatetime; 00133 static dundi_eid empty_eid = { { 0, 0, 0, 0, 0, 0 } }; 00134 00135 struct permission { 00136 AST_LIST_ENTRY(permission) list; 00137 int allow; 00138 char name[0]; 00139 }; 00140 00141 struct dundi_packet { 00142 AST_LIST_ENTRY(dundi_packet) list; 00143 struct dundi_hdr *h; 00144 int datalen; 00145 struct dundi_transaction *parent; 00146 int retransid; 00147 int retrans; 00148 unsigned char data[0]; 00149 }; 00150 00151 struct dundi_hint_metadata { 00152 unsigned short flags; 00153 char exten[AST_MAX_EXTENSION]; 00154 }; 00155 00156 struct dundi_precache_queue { 00157 AST_LIST_ENTRY(dundi_precache_queue) list; 00158 char *context; 00159 time_t expiration; 00160 char number[0]; 00161 }; 00162 00163 struct dundi_request; 00164 00165 struct dundi_transaction { 00166 struct sockaddr_in addr; /*!< Other end of transaction */ 00167 struct timeval start; /*!< When this transaction was created */ 00168 dundi_eid eids[DUNDI_MAX_STACK + 1]; 00169 int eidcount; /*!< Number of eids in eids */ 00170 dundi_eid us_eid; /*!< Our EID, to them */ 00171 dundi_eid them_eid; /*!< Their EID, to us */ 00172 aes_encrypt_ctx ecx; /*!< AES 128 Encryption context */ 00173 aes_decrypt_ctx dcx; /*!< AES 128 Decryption context */ 00174 unsigned int flags; /*!< Has final packet been sent */ 00175 int ttl; /*!< Remaining TTL for queries on this one */ 00176 int thread; /*!< We have a calling thread */ 00177 int retranstimer; /*!< How long to wait before retransmissions */ 00178 int autokillid; /*!< ID to kill connection if answer doesn't come back fast enough */ 00179 int autokilltimeout; /*!< Recommended timeout for autokill */ 00180 unsigned short strans; /*!< Our transaction identifier */ 00181 unsigned short dtrans; /*!< Their transaction identifer */ 00182 unsigned char iseqno; /*!< Next expected received seqno */ 00183 unsigned char oiseqno; /*!< Last received incoming seqno */ 00184 unsigned char oseqno; /*!< Next transmitted seqno */ 00185 unsigned char aseqno; /*!< Last acknowledge seqno */ 00186 AST_LIST_HEAD_NOLOCK(packetlist, dundi_packet) packets; /*!< Packets to be retransmitted */ 00187 struct packetlist lasttrans; /*!< Last transmitted / ACK'd packet */ 00188 struct dundi_request *parent; /*!< Parent request (if there is one) */ 00189 AST_LIST_ENTRY(dundi_transaction) parentlist; /*!< Next with respect to the parent */ 00190 AST_LIST_ENTRY(dundi_transaction) all; /*!< Next with respect to all DUNDi transactions */ 00191 }; 00192 00193 struct dundi_request { 00194 char dcontext[AST_MAX_EXTENSION]; 00195 char number[AST_MAX_EXTENSION]; 00196 dundi_eid query_eid; 00197 dundi_eid root_eid; 00198 struct dundi_result *dr; 00199 struct dundi_entity_info *dei; 00200 struct dundi_hint_metadata *hmd; 00201 int maxcount; 00202 int respcount; 00203 int expiration; 00204 int cbypass; 00205 int pfds[2]; 00206 unsigned long crc32; /*!< CRC-32 of all but root EID's in avoid list */ 00207 AST_LIST_HEAD_NOLOCK(, dundi_transaction) trans; /*!< Transactions */ 00208 AST_LIST_ENTRY(dundi_request) list; 00209 }; 00210 00211 struct dundi_mapping { 00212 char dcontext[AST_MAX_EXTENSION]; 00213 char lcontext[AST_MAX_EXTENSION]; 00214 int weight; 00215 int options; 00216 int tech; 00217 int dead; 00218 char dest[AST_MAX_EXTENSION]; 00219 AST_LIST_ENTRY(dundi_mapping) list; 00220 }; 00221 00222 struct dundi_peer { 00223 dundi_eid eid; 00224 struct sockaddr_in addr; /*!< Address of DUNDi peer */ 00225 AST_LIST_HEAD_NOLOCK(permissionlist, permission) permit; 00226 struct permissionlist include; 00227 dundi_eid us_eid; 00228 char inkey[80]; 00229 char outkey[80]; 00230 int dead; 00231 int registerid; 00232 int qualifyid; 00233 int sentfullkey; 00234 int order; 00235 unsigned char txenckey[256]; /*!< Transmitted encrypted key + sig */ 00236 unsigned char rxenckey[256]; /*!< Cache received encrypted key + sig */ 00237 unsigned long us_keycrc32; /*!< CRC-32 of our key */ 00238 aes_encrypt_ctx us_ecx; /*!< Cached AES 128 Encryption context */ 00239 aes_decrypt_ctx us_dcx; /*!< Cached AES 128 Decryption context */ 00240 unsigned long them_keycrc32; /*!< CRC-32 of our key */ 00241 aes_encrypt_ctx them_ecx; /*!< Cached AES 128 Encryption context */ 00242 aes_decrypt_ctx them_dcx; /*!< Cached AES 128 Decryption context */ 00243 time_t keyexpire; /*!< When to expire/recreate key */ 00244 int registerexpire; 00245 int lookuptimes[DUNDI_TIMING_HISTORY]; 00246 char *lookups[DUNDI_TIMING_HISTORY]; 00247 int avgms; 00248 struct dundi_transaction *regtrans; /*!< Registration transaction */ 00249 struct dundi_transaction *qualtrans; /*!< Qualify transaction */ 00250 int model; /*!< Pull model */ 00251 int pcmodel; /*!< Push/precache model */ 00252 int dynamic; /*!< Are we dynamic? */ 00253 int lastms; /*!< Last measured latency */ 00254 int maxms; /*!< Max permissible latency */ 00255 struct timeval qualtx; /*!< Time of transmit */ 00256 AST_LIST_ENTRY(dundi_peer) list; 00257 }; 00258 00259 static AST_LIST_HEAD_STATIC(peers, dundi_peer); 00260 static AST_LIST_HEAD_STATIC(pcq, dundi_precache_queue); 00261 static AST_LIST_HEAD_NOLOCK_STATIC(mappings, dundi_mapping); 00262 static AST_LIST_HEAD_NOLOCK_STATIC(requests, dundi_request); 00263 static AST_LIST_HEAD_NOLOCK_STATIC(alltrans, dundi_transaction); 00264 00265 static int dundi_xmit(struct dundi_packet *pack); 00266 00267 static void dundi_debug_output(const char *data) 00268 { 00269 if (dundidebug) 00270 ast_verbose("%s", data); 00271 } 00272 00273 static void dundi_error_output(const char *data) 00274 { 00275 ast_log(LOG_WARNING, "%s", data); 00276 } 00277 00278 static int has_permission(struct permissionlist *permlist, char *cont) 00279 { 00280 struct permission *perm; 00281 int res = 0; 00282 00283 AST_LIST_TRAVERSE(permlist, perm, list) { 00284 if (!strcasecmp(perm->name, "all") || !strcasecmp(perm->name, cont)) 00285 res = perm->allow; 00286 } 00287 00288 return res; 00289 } 00290 00291 static char *tech2str(int tech) 00292 { 00293 switch(tech) { 00294 case DUNDI_PROTO_NONE: 00295 return "None"; 00296 case DUNDI_PROTO_IAX: 00297 return "IAX2"; 00298 case DUNDI_PROTO_SIP: 00299 return "SIP"; 00300 case DUNDI_PROTO_H323: 00301 return "H323"; 00302 default: 00303 return "Unknown"; 00304 } 00305 } 00306 00307 static int str2tech(char *str) 00308 { 00309 if (!strcasecmp(str, "IAX") || !strcasecmp(str, "IAX2")) 00310 return DUNDI_PROTO_IAX; 00311 else if (!strcasecmp(str, "SIP")) 00312 return DUNDI_PROTO_SIP; 00313 else if (!strcasecmp(str, "H323")) 00314 return DUNDI_PROTO_H323; 00315 else 00316 return -1; 00317 } 00318 00319 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[]); 00320 static int dundi_precache_internal(const char *context, const char *number, int ttl, dundi_eid *avoids[]); 00321 static struct dundi_transaction *create_transaction(struct dundi_peer *p); 00322 static struct dundi_transaction *find_transaction(struct dundi_hdr *hdr, struct sockaddr_in *sin) 00323 { 00324 struct dundi_transaction *trans; 00325 00326 /* Look for an exact match first */ 00327 AST_LIST_TRAVERSE(&alltrans, trans, all) { 00328 if (!inaddrcmp(&trans->addr, sin) && 00329 ((trans->strans == (ntohs(hdr->dtrans) & 32767)) /* Matches our destination */ || 00330 ((trans->dtrans == (ntohs(hdr->strans) & 32767)) && (!hdr->dtrans))) /* We match their destination */) { 00331 if (hdr->strans) 00332 trans->dtrans = ntohs(hdr->strans) & 32767; 00333 return trans; 00334 } 00335 } 00336 00337 switch(hdr->cmdresp & 0x7f) { 00338 case DUNDI_COMMAND_DPDISCOVER: 00339 case DUNDI_COMMAND_EIDQUERY: 00340 case DUNDI_COMMAND_PRECACHERQ: 00341 case DUNDI_COMMAND_REGREQ: 00342 case DUNDI_COMMAND_NULL: 00343 case DUNDI_COMMAND_ENCRYPT: 00344 if (!hdr->strans) 00345 break; 00346 /* Create new transaction */ 00347 if (!(trans = create_transaction(NULL))) 00348 break; 00349 memcpy(&trans->addr, sin, sizeof(trans->addr)); 00350 trans->dtrans = ntohs(hdr->strans) & 32767; 00351 default: 00352 break; 00353 } 00354 00355 return trans; 00356 } 00357 00358 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied); 00359 00360 static int dundi_ack(struct dundi_transaction *trans, int final) 00361 { 00362 return dundi_send(trans, DUNDI_COMMAND_ACK, 0, final, NULL); 00363 } 00364 static void dundi_reject(struct dundi_hdr *h, struct sockaddr_in *sin) 00365 { 00366 struct { 00367 struct dundi_packet pack; 00368 struct dundi_hdr hdr; 00369 } tmp; 00370 struct dundi_transaction trans; 00371 /* Never respond to an INVALID with another INVALID */ 00372 if (h->cmdresp == DUNDI_COMMAND_INVALID) 00373 return; 00374 memset(&tmp, 0, sizeof(tmp)); 00375 memset(&trans, 0, sizeof(trans)); 00376 memcpy(&trans.addr, sin, sizeof(trans.addr)); 00377 tmp.hdr.strans = h->dtrans; 00378 tmp.hdr.dtrans = h->strans; 00379 tmp.hdr.iseqno = h->oseqno; 00380 tmp.hdr.oseqno = h->iseqno; 00381 tmp.hdr.cmdresp = DUNDI_COMMAND_INVALID; 00382 tmp.hdr.cmdflags = 0; 00383 tmp.pack.h = (struct dundi_hdr *)tmp.pack.data; 00384 tmp.pack.datalen = sizeof(struct dundi_hdr); 00385 tmp.pack.parent = &trans; 00386 dundi_xmit(&tmp.pack); 00387 } 00388 00389 static void reset_global_eid(void) 00390 { 00391 #if defined(SIOCGIFHWADDR) 00392 int s, x = 0; 00393 char eid_str[20]; 00394 struct ifreq ifr; 00395 00396 s = socket(AF_INET, SOCK_STREAM, 0); 00397 if (s < 0) 00398 return; 00399 for (x = 0; x < 10; x++) { 00400 memset(&ifr, 0, sizeof(ifr)); 00401 snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "eth%d", x); 00402 if (ioctl(s, SIOCGIFHWADDR, &ifr)) 00403 continue; 00404 memcpy(&global_eid, ((unsigned char *)&ifr.ifr_hwaddr) + 2, sizeof(global_eid)); 00405 if (option_debug) { 00406 ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s'\n", 00407 dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifr.ifr_name); 00408 } 00409 break; 00410 } 00411 close(s); 00412 #else 00413 #if defined(ifa_broadaddr) && !defined(SOLARIS) 00414 char eid_str[20]; 00415 struct ifaddrs *ifap; 00416 00417 if (getifaddrs(&ifap) == 0) { 00418 struct ifaddrs *p; 00419 for (p = ifap; p; p = p->ifa_next) { 00420 if (p->ifa_addr->sa_family == AF_LINK) { 00421 struct sockaddr_dl* sdp = (struct sockaddr_dl*) p->ifa_addr; 00422 memcpy( 00423 &(global_eid.eid), 00424 sdp->sdl_data + sdp->sdl_nlen, 6); 00425 if (option_debug) 00426 ast_log(LOG_DEBUG, "Seeding global EID '%s' from '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid), ifap->ifa_name); 00427 freeifaddrs(ifap); 00428 return; 00429 } 00430 } 00431 freeifaddrs(ifap); 00432 } 00433 #endif 00434 #endif 00435 ast_log(LOG_NOTICE, "No ethernet interface found for seeding global EID You will have to set it manually.\n"); 00436 } 00437 00438 static int get_trans_id(void) 00439 { 00440 struct dundi_transaction *t; 00441 int stid = (ast_random() % 32766) + 1; 00442 int tid = stid; 00443 00444 do { 00445 AST_LIST_TRAVERSE(&alltrans, t, all) { 00446 if (t->strans == tid) 00447 break; 00448 } 00449 if (!t) 00450 return tid; 00451 tid = (tid % 32766) + 1; 00452 } while (tid != stid); 00453 00454 return 0; 00455 } 00456 00457 static int reset_transaction(struct dundi_transaction *trans) 00458 { 00459 int tid; 00460 tid = get_trans_id(); 00461 if (tid < 1) 00462 return -1; 00463 trans->strans = tid; 00464 trans->dtrans = 0; 00465 trans->iseqno = 0; 00466 trans->oiseqno = 0; 00467 trans->oseqno = 0; 00468 trans->aseqno = 0; 00469 ast_clear_flag(trans, FLAG_FINAL); 00470 return 0; 00471 } 00472 00473 static struct dundi_peer *find_peer(dundi_eid *eid) 00474 { 00475 struct dundi_peer *cur = NULL; 00476 00477 if (!eid) 00478 eid = &empty_eid; 00479 00480 AST_LIST_TRAVERSE(&peers, cur, list) { 00481 if (!dundi_eid_cmp(&cur->eid,eid)) 00482 break; 00483 } 00484 00485 return cur; 00486 } 00487 00488 static void build_iv(unsigned char *iv) 00489 { 00490 /* XXX Would be nice to be more random XXX */ 00491 unsigned int *fluffy; 00492 int x; 00493 fluffy = (unsigned int *)(iv); 00494 for (x=0;x<4;x++) 00495 fluffy[x] = ast_random(); 00496 } 00497 00498 struct dundi_query_state { 00499 dundi_eid *eids[DUNDI_MAX_STACK + 1]; 00500 int directs[DUNDI_MAX_STACK + 1]; 00501 dundi_eid reqeid; 00502 char called_context[AST_MAX_EXTENSION]; 00503 char called_number[AST_MAX_EXTENSION]; 00504 struct dundi_mapping *maps; 00505 int nummaps; 00506 int nocache; 00507 struct dundi_transaction *trans; 00508 void *chal; 00509 int challen; 00510 int ttl; 00511 char fluffy[0]; 00512 }; 00513 00514 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) 00515 { 00516 struct ast_flags flags = {0}; 00517 int x; 00518 if (!ast_strlen_zero(map->lcontext)) { 00519 if (ast_exists_extension(NULL, map->lcontext, called_number, 1, NULL)) 00520 ast_set_flag(&flags, DUNDI_FLAG_EXISTS); 00521 if (ast_canmatch_extension(NULL, map->lcontext, called_number, 1, NULL)) 00522 ast_set_flag(&flags, DUNDI_FLAG_CANMATCH); 00523 if (ast_matchmore_extension(NULL, map->lcontext, called_number, 1, NULL)) 00524 ast_set_flag(&flags, DUNDI_FLAG_MATCHMORE); 00525 if (ast_ignore_pattern(map->lcontext, called_number)) 00526 ast_set_flag(&flags, DUNDI_FLAG_IGNOREPAT); 00527 00528 /* Clearly we can't say 'don't ask' anymore if we found anything... */ 00529 if (ast_test_flag(&flags, AST_FLAGS_ALL)) 00530 ast_clear_flag_nonstd(hmd, DUNDI_HINT_DONT_ASK); 00531 00532 if (map->options & DUNDI_FLAG_INTERNAL_NOPARTIAL) { 00533 /* Skip partial answers */ 00534 ast_clear_flag(&flags, DUNDI_FLAG_MATCHMORE|DUNDI_FLAG_CANMATCH); 00535 } 00536 if (ast_test_flag(&flags, AST_FLAGS_ALL)) { 00537 struct varshead headp; 00538 struct ast_var_t *newvariable; 00539 ast_set_flag(&flags, map->options & 0xffff); 00540 ast_copy_flags(dr + anscnt, &flags, AST_FLAGS_ALL); 00541 dr[anscnt].techint = map->tech; 00542 dr[anscnt].weight = map->weight; 00543 dr[anscnt].expiration = dundi_cache_time; 00544 ast_copy_string(dr[anscnt].tech, tech2str(map->tech), sizeof(dr[anscnt].tech)); 00545 dr[anscnt].eid = *us_eid; 00546 dundi_eid_to_str(dr[anscnt].eid_str, sizeof(dr[anscnt].eid_str), &dr[anscnt].eid); 00547 if (ast_test_flag(&flags, DUNDI_FLAG_EXISTS)) { 00548 AST_LIST_HEAD_INIT_NOLOCK(&headp); 00549 newvariable = ast_var_assign("NUMBER", called_number); 00550 AST_LIST_INSERT_HEAD(&headp, newvariable, entries); 00551 newvariable = ast_var_assign("EID", dr[anscnt].eid_str); 00552 AST_LIST_INSERT_HEAD(&headp, newvariable, entries); 00553 newvariable = ast_var_assign("SECRET", cursecret); 00554 AST_LIST_INSERT_HEAD(&headp, newvariable, entries); 00555 newvariable = ast_var_assign("IPADDR", ipaddr); 00556 AST_LIST_INSERT_HEAD(&headp, newvariable, entries); 00557 pbx_substitute_variables_varshead(&headp, map->dest, dr[anscnt].dest, sizeof(dr[anscnt].dest)); 00558 while ((newvariable = AST_LIST_REMOVE_HEAD(&headp, entries))) 00559 ast_var_delete(newvariable); 00560 } else 00561 dr[anscnt].dest[0] = '\0'; 00562 anscnt++; 00563 } else { 00564 /* No answers... Find the fewest number of digits from the 00565 number for which we have no answer. */ 00566 char tmp[AST_MAX_EXTENSION]; 00567 for (x=0;x<AST_MAX_EXTENSION;x++) { 00568 tmp[x] = called_number[x]; 00569 if (!tmp[x]) 00570 break; 00571 if (!ast_canmatch_extension(NULL, map->lcontext, tmp, 1, NULL)) { 00572 /* Oops found something we can't match. If this is longer 00573 than the running hint, we have to consider it */ 00574 if (strlen(tmp) > strlen(hmd->exten)) { 00575 ast_copy_string(hmd->exten, tmp, sizeof(hmd->exten)); 00576 } 00577 break; 00578 } 00579 } 00580 } 00581 } 00582 return anscnt; 00583 } 00584 00585 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout); 00586 00587 static void *dundi_lookup_thread(void *data) 00588 { 00589 struct dundi_query_state *st = data; 00590 struct dundi_result dr[MAX_RESULTS]; 00591 struct dundi_ie_data ied; 00592 struct dundi_hint_metadata hmd; 00593 char eid_str[20]; 00594 int res, x; 00595 int ouranswers=0; 00596 int max = 999999; 00597 int expiration = dundi_cache_time; 00598 00599 if (option_debug) 00600 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context, 00601 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves"); 00602 memset(&ied, 0, sizeof(ied)); 00603 memset(&dr, 0, sizeof(dr)); 00604 memset(&hmd, 0, sizeof(hmd)); 00605 /* Assume 'don't ask for anything' and 'unaffected', no TTL expired */ 00606 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED; 00607 for (x=0;x<st->nummaps;x++) 00608 ouranswers = dundi_lookup_local(dr, st->maps + x, st->called_number, &st->trans->us_eid, ouranswers, &hmd); 00609 if (ouranswers < 0) 00610 ouranswers = 0; 00611 for (x=0;x<ouranswers;x++) { 00612 if (dr[x].weight < max) 00613 max = dr[x].weight; 00614 } 00615 00616 if (max) { 00617 /* If we do not have a canonical result, keep looking */ 00618 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); 00619 if (res > 0) { 00620 /* Append answer in result */ 00621 ouranswers += res; 00622 } else { 00623 if ((res < -1) && (!ouranswers)) 00624 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_DUPLICATE, "Duplicate Request Pending"); 00625 } 00626 } 00627 AST_LIST_LOCK(&peers); 00628 /* Truncate if "don't ask" isn't present */ 00629 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK)) 00630 hmd.exten[0] = '\0'; 00631 if (ast_test_flag(st->trans, FLAG_DEAD)) { 00632 if (option_debug) 00633 ast_log(LOG_DEBUG, "Our transaction went away!\n"); 00634 st->trans->thread = 0; 00635 destroy_trans(st->trans, 0); 00636 } else { 00637 for (x=0;x<ouranswers;x++) { 00638 /* Add answers */ 00639 if (dr[x].expiration && (expiration > dr[x].expiration)) 00640 expiration = dr[x].expiration; 00641 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest); 00642 } 00643 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten); 00644 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration); 00645 dundi_send(st->trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied); 00646 st->trans->thread = 0; 00647 } 00648 AST_LIST_UNLOCK(&peers); 00649 free(st); 00650 return NULL; 00651 } 00652 00653 static void *dundi_precache_thread(void *data) 00654 { 00655 struct dundi_query_state *st = data; 00656 struct dundi_ie_data ied; 00657 struct dundi_hint_metadata hmd; 00658 char eid_str[20]; 00659 00660 if (option_debug) 00661 ast_log(LOG_DEBUG, "Whee, precaching '%s@%s' for '%s'\n", st->called_number, st->called_context, 00662 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves"); 00663 memset(&ied, 0, sizeof(ied)); 00664 00665 /* Now produce precache */ 00666 dundi_precache_internal(st->called_context, st->called_number, st->ttl, st->eids); 00667 00668 AST_LIST_LOCK(&peers); 00669 /* Truncate if "don't ask" isn't present */ 00670 if (!ast_test_flag_nonstd(&hmd, DUNDI_HINT_DONT_ASK)) 00671 hmd.exten[0] = '\0'; 00672 if (ast_test_flag(st->trans, FLAG_DEAD)) { 00673 if (option_debug) 00674 ast_log(LOG_DEBUG, "Our transaction went away!\n"); 00675 st->trans->thread = 0; 00676 destroy_trans(st->trans, 0); 00677 } else { 00678 dundi_send(st->trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied); 00679 st->trans->thread = 0; 00680 } 00681 AST_LIST_UNLOCK(&peers); 00682 free(st); 00683 return NULL; 00684 } 00685 00686 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[]); 00687 00688 static void *dundi_query_thread(void *data) 00689 { 00690 struct dundi_query_state *st = data; 00691 struct dundi_entity_info dei; 00692 struct dundi_ie_data ied; 00693 struct dundi_hint_metadata hmd; 00694 char eid_str[20]; 00695 int res; 00696 00697 if (option_debug) 00698 ast_log(LOG_DEBUG, "Whee, looking up '%s@%s' for '%s'\n", st->called_number, st->called_context, 00699 st->eids[0] ? dundi_eid_to_str(eid_str, sizeof(eid_str), st->eids[0]) : "ourselves"); 00700 memset(&ied, 0, sizeof(ied)); 00701 memset(&dei, 0, sizeof(dei)); 00702 memset(&hmd, 0, sizeof(hmd)); 00703 if (!dundi_eid_cmp(&st->trans->us_eid, &st->reqeid)) { 00704 /* Ooh, it's us! */ 00705 if (option_debug) 00706 ast_log(LOG_DEBUG, "Neat, someone look for us!\n"); 00707 ast_copy_string(dei.orgunit, dept, sizeof(dei.orgunit)); 00708 ast_copy_string(dei.org, org, sizeof(dei.org)); 00709 ast_copy_string(dei.locality, locality, sizeof(dei.locality)); 00710 ast_copy_string(dei.stateprov, stateprov, sizeof(dei.stateprov)); 00711 ast_copy_string(dei.country, country, sizeof(dei.country)); 00712 ast_copy_string(dei.email, email, sizeof(dei.email)); 00713 ast_copy_string(dei.phone, phone, sizeof(dei.phone)); 00714 res = 1; 00715 } else { 00716 /* If we do not have a canonical result, keep looking */ 00717 res = dundi_query_eid_internal(&dei, st->called_context, &st->reqeid, &hmd, st->ttl, 1, st->eids); 00718 } 00719 AST_LIST_LOCK(&peers); 00720 if (ast_test_flag(st->trans, FLAG_DEAD)) { 00721 if (option_debug) 00722 ast_log(LOG_DEBUG, "Our transaction went away!\n"); 00723 st->trans->thread = 0; 00724 destroy_trans(st->trans, 0); 00725 } else { 00726 if (res) { 00727 dundi_ie_append_str(&ied, DUNDI_IE_DEPARTMENT, dei.orgunit); 00728 dundi_ie_append_str(&ied, DUNDI_IE_ORGANIZATION, dei.org); 00729 dundi_ie_append_str(&ied, DUNDI_IE_LOCALITY, dei.locality); 00730 dundi_ie_append_str(&ied, DUNDI_IE_STATE_PROV, dei.stateprov); 00731 dundi_ie_append_str(&ied, DUNDI_IE_COUNTRY, dei.country); 00732 dundi_ie_append_str(&ied, DUNDI_IE_EMAIL, dei.email); 00733 dundi_ie_append_str(&ied, DUNDI_IE_PHONE, dei.phone); 00734 if (!ast_strlen_zero(dei.ipaddr)) 00735 dundi_ie_append_str(&ied, DUNDI_IE_IPADDR, dei.ipaddr); 00736 } 00737 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten); 00738 dundi_send(st->trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied); 00739 st->trans->thread = 0; 00740 } 00741 AST_LIST_UNLOCK(&peers); 00742 free(st); 00743 return NULL; 00744 } 00745 00746 static int dundi_answer_entity(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext) 00747 { 00748 struct dundi_query_state *st; 00749 int totallen; 00750 int x; 00751 int skipfirst=0; 00752 struct dundi_ie_data ied; 00753 char eid_str[20]; 00754 char *s; 00755 pthread_t lookupthread; 00756 pthread_attr_t attr; 00757 if (ies->eidcount > 1) { 00758 /* Since it is a requirement that the first EID is the authenticating host 00759 and the last EID is the root, it is permissible that the first and last EID 00760 could be the same. In that case, we should go ahead copy only the "root" section 00761 since we will not need it for authentication. */ 00762 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1])) 00763 skipfirst = 1; 00764 } 00765 totallen = sizeof(struct dundi_query_state); 00766 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid); 00767 st = ast_calloc(1, totallen); 00768 if (st) { 00769 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context)); 00770 memcpy(&st->reqeid, ies->reqeid, sizeof(st->reqeid)); 00771 st->trans = trans; 00772 st->ttl = ies->ttl - 1; 00773 if (st->ttl < 0) 00774 st->ttl = 0; 00775 s = st->fluffy; 00776 for (x=skipfirst;ies->eids[x];x++) { 00777 st->eids[x-skipfirst] = (dundi_eid *)s; 00778 *st->eids[x-skipfirst] = *ies->eids[x]; 00779 s += sizeof(dundi_eid); 00780 } 00781 if (option_debug) 00782 ast_log(LOG_DEBUG, "Answering EID query for '%s@%s'!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), ies->reqeid), ies->called_context); 00783 pthread_attr_init(&attr); 00784 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 00785 trans->thread = 1; 00786 if (ast_pthread_create(&lookupthread, &attr, dundi_query_thread, st)) { 00787 trans->thread = 0; 00788 ast_log(LOG_WARNING, "Unable to create thread!\n"); 00789 free(st); 00790 memset(&ied, 0, sizeof(ied)); 00791 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads"); 00792 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied); 00793 return -1; 00794 } 00795 } else { 00796 ast_log(LOG_WARNING, "Out of memory!\n"); 00797 memset(&ied, 0, sizeof(ied)); 00798 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory"); 00799 dundi_send(trans, DUNDI_COMMAND_EIDRESPONSE, 0, 1, &ied); 00800 return -1; 00801 } 00802 return 0; 00803 } 00804 00805 static int cache_save_hint(dundi_eid *eidpeer, struct dundi_request *req, struct dundi_hint *hint, int expiration) 00806 { 00807 int unaffected; 00808 char key1[256]; 00809 char key2[256]; 00810 char eidpeer_str[20]; 00811 char eidroot_str[20]; 00812 char data[80]; 00813 time_t timeout; 00814 00815 if (expiration < 0) 00816 expiration = dundi_cache_time; 00817 00818 /* Only cache hint if "don't ask" is there... */ 00819 if (!ast_test_flag_nonstd(hint, htons(DUNDI_HINT_DONT_ASK))) 00820 return 0; 00821 00822 unaffected = ast_test_flag_nonstd(hint, htons(DUNDI_HINT_UNAFFECTED)); 00823 00824 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer); 00825 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid); 00826 snprintf(key1, sizeof(key1), "hint/%s/%s/%s/e%08lx", eidpeer_str, hint->data, req->dcontext, unaffected ? 0 : req->crc32); 00827 snprintf(key2, sizeof(key2), "hint/%s/%s/%s/r%s", eidpeer_str, hint->data, req->dcontext, eidroot_str); 00828 00829 time(&timeout); 00830 timeout += expiration; 00831 snprintf(data, sizeof(data), "%ld|", (long)(timeout)); 00832 00833 ast_db_put("dundi/cache", key1, data); 00834 if (option_debug) 00835 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key1); 00836 ast_db_put("dundi/cache", key2, data); 00837 if (option_debug) 00838 ast_log(LOG_DEBUG, "Caching hint at '%s'\n", key2); 00839 return 0; 00840 } 00841 00842 static int cache_save(dundi_eid *eidpeer, struct dundi_request *req, int start, int unaffected, int expiration, int push) 00843 { 00844 int x; 00845 char key1[256]; 00846 char key2[256]; 00847 char data[1024]; 00848 char eidpeer_str[20]; 00849 char eidroot_str[20]; 00850 time_t timeout; 00851 00852 if (expiration < 1) 00853 expiration = dundi_cache_time; 00854 00855 /* Keep pushes a little longer, cut pulls a little short */ 00856 if (push) 00857 expiration += 10; 00858 else 00859 expiration -= 10; 00860 if (expiration < 1) 00861 expiration = 1; 00862 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), eidpeer); 00863 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid); 00864 snprintf(key1, sizeof(key1), "%s/%s/%s/e%08lx", eidpeer_str, req->number, req->dcontext, unaffected ? 0 : req->crc32); 00865 snprintf(key2, sizeof(key2), "%s/%s/%s/r%s", eidpeer_str, req->number, req->dcontext, eidroot_str); 00866 /* Build request string */ 00867 time(&timeout); 00868 timeout += expiration; 00869 snprintf(data, sizeof(data), "%ld|", (long)(timeout)); 00870 for (x=start;x<req->respcount;x++) { 00871 /* Skip anything with an illegal pipe in it */ 00872 if (strchr(req->dr[x].dest, '|')) 00873 continue; 00874 snprintf(data + strlen(data), sizeof(data) - strlen(data), "%d/%d/%d/%s/%s|", 00875 req->dr[x].flags, req->dr[x].weight, req->dr[x].techint, req->dr[x].dest, 00876 dundi_eid_to_str_short(eidpeer_str, sizeof(eidpeer_str), &req->dr[x].eid)); 00877 } 00878 ast_db_put("dundi/cache", key1, data); 00879 ast_db_put("dundi/cache", key2, data); 00880 return 0; 00881 } 00882 00883 static int dundi_prop_precache(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext) 00884 { 00885 struct dundi_query_state *st; 00886 int totallen; 00887 int x,z; 00888 struct dundi_ie_data ied; 00889 char *s; 00890 struct dundi_result dr2[MAX_RESULTS]; 00891 struct dundi_request dr; 00892 struct dundi_hint_metadata hmd; 00893 00894 struct dundi_mapping *cur; 00895 int mapcount; 00896 int skipfirst = 0; 00897 00898 pthread_t lookupthread; 00899 pthread_attr_t attr; 00900 00901 memset(&dr2, 0, sizeof(dr2)); 00902 memset(&dr, 0, sizeof(dr)); 00903 memset(&hmd, 0, sizeof(hmd)); 00904 00905 /* Forge request structure to hold answers for cache */ 00906 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED; 00907 dr.dr = dr2; 00908 dr.maxcount = MAX_RESULTS; 00909 dr.expiration = dundi_cache_time; 00910 dr.hmd = &hmd; 00911 dr.pfds[0] = dr.pfds[1] = -1; 00912 trans->parent = &dr; 00913 ast_copy_string(dr.dcontext, ies->called_context ? ies->called_context : "e164", sizeof(dr.dcontext)); 00914 ast_copy_string(dr.number, ies->called_number, sizeof(dr.number)); 00915 00916 for (x=0;x<ies->anscount;x++) { 00917 if (trans->parent->respcount < trans->parent->maxcount) { 00918 /* Make sure it's not already there */ 00919 for (z=0;z<trans->parent->respcount;z++) { 00920 if ((trans->parent->dr[z].techint == ies->answers[x]->protocol) && 00921 !strcmp(trans->parent->dr[z].dest, (char *)ies->answers[x]->data)) 00922 break; 00923 } 00924 if (z == trans->parent->respcount) { 00925 /* Copy into parent responses */ 00926 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies->answers[x]->flags); 00927 trans->parent->dr[trans->parent->respcount].techint = ies->answers[x]->protocol; 00928 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies->answers[x]->weight); 00929 trans->parent->dr[trans->parent->respcount].eid = ies->answers[x]->eid; 00930 if (ies->expiration > 0) 00931 trans->parent->dr[trans->parent->respcount].expiration = ies->expiration; 00932 else 00933 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time; 00934 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str, 00935 sizeof(trans->parent->dr[trans->parent->respcount].eid_str), 00936 &ies->answers[x]->eid); 00937 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies->answers[x]->data, 00938 sizeof(trans->parent->dr[trans->parent->respcount].dest)); 00939 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies->answers[x]->protocol), 00940 sizeof(trans->parent->dr[trans->parent->respcount].tech)); 00941 trans->parent->respcount++; 00942 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK); 00943 } else if (trans->parent->dr[z].weight > ies->answers[x]->weight) { 00944 /* Update weight if appropriate */ 00945 trans->parent->dr[z].weight = ies->answers[x]->weight; 00946 } 00947 } else 00948 ast_log(LOG_NOTICE, "Dropping excessive answers in precache for %s@%s\n", 00949 trans->parent->number, trans->parent->dcontext); 00950 00951 } 00952 /* Save all the results (if any) we had. Even if no results, still cache lookup. */ 00953 cache_save(&trans->them_eid, trans->parent, 0, 0, ies->expiration, 1); 00954 if (ies->hint) 00955 cache_save_hint(&trans->them_eid, trans->parent, ies->hint, ies->expiration); 00956 00957 totallen = sizeof(struct dundi_query_state); 00958 /* Count matching map entries */ 00959 mapcount = 0; 00960 AST_LIST_TRAVERSE(&mappings, cur, list) { 00961 if (!strcasecmp(cur->dcontext, ccontext)) 00962 mapcount++; 00963 } 00964 00965 /* If no maps, return -1 immediately */ 00966 if (!mapcount) 00967 return -1; 00968 00969 if (ies->eidcount > 1) { 00970 /* Since it is a requirement that the first EID is the authenticating host 00971 and the last EID is the root, it is permissible that the first and last EID 00972 could be the same. In that case, we should go ahead copy only the "root" section 00973 since we will not need it for authentication. */ 00974 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1])) 00975 skipfirst = 1; 00976 } 00977 00978 /* Prepare to run a query and then propagate that as necessary */ 00979 totallen += mapcount * sizeof(struct dundi_mapping); 00980 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid); 00981 st = ast_calloc(1, totallen); 00982 if (st) { 00983 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context)); 00984 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number)); 00985 st->trans = trans; 00986 st->ttl = ies->ttl - 1; 00987 st->nocache = ies->cbypass; 00988 if (st->ttl < 0) 00989 st->ttl = 0; 00990 s = st->fluffy; 00991 for (x=skipfirst;ies->eids[x];x++) { 00992 st->eids[x-skipfirst] = (dundi_eid *)s; 00993 *st->eids[x-skipfirst] = *ies->eids[x]; 00994 st->directs[x-skipfirst] = ies->eid_direct[x]; 00995 s += sizeof(dundi_eid); 00996 } 00997 /* Append mappings */ 00998 x = 0; 00999 st->maps = (struct dundi_mapping *)s; 01000 AST_LIST_TRAVERSE(&mappings, cur, list) { 01001 if (!strcasecmp(cur->dcontext, ccontext)) { 01002 if (x < mapcount) { 01003 st->maps[x] = *cur; 01004 st->maps[x].list.next = NULL; 01005 x++; 01006 } 01007 } 01008 } 01009 st->nummaps = mapcount; 01010 if (option_debug) 01011 ast_log(LOG_DEBUG, "Forwarding precache for '%s@%s'!\n", ies->called_number, ies->called_context); 01012 pthread_attr_init(&attr); 01013 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 01014 trans->thread = 1; 01015 if (ast_pthread_create(&lookupthread, &attr, dundi_precache_thread, st)) { 01016 trans->thread = 0; 01017 ast_log(LOG_WARNING, "Unable to create thread!\n"); 01018 free(st); 01019 memset(&ied, 0, sizeof(ied)); 01020 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads"); 01021 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied); 01022 return -1; 01023 } 01024 } else { 01025 ast_log(LOG_WARNING, "Out of memory!\n"); 01026 memset(&ied, 0, sizeof(ied)); 01027 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory"); 01028 dundi_send(trans, DUNDI_COMMAND_PRECACHERP, 0, 1, &ied); 01029 return -1; 01030 } 01031 return 0; 01032 } 01033 01034 static int dundi_answer_query(struct dundi_transaction *trans, struct dundi_ies *ies, char *ccontext) 01035 { 01036 struct dundi_query_state *st; 01037 int totallen; 01038 int x; 01039 struct dundi_ie_data ied; 01040 char *s; 01041 struct dundi_mapping *cur; 01042 int mapcount = 0; 01043 int skipfirst = 0; 01044 01045 pthread_t lookupthread; 01046 pthread_attr_t attr; 01047 totallen = sizeof(struct dundi_query_state); 01048 /* Count matching map entries */ 01049 AST_LIST_TRAVERSE(&mappings, cur, list) { 01050 if (!strcasecmp(cur->dcontext, ccontext)) 01051 mapcount++; 01052 } 01053 /* If no maps, return -1 immediately */ 01054 if (!mapcount) 01055 return -1; 01056 01057 if (ies->eidcount > 1) { 01058 /* Since it is a requirement that the first EID is the authenticating host 01059 and the last EID is the root, it is permissible that the first and last EID 01060 could be the same. In that case, we should go ahead copy only the "root" section 01061 since we will not need it for authentication. */ 01062 if (!dundi_eid_cmp(ies->eids[0], ies->eids[ies->eidcount - 1])) 01063 skipfirst = 1; 01064 } 01065 01066 totallen += mapcount * sizeof(struct dundi_mapping); 01067 totallen += (ies->eidcount - skipfirst) * sizeof(dundi_eid); 01068 st = ast_calloc(1, totallen); 01069 if (st) { 01070 ast_copy_string(st->called_context, ies->called_context, sizeof(st->called_context)); 01071 ast_copy_string(st->called_number, ies->called_number, sizeof(st->called_number)); 01072 st->trans = trans; 01073 st->ttl = ies->ttl - 1; 01074 st->nocache = ies->cbypass; 01075 if (st->ttl < 0) 01076 st->ttl = 0; 01077 s = st->fluffy; 01078 for (x=skipfirst;ies->eids[x];x++) { 01079 st->eids[x-skipfirst] = (dundi_eid *)s; 01080 *st->eids[x-skipfirst] = *ies->eids[x]; 01081 st->directs[x-skipfirst] = ies->eid_direct[x]; 01082 s += sizeof(dundi_eid); 01083 } 01084 /* Append mappings */ 01085 x = 0; 01086 st->maps = (struct dundi_mapping *)s; 01087 AST_LIST_TRAVERSE(&mappings, cur, list) { 01088 if (!strcasecmp(cur->dcontext, ccontext)) { 01089 if (x < mapcount) { 01090 st->maps[x] = *cur; 01091 st->maps[x].list.next = NULL; 01092 x++; 01093 } 01094 } 01095 } 01096 st->nummaps = mapcount; 01097 if (option_debug) 01098 ast_log(LOG_DEBUG, "Answering query for '%s@%s'!\n", ies->called_number, ies->called_context); 01099 pthread_attr_init(&attr); 01100 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 01101 trans->thread = 1; 01102 if (ast_pthread_create(&lookupthread, &attr, dundi_lookup_thread, st)) { 01103 trans->thread = 0; 01104 ast_log(LOG_WARNING, "Unable to create thread!\n"); 01105 free(st); 01106 memset(&ied, 0, sizeof(ied)); 01107 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of threads"); 01108 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied); 01109 return -1; 01110 } 01111 } else { 01112 ast_log(LOG_WARNING, "Out of memory!\n"); 01113 memset(&ied, 0, sizeof(ied)); 01114 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Out of memory"); 01115 dundi_send(trans, DUNDI_COMMAND_DPRESPONSE, 0, 1, &ied); 01116 return -1; 01117 } 01118 return 0; 01119 } 01120 01121 static int cache_lookup_internal(time_t now, struct dundi_request *req, char *key, char *eid_str_full, int *lowexpiration) 01122 { 01123 char data[1024]; 01124 char *ptr, *term, *src; 01125 int tech; 01126 struct ast_flags flags; 01127 int weight; 01128 int length; 01129 int z; 01130 char fs[256]; 01131 01132 /* Build request string */ 01133 if (!ast_db_get("dundi/cache", key, data, sizeof(data))) { 01134 time_t timeout; 01135 ptr = data; 01136 if (!ast_get_time_t(ptr, &timeout, 0, &length)) { 01137 int expiration = timeout - now; 01138 if (expiration > 0) { 01139 if (option_debug) 01140 ast_log(LOG_DEBUG, "Found cache expiring in %d seconds!\n", expiration); 01141 ptr += length + 1; 01142 while((sscanf(ptr, "%d/%d/%d/%n", &(flags.flags), &weight, &tech, &length) == 3)) { 01143 ptr += length; 01144 term = strchr(ptr, '|'); 01145 if (term) { 01146 *term = '\0'; 01147 src = strrchr(ptr, '/'); 01148 if (src) { 01149 *src = '\0'; 01150 src++; 01151 } else 01152 src = ""; 01153 if (option_debug) 01154 ast_log(LOG_DEBUG, "Found cached answer '%s/%s' originally from '%s' with flags '%s' on behalf of '%s'\n", 01155 tech2str(tech), ptr, src, dundi_flags2str(fs, sizeof(fs), flags.flags), eid_str_full); 01156 /* Make sure it's not already there */ 01157 for (z=0;z<req->respcount;z++) { 01158 if ((req->dr[z].techint == tech) && 01159 !strcmp(req->dr[z].dest, ptr)) 01160 break; 01161 } 01162 if (z == req->respcount) { 01163 /* Copy into parent responses */ 01164 ast_copy_flags(&(req->dr[req->respcount]), &flags, AST_FLAGS_ALL); 01165 req->dr[req->respcount].weight = weight; 01166 req->dr[req->respcount].techint = tech; 01167 req->dr[req->respcount].expiration = expiration; 01168 dundi_str_short_to_eid(&req->dr[req->respcount].eid, src); 01169 dundi_eid_to_str(req->dr[req->respcount].eid_str, 01170 sizeof(req->dr[req->respcount].eid_str), &req->dr[req->respcount].eid); 01171 ast_copy_string(req->dr[req->respcount].dest, ptr, 01172 sizeof(req->dr[req->respcount].dest)); 01173 ast_copy_string(req->dr[req->respcount].tech, tech2str(tech), 01174 sizeof(req->dr[req->respcount].tech)); 01175 req->respcount++; 01176 ast_clear_flag_nonstd(req->hmd, DUNDI_HINT_DONT_ASK); 01177 } else if (req->dr[z].weight > weight) 01178 req->dr[z].weight = weight; 01179 ptr = term + 1; 01180 } 01181 } 01182 /* We found *something* cached */ 01183 if (expiration < *lowexpiration) 01184 *lowexpiration = expiration; 01185 return 1; 01186 } else 01187 ast_db_del("dundi/cache", key); 01188 } else 01189 ast_db_del("dundi/cache", key); 01190 } 01191 01192 return 0; 01193 } 01194 01195 static int cache_lookup(struct dundi_request *req, dundi_eid *peer_eid, unsigned long crc32, int *lowexpiration) 01196 { 01197 char key[256]; 01198 char eid_str[20]; 01199 char eidroot_str[20]; 01200 time_t now; 01201 int res=0; 01202 int res2=0; 01203 char eid_str_full[20]; 01204 char tmp[256]=""; 01205 int x; 01206 01207 time(&now); 01208 dundi_eid_to_str_short(eid_str, sizeof(eid_str), peer_eid); 01209 dundi_eid_to_str_short(eidroot_str, sizeof(eidroot_str), &req->root_eid); 01210 dundi_eid_to_str(eid_str_full, sizeof(eid_str_full), peer_eid); 01211 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, crc32); 01212 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration); 01213 snprintf(key, sizeof(key), "%s/%s/%s/e%08lx", eid_str, req->number, req->dcontext, 0L); 01214 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration); 01215 snprintf(key, sizeof(key), "%s/%s/%s/r%s", eid_str, req->number, req->dcontext, eidroot_str); 01216 res |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration); 01217 x = 0; 01218 if (!req->respcount) { 01219 while(!res2) { 01220 /* Look and see if we have a hint that would preclude us from looking at this 01221 peer for this number. */ 01222 if (!(tmp[x] = req->number[x])) 01223 break; 01224 x++; 01225 /* Check for hints */ 01226 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, crc32); 01227 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration); 01228 snprintf(key, sizeof(key), "hint/%s/%s/%s/e%08lx", eid_str, tmp, req->dcontext, 0L); 01229 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration); 01230 snprintf(key, sizeof(key), "hint/%s/%s/%s/r%s", eid_str, tmp, req->dcontext, eidroot_str); 01231 res2 |= cache_lookup_internal(now, req, key, eid_str_full, lowexpiration); 01232 if (res2) { 01233 if (strlen(tmp) > strlen(req->hmd->exten)) { 01234 /* Update meta data if appropriate */ 01235 ast_copy_string(req->hmd->exten, tmp, sizeof(req->hmd->exten)); 01236 } 01237 } 01238 } 01239 res |= res2; 01240 } 01241 01242 return res; 01243 } 01244 01245 static void qualify_peer(struct dundi_peer *peer, int schedonly); 01246 01247 static void apply_peer(struct dundi_transaction *trans, struct dundi_peer *p) 01248 { 01249 if (!trans->addr.sin_addr.s_addr) 01250 memcpy(&trans->addr, &p->addr, sizeof(trans->addr)); 01251 trans->us_eid = p->us_eid; 01252 trans->them_eid = p->eid; 01253 /* Enable encryption if appropriate */ 01254 if (!ast_strlen_zero(p->inkey)) 01255 ast_set_flag(trans, FLAG_ENCRYPT); 01256 if (p->maxms) { 01257 trans->autokilltimeout = p->maxms; 01258 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER; 01259 if (p->lastms > 1) { 01260 trans->retranstimer = p->lastms * 2; 01261 /* Keep it from being silly */ 01262 if (trans->retranstimer < 150) 01263 trans->retranstimer = 150; 01264 } 01265 if (trans->retranstimer > DUNDI_DEFAULT_RETRANS_TIMER) 01266 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER; 01267 } else 01268 trans->autokilltimeout = global_autokilltimeout; 01269 } 01270 01271 /*! \note Called with the peers list already locked */ 01272 static int do_register_expire(void *data) 01273 { 01274 struct dundi_peer *peer = data; 01275 char eid_str[20]; 01276 if (option_debug) 01277 ast_log(LOG_DEBUG, "Register expired for '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid)); 01278 peer->registerexpire = -1; 01279 peer->lastms = 0; 01280 memset(&peer->addr, 0, sizeof(peer->addr)); 01281 return 0; 01282 } 01283 01284 static int update_key(struct dundi_peer *peer) 01285 { 01286 unsigned char key[16]; 01287 struct ast_key *ekey, *skey; 01288 char eid_str[20]; 01289 int res; 01290 if (!peer->keyexpire || (peer->keyexpire < time(NULL))) { 01291 build_iv(key); 01292 aes_encrypt_key128(key, &peer->us_ecx); 01293 aes_decrypt_key128(key, &peer->us_dcx); 01294 ekey = ast_key_get(peer->inkey, AST_KEY_PUBLIC); 01295 if (!ekey) { 01296 ast_log(LOG_NOTICE, "No such key '%s' for creating RSA encrypted shared key for '%s'!\n", 01297 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid)); 01298 return -1; 01299 } 01300 skey = ast_key_get(peer->outkey, AST_KEY_PRIVATE); 01301 if (!skey) { 01302 ast_log(LOG_NOTICE, "No such key '%s' for signing RSA encrypted shared key for '%s'!\n", 01303 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid)); 01304 return -1; 01305 } 01306 if ((res = ast_encrypt_bin(peer->txenckey, key, sizeof(key), ekey)) != 128) { 01307 ast_log(LOG_NOTICE, "Whoa, got a weird encrypt size (%d != %d)!\n", res, 128); 01308 return -1; 01309 } 01310 if ((res = ast_sign_bin(skey, (char *)peer->txenckey, 128, peer->txenckey + 128))) { 01311 ast_log(LOG_NOTICE, "Failed to sign key (%d)!\n", res); 01312 return -1; 01313 } 01314 peer->us_keycrc32 = crc32(0L, peer->txenckey, 128); 01315 peer->sentfullkey = 0; 01316 /* Looks good */ 01317 time(&peer->keyexpire); 01318 peer->keyexpire += dundi_key_ttl; 01319 } 01320 return 0; 01321 } 01322 01323 static int encrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_encrypt_ctx *ecx) 01324 { 01325 unsigned char curblock[16]; 01326 int x; 01327 memcpy(curblock, iv, sizeof(curblock)); 01328 while(len > 0) { 01329 for (x=0;x<16;x++) 01330 curblock[x] ^= src[x]; 01331 aes_encrypt(curblock, dst, ecx); 01332 memcpy(curblock, dst, sizeof(curblock)); 01333 dst += 16; 01334 src += 16; 01335 len -= 16; 01336 } 01337 return 0; 01338 } 01339 static int decrypt_memcpy(unsigned char *dst, unsigned char *src, int len, unsigned char *iv, aes_decrypt_ctx *dcx) 01340 { 01341 unsigned char lastblock[16]; 01342 int x; 01343 memcpy(lastblock, iv, sizeof(lastblock)); 01344 while(len > 0) { 01345 aes_decrypt(src, dst, dcx); 01346 for (x=0;x<16;x++) 01347 dst[x] ^= lastblock[x]; 01348 memcpy(lastblock, src, sizeof(lastblock)); 01349 dst += 16; 01350 src += 16; 01351 len -= 16; 01352 } 01353 return 0; 01354 } 01355 01356 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) 01357 { 01358 int space = *dstlen; 01359 unsigned long bytes; 01360 struct dundi_hdr *h; 01361 unsigned char *decrypt_space; 01362 decrypt_space = alloca(srclen); 01363 if (!decrypt_space) 01364 return NULL; 01365 decrypt_memcpy(decrypt_space, src->encdata, srclen, src->iv, &trans->dcx); 01366 /* Setup header */ 01367 h = (struct dundi_hdr *)dst; 01368 *h = *ohdr; 01369 bytes = space - 6; 01370 if (uncompress(dst + 6, &bytes, decrypt_space, srclen) != Z_OK) { 01371 if (option_debug) 01372 ast_log(LOG_DEBUG, "Ouch, uncompress failed :(\n"); 01373 return NULL; 01374 } 01375 /* Update length */ 01376 *dstlen = bytes + 6; 01377 /* Return new header */ 01378 return h; 01379 } 01380 01381 static int dundi_encrypt(struct dundi_transaction *trans, struct dundi_packet *pack) 01382 { 01383 unsigned char *compress_space; 01384 int len; 01385 int res; 01386 unsigned long bytes; 01387 struct dundi_ie_data ied; 01388 struct dundi_peer *peer; 01389 unsigned char iv[16]; 01390 len = pack->datalen + pack->datalen / 100 + 42; 01391 compress_space = alloca(len); 01392 if (compress_space) { 01393 memset(compress_space, 0, len); 01394 /* We care about everthing save the first 6 bytes of header */ 01395 bytes = len; 01396 res = compress(compress_space, &bytes, pack->data + 6, pack->datalen - 6); 01397 if (res != Z_OK) { 01398 if (option_debug) 01399 ast_log(LOG_DEBUG, "Ouch, compression failed!\n"); 01400 return -1; 01401 } 01402 memset(&ied, 0, sizeof(ied)); 01403 /* Say who we are */ 01404 if (!pack->h->iseqno && !pack->h->oseqno) { 01405 /* Need the key in the first copy */ 01406 if (!(peer = find_peer(&trans->them_eid))) 01407 return -1; 01408 if (update_key(peer)) 01409 return -1; 01410 if (!peer->sentfullkey) 01411 ast_set_flag(trans, FLAG_SENDFULLKEY); 01412 /* Append key data */ 01413 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid); 01414 if (ast_test_flag(trans, FLAG_SENDFULLKEY)) { 01415 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128); 01416 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128); 01417 } else { 01418 dundi_ie_append_int(&ied, DUNDI_IE_KEYCRC32, peer->us_keycrc32); 01419 } 01420 /* Setup contexts */ 01421 trans->ecx = peer->us_ecx; 01422 trans->dcx = peer->us_dcx; 01423 01424 /* We've sent the full key */ 01425 peer->sentfullkey = 1; 01426 } 01427 /* Build initialization vector */ 01428 build_iv(iv); 01429 /* Add the field, rounded up to 16 bytes */ 01430 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, iv, NULL, ((bytes + 15) / 16) * 16); 01431 /* Copy the data */ 01432 if ((ied.pos + bytes) >= sizeof(ied.buf)) { 01433 ast_log(LOG_NOTICE, "Final packet too large!\n"); 01434 return -1; 01435 } 01436 encrypt_memcpy(ied.buf + ied.pos, compress_space, bytes, iv, &trans->ecx); 01437 ied.pos += ((bytes + 15) / 16) * 16; 01438 /* Reconstruct header */ 01439 pack->datalen = sizeof(struct dundi_hdr); 01440 pack->h->cmdresp = DUNDI_COMMAND_ENCRYPT; 01441 pack->h->cmdflags = 0; 01442 memcpy(pack->h->ies, ied.buf, ied.pos); 01443 pack->datalen += ied.pos; 01444 return 0; 01445 } 01446 return -1; 01447 } 01448 01449 static int check_key(struct dundi_peer *peer, unsigned char *newkey, unsigned char *newsig, unsigned long keycrc32) 01450 { 01451 unsigned char dst[128]; 01452 int res; 01453 struct ast_key *key, *skey; 01454 char eid_str[20]; 01455 if (option_debug) 01456 ast_log(LOG_DEBUG, "Expected '%08lx' got '%08lx'\n", peer->them_keycrc32, keycrc32); 01457 if (peer->them_keycrc32 && (peer->them_keycrc32 == keycrc32)) { 01458 /* A match */ 01459 return 1; 01460 } else if (!newkey || !newsig) 01461 return 0; 01462 if (!memcmp(peer->rxenckey, newkey, 128) && 01463 !memcmp(peer->rxenckey + 128, newsig, 128)) { 01464 /* By definition, a match */ 01465 return 1; 01466 } 01467 /* Decrypt key */ 01468 key = ast_key_get(peer->outkey, AST_KEY_PRIVATE); 01469 if (!key) { 01470 ast_log(LOG_NOTICE, "Unable to find key '%s' to decode shared key from '%s'\n", 01471 peer->outkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid)); 01472 return -1; 01473 } 01474 01475 skey = ast_key_get(peer->inkey, AST_KEY_PUBLIC); 01476 if (!skey) { 01477 ast_log(LOG_NOTICE, "Unable to find key '%s' to verify shared key from '%s'\n", 01478 peer->inkey, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid)); 01479 return -1; 01480 } 01481 01482 /* First check signature */ 01483 res = ast_check_signature_bin(skey, (char *)newkey, 128, newsig); 01484 if (res) 01485 return 0; 01486 01487 res = ast_decrypt_bin(dst, newkey, sizeof(dst), key); 01488 if (res != 16) { 01489 if (res >= 0) 01490 ast_log(LOG_NOTICE, "Weird, key decoded to the wrong size (%d)\n", res); 01491 return 0; 01492 } 01493 /* Decrypted, passes signature */ 01494 if (option_debug) 01495 ast_log(LOG_DEBUG, "Wow, new key combo passed signature and decrypt!\n"); 01496 memcpy(peer->rxenckey, newkey, 128); 01497 memcpy(peer->rxenckey + 128, newsig, 128); 01498 peer->them_keycrc32 = crc32(0L, peer->rxenckey, 128); 01499 aes_decrypt_key128(dst, &peer->them_dcx); 01500 aes_encrypt_key128(dst, &peer->them_ecx); 01501 return 1; 01502 } 01503 01504 static int handle_command_response(struct dundi_transaction *trans, struct dundi_hdr *hdr, int datalen, int encrypted) 01505 { 01506 /* Handle canonical command / response */ 01507 int final = hdr->cmdresp & 0x80; 01508 int cmd = hdr->cmdresp & 0x7f; 01509 int x,y,z; 01510 int resp; 01511 int res; 01512 int authpass=0; 01513 unsigned char *bufcpy; 01514 struct dundi_ie_data ied; 01515 struct dundi_ies ies; 01516 struct dundi_peer *peer; 01517 char eid_str[20]; 01518 char eid_str2[20]; 01519 memset(&ied, 0, sizeof(ied)); 01520 memset(&ies, 0, sizeof(ies)); 01521 if (datalen) { 01522 bufcpy = alloca(datalen); 01523 if (!bufcpy) 01524 return -1; 01525 /* Make a copy for parsing */ 01526 memcpy(bufcpy, hdr->ies, datalen); 01527 if (option_debug) 01528 ast_log(LOG_DEBUG, "Got canonical message %d (%d), %d bytes data%s\n", cmd, hdr->oseqno, datalen, final ? " (Final)" : ""); 01529 if (dundi_parse_ies(&ies, bufcpy, datalen) < 0) { 01530 ast_log(LOG_WARNING, "Failed to parse DUNDI information elements!\n"); 01531 return -1; 01532 } 01533 } 01534 switch(cmd) { 01535 case DUNDI_COMMAND_DPDISCOVER: 01536 case DUNDI_COMMAND_EIDQUERY: 01537 case DUNDI_COMMAND_PRECACHERQ: 01538 if (cmd == DUNDI_COMMAND_EIDQUERY) 01539 resp = DUNDI_COMMAND_EIDRESPONSE; 01540 else if (cmd == DUNDI_COMMAND_PRECACHERQ) 01541 resp = DUNDI_COMMAND_PRECACHERP; 01542 else 01543 resp = DUNDI_COMMAND_DPRESPONSE; 01544 /* A dialplan or entity discover -- qualify by highest level entity */ 01545 peer = find_peer(ies.eids[0]); 01546 if (!peer) { 01547 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL); 01548 dundi_send(trans, resp, 0, 1, &ied); 01549 } else { 01550 int hasauth = 0; 01551 trans->us_eid = peer->us_eid; 01552 if (strlen(peer->inkey)) { 01553 hasauth = encrypted; 01554 } else 01555 hasauth = 1; 01556 if (hasauth) { 01557 /* Okay we're authentiated and all, now we check if they're authorized */ 01558 if (!ies.called_context) 01559 ies.called_context = "e164"; 01560 if (cmd == DUNDI_COMMAND_EIDQUERY) { 01561 res = dundi_answer_entity(trans, &ies, ies.called_context); 01562 } else { 01563 if (ast_strlen_zero(ies.called_number)) { 01564 /* They're not permitted to access that context */ 01565 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_GENERAL, "Invalid or missing number/entity"); 01566 dundi_send(trans, resp, 0, 1, &ied); 01567 } else if ((cmd == DUNDI_COMMAND_DPDISCOVER) && 01568 (peer->model & DUNDI_MODEL_INBOUND) && 01569 has_permission(&peer->permit, ies.called_context)) { 01570 res = dundi_answer_query(trans, &ies, ies.called_context); 01571 if (res < 0) { 01572 /* There is no such dundi context */ 01573 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context"); 01574 dundi_send(trans, resp, 0, 1, &ied); 01575 } 01576 } else if ((cmd = DUNDI_COMMAND_PRECACHERQ) && 01577 (peer->pcmodel & DUNDI_MODEL_INBOUND) && 01578 has_permission(&peer->include, ies.called_context)) { 01579 res = dundi_prop_precache(trans, &ies, ies.called_context); 01580 if (res < 0) { 01581 /* There is no such dundi context */ 01582 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unsupported DUNDI Context"); 01583 dundi_send(trans, resp, 0, 1, &ied); 01584 } 01585 } else { 01586 /* They're not permitted to access that context */ 01587 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Permission to context denied"); 01588 dundi_send(trans, resp, 0, 1, &ied); 01589 } 01590 } 01591 } else { 01592 /* They're not permitted to access that context */ 01593 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Unencrypted responses not permitted"); 01594 dundi_send(trans, resp, 0, 1, &ied); 01595 } 01596 } 01597 break; 01598 case DUNDI_COMMAND_REGREQ: 01599 /* A register request -- should only have one entity */ 01600 peer = find_peer(ies.eids[0]); 01601 if (!peer || !peer->dynamic) { 01602 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, NULL); 01603 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied); 01604 } else { 01605 int hasauth = 0; 01606 trans->us_eid = peer->us_eid; 01607 if (!ast_strlen_zero(peer->inkey)) { 01608 hasauth = encrypted; 01609 } else 01610 hasauth = 1; 01611 if (hasauth) { 01612 int expire = default_expiration; 01613 char data[256]; 01614 int needqual = 0; 01615 if (peer->registerexpire > -1) 01616 ast_sched_del(sched, peer->registerexpire); 01617 peer->registerexpire = ast_sched_add(sched, (expire + 10) * 1000, do_register_expire, peer); 01618 snprintf(data, sizeof(data), "%s:%d:%d", ast_inet_ntoa(trans->addr.sin_addr), 01619 ntohs(trans->addr.sin_port), expire); 01620 ast_db_put("dundi/dpeers", dundi_eid_to_str_short(eid_str, sizeof(eid_str), &peer->eid), data); 01621 if (inaddrcmp(&peer->addr, &trans->addr)) { 01622 if (option_verbose > 2) { 01623 ast_verbose(VERBOSE_PREFIX_3 "Registered DUNDi peer '%s' at '%s:%d'\n", 01624 dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), 01625 ast_inet_ntoa(trans->addr.sin_addr), ntohs(trans->addr.sin_port)); 01626 } 01627 needqual = 1; 01628 } 01629 01630 memcpy(&peer->addr, &trans->addr, sizeof(peer->addr)); 01631 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, default_expiration); 01632 dundi_send(trans, DUNDI_COMMAND_REGRESPONSE, 0, 1, &ied); 01633 if (needqual) 01634 qualify_peer(peer, 1); 01635 } 01636 } 01637 break; 01638 case DUNDI_COMMAND_DPRESPONSE: 01639 /* A dialplan response, lets see what we got... */ 01640 if (ies.cause < 1) { 01641 /* Success of some sort */ 01642 if (option_debug) 01643 ast_log(LOG_DEBUG, "Looks like success of some sort (%d), %d answers\n", ies.cause, ies.anscount); 01644 if (ast_test_flag(trans, FLAG_ENCRYPT)) { 01645 authpass = encrypted; 01646 } else 01647 authpass = 1; 01648 if (authpass) { 01649 /* Pass back up answers */ 01650 if (trans->parent && trans->parent->dr) { 01651 y = trans->parent->respcount; 01652 for (x=0;x<ies.anscount;x++) { 01653 if (trans->parent->respcount < trans->parent->maxcount) { 01654 /* Make sure it's not already there */ 01655 for (z=0;z<trans->parent->respcount;z++) { 01656 if ((trans->parent->dr[z].techint == ies.answers[x]->protocol) && 01657 !strcmp(trans->parent->dr[z].dest, (char *)ies.answers[x]->data)) 01658 break; 01659 } 01660 if (z == trans->parent->respcount) { 01661 /* Copy into parent responses */ 01662 trans->parent->dr[trans->parent->respcount].flags = ntohs(ies.answers[x]->flags); 01663 trans->parent->dr[trans->parent->respcount].techint = ies.answers[x]->protocol; 01664 trans->parent->dr[trans->parent->respcount].weight = ntohs(ies.answers[x]->weight); 01665 trans->parent->dr[trans->parent->respcount].eid = ies.answers[x]->eid; 01666 if (ies.expiration > 0) 01667 trans->parent->dr[trans->parent->respcount].expiration = ies.expiration; 01668 else 01669 trans->parent->dr[trans->parent->respcount].expiration = dundi_cache_time; 01670 dundi_eid_to_str(trans->parent->dr[trans->parent->respcount].eid_str, 01671 sizeof(trans->parent->dr[trans->parent->respcount].eid_str), 01672 &ies.answers[x]->eid); 01673 ast_copy_string(trans->parent->dr[trans->parent->respcount].dest, (char *)ies.answers[x]->data, 01674 sizeof(trans->parent->dr[trans->parent->respcount].dest)); 01675 ast_copy_string(trans->parent->dr[trans->parent->respcount].tech, tech2str(ies.answers[x]->protocol), 01676 sizeof(trans->parent->dr[trans->parent->respcount].tech)); 01677 trans->parent->respcount++; 01678 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK); 01679 } else if (trans->parent->dr[z].weight > ies.answers[x]->weight) { 01680 /* Update weight if appropriate */ 01681 trans->parent->dr[z].weight = ies.answers[x]->weight; 01682 } 01683 } else 01684 ast_log(LOG_NOTICE, "Dropping excessive answers to request for %s@%s\n", 01685 trans->parent->number, trans->parent->dcontext); 01686 } 01687 /* Save all the results (if any) we had. Even if no results, still cache lookup. Let 01688 the cache know if this request was unaffected by our entity list. */ 01689 cache_save(&trans->them_eid, trans->parent, y, 01690 ies.hint ? ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_UNAFFECTED)) : 0, ies.expiration, 0); 01691 if (ies.hint) { 01692 cache_save_hint(&trans->them_eid, trans->parent, ies.hint, ies.expiration); 01693 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED))) 01694 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED); 01695 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_DONT_ASK))) { 01696 if (strlen((char *)ies.hint->data) > strlen(trans->parent->hmd->exten)) { 01697 ast_copy_string(trans->parent->hmd->exten, (char *)ies.hint->data, 01698 sizeof(trans->parent->hmd->exten)); 01699 } 01700 } else { 01701 ast_clear_flag_nonstd(trans->parent->hmd, DUNDI_HINT_DONT_ASK); 01702 } 01703 } 01704 if (ies.expiration > 0) { 01705 if (trans->parent->expiration > ies.expiration) { 01706 trans->parent->expiration = ies.expiration; 01707 } 01708 } 01709 } 01710 /* Close connection if not final */ 01711 if (!final) 01712 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL); 01713 } 01714 01715 } else { 01716 /* Auth failure, check for data */ 01717 if (!final) { 01718 /* Cancel if they didn't already */ 01719 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL); 01720 } 01721 } 01722 break; 01723 case DUNDI_COMMAND_EIDRESPONSE: 01724 /* A dialplan response, lets see what we got... */ 01725 if (ies.cause < 1) { 01726 /* Success of some sort */ 01727 if (option_debug) 01728 ast_log(LOG_DEBUG, "Looks like success of some sort (%d)\n", ies.cause); 01729 if (ast_test_flag(trans, FLAG_ENCRYPT)) { 01730 authpass = encrypted; 01731 } else 01732 authpass = 1; 01733 if (authpass) { 01734 /* Pass back up answers */ 01735 if (trans->parent && trans->parent->dei && ies.q_org) { 01736 if (!trans->parent->respcount) { 01737 trans->parent->respcount++; 01738 if (ies.q_dept) 01739 ast_copy_string(trans->parent->dei->orgunit, ies.q_dept, sizeof(trans->parent->dei->orgunit)); 01740 if (ies.q_org) 01741 ast_copy_string(trans->parent->dei->org, ies.q_org, sizeof(trans->parent->dei->org)); 01742 if (ies.q_locality) 01743 ast_copy_string(trans->parent->dei->locality, ies.q_locality, sizeof(trans->parent->dei->locality)); 01744 if (ies.q_stateprov) 01745 ast_copy_string(trans->parent->dei->stateprov, ies.q_stateprov, sizeof(trans->parent->dei->stateprov)); 01746 if (ies.q_country) 01747 ast_copy_string(trans->parent->dei->country, ies.q_country, sizeof(trans->parent->dei->country)); 01748 if (ies.q_email) 01749 ast_copy_string(trans->parent->dei->email, ies.q_email, sizeof(trans->parent->dei->email)); 01750 if (ies.q_phone) 01751 ast_copy_string(trans->parent->dei->phone, ies.q_phone, sizeof(trans->parent->dei->phone)); 01752 if (ies.q_ipaddr) 01753 ast_copy_string(trans->parent->dei->ipaddr, ies.q_ipaddr, sizeof(trans->parent->dei->ipaddr)); 01754 if (!dundi_eid_cmp(&trans->them_eid, &trans->parent->query_eid)) { 01755 /* If it's them, update our address */ 01756 ast_copy_string(trans->parent->dei->ipaddr, ast_inet_ntoa(trans->addr.sin_addr), sizeof(trans->parent->dei->ipaddr)); 01757 } 01758 } 01759 if (ies.hint) { 01760 if (ast_test_flag_nonstd(ies.hint, htons(DUNDI_HINT_TTL_EXPIRED))) 01761 ast_set_flag_nonstd(trans->parent->hmd, DUNDI_HINT_TTL_EXPIRED); 01762 } 01763 } 01764 /* Close connection if not final */ 01765 if (!final) 01766 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL); 01767 } 01768 01769 } else { 01770 /* Auth failure, check for data */ 01771 if (!final) { 01772 /* Cancel if they didn't already */ 01773 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL); 01774 } 01775 } 01776 break; 01777 case DUNDI_COMMAND_REGRESPONSE: 01778 /* A dialplan response, lets see what we got... */ 01779 if (ies.cause < 1) { 01780 int hasauth; 01781 /* Success of some sort */ 01782 if (ast_test_flag(trans, FLAG_ENCRYPT)) { 01783 hasauth = encrypted; 01784 } else 01785 hasauth = 1; 01786 01787 if (!hasauth) { 01788 ast_log(LOG_NOTICE, "Reponse to register not authorized!\n"); 01789 if (!final) { 01790 dundi_ie_append_cause(&ied, DUNDI_IE_CAUSE, DUNDI_CAUSE_NOAUTH, "Improper signature in answer"); 01791 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, &ied); 01792 } 01793 } else { 01794 if (option_debug) 01795 ast_log(LOG_DEBUG, "Yay, we've registered as '%s' to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->us_eid), 01796 dundi_eid_to_str(eid_str2, sizeof(eid_str2), &trans->them_eid)); 01797 /* Close connection if not final */ 01798 if (!final) 01799 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL); 01800 } 01801 } else { 01802 /* Auth failure, cancel if they didn't for some reason */ 01803 if (!final) { 01804 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL); 01805 } 01806 } 01807 break; 01808 case DUNDI_COMMAND_INVALID: 01809 case DUNDI_COMMAND_NULL: 01810 case DUNDI_COMMAND_PRECACHERP: 01811 /* Do nothing special */ 01812 if (!final) 01813 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL); 01814 break; 01815 case DUNDI_COMMAND_ENCREJ: 01816 if ((ast_test_flag(trans, FLAG_SENDFULLKEY)) || AST_LIST_EMPTY(&trans->lasttrans) || !(peer = find_peer(&trans->them_eid))) { 01817 /* No really, it's over at this point */ 01818 if (!final) 01819 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL); 01820 } else { 01821 /* Send with full key */ 01822 ast_set_flag(trans, FLAG_SENDFULLKEY); 01823 if (final) { 01824 /* Ooops, we got a final message, start by sending ACK... */ 01825 dundi_ack(trans, hdr->cmdresp & 0x80); 01826 trans->aseqno = trans->iseqno; 01827 /* Now, we gotta create a new transaction */ 01828 if (!reset_transaction(trans)) { 01829 /* Make sure handle_frame doesn't destroy us */ 01830 hdr->cmdresp &= 0x7f; 01831 /* Parse the message we transmitted */ 01832 memset(&ies, 0, sizeof(ies)); 01833 dundi_parse_ies(&ies, (AST_LIST_FIRST(&trans->lasttrans))->h->ies, (AST_LIST_FIRST(&trans->lasttrans))->datalen - sizeof(struct dundi_hdr)); 01834 /* Reconstruct outgoing encrypted packet */ 01835 memset(&ied, 0, sizeof(ied)); 01836 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid); 01837 dundi_ie_append_raw(&ied, DUNDI_IE_SHAREDKEY, peer->txenckey, 128); 01838 dundi_ie_append_raw(&ied, DUNDI_IE_SIGNATURE, peer->txenckey + 128, 128); 01839 if (ies.encblock) 01840 dundi_ie_append_encdata(&ied, DUNDI_IE_ENCDATA, ies.encblock->iv, ies.encblock->encdata, ies.enclen); 01841 dundi_send(trans, DUNDI_COMMAND_ENCRYPT, 0, (AST_LIST_FIRST(&trans->lasttrans))->h->cmdresp & 0x80, &ied); 01842 peer->sentfullkey = 1; 01843 } 01844 } 01845 } 01846 break; 01847 case DUNDI_COMMAND_ENCRYPT: 01848 if (!encrypted) { 01849 /* No nested encryption! */ 01850 if ((trans->iseqno == 1) && !trans->oseqno) { 01851 if (!ies.eids[0] || !(peer = find_peer(ies.eids[0])) || 01852 ((!ies.encsharedkey || !ies.encsig) && !ies.keycrc32) || 01853 (check_key(peer, ies.encsharedkey, ies.encsig, ies.keycrc32) < 1)) { 01854 if (!final) { 01855 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL); 01856 } 01857 break; 01858 } 01859 apply_peer(trans, peer); 01860 /* Key passed, use new contexts for this session */ 01861 trans->ecx = peer->them_ecx; 01862 trans->dcx = peer->them_dcx; 01863 } 01864 if (ast_test_flag(trans, FLAG_ENCRYPT) && ies.encblock && ies.enclen) { 01865 struct dundi_hdr *dhdr; 01866 unsigned char decoded[MAX_PACKET_SIZE]; 01867 int ddatalen; 01868 ddatalen = sizeof(decoded); 01869 dhdr = dundi_decrypt(trans, decoded, &ddatalen, hdr, ies.encblock, ies.enclen); 01870 if (dhdr) { 01871 /* Handle decrypted response */ 01872 if (dundidebug) 01873 dundi_showframe(dhdr, 3, &trans->addr, ddatalen - sizeof(struct dundi_hdr)); 01874 handle_command_response(trans, dhdr, ddatalen - sizeof(struct dundi_hdr), 1); 01875 /* Carry back final flag */ 01876 hdr->cmdresp |= dhdr->cmdresp & 0x80; 01877 break; 01878 } else { 01879 if (option_debug) 01880 ast_log(LOG_DEBUG, "Ouch, decrypt failed :(\n"); 01881 } 01882 } 01883 } 01884 if (!final) { 01885 /* Turn off encryption */ 01886 ast_clear_flag(trans, FLAG_ENCRYPT); 01887 dundi_send(trans, DUNDI_COMMAND_ENCREJ, 0, 1, NULL); 01888 } 01889 break; 01890 default: 01891 /* Send unknown command if we don't know it, with final flag IFF it's the 01892 first command in the dialog and only if we haven't recieved final notification */ 01893 if (!final) { 01894 dundi_ie_append_byte(&ied, DUNDI_IE_UNKNOWN, cmd); 01895 dundi_send(trans, DUNDI_COMMAND_UNKNOWN, 0, !hdr->oseqno, &ied); 01896 } 01897 } 01898 return 0; 01899 } 01900 01901 static void destroy_packet(struct dundi_packet *pack, int needfree); 01902 static void destroy_packets(struct packetlist *p) 01903 { 01904 struct dundi_packet *pack; 01905 01906 while ((pack = AST_LIST_REMOVE_HEAD(p, list))) { 01907 if (pack->retransid > -1) 01908 ast_sched_del(sched, pack->retransid); 01909 free(pack); 01910 } 01911 } 01912 01913 01914 static int ack_trans(struct dundi_transaction *trans, int iseqno) 01915 { 01916 struct dundi_packet *pack; 01917 01918 /* Ack transmitted packet corresponding to iseqno */ 01919 AST_LIST_TRAVERSE(&trans->packets, pack, list) { 01920 if ((pack->h->oseqno + 1) % 255 == iseqno) { 01921 destroy_packet(pack, 0); 01922 if (!AST_LIST_EMPTY(&trans->lasttrans)) { 01923 ast_log(LOG_WARNING, "Whoa, there was still a last trans?\n"); 01924 destroy_packets(&trans->lasttrans); 01925 } 01926 AST_LIST_INSERT_HEAD(&trans->lasttrans, pack, list); 01927 if (trans->autokillid > -1) 01928 ast_sched_del(sched, trans->autokillid); 01929 trans->autokillid = -1; 01930 return 1; 01931 } 01932 } 01933 01934 return 0; 01935 } 01936 01937 static int handle_frame(struct dundi_hdr *h, struct sockaddr_in *sin, int datalen) 01938 { 01939 struct dundi_transaction *trans; 01940 trans = find_transaction(h, sin); 01941 if (!trans) { 01942 dundi_reject(h, sin); 01943 return 0; 01944 } 01945 /* Got a transaction, see where this header fits in */ 01946 if (h->oseqno == trans->iseqno) { 01947 /* Just what we were looking for... Anything but ack increments iseqno */ 01948 if (ack_trans(trans, h->iseqno) && ast_test_flag(trans, FLAG_FINAL)) { 01949 /* If final, we're done */ 01950 destroy_trans(trans, 0); 01951 return 0; 01952 } 01953 if (h->cmdresp != DUNDI_COMMAND_ACK) { 01954 trans->oiseqno = trans->iseqno; 01955 trans->iseqno++; 01956 handle_command_response(trans, h, datalen, 0); 01957 } 01958 if (trans->aseqno != trans->iseqno) { 01959 dundi_ack(trans, h->cmdresp & 0x80); 01960 trans->aseqno = trans->iseqno; 01961 } 01962 /* Delete any saved last transmissions */ 01963 destroy_packets(&trans->lasttrans); 01964 if (h->cmdresp & 0x80) { 01965 /* Final -- destroy now */ 01966 destroy_trans(trans, 0); 01967 } 01968 } else if (h->oseqno == trans->oiseqno) { 01969 /* Last incoming sequence number -- send ACK without processing */ 01970 dundi_ack(trans, 0); 01971 } else { 01972 /* Out of window -- simply drop */ 01973 if (option_debug) 01974 ast_log(LOG_DEBUG, "Dropping packet out of window!\n"); 01975 } 01976 return 0; 01977 } 01978 01979 static int socket_read(int *id, int fd, short events, void *cbdata) 01980 { 01981 struct sockaddr_in sin; 01982 int res; 01983 struct dundi_hdr *h; 01984 char buf[MAX_PACKET_SIZE]; 01985 socklen_t len = sizeof(sin); 01986 01987 res = recvfrom(netsocket, buf, sizeof(buf) - 1, 0,(struct sockaddr *) &sin, &len); 01988 if (res < 0) { 01989 if (errno != ECONNREFUSED) 01990 ast_log(LOG_WARNING, "Error: %s\n", strerror(errno)); 01991 return 1; 01992 } 01993 if (res < sizeof(struct dundi_hdr)) { 01994 ast_log(LOG_WARNING, "midget packet received (%d of %d min)\n", res, (int)sizeof(struct dundi_hdr)); 01995 return 1; 01996 } 01997 buf[res] = '\0'; 01998 h = (struct dundi_hdr *) buf; 01999 if (dundidebug) 02000 dundi_showframe(h, 1, &sin, res - sizeof(struct dundi_hdr)); 02001 AST_LIST_LOCK(&peers); 02002 handle_frame(h, &sin, res - sizeof(struct dundi_hdr)); 02003 AST_LIST_UNLOCK(&peers); 02004 return 1; 02005 } 02006 02007 static void build_secret(char *secret, int seclen) 02008 { 02009 unsigned char tmp[16]; 02010 char *s; 02011 build_iv(tmp); 02012 secret[0] = '\0'; 02013 ast_base64encode(secret, tmp, sizeof(tmp), seclen); 02014 /* Eliminate potential bad characters */ 02015 while((s = strchr(secret, ';'))) *s = '+'; 02016 while((s = strchr(secret, '/'))) *s = '+'; 02017 while((s = strchr(secret, ':'))) *s = '+'; 02018 while((s = strchr(secret, '@'))) *s = '+'; 02019 } 02020 02021 02022 static void save_secret(const char *newkey, const char *oldkey) 02023 { 02024 char tmp[256]; 02025 if (oldkey) 02026 snprintf(tmp, sizeof(tmp), "%s;%s", oldkey, newkey); 02027 else 02028 snprintf(tmp, sizeof(tmp), "%s", newkey); 02029 rotatetime = time(NULL) + DUNDI_SECRET_TIME; 02030 ast_db_put(secretpath, "secret", tmp); 02031 snprintf(tmp, sizeof(tmp), "%d", (int)rotatetime); 02032 ast_db_put(secretpath, "secretexpiry", tmp); 02033 } 02034 02035 static void load_password(void) 02036 { 02037 char *current=NULL; 02038 char *last=NULL; 02039 char tmp[256]; 02040 time_t expired; 02041 02042 ast_db_get(secretpath, "secretexpiry", tmp, sizeof(tmp)); 02043 if (!ast_get_time_t(tmp, &expired, 0, NULL)) { 02044 ast_db_get(secretpath, "secret", tmp, sizeof(tmp)); 02045 current = strchr(tmp, ';'); 02046 if (!current) 02047 current = tmp; 02048 else { 02049 *current = '\0'; 02050 current++; 02051 }; 02052 if ((time(NULL) - expired) < 0) { 02053 if ((expired - time(NULL)) > DUNDI_SECRET_TIME) 02054 expired = time(NULL) + DUNDI_SECRET_TIME; 02055 } else if ((time(NULL) - (expired + DUNDI_SECRET_TIME)) < 0) { 02056 last = current; 02057 current = NULL; 02058 } else { 02059 last = NULL; 02060 current = NULL; 02061 } 02062 } 02063 if (current) { 02064 /* Current key is still valid, just setup rotatation properly */ 02065 ast_copy_string(cursecret, current, sizeof(cursecret)); 02066 rotatetime = expired; 02067 } else { 02068 /* Current key is out of date, rotate or eliminate all together */ 02069 build_secret(cursecret, sizeof(cursecret)); 02070 save_secret(cursecret, last); 02071 } 02072 } 02073 02074 static void check_password(void) 02075 { 02076 char oldsecret[80]; 02077 time_t now; 02078 02079 time(&now); 02080 #if 0 02081 printf("%ld/%ld\n", now, rotatetime); 02082 #endif 02083 if ((now - rotatetime) >= 0) { 02084 /* Time to rotate keys */ 02085 ast_copy_string(oldsecret, cursecret, sizeof(oldsecret)); 02086 build_secret(cursecret, sizeof(cursecret)); 02087 save_secret(cursecret, oldsecret); 02088 } 02089 } 02090 02091 static void *network_thread(void *ignore) 02092 { 02093 /* Our job is simple: Send queued messages, retrying if necessary. Read frames 02094 from the network, and queue them for delivery to the channels */ 02095 int res; 02096 /* Establish I/O callback for socket read */ 02097 ast_io_add(io, netsocket, socket_read, AST_IO_IN, NULL); 02098 for(;;) { 02099 res = ast_sched_wait(sched); 02100 if ((res > 1000) || (res < 0)) 02101 res = 1000; 02102 res = ast_io_wait(io, res); 02103 if (res >= 0) { 02104 AST_LIST_LOCK(&peers); 02105 ast_sched_runq(sched); 02106 AST_LIST_UNLOCK(&peers); 02107 } 02108 check_password(); 02109 } 02110 return NULL; 02111 } 02112 02113 static void *process_precache(void *ign) 02114 { 02115 struct dundi_precache_queue *qe; 02116 time_t now; 02117 char context[256]; 02118 char number[256]; 02119 int run; 02120 02121 for (;;) { 02122 time(&now); 02123 run = 0; 02124 AST_LIST_LOCK(&pcq); 02125 if ((qe = AST_LIST_FIRST(&pcq))) { 02126 if (!qe->expiration) { 02127 /* Gone... Remove... */ 02128 AST_LIST_REMOVE_HEAD(&pcq, list); 02129 free(qe); 02130 } else if (qe->expiration < now) { 02131 /* Process this entry */ 02132 qe->expiration = 0; 02133 ast_copy_string(context, qe->context, sizeof(context)); 02134 ast_copy_string(number, qe->number, sizeof(number)); 02135 run = 1; 02136 } 02137 } 02138 AST_LIST_UNLOCK(&pcq); 02139 if (run) { 02140 dundi_precache(context, number); 02141 } else 02142 sleep(1); 02143 } 02144 02145 return NULL; 02146 } 02147 02148 static int start_network_thread(void) 02149 { 02150 ast_pthread_create_background(&netthreadid, NULL, network_thread, NULL); 02151 ast_pthread_create_background(&precachethreadid, NULL, process_precache, NULL); 02152 return 0; 02153 } 02154 02155 static int dundi_do_debug(int fd, int argc, char *argv[]) 02156 { 02157 if (argc != 2) 02158 return RESULT_SHOWUSAGE; 02159 dundidebug = 1; 02160 ast_cli(fd, "DUNDi Debugging Enabled\n"); 02161 return RESULT_SUCCESS; 02162 } 02163 02164 static int dundi_do_store_history(int fd, int argc, char *argv[]) 02165 { 02166 if (argc != 3) 02167 return RESULT_SHOWUSAGE; 02168 global_storehistory = 1; 02169 ast_cli(fd, "DUNDi History Storage Enabled\n"); 02170 return RESULT_SUCCESS; 02171 } 02172 02173 static int dundi_flush(int fd, int argc, char *argv[]) 02174 { 02175 int stats = 0; 02176 if ((argc < 2) || (argc > 3)) 02177 return RESULT_SHOWUSAGE; 02178 if (argc > 2) { 02179 if (!strcasecmp(argv[2], "stats")) 02180 stats = 1; 02181 else 02182 return RESULT_SHOWUSAGE; 02183 } 02184 if (stats) { 02185 /* Flush statistics */ 02186 struct dundi_peer *p; 02187 int x; 02188 AST_LIST_LOCK(&peers); 02189 AST_LIST_TRAVERSE(&peers, p, list) { 02190 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) { 02191 if (p->lookups[x]) 02192 free(p->lookups[x]); 02193 p->lookups[x] = NULL; 02194 p->lookuptimes[x] = 0; 02195 } 02196 p->avgms = 0; 02197 } 02198 AST_LIST_UNLOCK(&peers); 02199 } else { 02200 ast_db_deltree("dundi/cache", NULL); 02201 ast_cli(fd, "DUNDi Cache Flushed\n"); 02202 } 02203 return RESULT_SUCCESS; 02204 } 02205 02206 static int dundi_no_debug(int fd, int argc, char *argv[]) 02207 { 02208 if (argc != 3) 02209 return RESULT_SHOWUSAGE; 02210 dundidebug = 0; 02211 ast_cli(fd, "DUNDi Debugging Disabled\n"); 02212 return RESULT_SUCCESS; 02213 } 02214 02215 static int dundi_no_store_history(int fd, int argc, char *argv[]) 02216 { 02217 if (argc != 4) 02218 return RESULT_SHOWUSAGE; 02219 global_storehistory = 0; 02220 ast_cli(fd, "DUNDi History Storage Disabled\n"); 02221 return RESULT_SUCCESS; 02222 } 02223 02224 static char *model2str(int model) 02225 { 02226 switch(model) { 02227 case DUNDI_MODEL_INBOUND: 02228 return "Inbound"; 02229 case DUNDI_MODEL_OUTBOUND: 02230 return "Outbound"; 02231 case DUNDI_MODEL_SYMMETRIC: 02232 return "Symmetric"; 02233 default: 02234 return "Unknown"; 02235 } 02236 } 02237 02238 static char *complete_peer_helper(const char *line, const char *word, int pos, int state, int rpos) 02239 { 02240 int which=0, len; 02241 char *ret = NULL; 02242 struct dundi_peer *p; 02243 char eid_str[20]; 02244 02245 if (pos != rpos) 02246 return NULL; 02247 AST_LIST_LOCK(&peers); 02248 len = strlen(word); 02249 AST_LIST_TRAVERSE(&peers, p, list) { 02250 const char *s = dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid); 02251 if (!strncasecmp(word, s, len) && ++which > state) 02252 ret = ast_strdup(s); 02253 } 02254 AST_LIST_UNLOCK(&peers); 02255 return ret; 02256 } 02257 02258 static char *complete_peer_4(const char *line, const char *word, int pos, int state) 02259 { 02260 return complete_peer_helper(line, word, pos, state, 3); 02261 } 02262 02263 static int rescomp(const void *a, const void *b) 02264 { 02265 const struct dundi_result *resa, *resb; 02266 resa = a; 02267 resb = b; 02268 if (resa->weight < resb->weight) 02269 return -1; 02270 if (resa->weight > resb->weight) 02271 return 1; 02272 return 0; 02273 } 02274 02275 static void sort_results(struct dundi_result *results, int count) 02276 { 02277 qsort(results, count, sizeof(results[0]), rescomp); 02278 } 02279 02280 static int dundi_do_lookup(int fd, int argc, char *argv[]) 02281 { 02282 int res; 02283 char tmp[256]; 02284 char fs[80] = ""; 02285 char *context; 02286 int x; 02287 int bypass = 0; 02288 struct dundi_result dr[MAX_RESULTS]; 02289 struct timeval start; 02290 if ((argc < 3) || (argc > 4)) 02291 return RESULT_SHOWUSAGE; 02292 if (argc > 3) { 02293 if (!strcasecmp(argv[3], "bypass")) 02294 bypass=1; 02295 else 02296 return RESULT_SHOWUSAGE; 02297 } 02298 ast_copy_string(tmp, argv[2], sizeof(tmp)); 02299 context = strchr(tmp, '@'); 02300 if (context) { 02301 *context = '\0'; 02302 context++; 02303 } 02304 start = ast_tvnow(); 02305 res = dundi_lookup(dr, MAX_RESULTS, NULL, context, tmp, bypass); 02306 02307 if (res < 0) 02308 ast_cli(fd, "DUNDi lookup returned error.\n"); 02309 else if (!res) 02310 ast_cli(fd, "DUNDi lookup returned no results.\n"); 02311 else 02312 sort_results(dr, res); 02313 for (x=0;x<res;x++) { 02314 ast_cli(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)); 02315 ast_cli(fd, " from %s, expires in %d s\n", dr[x].eid_str, dr[x].expiration); 02316 } 02317 ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start)); 02318 return RESULT_SUCCESS; 02319 } 02320 02321 static int dundi_do_precache(int fd, int argc, char *argv[]) 02322 { 02323 int res; 02324 char tmp[256]; 02325 char *context; 02326 struct timeval start; 02327 if ((argc < 3) || (argc > 3)) 02328 return RESULT_SHOWUSAGE; 02329 ast_copy_string(tmp, argv[2], sizeof(tmp)); 02330 context = strchr(tmp, '@'); 02331 if (context) { 02332 *context = '\0'; 02333 context++; 02334 } 02335 start = ast_tvnow(); 02336 res = dundi_precache(context, tmp); 02337 02338 if (res < 0) 02339 ast_cli(fd, "DUNDi precache returned error.\n"); 02340 else if (!res) 02341 ast_cli(fd, "DUNDi precache returned no error.\n"); 02342 ast_cli(fd, "DUNDi lookup completed in %d ms\n", ast_tvdiff_ms(ast_tvnow(), start)); 02343 return RESULT_SUCCESS; 02344 } 02345 02346 static int dundi_do_query(int fd, int argc, char *argv[]) 02347 { 02348 int res; 02349 char tmp[256]; 02350 char *context; 02351 dundi_eid eid; 02352 struct dundi_entity_info dei; 02353 if ((argc < 3) || (argc > 3)) 02354 return RESULT_SHOWUSAGE; 02355 if (dundi_str_to_eid(&eid, argv[2])) { 02356 ast_cli(fd, "'%s' is not a valid EID!\n", argv[2]); 02357 return RESULT_SHOWUSAGE; 02358 } 02359 ast_copy_string(tmp, argv[2], sizeof(tmp)); 02360 context = strchr(tmp, '@'); 02361 if (context) { 02362 *context = '\0'; 02363 context++; 02364 } 02365 res = dundi_query_eid(&dei, context, eid); 02366 if (res < 0) 02367 ast_cli(fd, "DUNDi Query EID returned error.\n"); 02368 else if (!res) 02369 ast_cli(fd, "DUNDi Query EID returned no results.\n"); 02370 else { 02371 ast_cli(fd, "DUNDi Query EID succeeded:\n"); 02372 ast_cli(fd, "Department: %s\n", dei.orgunit); 02373 ast_cli(fd, "Organization: %s\n", dei.org); 02374 ast_cli(fd, "City/Locality: %s\n", dei.locality); 02375 ast_cli(fd, "State/Province: %s\n", dei.stateprov); 02376 ast_cli(fd, "Country: %s\n", dei.country); 02377 ast_cli(fd, "E-mail: %s\n", dei.email); 02378 ast_cli(fd, "Phone: %s\n", dei.phone); 02379 ast_cli(fd, "IP Address: %s\n", dei.ipaddr); 02380 } 02381 return RESULT_SUCCESS; 02382 } 02383 02384 static int dundi_show_peer(int fd, int argc, char *argv[]) 02385 { 02386 struct dundi_peer *peer; 02387 struct permission *p; 02388 char *order; 02389 char eid_str[20]; 02390 int x, cnt; 02391 02392 if (argc != 4) 02393 return RESULT_SHOWUSAGE; 02394 AST_LIST_LOCK(&peers); 02395 AST_LIST_TRAVERSE(&peers, peer, list) { 02396 if (!strcasecmp(dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), argv[3])) 02397 break; 02398 } 02399 if (peer) { 02400 switch(peer->order) { 02401 case 0: 02402 order = "Primary"; 02403 break; 02404 case 1: 02405 order = "Secondary"; 02406 break; 02407 case 2: 02408 order = "Tertiary"; 02409 break; 02410 case 3: 02411 order = "Quartiary"; 02412 break; 02413 default: 02414 order = "Unknown"; 02415 } 02416 ast_cli(fd, "Peer: %s\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid)); 02417 ast_cli(fd, "Model: %s\n", model2str(peer->model)); 02418 ast_cli(fd, "Host: %s\n", peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "<Unspecified>"); 02419 ast_cli(fd, "Dynamic: %s\n", peer->dynamic ? "yes" : "no"); 02420 ast_cli(fd, "Reg: %s\n", peer->registerid < 0 ? "No" : "Yes"); 02421 ast_cli(fd, "In Key: %s\n", ast_strlen_zero(peer->inkey) ? "<None>" : peer->inkey); 02422 ast_cli(fd, "Out Key: %s\n", ast_strlen_zero(peer->outkey) ? "<None>" : peer->outkey); 02423 if (!AST_LIST_EMPTY(&peer->include)) 02424 ast_cli(fd, "Include logic%s:\n", peer->model & DUNDI_MODEL_OUTBOUND ? "" : " (IGNORED)"); 02425 AST_LIST_TRAVERSE(&peer->include, p, list) 02426 ast_cli(fd, "-- %s %s\n", p->allow ? "include" : "do not include", p->name); 02427 if (!AST_LIST_EMPTY(&peer->permit)) 02428 ast_cli(fd, "Query logic%s:\n", peer->model & DUNDI_MODEL_INBOUND ? "" : " (IGNORED)"); 02429 AST_LIST_TRAVERSE(&peer->permit, p, list) 02430 ast_cli(fd, "-- %s %s\n", p->allow ? "permit" : "deny", p->name); 02431 cnt = 0; 02432 for (x = 0;x < DUNDI_TIMING_HISTORY; x++) { 02433 if (peer->lookups[x]) { 02434 if (!cnt) 02435 ast_cli(fd, "Last few query times:\n"); 02436 ast_cli(fd, "-- %d. %s (%d ms)\n", x + 1, peer->lookups[x], peer->lookuptimes[x]); 02437 cnt++; 02438 } 02439 } 02440 if (cnt) 02441 ast_cli(fd, "Average query time: %d ms\n", peer->avgms); 02442 } else 02443 ast_cli(fd, "No such peer '%s'\n", argv[3]); 02444 AST_LIST_UNLOCK(&peers); 02445 return RESULT_SUCCESS; 02446 } 02447 02448 static int dundi_show_peers(int fd, int argc, char *argv[]) 02449 { 02450 #define FORMAT2 "%-20.20s %-15.15s %-10.10s %-8.8s %-15.15s\n" 02451 #define FORMAT "%-20.20s %-15.15s %s %-10.10s %-8.8s %-15.15s\n" 02452 struct dundi_peer *peer; 02453 int registeredonly=0; 02454 char avgms[20]; 02455 char eid_str[20]; 02456 int online_peers = 0; 02457 int offline_peers = 0; 02458 int unmonitored_peers = 0; 02459 int total_peers = 0; 02460 02461 if ((argc != 3) && (argc != 4) && (argc != 5)) 02462 return RESULT_SHOWUSAGE; 02463 if ((argc == 4)) { 02464 if (!strcasecmp(argv[3], "registered")) { 02465 registeredonly = 1; 02466 } else 02467 return RESULT_SHOWUSAGE; 02468 } 02469 AST_LIST_LOCK(&peers); 02470 ast_cli(fd, FORMAT2, "EID", "Host", "Model", "AvgTime", "Status"); 02471 AST_LIST_TRAVERSE(&peers, peer, list) { 02472 char status[20]; 02473 int print_line = -1; 02474 char srch[2000]; 02475 total_peers++; 02476 if (registeredonly && !peer->addr.sin_addr.s_addr) 02477 continue; 02478 if (peer->maxms) { 02479 if (peer->lastms < 0) { 02480 strcpy(status, "UNREACHABLE"); 02481 offline_peers++; 02482 } 02483 else if (peer->lastms > peer->maxms) { 02484 snprintf(status, sizeof(status), "LAGGED (%d ms)", peer->lastms); 02485 offline_peers++; 02486 } 02487 else if (peer->lastms) { 02488 snprintf(status, sizeof(status), "OK (%d ms)", peer->lastms); 02489 online_peers++; 02490 } 02491 else { 02492 strcpy(status, "UNKNOWN"); 02493 offline_peers++; 02494 } 02495 } else { 02496 strcpy(status, "Unmonitored"); 02497 unmonitored_peers++; 02498 } 02499 if (peer->avgms) 02500 snprintf(avgms, sizeof(avgms), "%d ms", peer->avgms); 02501 else 02502 strcpy(avgms, "Unavail"); 02503 snprintf(srch, sizeof(srch), FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), 02504 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)", 02505 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status); 02506 02507 if (argc == 5) { 02508 if (!strcasecmp(argv[3],"include") && strstr(srch,argv[4])) { 02509 print_line = -1; 02510 } else if (!strcasecmp(argv[3],"exclude") && !strstr(srch,argv[4])) { 02511 print_line = 1; 02512 } else if (!strcasecmp(argv[3],"begin") && !strncasecmp(srch,argv[4],strlen(argv[4]))) { 02513 print_line = -1; 02514 } else { 02515 print_line = 0; 02516 } 02517 } 02518 02519 if (print_line) { 02520 ast_cli(fd, FORMAT, dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), 02521 peer->addr.sin_addr.s_addr ? ast_inet_ntoa(peer->addr.sin_addr) : "(Unspecified)", 02522 peer->dynamic ? "(D)" : "(S)", model2str(peer->model), avgms, status); 02523 } 02524 } 02525 ast_cli(fd, "%d dundi peers [%d online, %d offline, %d unmonitored]\n", total_peers, online_peers, offline_peers, unmonitored_peers); 02526 AST_LIST_UNLOCK(&peers); 02527 return RESULT_SUCCESS; 02528 #undef FORMAT 02529 #undef FORMAT2 02530 } 02531 02532 static int dundi_show_trans(int fd, int argc, char *argv[]) 02533 { 02534 #define FORMAT2 "%-22.22s %-5.5s %-5.5s %-3.3s %-3.3s %-3.3s\n" 02535 #define FORMAT "%-16.16s:%5d %-5.5d %-5.5d %-3.3d %-3.3d %-3.3d\n" 02536 struct dundi_transaction *trans; 02537 if (argc != 3) 02538 return RESULT_SHOWUSAGE; 02539 AST_LIST_LOCK(&peers); 02540 ast_cli(fd, FORMAT2, "Remote", "Src", "Dst", "Tx", "Rx", "Ack"); 02541 AST_LIST_TRAVERSE(&alltrans, trans, all) { 02542 ast_cli(fd, FORMAT, ast_inet_ntoa(trans->addr.sin_addr), 02543 ntohs(trans->addr.sin_port), trans->strans, trans->dtrans, trans->oseqno, trans->iseqno, trans->aseqno); 02544 } 02545 AST_LIST_UNLOCK(&peers); 02546 return RESULT_SUCCESS; 02547 #undef FORMAT 02548 #undef FORMAT2 02549 } 02550 02551 static int dundi_show_entityid(int fd, int argc, char *argv[]) 02552 { 02553 char eid_str[20]; 02554 if (argc != 3) 02555 return RESULT_SHOWUSAGE; 02556 AST_LIST_LOCK(&peers); 02557 dundi_eid_to_str(eid_str, sizeof(eid_str), &global_eid); 02558 AST_LIST_UNLOCK(&peers); 02559 ast_cli(fd, "Global EID for this system is '%s'\n", eid_str); 02560 return RESULT_SUCCESS; 02561 } 02562 02563 static int dundi_show_requests(int fd, int argc, char *argv[]) 02564 { 02565 #define FORMAT2 "%-15s %-15s %-15s %-3.3s %-3.3s\n" 02566 #define FORMAT "%-15s %-15s %-15s %-3.3d %-3.3d\n" 02567 struct dundi_request *req; 02568 char eidstr[20]; 02569 if (argc != 3) 02570 return RESULT_SHOWUSAGE; 02571 AST_LIST_LOCK(&peers); 02572 ast_cli(fd, FORMAT2, "Number", "Context", "Root", "Max", "Rsp"); 02573 AST_LIST_TRAVERSE(&requests, req, list) { 02574 ast_cli(fd, FORMAT, req->number, req->dcontext, 02575 dundi_eid_zero(&req->root_eid) ? "<unspecified>" : dundi_eid_to_str(eidstr, sizeof(eidstr), &req->root_eid), req->maxcount, req->respcount); 02576 } 02577 AST_LIST_UNLOCK(&peers); 02578 return RESULT_SUCCESS; 02579 #undef FORMAT 02580 #undef FORMAT2 02581 } 02582 02583 /* Grok-a-dial DUNDi */ 02584 02585 static int dundi_show_mappings(int fd, int argc, char *argv[]) 02586 { 02587 #define FORMAT2 "%-12.12s %-7.7s %-12.12s %-10.10s %-5.5s %-25.25s\n" 02588 #define FORMAT "%-12.12s %-7d %-12.12s %-10.10s %-5.5s %-25.25s\n" 02589 struct dundi_mapping *map; 02590 char fs[256]; 02591 if (argc != 3) 02592 return RESULT_SHOWUSAGE; 02593 AST_LIST_LOCK(&peers); 02594 ast_cli(fd, FORMAT2, "DUNDi Cntxt", "Weight", "Local Cntxt", "Options", "Tech", "Destination"); 02595 AST_LIST_TRAVERSE(&mappings, map, list) { 02596 ast_cli(fd, FORMAT, map->dcontext, map->weight, 02597 ast_strlen_zero(map->lcontext) ? "<none>" : map->lcontext, 02598 dundi_flags2str(fs, sizeof(fs), map->options), tech2str(map->tech), map->dest); 02599 } 02600 AST_LIST_UNLOCK(&peers); 02601 return RESULT_SUCCESS; 02602 #undef FORMAT 02603 #undef FORMAT2 02604 } 02605 02606 static int dundi_show_precache(int fd, int argc, char *argv[]) 02607 { 02608 #define FORMAT2 "%-12.12s %-12.12s %-10.10s\n" 02609 #define FORMAT "%-12.12s %-12.12s %02d:%02d:%02d\n" 02610 struct dundi_precache_queue *qe; 02611 int h,m,s; 02612 time_t now; 02613 02614 if (argc != 3) 02615 return RESULT_SHOWUSAGE; 02616 time(&now); 02617 ast_cli(fd, FORMAT2, "Number", "Context", "Expiration"); 02618 AST_LIST_LOCK(&pcq); 02619 AST_LIST_TRAVERSE(&pcq, qe, list) { 02620 s = qe->expiration - now; 02621 h = s / 3600; 02622 s = s % 3600; 02623 m = s / 60; 02624 s = s % 60; 02625 ast_cli(fd, FORMAT, qe->number, qe->context, h,m,s); 02626 } 02627 AST_LIST_UNLOCK(&pcq); 02628 02629 return RESULT_SUCCESS; 02630 #undef FORMAT 02631 #undef FORMAT2 02632 } 02633 02634 static const char debug_usage[] = 02635 "Usage: dundi debug\n" 02636 " Enables dumping of DUNDi packets for debugging purposes\n"; 02637 02638 static const char no_debug_usage[] = 02639 "Usage: dundi no debug\n" 02640 " Disables dumping of DUNDi packets for debugging purposes\n"; 02641 02642 static const char store_history_usage[] = 02643 "Usage: dundi store history\n" 02644 " Enables storing of DUNDi requests and times for debugging\n" 02645 "purposes\n"; 02646 02647 static const char no_store_history_usage[] = 02648 "Usage: dundi no store history\n" 02649 " Disables storing of DUNDi requests and times for debugging\n" 02650 "purposes\n"; 02651 02652 static const char show_peers_usage[] = 02653 "Usage: dundi show peers\n" 02654 " Lists all known DUNDi peers.\n"; 02655 02656 static const char show_trans_usage[] = 02657 "Usage: dundi show trans\n" 02658 " Lists all known DUNDi transactions.\n"; 02659 02660 static const char show_mappings_usage[] = 02661 "Usage: dundi show mappings\n" 02662 " Lists all known DUNDi mappings.\n"; 02663 02664 static const char show_precache_usage[] = 02665 "Usage: dundi show precache\n" 02666 " Lists all known DUNDi scheduled precache updates.\n"; 02667 02668 static const char show_entityid_usage[] = 02669 "Usage: dundi show entityid\n" 02670 " Displays the global entityid for this host.\n"; 02671 02672 static const char show_peer_usage[] = 02673 "Usage: dundi show peer [peer]\n" 02674 " Provide a detailed description of a specifid DUNDi peer.\n"; 02675 02676 static const char show_requests_usage[] = 02677 "Usage: dundi show requests\n" 02678 " Lists all known pending DUNDi requests.\n"; 02679 02680 static const char lookup_usage[] = 02681 "Usage: dundi lookup <number>[@context] [bypass]\n" 02682 " Lookup the given number within the given DUNDi context\n" 02683 "(or e164 if none is specified). Bypasses cache if 'bypass'\n" 02684 "keyword is specified.\n"; 02685 02686 static const char precache_usage[] = 02687 "Usage: dundi precache <number>[@context]\n" 02688 " Lookup the given number within the given DUNDi context\n" 02689 "(or e164 if none is specified) and precaches the results to any\n" 02690 "upstream DUNDi push servers.\n"; 02691 02692 static const char query_usage[] = 02693 "Usage: dundi query <entity>[@context]\n" 02694 " Attempts to retrieve contact information for a specific\n" 02695 "DUNDi entity identifier (EID) within a given DUNDi context (or\n" 02696 "e164 if none is specified).\n"; 02697 02698 static const char flush_usage[] = 02699 "Usage: dundi flush [stats]\n" 02700 " Flushes DUNDi answer cache, used primarily for debug. If\n" 02701 "'stats' is present, clears timer statistics instead of normal\n" 02702 "operation.\n"; 02703 02704 static struct ast_cli_entry cli_dundi[] = { 02705 { { "dundi", "debug", NULL }, 02706 dundi_do_debug, "Enable DUNDi debugging", 02707 debug_usage }, 02708 02709 { { "dundi", "store", "history", NULL }, 02710 dundi_do_store_history, "Enable DUNDi historic records", 02711 store_history_usage }, 02712 02713 { { "dundi", "no", "store", "history", NULL }, 02714 dundi_no_store_history, "Disable DUNDi historic records", 02715 no_store_history_usage }, 02716 02717 { { "dundi", "flush", NULL }, 02718 dundi_flush, "Flush DUNDi cache", 02719 flush_usage }, 02720 02721 { { "dundi", "no", "debug", NULL }, 02722 dundi_no_debug, "Disable DUNDi debugging", 02723 no_debug_usage }, 02724 02725 { { "dundi", "show", "peers", NULL }, 02726 dundi_show_peers, "Show defined DUNDi peers", 02727 show_peers_usage }, 02728 02729 { { "dundi", "show", "trans", NULL }, 02730 dundi_show_trans, "Show active DUNDi transactions", 02731 show_trans_usage }, 02732 02733 { { "dundi", "show", "entityid", NULL }, 02734 dundi_show_entityid, "Display Global Entity ID", 02735 show_entityid_usage }, 02736 02737 { { "dundi", "show", "mappings", NULL }, 02738 dundi_show_mappings, "Show DUNDi mappings", 02739 show_mappings_usage }, 02740 02741 { { "dundi", "show", "precache", NULL }, 02742 dundi_show_precache, "Show DUNDi precache", 02743 show_precache_usage }, 02744 02745 { { "dundi", "show", "requests", NULL }, 02746 dundi_show_requests, "Show DUNDi requests", 02747 show_requests_usage }, 02748 02749 { { "dundi", "show", "peer", NULL }, 02750 dundi_show_peer, "Show info on a specific DUNDi peer", 02751 show_peer_usage, complete_peer_4 }, 02752 02753 { { "dundi", "lookup", NULL }, 02754 dundi_do_lookup, "Lookup a number in DUNDi", 02755 lookup_usage }, 02756 02757 { { "dundi", "precache", NULL }, 02758 dundi_do_precache, "Precache a number in DUNDi", 02759 precache_usage }, 02760 02761 { { "dundi", "query", NULL }, 02762 dundi_do_query, "Query a DUNDi EID", 02763 query_usage }, 02764 }; 02765 02766 static struct dundi_transaction *create_transaction(struct dundi_peer *p) 02767 { 02768 struct dundi_transaction *trans; 02769 int tid; 02770 02771 /* Don't allow creation of transactions to non-registered peers */ 02772 if (p && !p->addr.sin_addr.s_addr) 02773 return NULL; 02774 tid = get_trans_id(); 02775 if (tid < 1) 02776 return NULL; 02777 if (!(trans = ast_calloc(1, sizeof(*trans)))) 02778 return NULL; 02779 02780 if (global_storehistory) { 02781 trans->start = ast_tvnow(); 02782 ast_set_flag(trans, FLAG_STOREHIST); 02783 } 02784 trans->retranstimer = DUNDI_DEFAULT_RETRANS_TIMER; 02785 trans->autokillid = -1; 02786 if (p) { 02787 apply_peer(trans, p); 02788 if (!p->sentfullkey) 02789 ast_set_flag(trans, FLAG_SENDFULLKEY); 02790 } 02791 trans->strans = tid; 02792 AST_LIST_INSERT_HEAD(&alltrans, trans, all); 02793 02794 return trans; 02795 } 02796 02797 static int dundi_xmit(struct dundi_packet *pack) 02798 { 02799 int res; 02800 if (dundidebug) 02801 dundi_showframe(pack->h, 0, &pack->parent->addr, pack->datalen - sizeof(struct dundi_hdr)); 02802 res = sendto(netsocket, pack->data, pack->datalen, 0, (struct sockaddr *)&pack->parent->addr, sizeof(pack->parent->addr)); 02803 if (res < 0) { 02804 ast_log(LOG_WARNING, "Failed to transmit to '%s:%d': %s\n", 02805 ast_inet_ntoa(pack->parent->addr.sin_addr), 02806 ntohs(pack->parent->addr.sin_port), strerror(errno)); 02807 } 02808 if (res > 0) 02809 res = 0; 02810 return res; 02811 } 02812 02813 static void destroy_packet(struct dundi_packet *pack, int needfree) 02814 { 02815 if (pack->parent) 02816 AST_LIST_REMOVE(&pack->parent->packets, pack, list); 02817 if (pack->retransid > -1) 02818 ast_sched_del(sched, pack->retransid); 02819 if (needfree) 02820 free(pack); 02821 else 02822 pack->retransid = -1; 02823 } 02824 02825 static void destroy_trans(struct dundi_transaction *trans, int fromtimeout) 02826 { 02827 struct dundi_peer *peer; 02828 int ms; 02829 int x; 02830 int cnt; 02831 char eid_str[20]; 02832 if (ast_test_flag(trans, FLAG_ISREG | FLAG_ISQUAL | FLAG_STOREHIST)) { 02833 AST_LIST_TRAVERSE(&peers, peer, list) { 02834 if (peer->regtrans == trans) 02835 peer->regtrans = NULL; 02836 if (peer->qualtrans == trans) { 02837 if (fromtimeout) { 02838 if (peer->lastms > -1) 02839 ast_log(LOG_NOTICE, "Peer '%s' has become UNREACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid)); 02840 peer->lastms = -1; 02841 } else { 02842 ms = ast_tvdiff_ms(ast_tvnow(), peer->qualtx); 02843 if (ms < 1) 02844 ms = 1; 02845 if (ms < peer->maxms) { 02846 if ((peer->lastms >= peer->maxms) || (peer->lastms < 0)) 02847 ast_log(LOG_NOTICE, "Peer '%s' has become REACHABLE!\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid)); 02848 } else if (peer->lastms < peer->maxms) { 02849 ast_log(LOG_NOTICE, "Peer '%s' has become TOO LAGGED (%d ms)\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &peer->eid), ms); 02850 } 02851 peer->lastms = ms; 02852 } 02853 peer->qualtrans = NULL; 02854 } 02855 if (ast_test_flag(trans, FLAG_STOREHIST)) { 02856 if (trans->parent && !ast_strlen_zero(trans->parent->number)) { 02857 if (!dundi_eid_cmp(&trans->them_eid, &peer->eid)) { 02858 peer->avgms = 0; 02859 cnt = 0; 02860 if (peer->lookups[DUNDI_TIMING_HISTORY-1]) 02861 free(peer->lookups[DUNDI_TIMING_HISTORY-1]); 02862 for (x=DUNDI_TIMING_HISTORY-1;x>0;x--) { 02863 peer->lookuptimes[x] = peer->lookuptimes[x-1]; 02864 peer->lookups[x] = peer->lookups[x-1]; 02865 if (peer->lookups[x]) { 02866 peer->avgms += peer->lookuptimes[x]; 02867 cnt++; 02868 } 02869 } 02870 peer->lookuptimes[0] = ast_tvdiff_ms(ast_tvnow(), trans->start); 02871 peer->lookups[0] = ast_malloc(strlen(trans->parent->number) + strlen(trans->parent->dcontext) + 2); 02872 if (peer->lookups[0]) { 02873 sprintf(peer->lookups[0], "%s@%s", trans->parent->number, trans->parent->dcontext); 02874 peer->avgms += peer->lookuptimes[0]; 02875 cnt++; 02876 } 02877 if (cnt) 02878 peer->avgms /= cnt; 02879 } 02880 } 02881 } 02882 } 02883 } 02884 if (trans->parent) { 02885 /* Unlink from parent if appropriate */ 02886 AST_LIST_REMOVE(&trans->parent->trans, trans, parentlist); 02887 if (AST_LIST_EMPTY(&trans->parent->trans)) { 02888 /* Wake up sleeper */ 02889 if (trans->parent->pfds[1] > -1) { 02890 write(trans->parent->pfds[1], "killa!", 6); 02891 } 02892 } 02893 } 02894 /* Unlink from all trans */ 02895 AST_LIST_REMOVE(&alltrans, trans, all); 02896 destroy_packets(&trans->packets); 02897 destroy_packets(&trans->lasttrans); 02898 if (trans->autokillid > -1) 02899 ast_sched_del(sched, trans->autokillid); 02900 trans->autokillid = -1; 02901 if (trans->thread) { 02902 /* If used by a thread, mark as dead and be done */ 02903 ast_set_flag(trans, FLAG_DEAD); 02904 } else 02905 free(trans); 02906 } 02907 02908 static int dundi_rexmit(void *data) 02909 { 02910 struct dundi_packet *pack; 02911 int res; 02912 AST_LIST_LOCK(&peers); 02913 pack = data; 02914 if (pack->retrans < 1) { 02915 pack->retransid = -1; 02916 if (!ast_test_flag(pack->parent, FLAG_ISQUAL)) 02917 ast_log(LOG_NOTICE, "Max retries exceeded to host '%s:%d' msg %d on call %d\n", 02918 ast_inet_ntoa(pack->parent->addr.sin_addr), 02919 ntohs(pack->parent->addr.sin_port), pack->h->oseqno, ntohs(pack->h->strans)); 02920 destroy_trans(pack->parent, 1); 02921 res = 0; 02922 } else { 02923 /* Decrement retransmission, try again */ 02924 pack->retrans--; 02925 dundi_xmit(pack); 02926 res = 1; 02927 } 02928 AST_LIST_UNLOCK(&peers); 02929 return res; 02930 } 02931 02932 static int dundi_send(struct dundi_transaction *trans, int cmdresp, int flags, int final, struct dundi_ie_data *ied) 02933 { 02934 struct dundi_packet *pack; 02935 int res; 02936 int len; 02937 char eid_str[20]; 02938 len = sizeof(struct dundi_packet) + sizeof(struct dundi_hdr) + (ied ? ied->pos : 0); 02939 /* Reserve enough space for encryption */ 02940 if (ast_test_flag(trans, FLAG_ENCRYPT)) 02941 len += 384; 02942 pack = ast_calloc(1, len); 02943 if (pack) { 02944 pack->h = (struct dundi_hdr *)(pack->data); 02945 if (cmdresp != DUNDI_COMMAND_ACK) { 02946 pack->retransid = ast_sched_add(sched, trans->retranstimer, dundi_rexmit, pack); 02947 pack->retrans = DUNDI_DEFAULT_RETRANS - 1; 02948 AST_LIST_INSERT_HEAD(&trans->packets, pack, list); 02949 } 02950 pack->parent = trans; 02951 pack->h->strans = htons(trans->strans); 02952 pack->h->dtrans = htons(trans->dtrans); 02953 pack->h->iseqno = trans->iseqno; 02954 pack->h->oseqno = trans->oseqno; 02955 pack->h->cmdresp = cmdresp; 02956 pack->datalen = sizeof(struct dundi_hdr); 02957 if (ied) { 02958 memcpy(pack->h->ies, ied->buf, ied->pos); 02959 pack->datalen += ied->pos; 02960 } 02961 if (final) { 02962 pack->h->cmdresp |= DUNDI_COMMAND_FINAL; 02963 ast_set_flag(trans, FLAG_FINAL); 02964 } 02965 pack->h->cmdflags = flags; 02966 if (cmdresp != DUNDI_COMMAND_ACK) { 02967 trans->oseqno++; 02968 trans->oseqno = trans->oseqno % 256; 02969 } 02970 trans->aseqno = trans->iseqno; 02971 /* If we have their public key, encrypt */ 02972 if (ast_test_flag(trans, FLAG_ENCRYPT)) { 02973 switch(cmdresp) { 02974 case DUNDI_COMMAND_REGREQ: 02975 case DUNDI_COMMAND_REGRESPONSE: 02976 case DUNDI_COMMAND_DPDISCOVER: 02977 case DUNDI_COMMAND_DPRESPONSE: 02978 case DUNDI_COMMAND_EIDQUERY: 02979 case DUNDI_COMMAND_EIDRESPONSE: 02980 case DUNDI_COMMAND_PRECACHERQ: 02981 case DUNDI_COMMAND_PRECACHERP: 02982 if (dundidebug) 02983 dundi_showframe(pack->h, 2, &trans->addr, pack->datalen - sizeof(struct dundi_hdr)); 02984 res = dundi_encrypt(trans, pack); 02985 break; 02986 default: 02987 res = 0; 02988 } 02989 } else 02990 res = 0; 02991 if (!res) 02992 res = dundi_xmit(pack); 02993 if (res) 02994 ast_log(LOG_NOTICE, "Failed to send packet to '%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid)); 02995 02996 if (cmdresp == DUNDI_COMMAND_ACK) 02997 free(pack); 02998 return res; 02999 } 03000 return -1; 03001 } 03002 03003 static int do_autokill(void *data) 03004 { 03005 struct dundi_transaction *trans = data; 03006 char eid_str[20]; 03007 ast_log(LOG_NOTICE, "Transaction to '%s' took too long to ACK, destroying\n", 03008 dundi_eid_to_str(eid_str, sizeof(eid_str), &trans->them_eid)); 03009 trans->autokillid = -1; 03010 destroy_trans(trans, 0); /* We could actually set it to 1 instead of 0, but we won't ;-) */ 03011 return 0; 03012 } 03013 03014 static void dundi_ie_append_eid_appropriately(struct dundi_ie_data *ied, char *context, dundi_eid *eid, dundi_eid *us) 03015 { 03016 struct dundi_peer *p; 03017 if (!dundi_eid_cmp(eid, us)) { 03018 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid); 03019 return; 03020 } 03021 AST_LIST_LOCK(&peers); 03022 AST_LIST_TRAVERSE(&peers, p, list) { 03023 if (!dundi_eid_cmp(&p->eid, eid)) { 03024 if (has_permission(&p->include, context)) 03025 dundi_ie_append_eid(ied, DUNDI_IE_EID_DIRECT, eid); 03026 else 03027 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid); 03028 break; 03029 } 03030 } 03031 if (!p) 03032 dundi_ie_append_eid(ied, DUNDI_IE_EID, eid); 03033 AST_LIST_UNLOCK(&peers); 03034 } 03035 03036 static int dundi_discover(struct dundi_transaction *trans) 03037 { 03038 struct dundi_ie_data ied; 03039 int x; 03040 if (!trans->parent) { 03041 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n"); 03042 return -1; 03043 } 03044 memset(&ied, 0, sizeof(ied)); 03045 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION); 03046 if (!dundi_eid_zero(&trans->us_eid)) 03047 dundi_ie_append_eid(&ied, DUNDI_IE_EID_DIRECT, &trans->us_eid); 03048 for (x=0;x<trans->eidcount;x++) 03049 dundi_ie_append_eid_appropriately(&ied, trans->parent->dcontext, &trans->eids[x], &trans->us_eid); 03050 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number); 03051 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext); 03052 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl); 03053 if (trans->parent->cbypass) 03054 dundi_ie_append(&ied, DUNDI_IE_CACHEBYPASS); 03055 if (trans->autokilltimeout) 03056 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans); 03057 return dundi_send(trans, DUNDI_COMMAND_DPDISCOVER, 0, 0, &ied); 03058 } 03059 03060 static int precache_trans(struct dundi_transaction *trans, struct dundi_mapping *maps, int mapcount, int *minexp, int *foundanswers) 03061 { 03062 struct dundi_ie_data ied; 03063 int x, res; 03064 int max = 999999; 03065 int expiration = dundi_cache_time; 03066 int ouranswers=0; 03067 dundi_eid *avoid[1] = { NULL, }; 03068 int direct[1] = { 0, }; 03069 struct dundi_result dr[MAX_RESULTS]; 03070 struct dundi_hint_metadata hmd; 03071 if (!trans->parent) { 03072 ast_log(LOG_WARNING, "Tried to discover a transaction with no parent?!?\n"); 03073 return -1; 03074 } 03075 memset(&hmd, 0, sizeof(hmd)); 03076 memset(&dr, 0, sizeof(dr)); 03077 /* Look up the answers we're going to include */ 03078 for (x=0;x<mapcount;x++) 03079 ouranswers = dundi_lookup_local(dr, maps + x, trans->parent->number, &trans->us_eid, ouranswers, &hmd); 03080 if (ouranswers < 0) 03081 ouranswers = 0; 03082 for (x=0;x<ouranswers;x++) { 03083 if (dr[x].weight < max) 03084 max = dr[x].weight; 03085 } 03086 if (max) { 03087 /* If we do not have a canonical result, keep looking */ 03088 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); 03089 if (res > 0) { 03090 /* Append answer in result */ 03091 ouranswers += res; 03092 } 03093 } 03094 03095 if (ouranswers > 0) { 03096 *foundanswers += ouranswers; 03097 memset(&ied, 0, sizeof(ied)); 03098 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION); 03099 if (!dundi_eid_zero(&trans->us_eid)) 03100 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid); 03101 for (x=0;x<trans->eidcount;x++) 03102 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]); 03103 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_NUMBER, trans->parent->number); 03104 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext); 03105 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl); 03106 for (x=0;x<ouranswers;x++) { 03107 /* Add answers */ 03108 if (dr[x].expiration && (expiration > dr[x].expiration)) 03109 expiration = dr[x].expiration; 03110 dundi_ie_append_answer(&ied, DUNDI_IE_ANSWER, &dr[x].eid, dr[x].techint, dr[x].flags, dr[x].weight, dr[x].dest); 03111 } 03112 dundi_ie_append_hint(&ied, DUNDI_IE_HINT, hmd.flags, hmd.exten); 03113 dundi_ie_append_short(&ied, DUNDI_IE_EXPIRATION, expiration); 03114 if (trans->autokilltimeout) 03115 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans); 03116 if (expiration < *minexp) 03117 *minexp = expiration; 03118 return dundi_send(trans, DUNDI_COMMAND_PRECACHERQ, 0, 0, &ied); 03119 } else { 03120 /* Oops, nothing to send... */ 03121 destroy_trans(trans, 0); 03122 return 0; 03123 } 03124 } 03125 03126 static int dundi_query(struct dundi_transaction *trans) 03127 { 03128 struct dundi_ie_data ied; 03129 int x; 03130 if (!trans->parent) { 03131 ast_log(LOG_WARNING, "Tried to query a transaction with no parent?!?\n"); 03132 return -1; 03133 } 03134 memset(&ied, 0, sizeof(ied)); 03135 dundi_ie_append_short(&ied, DUNDI_IE_VERSION, DUNDI_DEFAULT_VERSION); 03136 if (!dundi_eid_zero(&trans->us_eid)) 03137 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->us_eid); 03138 for (x=0;x<trans->eidcount;x++) 03139 dundi_ie_append_eid(&ied, DUNDI_IE_EID, &trans->eids[x]); 03140 dundi_ie_append_eid(&ied, DUNDI_IE_REQEID, &trans->parent->query_eid); 03141 dundi_ie_append_str(&ied, DUNDI_IE_CALLED_CONTEXT, trans->parent->dcontext); 03142 dundi_ie_append_short(&ied, DUNDI_IE_TTL, trans->ttl); 03143 if (trans->autokilltimeout) 03144 trans->autokillid = ast_sched_add(sched, trans->autokilltimeout, do_autokill, trans); 03145 return dundi_send(trans, DUNDI_COMMAND_EIDQUERY, 0, 0, &ied); 03146 } 03147 03148 static int discover_transactions(struct dundi_request *dr) 03149 { 03150 struct dundi_transaction *trans; 03151 AST_LIST_LOCK(&peers); 03152 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) { 03153 dundi_discover(trans); 03154 } 03155 AST_LIST_UNLOCK(&peers); 03156 return 0; 03157 } 03158 03159 static int precache_transactions(struct dundi_request *dr, struct dundi_mapping *maps, int mapcount, int *expiration, int *foundanswers) 03160 { 03161 struct dundi_transaction *trans; 03162 03163 /* Mark all as "in thread" so they don't disappear */ 03164 AST_LIST_LOCK(&peers); 03165 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) { 03166 if (trans->thread) 03167 ast_log(LOG_WARNING, "This shouldn't happen, really...\n"); 03168 trans->thread = 1; 03169 } 03170 AST_LIST_UNLOCK(&peers); 03171 03172 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) { 03173 if (!ast_test_flag(trans, FLAG_DEAD)) 03174 precache_trans(trans, maps, mapcount, expiration, foundanswers); 03175 } 03176 03177 /* Cleanup any that got destroyed in the mean time */ 03178 AST_LIST_LOCK(&peers); 03179 AST_LIST_TRAVERSE_SAFE_BEGIN(&dr->trans, trans, parentlist) { 03180 trans->thread = 0; 03181 if (ast_test_flag(trans, FLAG_DEAD)) { 03182 if (option_debug) 03183 ast_log(LOG_DEBUG, "Our transaction went away!\n"); 03184 /* This is going to remove the transaction from the dundi_request's list, as well 03185 * as the global transactions list */ 03186 destroy_trans(trans, 0); 03187 } 03188 } 03189 AST_LIST_TRAVERSE_SAFE_END 03190 AST_LIST_UNLOCK(&peers); 03191 03192 return 0; 03193 } 03194 03195 static int query_transactions(struct dundi_request *dr) 03196 { 03197 struct dundi_transaction *trans; 03198 03199 AST_LIST_LOCK(&peers); 03200 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) { 03201 dundi_query(trans); 03202 } 03203 AST_LIST_UNLOCK(&peers); 03204 03205 return 0; 03206 } 03207 03208 static int optimize_transactions(struct dundi_request *dr, int order) 03209 { 03210 /* Minimize the message propagation through DUNDi by 03211 alerting the network to hops which should be not be considered */ 03212 struct dundi_transaction *trans; 03213 struct dundi_peer *peer; 03214 dundi_eid tmp; 03215 int x; 03216 int needpush; 03217 03218 AST_LIST_LOCK(&peers); 03219 AST_LIST_TRAVERSE(&dr->trans, trans, parentlist) { 03220 /* Pop off the true root */ 03221 if (trans->eidcount) { 03222 tmp = trans->eids[--trans->eidcount]; 03223 needpush = 1; 03224 } else { 03225 tmp = trans->us_eid; 03226 needpush = 0; 03227 } 03228 03229 AST_LIST_TRAVERSE(&peers, peer, list) { 03230 if (has_permission(&peer->include, dr->dcontext) && 03231 dundi_eid_cmp(&peer->eid, &trans->them_eid) && 03232 (peer->order <= order)) { 03233 /* For each other transaction, make sure we don't 03234 ask this EID about the others if they're not 03235 already in the list */ 03236 if (!dundi_eid_cmp(&tmp, &peer->eid)) 03237 x = -1; 03238 else { 03239 for (x=0;x<trans->eidcount;x++) { 03240 if (!dundi_eid_cmp(&trans->eids[x], &peer->eid)) 03241 break; 03242 } 03243 } 03244 if (x == trans->eidcount) { 03245 /* Nope not in the list, if needed, add us at the end since we're the source */ 03246 if (trans->eidcount < DUNDI_MAX_STACK - needpush) { 03247 trans->eids[trans->eidcount++] = peer->eid; 03248 /* Need to insert the real root (or us) at the bottom now as 03249 a requirement now. */ 03250 needpush = 1; 03251 } 03252 } 03253 } 03254 } 03255 /* If necessary, push the true root back on the end */ 03256 if (needpush) 03257 trans->eids[trans->eidcount++] = tmp; 03258 } 03259 AST_LIST_UNLOCK(&peers); 03260 03261 return 0; 03262 } 03263 03264 static int append_transaction(struct dundi_request *dr, struct dundi_peer *p, int ttl, dundi_eid *avoid[]) 03265 { 03266 struct dundi_transaction *trans; 03267 int x; 03268 char eid_str[20]; 03269 char eid_str2[20]; 03270 03271 /* Ignore if not registered */ 03272 if (!p->addr.sin_addr.s_addr) 03273 return 0; 03274 if (p->maxms && ((p->lastms < 0) || (p->lastms >= p->maxms))) 03275 return 0; 03276 03277 if (option_debug) { 03278 if (ast_strlen_zero(dr->number)) 03279 ast_log(LOG_DEBUG, "Will query peer '%s' for '%s' (context '%s')\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dundi_eid_to_str(eid_str2, sizeof(eid_str2), &dr->query_eid), dr->dcontext); 03280 else 03281 ast_log(LOG_DEBUG, "Will query peer '%s' for '%s@%s'\n", dundi_eid_to_str(eid_str, sizeof(eid_str), &p->eid), dr->number, dr->dcontext); 03282 } 03283 03284 trans = create_transaction(p); 03285 if (!trans) 03286 return -1; 03287 trans->parent = dr; 03288 trans->ttl = ttl; 03289 for (x = 0; avoid[x] && (x < DUNDI_MAX_STACK); x++) 03290 trans->eids[x] = *avoid[x]; 03291 trans->eidcount = x; 03292 AST_LIST_INSERT_HEAD(&dr->trans, trans, parentlist); 03293 03294 return 0; 03295 } 03296 03297 static void cancel_request(struct dundi_request *dr) 03298 { 03299 struct dundi_transaction *trans; 03300 03301 AST_LIST_LOCK(&peers); 03302 while ((trans = AST_LIST_REMOVE_HEAD(&dr->trans, parentlist))) { 03303 /* Orphan transaction from request */ 03304 trans->parent = NULL; 03305 /* Send final cancel */ 03306 dundi_send(trans, DUNDI_COMMAND_CANCEL, 0, 1, NULL); 03307 } 03308 AST_LIST_UNLOCK(&peers); 03309 } 03310 03311 static void abort_request(struct dundi_request *dr) 03312 { 03313 struct dundi_transaction *trans; 03314 03315 AST_LIST_LOCK(&peers); 03316 while ((trans = AST_LIST_FIRST(&dr->trans))) { 03317 /* This will remove the transaction from the list */ 03318 destroy_trans(trans, 0); 03319 } 03320 AST_LIST_UNLOCK(&peers); 03321 } 03322 03323 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[]) 03324 { 03325 struct dundi_peer *p; 03326 int x; 03327 int res; 03328 int pass; 03329 int allowconnect; 03330 char eid_str[20]; 03331 AST_LIST_LOCK(&peers); 03332 AST_LIST_TRAVERSE(&peers, p, list) { 03333 if (modeselect == 1) { 03334 /* Send the precache to push upstreams only! */ 03335 pass = has_permission(&p->permit, dr->dcontext) && (p->pcmodel & DUNDI_MODEL_OUTBOUND); 03336 allowconnect = 1; 03337 } else { 03338 /* Normal lookup / EID query */ 03339 pass = has_permission(&p->include, dr->dcontext); 03340 allowconnect = p->model & DUNDI_MODEL_OUTBOUND; 03341 } 03342 if (skip) { 03343 if (!dundi_eid_cmp(skip, &p->eid)) 03344 pass = 0; 03345 } 03346 if (pass) { 03347 if (p->order <= order) { 03348 /* Check order first, then check cache, regardless of 03349 omissions, this gets us more likely to not have an 03350 affected answer. */ 03351 if((nocache || !(res = cache_lookup(dr, &p->eid, dr->crc32, &dr->expiration)))) { 03352 res = 0; 03353 /* Make sure we haven't already seen it and that it won't 03354 affect our answer */ 03355 for (x=0;avoid[x];x++) { 03356 if (!dundi_eid_cmp(avoid[x], &p->eid) || !dundi_eid_cmp(avoid[x], &p->us_eid)) { 03357 /* If not a direct connection, it affects our answer */ 03358 if (directs && !directs[x]) 03359 ast_clear_flag_nonstd(dr->hmd, DUNDI_HINT_UNAFFECTED); 03360 break; 03361 } 03362 } 03363 /* Make sure we can ask */ 03364 if (allowconnect) { 03365 if (!avoid[x] && (!blockempty || !dundi_eid_zero(&p->us_eid))) { 03366 /* Check for a matching or 0 cache entry */ 03367 append_transaction(dr, p, ttl, avoid); 03368 } else { 03369 if (option_debug) 03370 ast_log(LOG_DEBUG, "Avoiding '%s' in transaction\n", dundi_eid_to_str(eid_str, sizeof(eid_str), avoid[x])); 03371 } 03372 } 03373 } 03374 *foundcache |= res; 03375 } else if (!*skipped || (p->order < *skipped)) 03376 *skipped = p->order; 03377 } 03378 } 03379 AST_LIST_UNLOCK(&peers); 03380 } 03381 03382 static int register_request(struct dundi_request *dr, struct dundi_request **pending) 03383 { 03384 struct dundi_request *cur; 03385 int res=0; 03386 char eid_str[20]; 03387 AST_LIST_LOCK(&peers); 03388 AST_LIST_TRAVERSE(&requests, cur, list) { 03389 if (option_debug) 03390 ast_log(LOG_DEBUG, "Checking '%s@%s' vs '%s@%s'\n", cur->dcontext, cur->number, 03391 dr->dcontext, dr->number); 03392 if (!strcasecmp(cur->dcontext, dr->dcontext) && 03393 !strcasecmp(cur->number, dr->number) && 03394 (!dundi_eid_cmp(&cur->root_eid, &dr->root_eid) || (cur->crc32 == dr->crc32))) { 03395 if (option_debug) 03396 ast_log(LOG_DEBUG, "Found existing query for '%s@%s' for '%s' crc '%08lx'\n", 03397 cur->dcontext, cur->number, dundi_eid_to_str(eid_str, sizeof(eid_str), &cur->root_eid), cur->crc32); 03398 *pending = cur; 03399 res = 1; 03400 break; 03401 } 03402 } 03403 if (!res) { 03404 if (option_debug) 03405 ast_log(LOG_DEBUG, "Registering request for '%s@%s' on behalf of '%s' crc '%08lx'\n", 03406 dr->number, dr->dcontext, dundi_eid_to_str(eid_str, sizeof(eid_str), &dr->root_eid), dr->crc32); 03407 /* Go ahead and link us in since nobody else is searching for this */ 03408 AST_LIST_INSERT_HEAD(&requests, dr, list); 03409 *pending = NULL; 03410 } 03411 AST_LIST_UNLOCK(&peers); 03412 return res; 03413 } 03414 03415 static void unregister_request(struct dundi_request *dr) 03416 { 03417 AST_LIST_LOCK(&peers); 03418 AST_LIST_REMOVE(&requests, dr, list); 03419 AST_LIST_UNLOCK(&peers); 03420 } 03421 03422 static int check_request(struct dundi_request *dr) 03423 { 03424 struct dundi_request *cur; 03425 03426 AST_LIST_LOCK(&peers); 03427 AST_LIST_TRAVERSE(&requests, cur, list) { 03428 if (cur == dr) 03429 break; 03430 } 03431 AST_LIST_UNLOCK(&peers); 03432 03433 return cur ? 1 : 0; 03434 } 03435 03436 static unsigned long avoid_crc32(dundi_eid *avoid[]) 03437 { 03438 /* Idea is that we're calculating a checksum which is independent of 03439 the order that the EID's are listed in */ 03440 unsigned long acrc32 = 0; 03441 int x; 03442 for (x=0;avoid[x];x++) { 03443 /* Order doesn't matter */ 03444 if (avoid[x+1]) { 03445 acrc32 ^= crc32(0L, (unsigned char *)avoid[x], sizeof(dundi_eid)); 03446 } 03447 } 03448 return acrc32; 03449 } 03450 03451 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[]) 03452 { 03453 int res; 03454 struct dundi_request dr, *pending; 03455 dundi_eid *rooteid=NULL; 03456 int x; 03457 int ttlms; 03458 int ms; 03459 int foundcache; 03460 int skipped=0; 03461 int order=0; 03462 char eid_str[20]; 03463 struct timeval start; 03464 03465 /* Don't do anthing for a hungup channel */ 03466 if (chan && chan->_softhangup) 03467 return 0; 03468 03469 ttlms = DUNDI_FLUFF_TIME + ttl * DUNDI_TTL_TIME; 03470 03471 for (x=0;avoid[x];x++) 03472 rooteid = avoid[x]; 03473 /* Now perform real check */ 03474 memset(&dr, 0, sizeof(dr)); 03475 if (pipe(dr.pfds)) { 03476 ast_log(LOG_WARNING, "pipe failed: %s\n" , strerror(errno)); 03477 return -1; 03478 } 03479 dr.dr = result; 03480 dr.hmd = hmd; 03481 dr.maxcount = maxret; 03482 dr.expiration = *expiration; 03483 dr.cbypass = cbypass; 03484 dr.crc32 = avoid_crc32(avoid); 03485 ast_copy_string(dr.dcontext, dcontext ? dcontext : "e164", sizeof(dr.dcontext)); 03486 ast_copy_string(dr.number, number, sizeof(dr.number)); 03487 if (rooteid) 03488 dr.root_eid = *rooteid; 03489 res = register_request(&dr, &pending); 03490 if (res) { 03491 /* Already a request */ 03492 if (rooteid && !dundi_eid_cmp(&dr.root_eid, &pending->root_eid)) { 03493 /* This is on behalf of someone else. Go ahead and close this out since 03494 they'll get their answer anyway. */ 03495 if (option_debug) 03496 ast_log(LOG_DEBUG, "Oooh, duplicate request for '%s@%s' for '%s'\n", 03497 dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &dr.root_eid)); 03498 close(dr.pfds[0]); 03499 close(dr.pfds[1]); 03500 return -2; 03501 } else { 03502 /* Wait for the cache to populate */ 03503 if (option_debug) 03504 ast_log(LOG_DEBUG, "Waiting for similar request for '%s@%s' for '%s'\n", 03505 dr.number,dr.dcontext,dundi_eid_to_str(eid_str, sizeof(eid_str), &pending->root_eid)); 03506 start = ast_tvnow(); 03507 while(check_request(pending) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !chan->_softhangup)) { 03508 /* XXX Would be nice to have a way to poll/select here XXX */ 03509 /* XXX this is a busy wait loop!!! */ 03510 usleep(1); 03511 } 03512 /* Continue on as normal, our cache should kick in */ 03513 } 03514 } 03515 /* Create transactions */ 03516 do { 03517 order = skipped; 03518 skipped = 0; 03519 foundcache = 0; 03520 build_transactions(&dr, ttl, order, &foundcache, &skipped, blockempty, cbypass, modeselect, skip, avoid, direct); 03521 } while (skipped && !foundcache && AST_LIST_EMPTY(&dr.trans)); 03522 /* If no TTL, abort and return 0 now after setting TTL expired hint. Couldn't 03523 do this earlier because we didn't know if we were going to have transactions 03524 or not. */ 03525 if (!ttl) { 03526 ast_set_flag_nonstd(hmd, DUNDI_HINT_TTL_EXPIRED); 03527 abort_request(&dr); 03528 unregister_request(&dr); 03529 close(dr.pfds[0]); 03530 close(dr.pfds[1]); 03531 return 0; 03532 } 03533 03534 /* Optimize transactions */ 03535 optimize_transactions(&dr, order); 03536 /* Actually perform transactions */ 03537 discover_transactions(&dr); 03538 /* Wait for transaction to come back */ 03539 start = ast_tvnow(); 03540 while (!AST_LIST_EMPTY(&dr.trans) && (ast_tvdiff_ms(ast_tvnow(), start) < ttlms) && (!chan || !chan->_softhangup)) { 03541 ms = 100; 03542 ast_waitfor_n_fd(dr.pfds, 1, &ms, NULL); 03543 } 03544 if (chan && chan->_softhangup && option_debug) 03545 ast_log(LOG_DEBUG, "Hrm, '%s' hungup before their query for %s@%s finished\n", chan->name, dr.number, dr.dcontext); 03546 cancel_request(&dr); 03547 unregister_request(&dr); 03548 res = dr.respcount; 03549 *expiration = dr.expiration; 03550 close(dr.pfds[0]); 03551 close(dr.pfds[1]); 03552 return res; 03553 } 03554 03555 int dundi_lookup(struct dundi_result *result, int maxret, struct ast_channel *chan, const char *dcontext, const char *number, int cbypass) 03556 { 03557 struct dundi_hint_metadata hmd; 03558 dundi_eid *avoid[1] = { NULL, }; 03559 int direct[1] = { 0, }; 03560 int expiration = dundi_cache_time; 03561 memset(&hmd, 0, sizeof(hmd)); 03562 hmd.flags = DUNDI_HINT_DONT_ASK | DUNDI_HINT_UNAFFECTED; 03563 return dundi_lookup_internal(result, maxret, chan, dcontext, number, dundi_ttl, 0, &hmd, &expiration, cbypass, 0, NULL, avoid, direct); 03564 } 03565 03566 static void reschedule_precache(const char *number, const char *context, int expiration) 03567 { 03568 int len; 03569 struct dundi_precache_queue *qe, *prev; 03570 03571 AST_LIST_LOCK(&pcq); 03572 AST_LIST_TRAVERSE_SAFE_BEGIN(&pcq, qe, list) { 03573 if (!strcmp(number, qe->number) && !strcasecmp(context, qe->context)) { 03574 AST_LIST_REMOVE_CURRENT(&pcq, list); 03575 break; 03576 } 03577 } 03578 AST_LIST_TRAVERSE_SAFE_END 03579 if (!qe) { 03580 len = sizeof(*qe); 03581 len += strlen(number) + 1; 03582 len += strlen(context) + 1; 03583 if (!(qe = ast_calloc(1, len))) { 03584 AST_LIST_UNLOCK(&pcq); 03585 return; 03586 } 03587 strcpy(qe->number, number); 03588 qe->context = qe->number + strlen(number) + 1; 03589 strcpy(qe->context, context); 03590 } 03591 time(&qe->expiration); 03592 qe->expiration += expiration; 03593 if ((prev = AST_LIST_FIRST(&pcq))) { 03594 while (AST_LIST_NEXT(prev, list) && ((AST_LIST_NEXT(prev, list))->expiration <= qe->expiration)) 03595 prev = AST_LIST_NEXT(prev, list); 03596 AST_LIST_INSERT_AFTER(&pcq, prev, qe, list); 03597 } else 03598 AST_LIST_INSERT_HEAD(&pcq, qe, list); 03599 AST_LIST_UNLOCK(&pcq); 03600 } 03601 03602 static void dundi_precache_full(void) 03603 { 03604 struct dundi_mapping *cur; 03605 struct ast_context *con; 03606 struct ast_exten *e; 03607 03608 AST_LIST_TRAVERSE(&mappings, cur, list) { 03609 ast_log(LOG_NOTICE, "Should precache context '%s'\n", cur->dcontext); 03610 ast_lock_contexts(); 03611 con = NULL; 03612 while ((con = ast_walk_contexts(con))) { 03613 if (strcasecmp(cur->lcontext, ast_get_context_name(con))) 03614 continue; 03615 /* Found the match, now queue them all up */ 03616 ast_lock_context(con); 03617 e = NULL; 03618 while ((e = ast_walk_context_extensions(con, e))) 03619 reschedule_precache(ast_get_extension_name(e), cur->