From 3636551ec6a97fbfa9bdea551534adf78c1d6770 Mon Sep 17 00:00:00 2001 From: Alex Hermann Date: Thu, 7 Jul 2016 18:10:31 +0200 Subject: [PATCH 1/3] tm: uac: Add failover support for local requests DNS failover code needs a uas and a dns_h to function. By creating the new cell earlier, dst2dst2 can put the dns_h into the cell instead of in a throwaway variable. The UAS is set to a faked message created from the outgoing request. --- modules/tm/uac.c | 101 +++++++++++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 42 deletions(-) diff --git a/modules/tm/uac.c b/modules/tm/uac.c index 7c488393d8e..fb35db07b16 100644 --- a/modules/tm/uac.c +++ b/modules/tm/uac.c @@ -208,24 +208,22 @@ static inline int t_uac_prepare(uac_req_t *uac_r, 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 + static struct sip_msg lreq; snd_flags_t snd_flags; tm_xlinks_t backup_xd; tm_xdata_t local_xd; int refresh_shortcuts = 0; + int sip_msg_len; ret=-1; hi=0; /* make gcc happy */ @@ -237,7 +235,7 @@ static inline int t_uac_prepare(uac_req_t *uac_r, */ if ((nhtype = w_calculate_hooks(uac_r->dialog)) < 0) /* if err's returned, the message is incorrect */ - goto error2; + goto error3; if (!uac_r->dialog->loc_seq.is_set) { /* this is the first request in the dialog, @@ -246,57 +244,40 @@ static inline int t_uac_prepare(uac_req_t *uac_r, uac_r->dialog->loc_seq.is_set = 1; } + /* build cell sets X/AVP lists to new transaction structure + * => backup 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 error3; + } + 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) + if ((uri2dst2(cfg_get(core, core_cfg, use_dns_failover) ? &new_cell->uac[0].dns_h : 0, + &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)){ +#endif /* USE_DNS_FAILOVER */ 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 & @@ -475,6 +456,40 @@ static inline int t_uac_prepare(uac_req_t *uac_r, } #endif +#ifdef USE_DNS_FAILOVER + /* Set the outgoing message as UAS, so the failover code has something to work with */ + if(cfg_get(core, core_cfg, use_dns_failover)) { + if (likely(build_sip_msg_from_buf(&lreq, buf, buf_len, inc_msg_no())== 0)) { + lreq.force_send_socket = uac_r->dialog->send_sock; + lreq.rcv.proto = dst.send_sock->proto; + lreq.rcv.src_ip = dst.send_sock->address; + su2ip_addr(&lreq.rcv.dst_ip, &dst.to); + lreq.rcv.src_port = dst.send_sock->port_no; + lreq.rcv.dst_port = su_getport(&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 */ + + if (parse_headers(&lreq, HDR_EOH_F, 0) == -1) { + LM_ERR("failed to parse headers on uas for failover\n"); + } else { + new_cell->uas.request = sip_msg_cloner(&lreq, &sip_msg_len); + lreq.buf=0; /* covers the obsolete DYN_BUF */ + free_sip_msg(&lreq); + if (!new_cell->uas.request) { + LM_ERR("no more shmem\n"); + goto error1; + } + new_cell->uas.end_request=((char*)new_cell->uas.request)+sip_msg_len; + } + } else { + LM_WARN("failed to build uas for failover\n"); + } + } +#endif /* USE_DNS_FAILOVER */ + new_cell->uac[0].on_reply = new_cell->on_reply; new_cell->uac[0].on_failure = new_cell->on_failure; @@ -518,14 +533,16 @@ static inline int t_uac_prepare(uac_req_t *uac_r, LOCK_HASH(hi); remove_from_hash_table_unsafe(new_cell); UNLOCK_HASH(hi); + } + +error2: #ifdef TM_DEL_UNREF + if (!is_ack) { UNREF_FREE(new_cell); }else -#else - } #endif free_cell(new_cell); -error2: +error3: return ret; } From a6cf977568b3b738e786b97109c69fc0c40f5a11 Mon Sep 17 00:00:00 2001 From: Alex Hermann Date: Fri, 8 Jul 2016 14:50:55 +0200 Subject: [PATCH 2/3] tm: uac: Split t_uac_prepare Move the code for local_request event out of the function body. This makes the overly long function easier to read and decreases indenting. Also, deduplicate code for creating a sip msg out of a msg buf. No functional changes. --- modules/tm/uac.c | 309 +++++++++++++++++++++++++---------------------- 1 file changed, 162 insertions(+), 147 deletions(-) diff --git a/modules/tm/uac.c b/modules/tm/uac.c index fb35db07b16..6f165aeb2b5 100644 --- a/modules/tm/uac.c +++ b/modules/tm/uac.c @@ -190,6 +190,162 @@ int uac_refresh_hdr_shortcuts(tm_cell_t *tcell, char *buf, int buf_len) return -1; } + +#if defined(USE_DNS_FAILOVER) || defined(WITH_EVENT_LOCAL_REQUEST) +static inline int t_build_msg_from_buf( + struct sip_msg *msg, char *buf, int buf_len, + uac_req_t *uac_r, struct dest_info *dst) +{ + if (unlikely(build_sip_msg_from_buf(msg, buf, buf_len, inc_msg_no()) != 0)) { + return -1; + } + msg->force_send_socket = uac_r->dialog->send_sock; + msg->rcv.proto = dst->send_sock->proto; + msg->rcv.src_ip = dst->send_sock->address; + su2ip_addr(&msg->rcv.dst_ip, &dst->to); + msg->rcv.src_port = dst->send_sock->port_no; + msg->rcv.dst_port = su_getport(&dst->to); + msg->rcv.src_su=dst->send_sock->su; + msg->rcv.bind_address=dst->send_sock; +#ifdef USE_COMP + msg->rcv.comp=dst->comp; +#endif /* USE_COMP */ + + return 0; +} + +#ifdef WITH_EVENT_LOCAL_REQUEST +static inline int t_run_local_req( + char **buf, int *buf_len, + uac_req_t *uac_r, + struct cell *new_cell, struct retr_buf *request) +{ + static struct sip_msg lreq; + struct onsend_info onsnd_info; + tm_xlinks_t backup_xd; + int sflag_bk; + char *buf1; + int buf_len1; + int backup_route_type; + struct cell *backup_t; + int backup_branch; + unsigned int backup_msgid; + int refresh_shortcuts = 0; + + DBG("executing event_route[tm:local-request]\n"); + if (unlikely(t_build_msg_from_buf(&lreq, *buf, *buf_len, uac_r, &request->dst))) { + return -1; + } + if (unlikely(set_dst_uri(&lreq, uac_r->dialog->hooks.next_hop))) { + LM_ERR("failed to set dst_uri"); + free_sip_msg(&lreq); + return -1; + } + sflag_bk = getsflags(); + tm_xdata_swap(new_cell, &backup_xd, 0); + + onsnd_info.to=&request->dst.to; + onsnd_info.send_sock=request->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); + + /* rebuild the new message content */ + if(lreq.force_send_socket != uac_r->dialog->send_sock) { + LM_DBG("Send socket updated to: %.*s", + lreq.force_send_socket->address_str.len, + lreq.force_send_socket->address_str.s); + + /* rebuild local Via - remove previous value + * and add the one for the new send socket */ + if (!del_lump(&lreq, lreq.h_via1->name.s - lreq.buf, + lreq.h_via1->len, 0)) { + LM_ERR("Failed to remove previous local Via\n"); + /* attempt a normal update to give it a chance */ + goto normal_update; + } + + /* reuse same branch value from previous local Via */ + memcpy(lreq.add_to_branch_s, lreq.via1->branch->value.s, + lreq.via1->branch->value.len); + lreq.add_to_branch_len = lreq.via1->branch->value.len; + + /* update also info about new destination and send sock */ + uac_r->dialog->send_sock=lreq.force_send_socket; + request->dst.send_sock = lreq.force_send_socket; + request->dst.proto = lreq.force_send_socket->proto; + + LM_DBG("apply new updates with Via to sip msg\n"); + buf1 = build_req_buf_from_sip_req(&lreq, + (unsigned int*)&buf_len1, &request->dst, BUILD_IN_SHM); + if (likely(buf1)){ + shm_free(*buf); + *buf = buf1; + *buf_len = buf_len1; + /* a possible change of the method is not handled! */ + refresh_shortcuts = 1; + } + + } else { +normal_update: + if (unlikely(lreq.add_rm || lreq.body_lumps || lreq.new_uri.s)) { + LM_DBG("apply new updates without Via to sip msg\n"); + buf1 = build_req_buf_from_sip_req(&lreq, + (unsigned int*)&buf_len1, + &request->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! */ + refresh_shortcuts = 1; + } + } + } + + /* clean local msg structure */ + 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; + } + lreq.buf=0; /* covers the obsolete DYN_BUF */ + free_sip_msg(&lreq); + return refresh_shortcuts; +} +#endif /* WITH_EVENT_LOCAL_REQUEST */ +#endif /* defined(USE_DNS_FAILOVER) || defined(WITH_EVENT_LOCAL_REQUEST) */ + + /* 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 @@ -203,27 +359,20 @@ static inline int t_uac_prepare(uac_req_t *uac_r, struct dest_info dst; struct cell *new_cell; struct retr_buf *request; - char* buf; + char *buf; int buf_len, ret; unsigned int hi; int is_ack; ticks_t lifetime; long nhtype; -#ifdef WITH_EVENT_LOCAL_REQUEST - struct cell *backup_t; - int backup_branch; - unsigned int backup_msgid; - char *buf1; - int buf_len1; - int sflag_bk; - int backup_route_type; -#endif - static struct sip_msg lreq; snd_flags_t snd_flags; tm_xlinks_t backup_xd; tm_xdata_t local_xd; int refresh_shortcuts = 0; int sip_msg_len; +#ifdef USE_DNS_FAILOVER + static struct sip_msg lreq; +#endif /* USE_DNS_FAILOVER */ ret=-1; hi=0; /* make gcc happy */ @@ -330,148 +479,14 @@ static inline int t_uac_prepare(uac_req_t *uac_r, #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); - - /* rebuild the new message content */ - if(lreq.force_send_socket != uac_r->dialog->send_sock) { - LM_DBG("Send socket updated to: %.*s", - lreq.force_send_socket->address_str.len, - lreq.force_send_socket->address_str.s); - - /* rebuild local Via - remove previous value - * and add the one for the new send socket */ - if (!del_lump(&lreq, lreq.h_via1->name.s - lreq.buf, - lreq.h_via1->len, 0)) { - LM_ERR("Failed to remove previous local Via\n"); - /* attempt a normal update to give it a chance */ - goto normal_update; - } - - /* reuse same branch value from previous local Via */ - memcpy(lreq.add_to_branch_s, lreq.via1->branch->value.s, - lreq.via1->branch->value.len); - lreq.add_to_branch_len = lreq.via1->branch->value.len; - - /* update also info about new destination and send sock */ - uac_r->dialog->send_sock=lreq.force_send_socket; - request->dst.send_sock = lreq.force_send_socket; - request->dst.proto = lreq.force_send_socket->proto; - - LM_DBG("apply new updates with Via to sip msg\n"); - buf1 = build_req_buf_from_sip_req(&lreq, - (unsigned int*)&buf_len1, &dst, BUILD_IN_SHM); - if (likely(buf1)){ - shm_free(buf); - buf = buf1; - buf_len = buf_len1; - /* a possible change of the method is not handled! */ - refresh_shortcuts = 1; - } - - } else { -normal_update: - if (unlikely(lreq.add_rm || lreq.body_lumps - || lreq.new_uri.s)) { - LM_DBG("apply new updates without Via 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! */ - refresh_shortcuts = 1; - } - } - } - - /* clean local msg structure */ - 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; - } - lreq.buf=0; /* covers the obsolete DYN_BUF */ - free_sip_msg(&lreq); - } - } + refresh_shortcuts = t_run_local_req(&buf, &buf_len, uac_r, new_cell, request); } #endif #ifdef USE_DNS_FAILOVER /* Set the outgoing message as UAS, so the failover code has something to work with */ if(cfg_get(core, core_cfg, use_dns_failover)) { - if (likely(build_sip_msg_from_buf(&lreq, buf, buf_len, inc_msg_no())== 0)) { - lreq.force_send_socket = uac_r->dialog->send_sock; - lreq.rcv.proto = dst.send_sock->proto; - lreq.rcv.src_ip = dst.send_sock->address; - su2ip_addr(&lreq.rcv.dst_ip, &dst.to); - lreq.rcv.src_port = dst.send_sock->port_no; - lreq.rcv.dst_port = su_getport(&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 */ - + if(likely(t_build_msg_from_buf(&lreq, buf, buf_len, uac_r, &dst) == 0)) { if (parse_headers(&lreq, HDR_EOH_F, 0) == -1) { LM_ERR("failed to parse headers on uas for failover\n"); } else { From 2f080bd3af63678b900f9f537afbd187007eb089 Mon Sep 17 00:00:00 2001 From: Alex Hermann Date: Fri, 8 Jul 2016 17:15:41 +0200 Subject: [PATCH 3/3] tm: uac: Add support for onsend route to local request --- modules/tm/uac.c | 73 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 65 insertions(+), 8 deletions(-) diff --git a/modules/tm/uac.c b/modules/tm/uac.c index 6f165aeb2b5..bb4149a9a77 100644 --- a/modules/tm/uac.c +++ b/modules/tm/uac.c @@ -57,6 +57,7 @@ #include "../../action.h" #include "../../onsend.h" #include "t_lookup.h" +#include "t_fwd.h" #endif #define FROM_TAG_LEN (MD5_LEN + 1 /* - */ + CRC16_LEN) /* length of FROM tags */ @@ -590,23 +591,63 @@ int prepare_req_within(uac_req_t *uac_r, return -1; } -static inline void send_prepared_request_impl(struct retr_buf *request, int retransmit) +static inline int send_prepared_request_impl(struct retr_buf *request, int retransmit, int branch) { + struct cell *t; + struct sip_msg *p_msg; + struct ua_client *uac; + struct ip_addr ip; /* logging */ + int ret; + + t = request->my_T; + uac = &t->uac[branch]; + p_msg = t->uas.request; + if (SEND_BUFFER(request) == -1) { LOG(L_ERR, "t_uac: Attempt to send to precreated request failed\n"); } - else if (unlikely(has_tran_tmcbs(request->my_T, TMCB_REQUEST_SENT))) + else if (unlikely(has_tran_tmcbs(t, TMCB_REQUEST_SENT))) /* we don't know the method here */ - run_trans_callbacks_with_buf(TMCB_REQUEST_SENT, request, 0, 0, + run_trans_callbacks_with_buf(TMCB_REQUEST_SENT, &uac->request, 0, 0, TMCB_LOCAL_F); - - if (retransmit && (start_retr(request)!=0)) - LOG(L_CRIT, "BUG: t_uac: failed to start retr. for %p\n", request); + + su2ip_addr(&ip, &uac->request.dst.to); + DBG("send_prepared_request_impl: uac: %p branch: %d to %s:%d\n", + uac, branch, ip_addr2a(&ip), su_getport(&uac->request.dst.to)); + + if (run_onsend(p_msg, &uac->request.dst, uac->request.buffer, + uac->request.buffer_len)==0){ + uac->last_received=408; + su2ip_addr(&ip, &uac->request.dst.to); + DBG("t_uac: onsend_route dropped msg. to %s:%d (%d)\n", + ip_addr2a(&ip), su_getport(&uac->request.dst.to), + uac->request.dst.proto); +#ifdef USE_DNS_FAILOVER + /* if the destination resolves to more ips, add another + * branch/uac */ + ret = add_uac_dns_fallback(t, p_msg, uac, retransmit); + if (ret > 0) { + su2ip_addr(&ip, &uac->request.dst.to); + DBG("t_uac: send on branch %d failed " + "(onsend_route), trying another ip %s:%d (%d)\n", + branch, ip_addr2a(&ip), + su_getport(&uac->request.dst.to), + uac->request.dst.proto); + /* success, return new branch */ + return ret; + } +#endif /* USE_DNS_FAILOVER*/ + return -1; + } + + if (retransmit && (start_retr(&uac->request)!=0)) + LOG(L_CRIT, "BUG: t_uac: failed to start retr. for %p\n", &uac->request); + return 0; } void send_prepared_request(struct retr_buf *request) { - send_prepared_request_impl(request, 1 /* retransmit */); + send_prepared_request_impl(request, 1 /* retransmit */, 0); } /* @@ -628,11 +669,27 @@ int t_uac_with_ids(uac_req_t *uac_r, struct cell *cell; int ret; int is_ack; + int branch_ret; + int i; + branch_bm_t added_branches = 1; ret = t_uac_prepare(uac_r, &request, &cell); if (ret < 0) return ret; is_ack = (uac_r->method->len == 3) && (memcmp("ACK", uac_r->method->s, 3)==0) ? 1 : 0; - send_prepared_request_impl(request, !is_ack /* retransmit */); + + /* equivalent loop to the one in t_forward_nonack */ + for (i=0; inr_of_outgoings; i++) { + if (added_branches & (1<=0){ /* some kind of success */ + if (branch_ret>i) { + /* new branch added */ + added_branches |= 1<