/* * Create Via header */ static inline int assemble_via(str* dest, struct cell* t, struct dest_info* dst, int branch) { static char branch_buf[MAX_BRANCH_PARAM_LEN]; char* via; int len; unsigned int via_len; str branch_str; struct hostport hp; if (!t_calc_branch(t, branch, branch_buf, &len)) { LM_ERR("branch calculation failed\n"); return -1; } branch_str.s = branch_buf; branch_str.len = len; #ifdef XL_DEBUG printf("!!!proto: %d\n", sock->proto); #endif set_hostport(&hp, 0); via = via_builder(&via_len, NULL, dst, &branch_str, 0, &hp); if (!via) { LM_ERR("via building failed\n"); return -2; } dest->s = via; dest->len = via_len; return 0; }
char *print_uac_request( struct cell *t, struct sip_msg *i_req, int branch, str *uri, unsigned int *len, struct socket_info *send_sock, enum sip_protos proto ) { char *buf, *shbuf; shbuf=0; /* ... we calculate branch ... */ if (!t_calc_branch(t, branch, i_req->add_to_branch_s, &i_req->add_to_branch_len )) { LOG(L_ERR, "ERROR: print_uac_request: branch computation failed\n"); goto error01; } /* ... update uri ... */ i_req->new_uri=*uri; /* run the specific callbacks for this transaction */ run_trans_callbacks( TMCB_REQUEST_FWDED , t, i_req, 0, -i_req->REQ_METHOD); /* ... and build it now */ buf=build_req_buf_from_sip_req( i_req, len, send_sock, proto ); #ifdef DBG_MSG_QA if (buf[*len-1]==0) { LOG(L_ERR, "ERROR: print_uac_request: sanity check failed\n"); abort(); } #endif if (!buf) { LOG(L_ERR, "ERROR: print_uac_request: no pkg_mem\n"); ser_error=E_OUT_OF_MEM; goto error01; } /* clean Via's we created now -- they would accumulate for other branches and for shmem i_req they would mix up shmem with pkg_mem */ free_via_clen_lump(&i_req->add_rm); shbuf=(char *)shm_malloc(*len); if (!shbuf) { ser_error=E_OUT_OF_MEM; LOG(L_ERR, "ERROR: print_uac_request: no shmem\n"); goto error02; } memcpy( shbuf, buf, *len ); error02: pkg_free( buf ); error01: return shbuf; }
static inline int pre_print_uac_request( struct cell *t, int branch, struct sip_msg *request, struct sip_msg_body **body_clone) { int backup_route_type; struct usr_avp **backup_list; char *p; /* ... we calculate branch ... */ if (!t_calc_branch(t, branch, request->add_to_branch_s, &request->add_to_branch_len )) { LM_ERR("branch computation failed\n"); goto error; } /* from now on, flag all new lumps with LUMPFLAG_BRANCH flag in order to * be able to remove them later --bogdan */ set_init_lump_flags(LUMPFLAG_BRANCH); /* copy path vector into branch */ if (request->path_vec.len) { t->uac[branch].path_vec.s = shm_realloc(t->uac[branch].path_vec.s, request->path_vec.len+1); if (t->uac[branch].path_vec.s==NULL) { LM_ERR("shm_realloc failed\n"); goto error; } t->uac[branch].path_vec.len = request->path_vec.len; memcpy( t->uac[branch].path_vec.s, request->path_vec.s, request->path_vec.len+1); } /* do the same for the advertised port & address */ if (request->set_global_address.len) { t->uac[branch].adv_address.s = shm_realloc(t->uac[branch].adv_address.s, request->set_global_address.len+1); if (t->uac[branch].adv_address.s==NULL) { LM_ERR("shm_realloc failed for storing the advertised address " "(len=%d)\n",request->set_global_address.len); goto error; } t->uac[branch].adv_address.len = request->set_global_address.len; memcpy( t->uac[branch].adv_address.s, request->set_global_address.s, request->set_global_address.len+1); } if (request->set_global_port.len) { t->uac[branch].adv_port.s = shm_realloc(t->uac[branch].adv_port.s, request->set_global_port.len+1); if (t->uac[branch].adv_port.s==NULL) { LM_ERR("shm_realloc failed for storing the advertised port " "(len=%d)\n",request->set_global_port.len); goto error; } t->uac[branch].adv_port.len = request->set_global_port.len; memcpy( t->uac[branch].adv_port.s, request->set_global_port.s, request->set_global_port.len+1); } /********** run route & callback ************/ /* run branch route, if any; run it before RURI's DNS lookup * to allow to be changed --bogdan */ if (t->on_branch) { /* need to pkg_malloc the dst_uri */ if ( request->dst_uri.s && request->dst_uri.len>0 ) { if ( (p=pkg_malloc(request->dst_uri.len))==0 ) { LM_ERR("no more pkg mem\n"); ser_error=E_OUT_OF_MEM; goto error; } memcpy( p, request->dst_uri.s, request->dst_uri.len); request->dst_uri.s = p; } /* need to pkg_malloc the new_uri */ if ( (p=pkg_malloc(request->new_uri.len))==0 ) { LM_ERR("no more pkg mem\n"); ser_error=E_OUT_OF_MEM; goto error; } memcpy( p, request->new_uri.s, request->new_uri.len); request->new_uri.s = p; request->parsed_uri_ok = 0; /* make a clone of the original body, to restore it later */ if (clone_sip_msg_body( request, NULL, body_clone, 0)!=0) { LM_ERR("faile to clone the body, branch route changes will be" " preserved\n"); } /* make available the avp list from transaction */ backup_list = set_avp_list( &t->user_avps ); /* run branch route */ swap_route_type( backup_route_type, BRANCH_ROUTE); _tm_branch_index = branch; if(run_top_route(sroutes->branch[t->on_branch].a,request)&ACT_FL_DROP){ LM_DBG("dropping branch <%.*s>\n", request->new_uri.len, request->new_uri.s); _tm_branch_index = 0; /* restore the route type */ set_route_type( backup_route_type ); /* restore original avp list */ set_avp_list( backup_list ); goto error; } _tm_branch_index = 0; /* restore the route type */ set_route_type( backup_route_type ); /* restore original avp list */ set_avp_list( backup_list ); } /* run the specific callbacks for this transaction */ run_trans_callbacks( TMCB_REQUEST_FWDED, t, request, 0, -request->REQ_METHOD); /* copy dst_uri into branch (after branch route possible updated it) */ if (request->dst_uri.len) { t->uac[branch].duri.s = shm_realloc(t->uac[branch].duri.s, request->dst_uri.len); if (t->uac[branch].duri.s==NULL) { LM_ERR("shm_realloc failed\n"); goto error; } t->uac[branch].duri.len = request->dst_uri.len; memcpy( t->uac[branch].duri.s,request->dst_uri.s,request->dst_uri.len); } return 0; error: return -1; }
/* Build a local request based on a previous request; main * customers of this function are local ACK and local CANCEL */ char *build_local(struct cell *Trans,unsigned int branch, unsigned int *len, char *method, int method_len, str *to #ifdef CANCEL_REASON_SUPPORT , struct cancel_reason* reason #endif /* CANCEL_REASON_SUPPORT */ ) { char *cancel_buf, *p, *via; unsigned int via_len; struct hdr_field *hdr; char branch_buf[MAX_BRANCH_PARAM_LEN]; int branch_len; str branch_str; str via_id; struct hostport hp; #ifdef CANCEL_REASON_SUPPORT int reason_len, code_len; struct hdr_field *reas1, *reas_last; #endif /* CANCEL_REASON_SUPPORT */ /* init */ via_id.s=0; via_id.len=0; /* method, separators, version: "CANCEL sip:[email protected] SIP/2.0" */ *len=SIP_VERSION_LEN + method_len + 2 /* spaces */ + CRLF_LEN; *len+=Trans->uac[branch].uri.len; /*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, (is_local(Trans))?0:(Trans->uas.request)); #ifdef USE_TCP if (!is_local(Trans) && ((Trans->uas.request->rcv.proto==PROTO_TCP) #ifdef USE_TLS || (Trans->uas.request->rcv.proto==PROTO_TLS) #endif /* USE_TLS */ )){ if ((via_id.s=id_builder(Trans->uas.request, (unsigned int*)&via_id.len))==0){ LM_ERR("id builder failed\n"); /* try to continue without id */ } } #endif /* USE_TCP */ via=via_builder(&via_len, NULL, &Trans->uac[branch].request.dst, &branch_str, via_id.s?&via_id:0 , &hp ); /* via_id.s not needed anylonger => free it */ if (via_id.s) { pkg_free(via_id.s); via_id.s=0; via_id.len=0; } 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+method_len+CRLF_LEN+MAXFWD_HEADER_LEN; /* copy'n'paste Route headers */ if (!is_local(Trans)) { for ( hdr=Trans->uas.request->headers ; hdr ; hdr=hdr->next ) if (hdr->type==HDR_ROUTE_T) *len+=hdr->len; } /* User Agent */ if (server_signature) { *len += user_agent_hdr.len + CRLF_LEN; } /* Content Length, EoM */ *len+=CONTENT_LENGTH_LEN+1 + CRLF_LEN; #ifdef CANCEL_REASON_SUPPORT reason_len = 0; reas1 = 0; reas_last = 0; /* compute reason size (if no reason or disabled => reason_len == 0)*/ if (reason && reason->cause != CANCEL_REAS_UNKNOWN){ if (likely(reason->cause > 0 && cfg_get(tm, tm_cfg, local_cancel_reason))){ /* Reason: SIP;cause=<reason->cause>[;text=<reason->u.text.s>] */ reason_len = REASON_PREFIX_LEN + USHORT2SBUF_MAX_LEN + (reason->u.text.s? REASON_TEXT_LEN + 1 + reason->u.text.len + 1 : 0) + CRLF_LEN; } else if (likely(reason->cause == CANCEL_REAS_PACKED_HDRS && !(Trans->flags & T_NO_E2E_CANCEL_REASON))) { reason_len = reason->u.packed_hdrs.len; } else if (reason->cause == CANCEL_REAS_RCVD_CANCEL && reason->u.e2e_cancel && !(Trans->flags & T_NO_E2E_CANCEL_REASON)) { /* parse the entire cancel, to get all the Reason headers */ if(parse_headers(reason->u.e2e_cancel, HDR_EOH_F, 0)<0) { LM_WARN("failed to parse headers\n"); } for(hdr=get_hdr(reason->u.e2e_cancel, HDR_REASON_T), reas1=hdr; hdr; hdr=next_sibling_hdr(hdr)) { /* hdr->len includes CRLF */ reason_len += hdr->len; reas_last=hdr; } } else if (unlikely(reason->cause < CANCEL_REAS_MIN)) LM_BUG("unhandled reason cause %d\n", reason->cause); } *len+= reason_len; #endif /* CANCEL_REASON_SUPPORT */ *len+= CRLF_LEN; /* end of msg. */ cancel_buf=shm_malloc( *len+1 ); if (!cancel_buf) { LM_ERR("cannot allocate memory\n"); goto error01; } p = cancel_buf; append_str( p, method, method_len ); append_str( p, " ", 1 ); append_str( p, Trans->uac[branch].uri.s, Trans->uac[branch].uri.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, method, method_len ); append_str( p, CRLF, CRLF_LEN ); append_str( p, MAXFWD_HEADER, MAXFWD_HEADER_LEN ); if (!is_local(Trans)) { for ( hdr=Trans->uas.request->headers ; hdr ; hdr=hdr->next ) if(hdr->type==HDR_ROUTE_T) { append_str(p, hdr->name.s, hdr->len ); } } /* User Agent header */ if (server_signature) { append_str(p, user_agent_hdr.s, user_agent_hdr.len ); append_str(p, CRLF, CRLF_LEN ); } /* Content Length */ append_str(p, CONTENT_LENGTH "0" CRLF, CONTENT_LENGTH_LEN + 1 + CRLF_LEN); #ifdef CANCEL_REASON_SUPPORT /* add reason if needed */ if (reason_len) { if (likely(reason->cause > 0)) { append_str(p, REASON_PREFIX, REASON_PREFIX_LEN); code_len=ushort2sbuf(reason->cause, p, *len-(int)(p-cancel_buf)); if (unlikely(code_len==0)) LM_BUG("not enough space to write reason code"); p+=code_len; if (reason->u.text.s){ append_str(p, REASON_TEXT, REASON_TEXT_LEN); *p='"'; p++; append_str(p, reason->u.text.s, reason->u.text.len); *p='"'; p++; } append_str(p, CRLF, CRLF_LEN); } else if (likely(reason->cause == CANCEL_REAS_PACKED_HDRS)) { append_str(p, reason->u.packed_hdrs.s, reason->u.packed_hdrs.len); } else if (reason->cause == CANCEL_REAS_RCVD_CANCEL) { for(hdr=reas1; hdr; hdr=next_sibling_hdr(hdr)) { /* hdr->len includes CRLF */ append_str(p, hdr->name.s, hdr->len); if (likely(hdr==reas_last)) break; } } } #endif /* CANCEL_REASON_SUPPORT */ append_str(p, CRLF, CRLF_LEN); /* msg. end */ *p=0; pkg_free(via); return cancel_buf; error01: pkg_free(via); error: return NULL; }
/** * build CANCEL from UAC side */ char *build_uac_cancel(str *headers,str *body,struct cell *cancelledT, unsigned int branch, unsigned int *len, struct dest_info* dst) { char *cancel_buf, *p; char branch_buf[MAX_BRANCH_PARAM_LEN]; str branch_str; struct hostport hp; str content_length, via; LM_DBG("sing FROM=<%.*s>, TO=<%.*s>, CSEQ_N=<%.*s>\n", cancelledT->from.len, cancelledT->from.s, cancelledT->to.len, cancelledT->to.s, cancelledT->cseq_n.len, cancelledT->cseq_n.s); branch_str.s=branch_buf; if (!t_calc_branch(cancelledT, branch, branch_str.s, &branch_str.len )){ LM_ERR("failed to create branch !\n"); goto error; } set_hostport(&hp,0); if (assemble_via(&via, cancelledT, dst, branch) < 0) { LM_ERR("Error while assembling Via\n"); return 0; } /* method, separators, version */ *len=CANCEL_LEN + 2 /* spaces */ +SIP_VERSION_LEN + CRLF_LEN; *len+=cancelledT->uac[branch].uri.len; /*via*/ *len+= via.len; /*From*/ *len+=cancelledT->from.len; /*To*/ *len+=cancelledT->to.len; /*CallId*/ *len+=cancelledT->callid.len; /*CSeq*/ *len+=cancelledT->cseq_n.len+1+CANCEL_LEN+CRLF_LEN; /* User Agent */ if (server_signature) { *len += USER_AGENT_LEN + CRLF_LEN; } /* Content Length */ if (print_content_length(&content_length, body) < 0) { LM_ERR("failed to print content-length\n"); return 0; } /* Content-Length */ *len += (body ? (CONTENT_LENGTH_LEN + content_length.len + CRLF_LEN) : 0); /*Additional headers*/ *len += (headers ? headers->len : 0); /*EoM*/ *len+= CRLF_LEN; /* Message body */ *len += (body ? body->len : 0); cancel_buf=shm_malloc( *len+1 ); if (!cancel_buf) { LM_ERR("no more share memory\n"); goto error01; } p = cancel_buf; memapp( p, CANCEL, CANCEL_LEN ); *(p++) = ' '; memapp( p, cancelledT->uac[branch].uri.s, cancelledT->uac[branch].uri.len); memapp( p, " " SIP_VERSION CRLF, 1+SIP_VERSION_LEN+CRLF_LEN ); /* insert our via */ memapp(p,via.s,via.len); /*other headers*/ memapp( p, cancelledT->from.s, cancelledT->from.len ); memapp( p, cancelledT->callid.s, cancelledT->callid.len ); memapp( p, cancelledT->to.s, cancelledT->to.len ); memapp( p, cancelledT->cseq_n.s, cancelledT->cseq_n.len ); *(p++) = ' '; memapp( p, CANCEL, CANCEL_LEN ); memapp( p, CRLF, CRLF_LEN ); /* User Agent header */ if (server_signature) { memapp(p,USER_AGENT CRLF, USER_AGENT_LEN+CRLF_LEN ); } /* Content Length*/ if (body) { memapp(p, CONTENT_LENGTH, CONTENT_LENGTH_LEN); memapp(p, content_length.s, content_length.len); memapp(p, CRLF, CRLF_LEN); } if(headers && headers->len){ memapp(p,headers->s,headers->len); } /*EoM*/ memapp(p,CRLF,CRLF_LEN); if(body && body->len){ memapp(p,body->s,body->len); } *p=0; pkg_free(via.s); return cancel_buf; error01: pkg_free(via.s); error: return NULL; }
/* * 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; }
/* * 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; }
/* Build a local request based on a previous request; the only customers of this function are local ACK and local CANCEL */ char *build_local(struct cell *Trans,unsigned int branch, str *method, str *extra, struct sip_msg* rpl, unsigned int *len) { char *cancel_buf, *p, *via; unsigned int via_len; struct hdr_field *buf_hdrs; struct hdr_field *hdr; struct sip_msg *req; char branch_buf[MAX_BRANCH_PARAM_LEN]; str branch_str; struct hostport hp; str from; str to; str cseq_n; req = Trans->uas.request; cseq_n = Trans->cseq_n; buf_hdrs = 0; if (rpl && rpl!=FAKED_REPLY) { /* take from and to hdrs from reply */ to.s = rpl->to->name.s; to.len = rpl->to->len; from.s = rpl->from->name.s; from.len = rpl->from->len; } else { to = Trans->to; from = Trans->from; if (req && req->msg_flags&(FL_USE_UAC_FROM|FL_USE_UAC_TO)) { if ( extract_ftc_hdrs( Trans->uac[branch].request.buffer.s, Trans->uac[branch].request.buffer.len, (req->msg_flags&FL_USE_UAC_FROM)?&from:0 , (req->msg_flags&FL_USE_UAC_TO)?&to:0 , 0 )!=0 ) { LM_ERR("build_local: failed to extract UAC hdrs\n"); goto error; } } } LM_DBG("using FROM=<%.*s>, TO=<%.*s>, CSEQ_N=<%.*s>\n", from.len,from.s , to.len,to.s , cseq_n.len,cseq_n.s); /* method, separators, version */ *len=SIP_VERSION_LEN + method->len + 2 /* spaces */ + CRLF_LEN; *len+=Trans->uac[branch].uri.len; /*via*/ branch_str.s=branch_buf; if (!t_calc_branch(Trans, branch, branch_str.s, &branch_str.len )) goto error; set_hostport(&hp, (is_local(Trans))?0:req); via=via_builder(&via_len, Trans->uac[branch].request.dst.send_sock, &branch_str, 0, Trans->uac[branch].request.dst.proto, &hp ); if (!via){ LM_ERR("no via header got from builder\n"); goto error; } *len+= via_len; /*headers*/ *len+=from.len+Trans->callid.len+to.len+cseq_n.len+1+method->len+CRLF_LEN; /* copy'n'paste Route headers that were sent out */ if (!is_local(Trans) && ( (req && req->route) || /* at least one route was received*/ (Trans->uac[branch].path_vec.len!=0)) ) /* path was forced */ { buf_hdrs = extract_parsed_hdrs(Trans->uac[branch].request.buffer.s, Trans->uac[branch].request.buffer.len ); if (buf_hdrs==NULL) { LM_ERR("failed to reparse the request buffer\n"); goto error01; } for ( hdr=buf_hdrs ; hdr ; hdr=hdr->next ) if (hdr->type==HDR_ROUTE_T) *len+=hdr->len; } /* User Agent */ if (server_signature) { *len += user_agent_header.len + CRLF_LEN; } /* Content Length, MaxFwd, EoM */ *len+=LOCAL_MAXFWD_HEADER_LEN + CONTENT_LENGTH_LEN+1 + (extra?extra->len:0) + (Trans->extra_hdrs.s?Trans->extra_hdrs.len:0) + CRLF_LEN + CRLF_LEN; cancel_buf=shm_malloc( *len+1 ); if (!cancel_buf) { LM_ERR("no more share memory\n"); goto error02; } p = cancel_buf; append_string( p, method->s, method->len ); *(p++) = ' '; append_string( p, Trans->uac[branch].uri.s, Trans->uac[branch].uri.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, from.s, from.len ); append_string( p, Trans->callid.s, Trans->callid.len ); append_string( p, to.s, to.len ); append_string( p, cseq_n.s, cseq_n.len ); *(p++) = ' '; append_string( p, method->s, method->len ); append_string( p, CRLF LOCAL_MAXFWD_HEADER, CRLF_LEN+LOCAL_MAXFWD_HEADER_LEN ); /* add Route hdrs (if any) */ for ( hdr=buf_hdrs ; hdr ; hdr=hdr->next ) if(hdr->type==HDR_ROUTE_T) { append_string(p, hdr->name.s, hdr->len ); } if (extra) append_string(p, extra->s, extra->len ); if (Trans->extra_hdrs.s) append_string(p, Trans->extra_hdrs.s, Trans->extra_hdrs.len ); /* 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_hdr_field_lst(buf_hdrs); return cancel_buf; error02: free_hdr_field_lst(buf_hdrs); error01: pkg_free(via); error: return NULL; }
/* atomic "new_tran" construct; it returns: <0 on error +1 if a request did not match a transaction - it that was an ack, the calling function shall forward statelessly - otherwise it means, a new transaction was introduced and the calling function shall reply/relay/whatever_appropriate 0 on retransmission */ int t_newtran( struct sip_msg* p_msg, int full_uas ) { int lret, my_err; context_p ctx_backup; /* is T still up-to-date ? */ LM_DBG("transaction on entrance=%p\n",T); if ( T && T!=T_UNDEFINED ) { LM_DBG("transaction already in process %p\n", T ); return E_SCRIPT; } T = T_UNDEFINED; /* first of all, parse everything -- we will store in shared memory and need to have all headers ready for generating potential replies later; parsing later on demand is not an option since the request will be in shmem and applying parse_headers to it would intermix shmem with pkg_mem */ if (parse_headers(p_msg, HDR_EOH_F, 0 )<0) { LM_ERR("parse_headers failed\n"); return E_BAD_REQ; } if ((p_msg->parsed_flag & HDR_EOH_F)!=HDR_EOH_F) { LM_ERR("EoH not parsed\n"); return E_OUT_OF_MEM; } /* t_lookup_requests attempts to find the transaction; it also calls check_transaction_quadruple -> it is safe to assume we have from/callid/cseq/to */ lret = t_lookup_request( p_msg, 1 /* leave locked if not found */ ); /* on error, pass the error in the stack ... nothing is locked yet if 0 is returned */ if (lret==0) return E_BAD_TUPEL; /* transaction found, it's a retransmission */ if (lret>0) { if (p_msg->REQ_METHOD==METHOD_ACK) { t_release_transaction(T); } else { t_retransmit_reply(T); } /* hide the transaction from the upper layer */ t_unref( p_msg ); /* things are done -- return from script */ return 0; } /* from now on, be careful -- hash table is locked */ if (lret==-2) { /* was it an e2e ACK ? if so, trigger a callback */ if (e2eack_T->relaied_reply_branch==-2) { /* if a ACK for a local replied transaction, release the INVITE transaction and break the script -> simply absorb the ACK request */ t_release_transaction(e2eack_T); return 0; } LM_DBG("building branch for end2end ACK - flags=%X\n",e2eack_T->flags); /* to ensure unigueness acros time and space, compute the ACK * branch in the same maner as for INVITE, but put a t->branch * value that cannot exist for that INVITE - as it is compute as * an INVITE, it will not overlapp with other INVITEs or requests. * But the faked value for t->branch guarantee no overalap with * corresponding INVITE --bogdan */ if (!t_calc_branch(e2eack_T, e2eack_T->nr_of_outgoings+1, p_msg->add_to_branch_s, &p_msg->add_to_branch_len )) { LM_ERR("ACK branch computation failed\n"); } return 1; } /* transaction not found, it's a new request (lret<0, lret!=-2); establish a new transaction ... */ if (p_msg->REQ_METHOD==METHOD_ACK) /* ... unless it is in ACK */ return 1; my_err=new_t(p_msg, full_uas); if (my_err<0) { LM_ERR("new_t failed\n"); goto new_err; } UNLOCK_HASH(p_msg->hash_index); /* now, when the transaction state exists, check if there is a meaningful Via and calculate it; better do it now than later: state is established so that subsequent retransmissions will be absorbed and will not possibly block during Via DNS resolution; doing it later would only burn more CPU as if there is an error, we cannot relay later whatever comes out of the the transaction */ if (!init_rb( &T->uas.response, p_msg)) { LM_ERR("unresolvable via1\n"); put_on_wait( T ); t_unref(p_msg); return E_BAD_VIA; } if (auto_100trying && p_msg->REQ_METHOD==METHOD_INVITE) { ctx_backup = current_processing_ctx; current_processing_ctx = NULL; t_reply( T, p_msg , 100 , &relay_reason_100); current_processing_ctx = ctx_backup; } return 1; new_err: UNLOCK_HASH(p_msg->hash_index); return my_err; }
static inline int pre_print_uac_request( struct cell *t, int branch, struct sip_msg *request) { int backup_route_type; struct usr_avp **backup_list; char *p; /* ... we calculate branch ... */ if (!t_calc_branch(t, branch, request->add_to_branch_s, &request->add_to_branch_len )) { LOG(L_ERR, "ERROR:pre_print_uac_request: branch computation failed\n"); goto error; } /* from now on, flag all new lumps with LUMPFLAG_BRANCH flag in order to * be able to remove them later --bogdan */ set_init_lump_flags(LUMPFLAG_BRANCH); /********** run route & callback ************/ /* run branch route, if any; run it before RURI's DNS lookup * to allow to be changed --bogdan */ if (t->on_branch) { /* need to pkg_malloc the dst_uri */ if ( request->dst_uri.len ) { if ( (p=pkg_malloc(request->dst_uri.len))==0 ) { LOG(L_ERR,"ERROR:tm:pre_print_uac_request: no more pkg mem\n"); ser_error=E_OUT_OF_MEM; goto error; } memcpy( p, request->dst_uri.s, request->dst_uri.len); request->dst_uri.s = p; } /* need to pkg_malloc the new_uri */ if ( (p=pkg_malloc(request->new_uri.len))==0 ) { LOG(L_ERR,"ERROR:tm:pre_print_uac_request: no more pkg mem\n"); ser_error=E_OUT_OF_MEM; goto error; } memcpy( p, request->new_uri.s, request->new_uri.len); request->new_uri.s = p; /* make available the avp list from transaction */ backup_list = set_avp_list( &t->user_avps ); /* run branch route */ swap_route_type( backup_route_type, BRANCH_ROUTE); if (run_actions(branch_rlist[t->on_branch], request)==0 && (action_flags&ACT_FL_DROP) ) { DBG("DEBUG:tm:pre_print_uac_request: dropping branch <%.*s>\n", request->dst_uri.len, request->dst_uri.s); goto error; } set_route_type( backup_route_type ); /* restore original avp list */ set_avp_list( backup_list ); } /* run the specific callbacks for this transaction */ run_trans_callbacks( TMCB_REQUEST_FWDED, t, request, 0, -request->REQ_METHOD); return 0; error: return -1; }