/** internal sip naptr resolver function: resolves a host name trying: * - NAPTR lookup if the address is not an ip and *proto==0 and *port==0. * The result of the NAPTR query will be used for a SRV lookup * - SRV lookup if the address is not an ip *port==0. The result of the SRV * query will be used for an A/AAAA lookup. * - normal A/AAAA lookup (either fallback from the above or if *port!=0 * and *proto!=0 or port==0 && proto==0) * when performing SRV lookup (*port==0) it will use proto to look for * tcp or udp hosts, otherwise proto is unused; if proto==0 => no SRV lookup * returns: hostent struct & *port filled with the port from the SRV record; * 0 on error */ struct hostent* naptr_sip_resolvehost(str* name, unsigned short* port, char* proto) { struct hostent* he; struct ip_addr* ip; static char tmp[MAX_DNS_NAME]; /* tmp. buff. for SRV lookups and null. term strings */ struct rdata* l; struct rdata* naptr_head; char n_proto; str srv_name; naptr_bmp_t tried_bmp; /* tried bitmap */ char origproto; if(proto) origproto = *proto; naptr_head=0; he=0; if (name->len >= MAX_DNS_NAME) { LM_ERR("domain name too long\n"); goto end; } /* try NAPTR if no port or protocol is specified and NAPTR lookup is * enabled */ if (port && proto && (*proto==0) && (*port==0)){ *proto=PROTO_UDP; /* just in case we don't find another */ if ( ((ip=str2ip(name))!=0) || ((ip=str2ip6(name))!=0) ){ /* we are lucky, this is an ip address */ he=ip_addr2he(name,ip); *port=SIP_PORT; goto end; } memcpy(tmp, name->s, name->len); tmp[name->len] = '\0'; naptr_head=get_record(tmp, T_NAPTR, RES_AR); naptr_iterate_init(&tried_bmp); while((l=naptr_sip_iterate(naptr_head, &tried_bmp, &srv_name, &n_proto))!=0){ if ((he=srv_sip_resolvehost(&srv_name, 1, port, proto, 1, l))!=0){ *proto=n_proto; return he; } } /*clean up on exit*/ LM_DBG("no NAPTR record found for %.*s, trying SRV lookup...\n", name->len, name->s); } /* fallback to srv lookup */ if(proto) *proto = origproto; he=no_naptr_srv_sip_resolvehost(name,port,proto); /* fallback all the way down to A/AAAA */ if (he==0) { he=dns_get_he(name,dns_flags); } end: if (naptr_head) free_rdata_list(naptr_head); return he; }
int i_enum_query_2(struct sip_msg* _msg, char* _suffix, char* _service) { char *user_s; int user_len, i, j; char name[MAX_DOMAIN_SIZE]; char apex[MAX_COMPONENT_SIZE + 1]; char separator[MAX_COMPONENT_SIZE + 1]; int sdl = 0; /* subdomain location: infrastructure enum offset */ int cc_len; struct rdata* head; char string[17]; str *suffix, *service; suffix = (str*)_suffix; service = (str*)_service; if (parse_sip_msg_uri(_msg) < 0) { LM_ERR("Parsing of R-URI failed\n"); return -1; } if (is_e164(&(_msg->parsed_uri.user)) == -1) { LM_ERR("R-URI user is not an E164 number\n"); return -1; } user_s = _msg->parsed_uri.user.s; user_len = _msg->parsed_uri.user.len; /* make sure we don't run out of space in strings */ if (( 2*user_len + MAX_COMPONENT_SIZE + MAX_COMPONENT_SIZE + 4) > MAX_DOMAIN_SIZE) { LM_ERR("Strings too long\n"); return -1; } if ( i_branchlabel.len > MAX_COMPONENT_SIZE ) { LM_ERR("i_branchlabel too long\n"); return -1; } if ( suffix->len > MAX_COMPONENT_SIZE ) { LM_ERR("Suffix too long\n"); return -1; } memcpy(&(string[0]), user_s, user_len); string[user_len] = (char)0; /* Set up parameters as for user-enum */ memcpy(apex, suffix->s , suffix->len); apex[suffix->len] = (char)0; sdl = 0; /* where to insert i-enum separator */ separator[0] = 0; /* don't insert anything */ cc_len = cclen(string + 1); if (!strncasecmp(i_bl_alg.s,"ebl",i_bl_alg.len)) { sdl = cc_len; /* default */ j = 0; memcpy(name, i_branchlabel.s, i_branchlabel.len); j += i_branchlabel.len; name[j++] = '.'; for (i = cc_len ; i > 0; i--) { name[j++] = user_s[i]; name[j++] = '.'; } memcpy(name + j, suffix->s, suffix->len + 1); LM_DBG("Looking for EBL record for %s.\n", name); head = get_record(name, T_EBL); if (head == 0) { LM_DBG("No EBL found for %s. Defaulting to user ENUM.\n",name); } else { struct ebl_rdata* ebl; ebl = (struct ebl_rdata *) head->rdata; LM_DBG("EBL record for %s is %d / %.*s / %.*s.\n", name, ebl->position, (int)ebl->separator_len, ebl->separator,(int)ebl->apex_len, ebl->apex); if ((ebl->apex_len > MAX_COMPONENT_SIZE) || (ebl->separator_len > MAX_COMPONENT_SIZE)) { LM_ERR("EBL strings too long\n"); return -1; } if (ebl->position > 15) { LM_ERR("EBL position too large (%d)\n", ebl->position); return -1; } sdl = ebl->position; memcpy(separator, ebl->separator, ebl->separator_len); separator[ebl->separator_len] = 0; memcpy(apex, ebl->apex, ebl->apex_len); apex[ebl->apex_len] = 0; free_rdata_list(head); } } else if (!strncasecmp(i_bl_alg.s,"txt",i_bl_alg.len)) { sdl = cc_len; /* default */ memcpy(separator, i_branchlabel.s, i_branchlabel.len); separator[i_branchlabel.len] = 0; /* no change to apex */ j = 0; memcpy(name, i_branchlabel.s, i_branchlabel.len); j += i_branchlabel.len; name[j++] = '.'; for (i = cc_len ; i > 0; i--) { name[j++] = user_s[i]; name[j++] = '.'; } memcpy(name + j, suffix->s, suffix->len + 1); head = get_record(name, T_TXT); if (head == 0) { LM_DBG("TXT found for %s. Defaulting to %d\n", name, cc_len); } else { sdl = atoi(((struct txt_rdata*)head->rdata)->txt); LM_DBG("TXT record for %s is %d.\n", name, sdl); if ((sdl < 0) || (sdl > 10)) { LM_ERR("Sdl %d out of bounds. Set back to cc_len.\n", sdl); sdl = cc_len; } free_rdata_list(head); } } else { /* defaults to CC */ sdl = cc_len; memcpy(separator, i_branchlabel.s, i_branchlabel.len); separator[i_branchlabel.len] = 0; /* no change to apex */ } j = 0; sdl++; /* to avoid comparing i to (sdl+1) */ for (i = user_len - 1; i > 0; i--) { name[j] = user_s[i]; name[j + 1] = '.'; j = j + 2; if (separator[0] && (i == sdl)) { /* insert the I-ENUM separator here? */ strcpy(name + j, separator); /* we've checked string sizes. */ j += strlen(separator); name[j++] = '.'; } } memcpy(name + j, apex, strlen(apex)+1); return do_query(_msg, string, name, service); }
/* * Makes enum query on name. On success, rewrites user part and * replaces Request-URI. */ int do_query(struct sip_msg* _msg, char *user, char *name, str *service) { char uri[MAX_URI_SIZE]; char new_uri[MAX_URI_SIZE]; unsigned int priority, curr_prio, first; qvalue_t q; struct rdata* head; struct rdata* l; struct naptr_rdata* naptr; str pattern, replacement, result, new_result; head = get_record(name, T_NAPTR); if (head == 0) { LM_DBG("No NAPTR record found for %s.\n", name); return -1; } naptr_sort(&head); q = MAX_Q - 10; curr_prio = 0; first = 1; for (l = head; l; l = l->next) { if (l->type != T_NAPTR) continue; /*should never happen*/ naptr = (struct naptr_rdata*)l->rdata; if (naptr == 0) { LM_ERR("Null rdata in DNS response\n"); continue; } LM_DBG("ENUM query on %s: order %u, pref %u, flen %u, flags '%.*s', " "slen %u, services '%.*s', rlen %u, regexp '%.*s'\n", name, naptr->order, naptr->pref, naptr->flags_len, (int)(naptr->flags_len), ZSW(naptr->flags), naptr->services_len, (int)(naptr->services_len), ZSW(naptr->services), naptr->regexp_len, (int)(naptr->regexp_len), ZSW(naptr->regexp)); if (sip_match(naptr, service) == 0) continue; if (parse_naptr_regexp(&(naptr->regexp[0]), naptr->regexp_len, &pattern, &replacement) < 0) { LM_ERR("Parsing of NAPTR regexp failed\n"); continue; } result.s = &(uri[0]); result.len = MAX_URI_SIZE; /* Avoid making copies of pattern and replacement */ pattern.s[pattern.len] = (char)0; replacement.s[replacement.len] = (char)0; if (reg_replace(pattern.s, replacement.s, user, &result) < 0) { pattern.s[pattern.len] = '!'; replacement.s[replacement.len] = '!'; LM_ERR("Regexp replace failed\n"); continue; } LM_DBG("Resulted in replacement: '%.*s'\n", result.len, ZSW(result.s)); pattern.s[pattern.len] = '!'; replacement.s[replacement.len] = '!'; if (param.len > 0) { if (result.len + param.len > MAX_URI_SIZE - 1) { LM_ERR("URI is too long\n"); continue; } new_result.s = &(new_uri[0]); new_result.len = MAX_URI_SIZE; if (add_uri_param(&result, ¶m, &new_result) == 0) { LM_ERR("Parsing of URI <%.*s> failed\n", result.len, result.s); continue; } if (new_result.len > 0) { result = new_result; } } if (first) { if (set_ruri(_msg, &result) == -1) { goto done; } set_ruri_q(q); first = 0; curr_prio = ((naptr->order) << 16) + naptr->pref; } else { priority = ((naptr->order) << 16) + naptr->pref; if (priority > curr_prio) { q = q - 10; curr_prio = priority; } if (append_branch(_msg, &result, 0, 0, q, 0, 0) == -1) { goto done; } } } done: free_rdata_list(head); return first ? -1 : 1; }
/* * Check if from user is a valid enum based user, and check to make sure * that the src_ip == an srv record that maps to the enum from user. */ int is_from_user_enum_2(struct sip_msg* _msg, char* _suffix, char* _service) { struct ip_addr addr; struct hostent* he; unsigned short zp; unsigned short proto; char *user_s; int user_len, i, j; char name[MAX_DOMAIN_SIZE]; char uri[MAX_URI_SIZE]; struct sip_uri *furi; struct sip_uri luri; struct rdata* head; str* suffix; str* service; struct rdata* l; struct naptr_rdata* naptr; str pattern, replacement, result; char string[17]; if (parse_from_header(_msg) < 0) { LM_ERR("Failed to parse From header\n"); return -1; } if(_msg->from==NULL || get_from(_msg)==NULL) { LM_DBG("No From header\n"); return -1; } if ((furi = parse_from_uri(_msg)) == NULL) { LM_ERR("Failed to parse From URI\n"); return -1; } suffix = (str*)_suffix; service = (str*)_service; if (is_e164(&(furi->user)) == -1) { LM_ERR("From URI user is not an E164 number\n"); return -1; } /* assert: the from user is a valid formatted e164 string */ user_s = furi->user.s; user_len = furi->user.len; j = 0; for (i = user_len - 1; i > 0; i--) { name[j] = user_s[i]; name[j + 1] = '.'; j = j + 2; } memcpy(name + j, suffix->s, suffix->len + 1); head = get_record(name, T_NAPTR); if (head == 0) { LM_DBG("No NAPTR record found for %s.\n", name); return -3; } /* we have the naptr records, loop and find an srv record with */ /* same ip address as source ip address, if we do then true is returned */ for (l = head; l; l = l->next) { if (l->type != T_NAPTR) continue; /*should never happen*/ naptr = (struct naptr_rdata*)l->rdata; if (naptr == 0) { LM_ERR("Null rdata in DNS response\n"); free_rdata_list(head); return -4; } LM_DBG("ENUM query on %s: order %u, pref %u, flen %u, flags " "'%.*s', slen %u, services '%.*s', rlen %u, " "regexp '%.*s'\n", name, naptr->order, naptr->pref, naptr->flags_len, (int)(naptr->flags_len), ZSW(naptr->flags), naptr->services_len, (int)(naptr->services_len), ZSW(naptr->services), naptr->regexp_len, (int)(naptr->regexp_len), ZSW(naptr->regexp)); if (sip_match(naptr, service) != 0) { if (parse_naptr_regexp(&(naptr->regexp[0]), naptr->regexp_len, &pattern, &replacement) < 0) { free_rdata_list(head); /*clean up*/ LM_ERR("Parsing of NAPTR regexp failed\n"); return -5; } #ifdef LATER if ((pattern.len == 4) && (strncmp(pattern.s, "^.*$", 4) == 0)) { LM_DBG("Resulted in replacement: '%.*s'\n", replacement.len, ZSW(replacement.s)); retval = set_uri(_msg, replacement.s, replacement.len); free_rdata_list(head); /*clean up*/ return retval; } #endif result.s = &(uri[0]); result.len = MAX_URI_SIZE; /* Avoid making copies of pattern and replacement */ pattern.s[pattern.len] = (char)0; replacement.s[replacement.len] = (char)0; /* We have already checked the size of _msg->parsed_uri.user.s */ memcpy(&(string[0]), user_s, user_len); string[user_len] = (char)0; if (reg_replace(pattern.s, replacement.s, &(string[0]), &result) < 0) { pattern.s[pattern.len] = '!'; replacement.s[replacement.len] = '!'; LM_ERR("Regexp replace failed\n"); free_rdata_list(head); /*clean up*/ return -6; } LM_DBG("Resulted in replacement: '%.*s'\n", result.len, ZSW(result.s)); if(parse_uri(result.s, result.len, &luri) < 0) { LM_ERR("Parsing of URI <%.*s> failed\n", result.len, result.s); free_rdata_list(head); /*clean up*/ return -7; } pattern.s[pattern.len] = '!'; replacement.s[replacement.len] = '!'; zp = 0; proto = PROTO_NONE; he = sip_resolvehost(&luri.host, &zp, &proto, (luri.type==SIPS_URI_T)?1:0 , 0); hostent2ip_addr(&addr, he, 0); if(ip_addr_cmp(&addr, &_msg->rcv.src_ip)) { free_rdata_list(head); return(1); } } } free_rdata_list(head); /*clean up*/ LM_DBG("FAIL\n"); /* must not have found the record */ return(-8); }
int enum_pv_query_3(struct sip_msg* _msg, char* _sp, char* _suffix, char* _service) { char *user_s; int user_len, i, j, first; char name[MAX_DOMAIN_SIZE]; char uri[MAX_URI_SIZE]; char new_uri[MAX_URI_SIZE]; unsigned int priority, curr_prio; qvalue_t q; char tostring[17]; struct rdata* head; struct rdata* l; struct naptr_rdata* naptr; str pattern, replacement, result, new_result; str *suffix, *service; char string[17]; pv_spec_t *sp; pv_value_t pv_val; sp = (pv_spec_t *)_sp; suffix = (str*)_suffix; service = (str*)_service; /* * Get R-URI user to tostring */ if (parse_sip_msg_uri(_msg) < 0) { LM_ERR("R-URI parsing failed\n"); return -1; } user_s = _msg->parsed_uri.user.s; user_len = _msg->parsed_uri.user.len; memcpy(&(tostring[0]), user_s, user_len); tostring[user_len] = (char)0; /* * Get E.164 number from pseudo variable */ if (sp && (pv_get_spec_value(_msg, sp, &pv_val) == 0)) { if (pv_val.flags & PV_VAL_STR) { if (pv_val.rs.len == 0 || pv_val.rs.s == NULL) { LM_DBG("Missing E.164 number\n"); return -1; } } else { LM_DBG("Pseudo variable value is not string\n"); return -1; } } else { LM_DBG("Cannot get pseudo variable value\n"); return -1; } if (is_e164(&(pv_val.rs)) == -1) { LM_ERR("pseudo variable does not contain an E164 number\n"); return -1; } user_s = pv_val.rs.s; user_len = pv_val.rs.len; memcpy(&(string[0]), user_s, user_len); string[user_len] = (char)0; j = 0; for (i = user_len - 1; i > 0; i--) { name[j] = user_s[i]; name[j + 1] = '.'; j = j + 2; } memcpy(name + j, suffix->s, suffix->len + 1); head = get_record(name, T_NAPTR); if (head == 0) { LM_DBG("No NAPTR record found for %s.\n", name); return -1; } naptr_sort(&head); q = MAX_Q - 10; curr_prio = 0; first = 1; for (l = head; l; l = l->next) { if (l->type != T_NAPTR) continue; /*should never happen*/ naptr = (struct naptr_rdata*)l->rdata; if (naptr == 0) { LM_ERR("Null rdata in DNS response\n"); continue; } LM_DBG("ENUM query on %s: order %u, pref %u, flen %u, flags " "'%.*s', slen %u, services '%.*s', rlen %u, " "regexp '%.*s'\n", name, naptr->order, naptr->pref, naptr->flags_len, (int)(naptr->flags_len), ZSW(naptr->flags), naptr->services_len, (int)(naptr->services_len), ZSW(naptr->services), naptr->regexp_len, (int)(naptr->regexp_len), ZSW(naptr->regexp)); if (sip_match(naptr, service) == 0) continue; if (parse_naptr_regexp(&(naptr->regexp[0]), naptr->regexp_len, &pattern, &replacement) < 0) { LM_ERR("Parsing of NAPTR regexp failed\n"); continue; } result.s = &(uri[0]); result.len = MAX_URI_SIZE; /* Avoid making copies of pattern and replacement */ pattern.s[pattern.len] = (char)0; replacement.s[replacement.len] = (char)0; if (reg_replace(pattern.s, replacement.s, &(tostring[0]), &result) < 0) { pattern.s[pattern.len] = '!'; replacement.s[replacement.len] = '!'; LM_ERR("Regexp replace failed\n"); continue; } LM_DBG("Resulted in replacement: '%.*s'\n", result.len, ZSW(result.s)); pattern.s[pattern.len] = '!'; replacement.s[replacement.len] = '!'; if (param.len > 0) { if (result.len + param.len > MAX_URI_SIZE - 1) { LM_ERR("URI is too long\n"); continue; } new_result.s = &(new_uri[0]); new_result.len = MAX_URI_SIZE; if (add_uri_param(&result, ¶m, &new_result) == 0) { LM_ERR("Parsing of URI <%.*s> failed\n", result.len, result.s); continue; } if (new_result.len > 0) { result = new_result; } } if (first) { if (set_ruri(_msg, &result) == -1) { goto done; } set_ruri_q(q); first = 0; curr_prio = ((naptr->order) << 16) + naptr->pref; } else { priority = ((naptr->order) << 16) + naptr->pref; if (priority > curr_prio) { q = q - 10; curr_prio = priority; } if (append_branch(_msg, &result, 0, 0, q, 0, 0) == -1) { goto done; } } } done: free_rdata_list(head); return first ? -1 : 1; }
static struct rdata* deserialize_dns_rdata(char *buff,int buf_len,int do_decoding) { unsigned char *p; int max_len=0,actual_len=0,entry_len=0; struct rdata *head,*it,**last; struct naptr_rdata *naptr_rd; struct srv_rdata *srv_rd; struct txt_rdata *txt_rd; struct ebl_rdata *ebl_rd; head=it=NULL; last=&head; if (do_decoding) { max_len = calc_max_base64_decode_len(buf_len); } else { max_len = buf_len; } if (dec_rdata_buf == NULL || max_len > dec_rdata_buf_len) { /* realloc buff if not enough space */ dec_rdata_buf = pkg_realloc(dec_rdata_buf,max_len); if (dec_rdata_buf == NULL) { LM_ERR("No more pkg\n"); return NULL; } dec_rdata_buf_len = max_len; } if (do_decoding) { /* decode base64 buf */ actual_len = base64decode(dec_rdata_buf,(unsigned char *)buff,buf_len); p = dec_rdata_buf; } else { memcpy(dec_rdata_buf,buff,buf_len); actual_len = buf_len; p = dec_rdata_buf; } while ( p < dec_rdata_buf+actual_len) { it = pkg_malloc(sizeof(struct rdata)); if (it == 0) { LM_ERR("no more pkg mem\n"); goto it_alloc_error; } /* copy type, class & ttl */ memcpy(it,p,rdata_struct_len); p+=rdata_struct_len; it->next=0; it->rdata=0; switch (it->type) { case T_A: it->rdata = pkg_malloc(sizeof(struct a_rdata)); if (it->rdata == 0) { LM_ERR("no more pkg\n"); goto rdata_alloc_error; } memcpy(p,it->rdata,sizeof(struct a_rdata)); p+=sizeof(struct a_rdata); *last=it; last=&(it->next); break; case T_AAAA: it->rdata = pkg_malloc(sizeof(struct aaaa_rdata)); if (it->rdata == 0) { LM_ERR("no more pkg\n"); goto rdata_alloc_error; } memcpy(p,it->rdata,sizeof(struct aaaa_rdata)); p+=sizeof(struct aaaa_rdata); *last=it; last=&(it->next); break; case T_CNAME: it->rdata = pkg_malloc(sizeof(struct cname_rdata)); if (it->rdata == 0) { LM_ERR("no more pkg\n"); goto rdata_alloc_error; } memcpy(&entry_len,p,sizeof(int)); p+=sizeof(int); memcpy(((struct cname_rdata*)it->rdata)->name, p,entry_len+1); p+=entry_len+1; *last=it; last=&(it->next); break; case T_NAPTR: it->rdata = pkg_malloc(sizeof(struct naptr_rdata)); if (it->rdata == 0) { LM_ERR("no more pkg\n"); goto rdata_alloc_error; } naptr_rd = (struct naptr_rdata*)it->rdata; memcpy(naptr_rd,p,2*sizeof(unsigned short) + sizeof(unsigned int)); p+=2*sizeof(unsigned short) + sizeof(unsigned int); memcpy(naptr_rd->flags,p,naptr_rd->flags_len+1); p+=naptr_rd->flags_len+1; memcpy(&naptr_rd->services_len,p,sizeof(unsigned int)); p+=sizeof(unsigned int); memcpy(naptr_rd->services,p,naptr_rd->services_len+1); p+=naptr_rd->services_len+1; memcpy(&naptr_rd->regexp_len,p,sizeof(unsigned int)); p+=sizeof(unsigned int); memcpy(naptr_rd->regexp,p,naptr_rd->regexp_len+1); p+=naptr_rd->regexp_len+1; memcpy(&naptr_rd->repl_len,p,sizeof(unsigned int)); p+=sizeof(unsigned int); memcpy(naptr_rd->repl,p,naptr_rd->repl_len+1); p+=naptr_rd->repl_len+1; *last=it; last=&(it->next); break; case T_SRV: it->rdata = pkg_malloc(sizeof(struct srv_rdata)); if (it->rdata == 0) { LM_ERR("no more pkg\n"); goto rdata_alloc_error; } srv_rd = (struct srv_rdata*)it->rdata; memcpy(srv_rd,p,4*sizeof(unsigned short) + sizeof(unsigned int)); p+=4*sizeof(unsigned short) + sizeof(unsigned int); memcpy(srv_rd->name,p,srv_rd->name_len+1); p+=srv_rd->name_len+1; *last=it; last=&(it->next); break; case T_TXT: it->rdata = pkg_malloc(sizeof(struct txt_rdata)); if (it->rdata == 0) { LM_ERR("no more pkg\n"); goto rdata_alloc_error; } txt_rd = (struct txt_rdata*)it->rdata; memcpy(&entry_len,p,sizeof(int)); p+=sizeof(int); memcpy(txt_rd->txt,p,entry_len+1); p+=entry_len+1; *last=it; last=&(it->next); break; case T_EBL: it->rdata = pkg_malloc(sizeof(struct ebl_rdata)); if (it->rdata == 0) { LM_ERR("no more pkg\n"); goto rdata_alloc_error; } ebl_rd = (struct ebl_rdata*)it->rdata; memcpy(ebl_rd,p,sizeof(unsigned char) + sizeof(unsigned int)); p+=sizeof(unsigned char)+sizeof(unsigned int); memcpy(ebl_rd->separator,p,ebl_rd->separator_len+1); p+=ebl_rd->separator_len+1; memcpy(&ebl_rd->apex_len,p,sizeof(unsigned int)); p+=sizeof(unsigned int); memcpy(ebl_rd->apex,p,ebl_rd->apex_len+1); p+=ebl_rd->apex_len+1; *last=it; last=&(it->next); break; } } return head; rdata_alloc_error: if (it) pkg_free(it); it_alloc_error: if (head) free_rdata_list(head); return NULL; }
/* gets the DNS records for name:type * returns a dyn. alloc'ed struct rdata linked list with the parsed responses * or 0 on error * see rfc1035 for the query/response format */ struct rdata* get_record(char* name, int type, int flags) { int size; int skip; int qno, answers_no; int i, r; static union dns_query buff; unsigned char* p; unsigned char* end; unsigned char* rd_end; static char rec_name[MAX_DNS_NAME]; /* placeholder for the record name */ int rec_name_len; unsigned short rtype, class, rdlength; unsigned int ttl; struct rdata* head; struct rdata** crt; struct rdata** last; struct rdata* rd; struct srv_rdata* srv_rd; struct srv_rdata* crt_srv; int search_list_used; int name_len; struct rdata* fullname_rd; char c; #ifdef USE_DNSSEC val_status_t val_status; #endif name_len=strlen(name); for (i = 0; i < name_len; i++) { c = name[i]; if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || ((c >= '0') && (c <= '9')) || (name[i] == '.') || (name[i] == '-') || (name[i] == '_')) continue; LM_DBG("'%s' is not domain name\n", name); return 0; } if (cfg_get(core, core_cfg, dns_search_list)==0) { search_list_used=0; name_len=0; } else { search_list_used=1; } fullname_rd=0; #ifndef USE_DNSSEC size=res_search(name, C_IN, type, buff.buff, sizeof(buff)); #else size=val_res_query((val_context_t *) NULL, (char *) name, (int) C_IN, (int) type, (unsigned char *) buff.buff, (int) sizeof(buff), &val_status); if(!val_istrusted(val_status)){ LOG(L_INFO, "INFO: got not trusted record when resolving %s\n",name); } #endif if (unlikely(size<0)) { DBG("get_record: lookup(%s, %d) failed\n", name, type); goto not_found; } else if (unlikely(size > sizeof(buff))) size=sizeof(buff); head=rd=0; last=crt=&head; p=buff.buff+DNS_HDR_SIZE; end=buff.buff+size; if (unlikely(p>=end)) goto error_boundary; qno=ntohs((unsigned short)buff.hdr.qdcount); for (r=0; r<qno; r++){ /* skip the name of the question */ if (unlikely((p=dns_skipname(p, end))==0)) { LOG(L_ERR, "ERROR: get_record: skipname==0\n"); goto error; } p+=2+2; /* skip QCODE & QCLASS */ #if 0 for (;(p<end && (*p)); p++); p+=1+2+2; /* skip the ending '\0, QCODE and QCLASS */ #endif if (unlikely(p>end)) { LOG(L_ERR, "ERROR: get_record: p>=end\n"); goto error; } }; answers_no=ntohs((unsigned short)buff.hdr.ancount); again: for (r=0; (r<answers_no) && (p<end); r++){ #if 0 /* ignore it the default domain name */ if ((p=dns_skipname(p, end))==0) { LOG(L_ERR, "ERROR: get_record: skip_name=0 (#2)\n"); goto error; } #else if (unlikely((skip=dn_expand(buff.buff, end, p, rec_name, MAX_DNS_NAME-1))==-1)){ LOG(L_ERR, "ERROR: get_record: dn_expand(rec_name) failed\n"); goto error; } #endif p+=skip; rec_name_len=strlen(rec_name); if (unlikely(rec_name_len>255)){ LOG(L_ERR, "ERROR: get_record: dn_expand(rec_name): name too" " long (%d)\n", rec_name_len); goto error; } /* check if enough space is left for type, class, ttl & size */ if (unlikely((p+2+2+4+2)>end)) goto error_boundary; /* get type */ memcpy((void*) &rtype, (void*)p, 2); rtype=ntohs(rtype); p+=2; /* get class */ memcpy((void*) &class, (void*)p, 2); class=ntohs(class); p+=2; /* get ttl*/ memcpy((void*) &ttl, (void*)p, 4); ttl=ntohl(ttl); p+=4; /* get size */ memcpy((void*)&rdlength, (void*)p, 2); rdlength=ntohs(rdlength); p+=2; rd_end=p+rdlength; if (unlikely((rd_end)>end)) goto error_boundary; if ((flags & RES_ONLY_TYPE) && (rtype!=type)){ /* skip */ p=rd_end; continue; } /* expand the "type" record (rdata)*/ rd=(struct rdata*) local_malloc(sizeof(struct rdata)+rec_name_len+ 1-1); if (rd==0){ LOG(L_ERR, "ERROR: get_record: out of memory\n"); goto error; } rd->type=rtype; rd->pclass=class; rd->ttl=ttl; rd->next=0; memcpy(rd->name, rec_name, rec_name_len); rd->name[rec_name_len]=0; rd->name_len=rec_name_len; /* check if full name matches */ if ((search_list_used==1)&&(fullname_rd==0)&& (rec_name_len>=name_len)&& (strncasecmp(rec_name, name, name_len)==0)) { /* now we have record whose name is the same (up-to the * name_len with the searched one): * if the length is the same - we found full match, no fake * cname needed, just clear the flag * if the length of the name differs - it has matched using * search list remember the rd, so we can create fake CNAME * record when all answers are used and no better match found */ if (rec_name_len==name_len) search_list_used=0; /* this is safe.... here was rec_name_len > name_len */ else if (rec_name[name_len]=='.') { #ifdef HAVE_RESOLV_RES if ((cfg_get(core, core_cfg, dns_search_fmatch)==0) || (match_search_list(&_res, rec_name+name_len+1)!=0)) #endif fullname_rd=rd; } } switch(rtype){ case T_SRV: srv_rd= dns_srv_parser(buff.buff, end, rd_end, p); rd->rdata=(void*)srv_rd; if (unlikely(srv_rd==0)) goto error_parse; /* insert sorted into the list */ for (crt=&head; *crt; crt= &((*crt)->next)){ if ((*crt)->type!=T_SRV) continue; crt_srv=(struct srv_rdata*)(*crt)->rdata; if ((srv_rd->priority < crt_srv->priority) || ( (srv_rd->priority == crt_srv->priority) && (srv_rd->weight > crt_srv->weight) ) ){ /* insert here */ goto skip; } } last=&(rd->next); /*end of for => this will be the last element*/ skip: /* insert here */ rd->next=*crt; *crt=rd; break; case T_A: rd->rdata=(void*) dns_a_parser(p, rd_end); if (unlikely(rd->rdata==0)) goto error_parse; *last=rd; /* last points to the last "next" or the list head*/ last=&(rd->next); break; case T_AAAA: rd->rdata=(void*) dns_aaaa_parser(p, rd_end); if (unlikely(rd->rdata==0)) goto error_parse; *last=rd; last=&(rd->next); break; case T_CNAME: rd->rdata=(void*) dns_cname_parser(buff.buff, end, p); if(unlikely(rd->rdata==0)) goto error_parse; *last=rd; last=&(rd->next); break; case T_NAPTR: rd->rdata=(void*)dns_naptr_parser(buff.buff, end, rd_end, p); if(unlikely(rd->rdata==0)) goto error_parse; *last=rd; last=&(rd->next); break; case T_TXT: rd->rdata= dns_txt_parser(buff.buff, rd_end, p); if (rd->rdata==0) goto error_parse; *last=rd; last=&(rd->next); break; case T_EBL: rd->rdata= dns_ebl_parser(buff.buff, end, rd_end, p); if (rd->rdata==0) goto error_parse; *last=rd; last=&(rd->next); break; case T_PTR: rd->rdata=(void*) dns_ptr_parser(buff.buff, end, p); if(unlikely(rd->rdata==0)) goto error_parse; *last=rd; last=&(rd->next); break; default: LOG(L_ERR, "WARNING: get_record: unknown type %d\n", rtype); rd->rdata=0; *last=rd; last=&(rd->next); } p+=rdlength; } if (flags & RES_AR){ flags&=~RES_AR; answers_no=ntohs((unsigned short)buff.hdr.nscount); #ifdef RESOLVE_DBG DBG("get_record: skipping %d NS (p=%p, end=%p)\n", answers_no, p, end); #endif for (r=0; (r<answers_no) && (p<end); r++){ /* skip over the ns records */ if ((p=dns_skipname(p, end))==0) { LOG(L_ERR, "ERROR: get_record: skip_name=0 (#3)\n"); goto error; } /* check if enough space is left for type, class, ttl & size */ if (unlikely((p+2+2+4+2)>end)) goto error_boundary; memcpy((void*)&rdlength, (void*)p+2+2+4, 2); p+=2+2+4+2+ntohs(rdlength); } answers_no=ntohs((unsigned short)buff.hdr.arcount); #ifdef RESOLVE_DBG DBG("get_record: parsing %d ARs (p=%p, end=%p)\n", answers_no, p, end); #endif goto again; /* add also the additional records */ } /* if the name was expanded using DNS search list * create fake CNAME record to convert the short name * (queried) to long name (answered) */ if ((search_list_used==1)&&(fullname_rd!=0)) { rd=(struct rdata*) local_malloc(sizeof(struct rdata)+name_len+1-1); if (unlikely(rd==0)){ LOG(L_ERR, "ERROR: get_record: out of memory\n"); goto error; } rd->type=T_CNAME; rd->pclass=fullname_rd->pclass; rd->ttl=fullname_rd->ttl; rd->next=head; memcpy(rd->name, name, name_len); rd->name[name_len]=0; rd->name_len=name_len; /* alloc sizeof struct + space for the null terminated name */ rd->rdata=(void*)local_malloc(sizeof(struct cname_rdata)-1+ head->name_len+1); if(unlikely(rd->rdata==0)){ LOG(L_ERR, "ERROR: get_record: out of memory\n"); goto error_rd; } ((struct cname_rdata*)(rd->rdata))->name_len=fullname_rd->name_len; memcpy(((struct cname_rdata*)(rd->rdata))->name, fullname_rd->name, fullname_rd->name_len); ((struct cname_rdata*)(rd->rdata))->name[head->name_len]=0; head=rd; } return head; error_boundary: LOG(L_ERR, "ERROR: get_record: end of query buff reached\n"); if (head) free_rdata_list(head); return 0; error_parse: LOG(L_ERR, "ERROR: get_record: rdata parse error (%s, %d), %p-%p" " rtype=%d, class=%d, ttl=%d, rdlength=%d \n", name, type, p, end, rtype, class, ttl, rdlength); error_rd: if (rd) local_free(rd); /* rd->rdata=0 & rd is not linked yet into the list */ error: LOG(L_ERR, "ERROR: get_record \n"); if (head) free_rdata_list(head); not_found: /* increment error counter */ counter_inc(dns_cnts_h.failed_dns_req); return 0; }
/* internal sip srv resolver: resolves a host name trying: * - SRV lookup if the address is not an ip *port==0. The result of the SRV * query will be used for an A/AAAA lookup. * - normal A/AAAA lookup (either fallback from the above or if *port!=0 * and *proto!=0 or port==0 && proto==0) * when performing SRV lookup (*port==0) it will use *proto to look for * tcp or udp hosts, otherwise proto is unused; if proto==0 => no SRV lookup * If zt is set, name will be assumed to be 0 terminated and some copy * operations will be avoided. * If is_srv is set it will assume name has the srv prefixes for sip already * appended and it's already 0-term'ed; if not it will append them internally. * If ars !=0, it will first try to look through them and only if the SRV * record is not found it will try doing a DNS query (ars will not be * freed, the caller should take care of them) * returns: hostent struct & *port filled with the port from the SRV record; * 0 on error */ struct hostent* srv_sip_resolvehost(str* name, int zt, unsigned short* port, char* proto, int is_srv, struct rdata* ars) { struct hostent* he; struct ip_addr* ip; static char tmp[MAX_DNS_NAME]; /* tmp. buff. for SRV lookups and null. term strings */ struct rdata* l; struct srv_rdata* srv; struct rdata* srv_head; char* srv_target; char srv_proto; /* init */ srv_head=0; srv_target=0; if (name->len >= MAX_DNS_NAME) { LOG(L_ERR, "sip_resolvehost: domain name too long\n"); he=0; goto end; } #ifdef RESOLVE_DBG DBG("srv_sip_resolvehost: %.*s:%d proto=%d\n", name->len, name->s, port?(int)*port:-1, proto?(int)*proto:-1); #endif if (is_srv){ /* skip directly to srv resolving */ srv_proto=(proto)?*proto:0; *port=(srv_proto==PROTO_TLS)?SIPS_PORT:SIP_PORT; if (zt){ srv_target=name->s; /* name.s must be 0 terminated in this case */ }else{ memcpy(tmp, name->s, name->len); tmp[name->len] = '\0'; srv_target=tmp; } goto do_srv; /* skip to the actual srv query */ } if (proto){ /* makes sure we have a protocol set*/ if (*proto==0) *proto=srv_proto=PROTO_UDP; /* default */ else srv_proto=*proto; }else{ srv_proto=PROTO_UDP; } /* try SRV if no port specified (draft-ietf-sip-srv-06) */ if ((port)&&(*port==0)){ *port=(srv_proto==PROTO_TLS)?SIPS_PORT:SIP_PORT; /* just in case we don't find another */ /* check if it's an ip address */ if (((ip=str2ip(name))!=0) #ifdef USE_IPV6 || ((ip=str2ip6(name))!=0) #endif ){ /* we are lucky, this is an ip address */ he=ip_addr2he(name, ip); goto end; } if ((name->len+SRV_MAX_PREFIX_LEN+1)>MAX_DNS_NAME){ LOG(L_WARN, "WARNING: sip_resolvehost: domain name too long (%d)," " unable to perform SRV lookup\n", name->len); }else{ switch(srv_proto){ case PROTO_NONE: /* no proto specified, use udp */ if (proto) *proto=PROTO_UDP; /* no break */ case PROTO_UDP: memcpy(tmp, SRV_UDP_PREFIX, SRV_UDP_PREFIX_LEN); memcpy(tmp+SRV_UDP_PREFIX_LEN, name->s, name->len); tmp[SRV_UDP_PREFIX_LEN + name->len] = '\0'; break; case PROTO_TCP: memcpy(tmp, SRV_TCP_PREFIX, SRV_TCP_PREFIX_LEN); memcpy(tmp+SRV_TCP_PREFIX_LEN, name->s, name->len); tmp[SRV_TCP_PREFIX_LEN + name->len] = '\0'; break; case PROTO_TLS: memcpy(tmp, SRV_TLS_PREFIX, SRV_TLS_PREFIX_LEN); memcpy(tmp+SRV_TLS_PREFIX_LEN, name->s, name->len); tmp[SRV_TLS_PREFIX_LEN + name->len] = '\0'; break; case PROTO_SCTP: memcpy(tmp, SRV_SCTP_PREFIX, SRV_SCTP_PREFIX_LEN); memcpy(tmp+SRV_SCTP_PREFIX_LEN, name->s, name->len); tmp[SRV_SCTP_PREFIX_LEN + name->len] = '\0'; break; default: LOG(L_CRIT, "BUG: sip_resolvehost: unknown proto %d\n", srv_proto); he=0; goto end; } srv_target=tmp; do_srv: /* try to find the SRV records inside previous ARs first*/ for (l=ars; l; l=l->next){ if (l->type!=T_SRV) continue; srv=(struct srv_rdata*) l->rdata; if (srv==0){ LOG(L_CRIT, "sip_resolvehost: BUG: null rdata\n"); /* cleanup on exit only */ break; } he=resolvehost(srv->name); if (he!=0){ /* we found it*/ #ifdef RESOLVE_DBG DBG("sip_resolvehost: found SRV(%s) = %s:%d in AR\n", srv_target, srv->name, srv->port); #endif *port=srv->port; /* cleanup on exit */ goto end; } } srv_head=get_record(srv_target, T_SRV, RES_ONLY_TYPE); for(l=srv_head; l; l=l->next){ if (l->type!=T_SRV) continue; /*should never happen*/ srv=(struct srv_rdata*) l->rdata; if (srv==0){ LOG(L_CRIT, "sip_resolvehost: BUG: null rdata\n"); /* cleanup on exit only */ break; } he=resolvehost(srv->name); if (he!=0){ /* we found it*/ #ifdef RESOLVE_DBG DBG("sip_resolvehost: SRV(%s) = %s:%d\n", srv_target, srv->name, srv->port); #endif *port=srv->port; /* cleanup on exit */ goto end; } } if (is_srv){ /* if the name was already into SRV format it doesn't make * any sense to fall back to A/AAAA */ he=0; goto end; } /* cleanup on exit */ #ifdef RESOLVE_DBG DBG("sip_resolvehost: no SRV record found for %.*s," " trying 'normal' lookup...\n", name->len, name->s); #endif } } /*skip_srv:*/ if (likely(!zt)){ memcpy(tmp, name->s, name->len); tmp[name->len] = '\0'; he=resolvehost(tmp); }else{ he=resolvehost(name->s); } end: #ifdef RESOLVE_DBG DBG("srv_sip_resolvehost: returning %p (%.*s:%d proto=%d)\n", he, name->len, name->s, port?(int)*port:-1, proto?(int)*proto:-1); #endif if (srv_head) free_rdata_list(srv_head); return he; }
int enum_query_2(struct sip_msg* _msg, char* _suffix, char* _service) { char *user_s; int user_len, i, j, first; char name[MAX_DOMAIN_SIZE]; char uri[MAX_URI_SIZE]; char new_uri[MAX_URI_SIZE]; unsigned int priority, curr_prio; qvalue_t q; struct rdata* head; struct rdata* l; struct naptr_rdata* naptr; str pattern, replacement, result, new_result; char string[17]; str *suffix, *service; suffix = (str*)_suffix; service = (str*)_service; if (parse_sip_msg_uri(_msg) < 0) { LOG(L_ERR, "enum_query(): uri parsing failed\n"); return -1; } if (is_e164(&(_msg->parsed_uri.user)) == -1) { LOG(L_ERR, "enum_query(): uri user is not an E164 number\n"); return -1; } user_s = _msg->parsed_uri.user.s; user_len = _msg->parsed_uri.user.len; memcpy(&(string[0]), user_s, user_len); string[user_len] = (char)0; j = 0; for (i = user_len - 1; i > 0; i--) { name[j] = user_s[i]; name[j + 1] = '.'; j = j + 2; } memcpy(name + j, suffix->s, suffix->len + 1); head = get_record(name, T_NAPTR); if (head == 0) { DBG("enum_query(): No NAPTR record found for %s.\n", name); return -1; } naptr_sort(&head); q = MAX_Q - 10; curr_prio = 0; first = 1; for (l = head; l; l = l->next) { if (l->type != T_NAPTR) continue; /*should never happen*/ naptr = (struct naptr_rdata*)l->rdata; if (naptr == 0) { LOG(L_CRIT, "enum_query: BUG: null rdata\n"); continue; } DBG("enum_query(): order %u, pref %u, flen %u, flags '%.*s', slen %u, " "services '%.*s', rlen %u, regexp '%.*s'\n", naptr->order, naptr->pref, naptr->flags_len, (int)(naptr->flags_len), ZSW(naptr->flags), naptr->services_len, (int)(naptr->services_len), ZSW(naptr->services), naptr->regexp_len, (int)(naptr->regexp_len), ZSW(naptr->regexp)); if (sip_match(naptr, service) == 0) continue; if (parse_naptr_regexp(&(naptr->regexp[0]), naptr->regexp_len, &pattern, &replacement) < 0) { LOG(L_ERR, "enum_query(): parsing of NAPTR regexp failed\n"); continue; } result.s = &(uri[0]); result.len = MAX_URI_SIZE; /* Avoid making copies of pattern and replacement */ pattern.s[pattern.len] = (char)0; replacement.s[replacement.len] = (char)0; if (reg_replace(pattern.s, replacement.s, &(string[0]), &result) < 0) { pattern.s[pattern.len] = '!'; replacement.s[replacement.len] = '!'; LOG(L_ERR, "enum_query(): regexp replace failed\n"); continue; } DBG("enum_query(): resulted in replacement: '%.*s'\n", result.len, ZSW(result.s)); pattern.s[pattern.len] = '!'; replacement.s[replacement.len] = '!'; if (param.len > 0) { if (result.len + param.len > MAX_URI_SIZE - 1) { LOG(L_ERR, "ERROR: enum_query(): URI is too long\n"); continue; } new_result.s = &(new_uri[0]); new_result.len = MAX_URI_SIZE; if (add_uri_param(&result, ¶m, &new_result) == 0) { LOG(L_ERR, "ERROR: enum_query(): Parsing of URI failed\n"); continue; } if (new_result.len > 0) { result = new_result; } } if (first) { if (rewrite_uri(_msg, &result) == -1) { goto done; } set_ruri_q(q); first = 0; curr_prio = ((naptr->order) << 16) + naptr->pref; } else { priority = ((naptr->order) << 16) + naptr->pref; if (priority > curr_prio) { q = q - 10; curr_prio = priority; } if (append_branch(_msg, &result, 0, q, 0, 0) == -1) { goto done; } } } done: free_rdata_list(head); return first ? -1 : 1; }