/* WARNING: - dst_cell contains the created cell, but it is un-referenced * (before using it make sure you REF() it first) * - if ACK (method==ACK), a cell will be created but it will not * be added in the hash table (should be either deleted by the * caller) */ static inline int t_uac_prepare(uac_req_t *uac_r, struct retr_buf **dst_req, struct cell **dst_cell) { struct dest_info dst; struct cell *new_cell; struct retr_buf *request; char* buf; int buf_len, ret; unsigned int hi; int is_ack; ticks_t lifetime; #ifdef USE_DNS_FAILOVER struct dns_srv_handle dns_h; #endif long nhtype; #ifdef WITH_EVENT_LOCAL_REQUEST struct cell *backup_t; int backup_branch; unsigned int backup_msgid; static struct sip_msg lreq; char *buf1; int buf_len1; int sflag_bk; int backup_route_type; #endif snd_flags_t snd_flags; tm_xlinks_t backup_xd; tm_xdata_t local_xd; ret=-1; hi=0; /* make gcc happy */ /*if (dst_req) *dst_req = NULL;*/ is_ack = (((uac_r->method->len == 3) && (memcmp("ACK", uac_r->method->s, 3)==0)) ? 1 : 0); /*** added by dcm * - needed by external ua to send a request within a dlg */ if ((nhtype = w_calculate_hooks(uac_r->dialog)) < 0) /* if err's returned, the message is incorrect */ goto error2; if (!uac_r->dialog->loc_seq.is_set) { /* this is the first request in the dialog, set cseq to default value now - Miklos */ uac_r->dialog->loc_seq.value = DEFAULT_CSEQ; uac_r->dialog->loc_seq.is_set = 1; } DBG("DEBUG:tm:t_uac: next_hop=<%.*s>\n",uac_r->dialog->hooks.next_hop->len, uac_r->dialog->hooks.next_hop->s); /* new message => take the dialog send_socket if set, or the default send_socket if not*/ SND_FLAGS_INIT(&snd_flags); #ifdef USE_DNS_FAILOVER if (cfg_get(core, core_cfg, use_dns_failover)){ dns_srv_handle_init(&dns_h); if ((uri2dst2(&dns_h, &dst, uac_r->dialog->send_sock, snd_flags, uac_r->dialog->hooks.next_hop, PROTO_NONE)==0) || (dst.send_sock==0)){ dns_srv_handle_put(&dns_h); ser_error = E_NO_SOCKET; ret=ser_error; LOG(L_ERR, "t_uac: no socket found\n"); goto error2; } dns_srv_handle_put(&dns_h); /* not needed anymore */ }else{ if ((uri2dst2(0, &dst, uac_r->dialog->send_sock, snd_flags, uac_r->dialog->hooks.next_hop, PROTO_NONE)==0) || (dst.send_sock==0)){ ser_error = E_NO_SOCKET; ret=ser_error; LOG(L_ERR, "t_uac: no socket found\n"); goto error2; } } #else /* USE_DNS_FAILOVER */ if ((uri2dst2(&dst, uac_r->dialog->send_sock, snd_flags, uac_r->dialog->hooks.next_hop, PROTO_NONE)==0) || (dst.send_sock==0)){ ser_error = E_NO_SOCKET; ret=ser_error; LOG(L_ERR, "t_uac: no socket found\n"); goto error2; } #endif /* USE_DNS_FAILOVER */ /* build cell sets X/AVP lists to new transaction structure * => bakup in a tmp struct and restore afterwards */ memset(&local_xd, 0, sizeof(tm_xdata_t)); tm_xdata_replace(&local_xd, &backup_xd); new_cell = build_cell(0); tm_xdata_replace(0, &backup_xd); if (!new_cell) { ret=E_OUT_OF_MEM; LOG(L_ERR, "t_uac: short of cell shmem\n"); goto error2; } if (uac_r->method->len==INVITE_LEN && memcmp(uac_r->method->s, INVITE, INVITE_LEN)==0){ new_cell->flags |= T_IS_INVITE_FLAG; new_cell->flags|=T_AUTO_INV_100 & (!cfg_get(tm, tm_cfg, tm_auto_inv_100) -1); #ifdef WITH_AS_SUPPORT if (uac_r->cb_flags & TMCB_DONT_ACK) new_cell->flags |= T_NO_AUTO_ACK; #endif lifetime=cfg_get(tm, tm_cfg, tm_max_inv_lifetime); }else lifetime=cfg_get(tm, tm_cfg, tm_max_noninv_lifetime); new_cell->flags |= T_IS_LOCAL_FLAG; /* init timers hack, new_cell->fr_timer and new_cell->fr_inv_timer * must be set, or else the fr will happen immediately * we can't call init_new_t() because we don't have a sip msg * => we'll ignore t_set_fr() or avp timer value and will use directly the * module params fr_inv_timer and fr_timer -- andrei */ new_cell->fr_timeout=cfg_get(tm, tm_cfg, fr_timeout); new_cell->fr_inv_timeout=cfg_get(tm, tm_cfg, fr_inv_timeout); new_cell->end_of_life=get_ticks_raw()+lifetime; #ifdef TM_DIFF_RT_TIMEOUT /* same as above for retransmission intervals */ new_cell->rt_t1_timeout_ms = cfg_get(tm, tm_cfg, rt_t1_timeout_ms); new_cell->rt_t2_timeout_ms = cfg_get(tm, tm_cfg, rt_t2_timeout_ms); #endif set_kr(REQ_FWDED); request = &new_cell->uac[0].request; request->dst = dst; request->flags |= nhtype; if (!is_ack) { #ifdef TM_DEL_UNREF INIT_REF(new_cell, 1); /* ref'ed only from the hash */ #endif hi=dlg2hash(uac_r->dialog); LOCK_HASH(hi); insert_into_hash_table_unsafe(new_cell, hi); UNLOCK_HASH(hi); } buf = build_uac_req(uac_r->method, uac_r->headers, uac_r->body, uac_r->dialog, 0, new_cell, &buf_len, &dst); if (!buf) { LOG(L_ERR, "t_uac: Error while building message\n"); ret=E_OUT_OF_MEM; goto error1; } #ifdef WITH_EVENT_LOCAL_REQUEST if (unlikely(goto_on_local_req>=0)) { DBG("executing event_route[tm:local-request]\n"); if(likely(build_sip_msg_from_buf(&lreq, buf, buf_len, inc_msg_no()) == 0)) { /* fill some field in sip_msg */ if (unlikely(set_dst_uri(&lreq, uac_r->dialog->hooks.next_hop))) { LM_ERR("failed to set dst_uri"); free_sip_msg(&lreq); } else { struct onsend_info onsnd_info; lreq.force_send_socket = uac_r->dialog->send_sock; lreq.rcv.proto = dst.send_sock->proto; lreq.rcv.src_ip = dst.send_sock->address; lreq.rcv.src_port = dst.send_sock->port_no; lreq.rcv.dst_port = su_getport(&dst.to); su2ip_addr(&lreq.rcv.dst_ip, &dst.to); lreq.rcv.src_su=dst.send_sock->su; lreq.rcv.bind_address=dst.send_sock; #ifdef USE_COMP lreq.rcv.comp=dst.comp; #endif /* USE_COMP */ sflag_bk = getsflags(); tm_xdata_swap(new_cell, &backup_xd, 0); onsnd_info.to=&dst.to; onsnd_info.send_sock=dst.send_sock; onsnd_info.buf=buf; onsnd_info.len=buf_len; p_onsend=&onsnd_info; /* run the route */ backup_route_type = get_route_type(); set_route_type(LOCAL_ROUTE); /* set T to the current transaction */ backup_t=get_t(); backup_branch=get_t_branch(); backup_msgid=global_msg_id; /* fake transaction and message id */ global_msg_id=lreq.id; set_t(new_cell, T_BR_UNDEFINED); run_top_route(event_rt.rlist[goto_on_local_req], &lreq, 0); /* restore original environment */ set_t(backup_t, backup_branch); global_msg_id=backup_msgid; set_route_type( backup_route_type ); p_onsend=0; /* restore original environment */ tm_xdata_swap(new_cell, &backup_xd, 1); setsflagsval(sflag_bk); if (unlikely(lreq.new_uri.s)) { pkg_free(lreq.new_uri.s); lreq.new_uri.s=0; lreq.new_uri.len=0; } if (unlikely(lreq.dst_uri.s)) { pkg_free(lreq.dst_uri.s); lreq.dst_uri.s=0; lreq.dst_uri.len=0; } if (unlikely(lreq.add_rm || lreq.body_lumps)) { LM_DBG("apply new updates to sip msg\n"); buf1 = build_req_buf_from_sip_req(&lreq, (unsigned int*)&buf_len1, &dst, BUILD_NO_LOCAL_VIA|BUILD_NO_VIA1_UPDATE| BUILD_IN_SHM); if (likely(buf1)){ shm_free(buf); buf = buf1; buf_len = buf_len1; /* a possible change of the method is not handled! */ } } lreq.buf=0; /* covers the obsolete DYN_BUF */ free_sip_msg(&lreq); } } } #endif new_cell->method.s = buf; new_cell->method.len = uac_r->method->len; request->buffer = buf; request->buffer_len = buf_len; new_cell->nr_of_outgoings++; /* Register the callbacks after everything is successful and nothing can fail. Otherwise the callback parameter would be freed twise, once from TMCB_DESTROY, and again because of the negative return code. */ if(uac_r->cb && insert_tmcb(&(new_cell->tmcb_hl), uac_r->cb_flags, *(uac_r->cb), uac_r->cbp, NULL)!=1){ ret=E_OUT_OF_MEM; LOG(L_ERR, "t_uac: short of tmcb shmem\n"); goto error1; } if (has_local_reqin_tmcbs()) run_local_reqin_callbacks(new_cell, 0, 0); #ifdef DIALOG_CALLBACKS run_trans_dlg_callbacks(uac_r->dialog, new_cell, request); #endif /* DIALOG_CALLBACKS */ if (dst_req) *dst_req = request; if (dst_cell) *dst_cell = new_cell; else if(is_ack && dst_req==0){ free_cell(new_cell); } return 1; error1: if (!is_ack) { LOCK_HASH(hi); remove_from_hash_table_unsafe(new_cell); UNLOCK_HASH(hi); #ifdef TM_DEL_UNREF UNREF_FREE(new_cell); }else #else } #endif free_cell(new_cell); error2: return ret; }
/*! * helper function for stateless reply */ int sl_reply_helper(struct sip_msg *msg, int code, char *reason, str *tag) { str buf = {0, 0}; str dset = {0, 0}; struct dest_info dst; struct bookmark dummy_bm; int backup_mhomed, ret; str text; int rt, backup_rt; struct run_act_ctx ctx; struct sip_msg pmsg; if (msg->first_line.u.request.method_value==METHOD_ACK) goto error; init_dest_info(&dst); if (reply_to_via) { if (update_sock_struct_from_via(&dst.to, msg, msg->via1 )==-1) { LOG(L_ERR, "ERROR: sl_reply_helper: cannot lookup reply dst: %s\n", msg->via1->host.s); goto error; } } else update_sock_struct_from_ip(&dst.to, msg); /* if that is a redirection message, dump current message set to it */ if (code>=300 && code<400) { dset.s=print_dset(msg, &dset.len); if (dset.s) { add_lump_rpl(msg, dset.s, dset.len, LUMP_RPL_HDR); } } text.s = reason; text.len = strlen(reason); /* add a to-tag if there is a To header field without it */ if ( /* since RFC3261, we append to-tags anywhere we can, except * 100 replies */ /* msg->first_line.u.request.method_value==METHOD_INVITE && */ code>=180 && (msg->to || (parse_headers(msg,HDR_TO_F, 0)!=-1 && msg->to)) && (get_to(msg)->tag_value.s==0 || get_to(msg)->tag_value.len==0) ) { if(tag!=NULL && tag->s!=NULL) { buf.s = build_res_buf_from_sip_req(code, &text, tag, msg, (unsigned int*)&buf.len, &dummy_bm); } else { calc_crc_suffix( msg, tag_suffix ); buf.s = build_res_buf_from_sip_req(code, &text, &sl_tag, msg, (unsigned int*)&buf.len, &dummy_bm); } } else { buf.s = build_res_buf_from_sip_req(code, &text, 0, msg, (unsigned int*)&buf.len, &dummy_bm); } if (!buf.s) { DBG("DEBUG: sl_reply_helper: response building failed\n"); goto error; } sl_run_callbacks(SLCB_REPLY_READY, msg, code, reason, &buf, &dst); /* supress multhoming support when sending a reply back -- that makes sure that replies will come from where requests came in; good for NATs (there is no known use for mhomed for locally generated replies; note: forwarded cross-interface replies do benefit of mhomed! */ backup_mhomed=mhomed; mhomed=0; /* use for sending the received interface -bogdan*/ dst.proto=msg->rcv.proto; dst.send_sock=msg->rcv.bind_address; dst.id=msg->rcv.proto_reserved1; #ifdef USE_COMP dst.comp=msg->via1->comp_no; #endif dst.send_flags=msg->rpl_send_flags; ret = msg_send(&dst, buf.s, buf.len); mhomed=backup_mhomed; rt = route_lookup(&event_rt, "sl:local-response"); if (unlikely(rt >= 0 && event_rt.rlist[rt] != NULL)) { if (likely(build_sip_msg_from_buf(&pmsg, buf.s, buf.len, inc_msg_no()) == 0)) { char *tmp = NULL; struct onsend_info onsnd_info; onsnd_info.to=&dst.to; onsnd_info.send_sock=dst.send_sock; onsnd_info.buf=buf.s; onsnd_info.len=buf.len; p_onsend=&onsnd_info; if (unlikely(!IS_SIP(msg))) { /* This is an HTTP reply... So fudge in a CSeq into the parsed message message structure so that $rm will work in the route */ struct hdr_field *hf; struct cseq_body *cseqb; char *tmp2; int len; if ((hf = (struct hdr_field *) pkg_malloc(sizeof(struct hdr_field))) == NULL) { LM_ERR("out of package memory\n"); goto event_route_error; } if ((cseqb = (struct cseq_body *) pkg_malloc(sizeof(struct cseq_body))) == NULL) { LM_ERR("out of package memory\n"); pkg_free(hf); goto event_route_error; } if ((tmp = (char *) pkg_malloc(sizeof(char) * (msg->first_line.u.request.method.len + 5))) == NULL) { LM_ERR("out of package memory\n"); pkg_free(cseqb); pkg_free(hf); goto event_route_error; } memset(hf, 0, sizeof(struct hdr_field)); memset(cseqb, 0, sizeof(struct cseq_body)); len = sprintf(tmp, "0 %.*s\r\n", msg->first_line.u.request.method.len, msg->first_line.u.request.method.s); tmp2 = parse_cseq(tmp, &tmp[len], cseqb); hf->type = HDR_CSEQ_T; hf->body.s = tmp; hf->body.len = tmp2 - tmp; hf->parsed = cseqb; pmsg.parsed_flag|=HDR_CSEQ_F; pmsg.cseq = hf; if (pmsg.last_header==0) { pmsg.headers=hf; pmsg.last_header=hf; } else { pmsg.last_header->next=hf; pmsg.last_header=hf; } } backup_rt = get_route_type(); set_route_type(LOCAL_ROUTE); init_run_actions_ctx(&ctx); run_top_route(event_rt.rlist[rt], &pmsg, 0); set_route_type(backup_rt); p_onsend=0; if (tmp != NULL) pkg_free(tmp); event_route_error: free_sip_msg(&pmsg); } } pkg_free(buf.s); if (ret<0) { goto error; } *(sl_timeout) = get_ticks() + SL_RPL_WAIT_TIME; update_sl_stats(code); return 1; error: update_sl_failures(); return -1; }