/** * Evaluates the routing elements in locally originated request or reply to * locally originated request. * If original INVITE was in-dialog (had to-tag), it uses the * routes present there (b/c the 2xx for it does not have a RR set, normally). * Otherwise, use the reply (b/c the INVITE does not have yet the complete * route set). * * @return: negative for failure; out params: * - list: route set; * - ruri: RURI to be used in ACK; * - nexthop: where to first send the ACK. * * NOTE: assumes rpl's parsed to EOF! * */ static int eval_uac_routing(sip_msg_t *rpl, const struct retr_buf *inv_rb, str* contact, struct rte **list, str *ruri, str *next_hop) { sip_msg_t orig_inv, *sipmsg; /* reparse original INVITE */ rte_t *t, *prev_t, *rtset = NULL; int is_req; struct sip_uri puri; static size_t chklen; /* parse the retr. buffer */ memset(&orig_inv, 0, sizeof(struct sip_msg)); orig_inv.buf = inv_rb->buffer; orig_inv.len = inv_rb->buffer_len; LM_DBG("reparsing retransmission buffer of original INVITE:\n%.*s\n", (int)orig_inv.len, orig_inv.buf); if (parse_msg(orig_inv.buf, orig_inv.len, &orig_inv) != 0) { LM_ERR("failed to parse retr buffer (weird!): \n%.*s\n", (int)orig_inv.len, orig_inv.buf); return -1; } /* check if we need to look at request or reply */ if ((parse_headers(&orig_inv, HDR_TO_F, 0) < 0) || (! orig_inv.to)) { /* the bug is at message assembly */ LM_BUG("failed to parse INVITE retr. buffer and/or extract 'To' HF:" "\n%.*s\n", (int)orig_inv.len, orig_inv.buf); goto error; } if (((struct to_body *)orig_inv.to->parsed)->tag_value.len) { LM_DBG("building ACK for in-dialog INVITE (using RS in orig. INV.)\n"); if (parse_headers(&orig_inv, HDR_EOH_F, 0) < 0) { LM_BUG("failed to parse INVITE retr. buffer to EOH:" "\n%.*s\n", (int)orig_inv.len, orig_inv.buf); goto error; } sipmsg = &orig_inv; is_req = 1; } else { LM_DBG("building ACK for out-of-dialog INVITE (using RS in RR set).\n"); sipmsg = rpl; is_req = 0; } /* extract the route set */ if (get_uac_rs(sipmsg, is_req, &rtset) < 0) { LM_ERR("failed to extract route set.\n"); goto error; } if (! rtset) { /* No routes */ *ruri = *contact; *next_hop = *contact; } else if (! is_req) { /* out of dialog req. */ if (parse_uri(rtset->ptr->nameaddr.uri.s, rtset->ptr->nameaddr.uri.len, &puri) < 0) { LM_ERR("failed to parse first route in set.\n"); goto error; } if (puri.lr.s) { /* Next hop is loose router */ *ruri = *contact; *next_hop = rtset->ptr->nameaddr.uri; } else { /* Next hop is strict router */ *ruri = rtset->ptr->nameaddr.uri; *next_hop = *ruri; /* consume first route, b/c it will be put in RURI */ t = rtset; rtset = rtset->next; pkg_free(t); } } else { unsigned long route_flags = inv_rb->flags; LM_DBG("UAC rb flags: 0x%x.\n", (unsigned int)route_flags); eval_flags: switch (route_flags & (F_RB_NH_LOOSE|F_RB_NH_STRICT)) { case 0: LM_WARN("calculate_hooks() not called when built the local UAC of " "in-dialog request, or called with empty route set.\n"); /* try to figure out what kind of hop is the next one * (strict/loose) by reading the original invite */ if ((route_flags = nhop_type(&orig_inv, rtset, &inv_rb->dst, contact))) { LM_DBG("original request's next hop type evaluated to: 0x%x.\n", (unsigned int)route_flags); goto eval_flags; } else { LM_ERR("failed to establish what kind of router the next " "hop is.\n"); goto error; } break; case F_RB_NH_LOOSE: *ruri = *contact; *next_hop = rtset->ptr->nameaddr.uri; break; case F_RB_NH_STRICT: /* find ptr to last route body that contains the (possibly) old * remote target */ for (t = rtset, prev_t = NULL; t->next; prev_t = t, t = t->next) ; if ((t->ptr->len == contact->len) && (memcmp(t->ptr->nameaddr.name.s, contact->s, contact->len) == 0)){ /* the remote target didn't update -> keep the whole route set, * including the last entry */ /* do nothing */ } else { /* trash last entry and replace with new remote target */ free_rte_list(t); /* compact the rr_t struct along with rte. this way, free'ing * it can be done along with rte chunk, independent of Route * header parser's allocator (using pkg/shm) */ chklen = sizeof(struct rte) + sizeof(rr_t); if (! (t = pkg_malloc(chklen))) { ERR("out of pkg memory (%d required)\n", (int)chklen); /* last element was freed, unlink it */ if(prev_t == NULL) { /* there is only one elem in route set: the remote target */ rtset = NULL; } else { prev_t->next = NULL; } goto error; } /* this way, .free_rr is also set to 0 (!!!) */ memset(t, 0, chklen); ((rr_t *)&t[1])->nameaddr.name = *contact; ((rr_t *)&t[1])->len = contact->len; /* chain the new route elem in set */ if (prev_t == NULL) /* there is only one elem in route set: the remote target */ rtset = t; else prev_t->next = t; } *ruri = *GET_RURI(&orig_inv); /* reuse original RURI */ *next_hop = *ruri; break; default: /* probably a mem corruption */ LM_BUG("next hop of original request marked as both loose" " and strict router (buffer: %.*s).\n", inv_rb->buffer_len, inv_rb->buffer); #ifdef EXTRA_DEBUG abort(); #else goto error; #endif } } *list = rtset; free_sip_msg(&orig_inv); /* all went well */ return 0; error: free_sip_msg(&orig_inv); if (rtset) free_rte_list(rtset); return -1; }
/** * Extract route set from the message (out of Record-Route, if reply, OR * Route, if request). * The route set is returned into the "UAC-format" (keep order for Rs, reverse * RRs). */ static inline int get_uac_rs(sip_msg_t *msg, int is_req, struct rte **rtset) { struct hdr_field* ptr; rr_t *p, *new_p; struct rte *t, *head, *old_head; head = 0; for (ptr = is_req ? msg->route : msg->record_route; ptr; ptr = ptr->next) { switch (ptr->type) { case HDR_RECORDROUTE_T: if (is_req) continue; break; case HDR_ROUTE_T: if (! is_req) continue; break; default: continue; } if (parse_rr(ptr) < 0) { LM_ERR("failed to parse Record-/Route HF (%d).\n", ptr->type); goto err; } p = (rr_t*)ptr->parsed; while(p) { if (! (t = pkg_malloc(sizeof(struct rte)))) { LM_ERR("out of pkg mem (asked for: %d).\n", (int)sizeof(struct rte)); goto err; } if (is_req) { /* in case of requests, the sip_msg structure is free'd before * rte list is evaluated => must do a copy of it */ if (duplicate_rr(&new_p, p) < 0) { pkg_free(t); LM_ERR("failed to duplicate RR"); goto err; } t->ptr = new_p; } else { t->ptr = p; } t->free_rr = is_req; t->next = head; head = t; p = p->next; } } if (is_req) { /* harvesting the R/RR HF above inserts at head, which suites RRs (as * they must be reversed, anyway), but not Rs => reverse once more */ old_head = head; head = 0; while (old_head) { t = old_head; old_head = old_head->next; t->next = head; head = t; } } *rtset = head; return 0; err: free_rte_list(head); return -1; }
/* * The function creates an ACK for a local INVITE. If 200 OK, route set * will be created and parsed */ char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans, unsigned int branch, str* to, unsigned int *len) { char *req_buf, *p, *via; unsigned int via_len; char branch_buf[MAX_BRANCH_PARAM_LEN]; int branch_len; str branch_str; struct hostport hp; struct rte* list; str contact, ruri, *cont; struct socket_info* send_sock; str next_hop; if (rpl->first_line.u.reply.statuscode < 300 ) { /* build e2e ack for 2xx reply -> we need the route set */ if (get_contact_uri(rpl, &contact) < 0) { return 0; } if (process_routeset(rpl, &contact, &list, &ruri, &next_hop) < 0) { return 0; } if ((contact.s != ruri.s) || (contact.len != ruri.len)) { /* contact != ruri means that the next * hop is a strict router, cont will be non-zero * and print_routeset will append it at the end * of the route set */ cont = &contact; } else { /* Next hop is a loose router, nothing to append */ cont = 0; } } else { /* build hop-by-hop ack for negative reply -> * ruri is the same as in INVITE; no route set */ ruri = Trans->uac[branch].uri; cont = 0; list = 0; } /* method, separators, version: "ACK sip:[email protected] SIP/2.0" */ *len = SIP_VERSION_LEN + ACK_LEN + 2 /* spaces */ + CRLF_LEN; *len += ruri.len; /* use same socket as for INVITE -bogdan */ send_sock = Trans->uac[branch].request.dst.send_sock; if (!t_calc_branch(Trans, branch, branch_buf, &branch_len)) goto error; branch_str.s = branch_buf; branch_str.len = branch_len; set_hostport(&hp, 0); /* build via */ via = via_builder(&via_len, send_sock, &branch_str, 0, send_sock->proto, &hp); if (!via) { LM_ERR("no via header got from builder\n"); goto error; } *len+= via_len; /*headers*/ *len += Trans->from.len + Trans->callid.len + to->len + Trans->cseq_n.len + 1 + ACK_LEN + CRLF_LEN; /* copy'n'paste Route headers */ *len += calc_routeset_len(list, cont); /* User Agent */ if (server_signature) *len += user_agent_header.len + CRLF_LEN; /* Content Length, EoM */ *len += CONTENT_LENGTH_LEN + 1 + CRLF_LEN + CRLF_LEN; req_buf = shm_malloc(*len + 1); if (!req_buf) { LM_ERR("no more share memory\n"); goto error01; } p = req_buf; append_string( p, ACK " ", ACK_LEN+1 ); append_string(p, ruri.s, ruri.len ); append_string( p, " " SIP_VERSION CRLF, 1 + SIP_VERSION_LEN + CRLF_LEN); /* insert our via */ append_string(p, via, via_len); /*other headers*/ append_string(p, Trans->from.s, Trans->from.len); append_string(p, Trans->callid.s, Trans->callid.len); append_string(p, to->s, to->len); append_string(p, Trans->cseq_n.s, Trans->cseq_n.len); *(p++) = ' '; append_string(p, ACK CRLF, ACK_LEN+CRLF_LEN); /* Routeset */ p = print_rs(p, list, cont); /* User Agent header, Content Length, EoM */ if (server_signature) { append_string(p, user_agent_header.s, user_agent_header.len); append_string(p, CRLF CONTENT_LENGTH "0" CRLF CRLF, CRLF_LEN+CONTENT_LENGTH_LEN + 1 + CRLF_LEN + CRLF_LEN); } else { append_string(p, CONTENT_LENGTH "0" CRLF CRLF, CONTENT_LENGTH_LEN + 1 + CRLF_LEN + CRLF_LEN); } *p = 0; pkg_free(via); free_rte_list(list); return req_buf; error01: pkg_free(via); error: free_rte_list(list); return 0; }
/* * The function creates an ACK to 200 OK. Route set will be created * and parsed and the dst parameter will contain the destination to which * the request should be send. The function is used by tm when it * generates local ACK to 200 OK (on behalf of applications using uac) */ char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans, unsigned int branch, str *hdrs, str *body, unsigned int *len, struct dest_info* dst) { char *req_buf, *p, *via; unsigned int via_len; char branch_buf[MAX_BRANCH_PARAM_LEN]; int branch_len; str branch_str; struct hostport hp; struct rte* list; str contact, ruri, *cont; str next_hop; str body_len; str _to, *to = &_to; #ifdef USE_DNS_FAILOVER struct dns_srv_handle dns_h; #endif #ifdef WITH_AS_SUPPORT /* With AS support, TM allows for external modules to generate building of * the ACK; in this case, the ACK's retransmission buffer is built once * and kept in memory (to help when retransmitted 2xx are received and ACK * must be resent). * Allocation of the string raw buffer that holds the ACK is piggy-backed * with allocation of the retransmission buffer (since both have the same * life-cycle): both the string buffer and retransm. buffer are placed * into the same allocated chunk of memory (retr. buffer first, string * buffer follows).In this case, the 'len' param is used as in-out * parameter: 'in' to give the extra space needed by the retr. buffer, * 'out' to return the lenght of the allocated string buffer. */ unsigned offset = *len; #endif if (parse_headers(rpl, HDR_EOH_F, 0) == -1 || !rpl->to) { LM_ERR("Error while parsing headers.\n"); return 0; } else { _to.s = rpl->to->name.s; _to.len = rpl->to->len; } if (get_contact_uri(rpl, &contact) < 0) { return 0; } if (eval_uac_routing(rpl, &Trans->uac[branch].request, &contact, &list, &ruri, &next_hop) < 0) { LM_ERR("failed to evaluate routing elements.\n"); return 0; } LM_DBG("ACK RURI: `%.*s', NH: `%.*s'.\n", STR_FMT(&ruri), STR_FMT(&next_hop)); if ((contact.s != ruri.s) || (contact.len != ruri.len)) { /* contact != ruri means that the next * hop is a strict router, cont will be non-zero * and print_routeset will append it at the end * of the route set */ cont = &contact; } else { /* Next hop is a loose router, nothing to append */ cont = 0; } /* method, separators, version: "ACK sip:[email protected] SIP/2.0" */ *len = SIP_VERSION_LEN + ACK_LEN + 2 /* spaces */ + CRLF_LEN; *len += ruri.len; /* dst */ switch(cfg_get(tm, tm_cfg, local_ack_mode)){ case 1: /* send the local 200 ack to the same dst as the corresp. invite*/ *dst=Trans->uac[branch].request.dst; break; case 2: /* send the local 200 ack to the same dst as the 200 reply source*/ init_dst_from_rcv(dst, &rpl->rcv); dst->send_flags=rpl->fwd_send_flags; break; case 0: default: /* rfc conformant behaviour: use the next_hop determined from the * contact and the route set */ #ifdef USE_DNS_FAILOVER if (cfg_get(core, core_cfg, use_dns_failover)){ dns_srv_handle_init(&dns_h); if ((uri2dst(&dns_h , dst, rpl, &next_hop, PROTO_NONE)==0) || (dst->send_sock==0)){ dns_srv_handle_put(&dns_h); LM_ERR("no socket found\n"); goto error; } dns_srv_handle_put(&dns_h); /* not needed any more */ }else{ if ((uri2dst(0 , dst, rpl, &next_hop, PROTO_NONE)==0) || (dst->send_sock==0)){ LM_ERR("no socket found\n"); goto error; } } #else /* USE_DNS_FAILOVER */ if ( (uri2dst( dst, rpl, &next_hop, PROTO_NONE)==0) || (dst->send_sock==0)){ LM_ERR("no socket found\n"); goto error; } #endif /* USE_DNS_FAILOVER */ break; } /* via */ if (!t_calc_branch(Trans, branch, branch_buf, &branch_len)) goto error; branch_str.s = branch_buf; branch_str.len = branch_len; set_hostport(&hp, 0); via = via_builder(&via_len, NULL, dst, &branch_str, 0, &hp); if (!via) { LM_ERR("No via header got from builder\n"); goto error; } *len+= via_len; /* headers */ *len += Trans->from.len + Trans->callid.len + to->len + Trans->cseq_n.len + 1 + ACK_LEN + CRLF_LEN; /* copy'n'paste Route headers */ *len += calc_routeset_len(list, cont); /* User Agent */ if (server_signature) *len += user_agent_hdr.len + CRLF_LEN; /* extra headers */ if (hdrs) *len += hdrs->len; /* body */ if (body) { body_len.s = int2str(body->len, &body_len.len); *len += body->len; } else { body_len.len = 0; body_len.s = NULL; /*4gcc*/ *len += 1; /* for the (Cont-Len:) `0' */ } /* Content Length, EoM */ *len += CONTENT_LENGTH_LEN + body_len.len + CRLF_LEN + CRLF_LEN; #if WITH_AS_SUPPORT req_buf = shm_malloc(offset + *len + 1); req_buf += offset; #else req_buf = shm_malloc(*len + 1); #endif if (!req_buf) { LM_ERR("Cannot allocate memory (%u+1)\n", *len); goto error01; } p = req_buf; append_str( p, ACK, ACK_LEN ); append_str( p, " ", 1 ); append_str(p, ruri.s, ruri.len); append_str( p, " " SIP_VERSION CRLF, 1 + SIP_VERSION_LEN + CRLF_LEN); /* insert our via */ append_str(p, via, via_len); /*other headers*/ append_str(p, Trans->from.s, Trans->from.len); append_str(p, Trans->callid.s, Trans->callid.len); append_str(p, to->s, to->len); append_str(p, Trans->cseq_n.s, Trans->cseq_n.len); append_str( p, " ", 1 ); append_str( p, ACK, ACK_LEN); append_str(p, CRLF, CRLF_LEN); /* Routeset */ p = print_rs(p, list, cont); /* User Agent header */ if (server_signature) { append_str(p, user_agent_hdr.s, user_agent_hdr.len); append_str(p, CRLF, CRLF_LEN); } /* extra headers */ if (hdrs) append_str(p, hdrs->s, hdrs->len); /* Content Length, EoH, (body) */ if (body) { append_str(p, CONTENT_LENGTH, CONTENT_LENGTH_LEN); append_str(p, body_len.s, body_len.len); append_str(p, /*end crr. header*/CRLF /*EoH*/CRLF, CRLF_LEN + CRLF_LEN); append_str(p, body->s, body->len); } else { append_str(p, CONTENT_LENGTH "0" CRLF CRLF, CONTENT_LENGTH_LEN + 1 + CRLF_LEN + CRLF_LEN); } /* EoM */ *p = 0; pkg_free(via); free_rte_list(list); return req_buf; error01: pkg_free(via); error: free_rte_list(list); return 0; }
static inline int process_routeset(struct sip_msg* msg, str* contact, struct rte** list, str* ruri, str* next_hop) { struct hdr_field* ptr; rr_t* p; struct rte* t, *head; struct sip_uri puri; ptr = msg->record_route; head = 0; while(ptr) { if (ptr->type == HDR_RECORDROUTE_T) { if (parse_rr(ptr) < 0) { LM_ERR("failed to parse Record-Route header\n"); return -1; } p = (rr_t*)ptr->parsed; while(p) { t = (struct rte*)pkg_malloc(sizeof(struct rte)); if (!t) { LM_ERR("no more pkg memory\n"); free_rte_list(head); return -1; } t->ptr = p; t->next = head; head = t; p = p->next; } } ptr = ptr->next; } if (head) { if (parse_uri(head->ptr->nameaddr.uri.s, head->ptr->nameaddr.uri.len, &puri) < 0) { LM_ERR("failed to parse URI\n"); free_rte_list(head); return -1; } if (puri.lr.s) { /* Next hop is loose router */ *ruri = *contact; *next_hop = head->ptr->nameaddr.uri; } else { /* Next hop is strict router */ *ruri = head->ptr->nameaddr.uri; *next_hop = *ruri; t = head; head = head->next; pkg_free(t); } } else { /* No routes */ *ruri = *contact; *next_hop = *contact; } *list = head; return 0; }