/* Suspends the transaction for later use. * Save the returned hash_index and label to get * back to the SIP request processing, see the readme. * * Return value: * 0 - success * <0 - failure */ int t_suspend(struct sip_msg *msg, unsigned int *hash_index, unsigned int *label) { struct cell *t; int branch; int sip_msg_len; t = get_t(); if (!t || t == T_UNDEFINED) { LM_ERR("transaction has not been created yet\n"); return -1; } if (t->flags & T_CANCELED) { /* The transaction has already been canceled */ LM_DBG("trying to suspend an already canceled transaction\n"); ser_error = E_CANCELED; return 1; } if (t->uas.status >= 200) { LM_DBG("trasaction sent out a final response already - %d\n", t->uas.status); return -3; } if (msg->first_line.type != SIP_REPLY) { /* send a 100 Trying reply, because the INVITE processing will probably take a long time */ if (msg->REQ_METHOD==METHOD_INVITE && (t->flags&T_AUTO_INV_100) && (t->uas.status < 100) ) { if (!t_reply( t, msg , 100 , cfg_get(tm, tm_cfg, tm_auto_inv_100_r))) LM_DBG("suspending request processing - sending 100 reply\n"); } if ((t->nr_of_outgoings==0) && /* if there had already been an UAC created, then the lumps were saved as well */ save_msg_lumps(t->uas.request, msg) ) { LM_ERR("failed to save the message lumps\n"); return -1; } /* save the message flags */ t->uas.request->flags = msg->flags; /* add a blind UAC to let the fr timer running */ if (add_blind_uac() < 0) { LM_ERR("failed to add the blind UAC\n"); return -1; } /* propagate failure route to new branch * - failure route to be executed if the branch is not continued * before timeout */ t->uac[t->async_backup.blind_uac].on_failure = t->on_failure; t->flags |= T_ASYNC_SUSPENDED; } else { LM_DBG("this is a suspend on reply - setting msg flag to SUSPEND\n"); msg->msg_flags |= FL_RPL_SUSPENDED; /* this is a reply suspend find which branch */ if (t_check( msg , &branch )==-1){ LOG(L_ERR, "ERROR: t_suspend_reply: " \ "failed find UAC branch\n"); return -1; } LM_DBG("found a a match with branch id [%d] - " "cloning reply message to t->uac[branch].reply\n", branch); sip_msg_len = 0; t->uac[branch].reply = sip_msg_cloner( msg, &sip_msg_len ); if (! t->uac[branch].reply ) { LOG(L_ERR, "can't alloc' clone memory\n"); return -1; } t->uac[branch].end_reply = ((char*)t->uac[branch].reply) + sip_msg_len; LM_DBG("saving transaction data\n"); t->uac[branch].reply->flags = msg->flags; t->flags |= T_ASYNC_SUSPENDED; } *hash_index = t->hash_index; *label = t->label; /* backup some extra info that can be used in continuation logic */ t->async_backup.backup_route = get_route_type(); t->async_backup.backup_branch = get_t_branch(); t->async_backup.ruri_new = ruri_get_forking_state(); return 0; }
/* 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; }