static int xl_get_branch(struct sip_msg *msg, str *res, str *hp, int hi, int hf) { str branch; qvalue_t q; if(msg==NULL || res==NULL) return -1; if(msg->first_line.type == SIP_REPLY) return xl_get_null(msg, res, hp, hi, hf); init_branch_iterator(); branch.s = next_branch(&branch.len, &q, 0, 0, 0); if (!branch.s) { return xl_get_null(msg, res, hp, hi, hf); } res->s = branch.s; res->len = branch.len; return 0; }
int t_append_branches(void) { struct cell *t = NULL; struct sip_msg *orig_msg = NULL; short outgoings; int success_branch; str current_uri; str dst_uri, path, instance, ruid, location_ua; struct socket_info* si; int q, i, found; flag_t backup_bflags = 0; flag_t bflags = 0; int new_branch, branch_ret, lowest_ret; branch_bm_t added_branches; int replies_locked = 0; t = get_t(); if(t == NULL) { LM_ERR("cannot get transaction\n"); return -1; } LM_DBG("transaction %u:%u in status %d\n", t->hash_index, t->label, t->uas.status); /* test if transaction has already been canceled */ if (t->flags & T_CANCELED) { ser_error=E_CANCELED; return -1; } if ((t->uas.status >= 200 && t->uas.status<=399) || ((t->uas.status >= 600 && t->uas.status) && !(t->flags & (T_6xx | T_DISABLE_6xx))) ) { LM_DBG("transaction %u:%u in status %d: cannot append new branch\n", t->hash_index, t->label, t->uas.status); return -1; } /* set the lock on the transaction here */ LOCK_REPLIES(t); replies_locked = 1; outgoings = t->nr_of_outgoings; orig_msg = t->uas.request; LM_DBG("Call %.*s: %d (%d) outgoing branches\n",orig_msg->callid->body.len, orig_msg->callid->body.s,outgoings, nr_branches); lowest_ret=E_UNSPEC; added_branches=0; /* it's a "late" branch so the on_branch variable has already been reset by previous execution of t_forward_nonack: we use the saved value */ if (t->on_branch_delayed) { /* tell add_uac that it should run branch route actions */ set_branch_route(t->on_branch_delayed); } outgoings = t->nr_of_outgoings; /* not really sure that the following is needed */ set_branch_iterator(nr_branches-1); found = 0; while((current_uri.s=next_branch( ¤t_uri.len, &q, &dst_uri, &path, &bflags, &si, &ruid, &instance, &location_ua))) { LM_DBG("Current uri %.*s\n",current_uri.len, current_uri.s); for (i=0; i<nr_branches; i++) { if (t->uac[i].ruid.len == ruid.len && !memcmp(t->uac[i].ruid.s, ruid.s, ruid.len)) { LM_DBG("branch already added [%.*s]\n", ruid.len, ruid.s); found = 1; break; } } if (found) continue; setbflagsval(0, bflags); new_branch=add_uac( t, orig_msg, ¤t_uri, (dst_uri.len) ? (&dst_uri) : ¤t_uri, &path, 0, si, orig_msg->fwd_send_flags, orig_msg->rcv.proto, (dst_uri.len)?-1:UAC_SKIP_BR_DST_F, &instance, &ruid, &location_ua); /* test if cancel was received meanwhile */ if (t->flags & T_CANCELED) goto canceled; if (new_branch>=0) added_branches |= 1<<new_branch; else lowest_ret=MIN_int(lowest_ret, new_branch); } clear_branches(); LM_DBG("Call %.*s: %d (%d) outgoing branches after clear_branches()\n", orig_msg->callid->body.len, orig_msg->callid->body.s,outgoings, nr_branches); setbflagsval(0, backup_bflags); /* update message flags, if changed in branch route */ t->uas.request->flags = orig_msg->flags; if (added_branches==0) { if(lowest_ret!=E_CFG) LOG(L_ERR, "ERROR: t_append_branch: failure to add branches\n"); ser_error=lowest_ret; replies_locked = 0; UNLOCK_REPLIES(t); return lowest_ret; } ser_error=0; /* clear branch adding errors */ /* send them out now */ success_branch=0; /* since t_append_branch can only be called from REQUEST_ROUTE, always lock replies */ for (i=outgoings; i<t->nr_of_outgoings; i++) { if (added_branches & (1<<i)) { branch_ret=t_send_branch(t, i, orig_msg , 0, 0 /* replies are already locked */ ); if (branch_ret>=0){ /* some kind of success */ if (branch_ret==i) { /* success */ success_branch++; if (unlikely(has_tran_tmcbs(t, TMCB_REQUEST_OUT))) run_trans_callbacks_with_buf( TMCB_REQUEST_OUT, &t->uac[nr_branches].request, orig_msg, 0, -orig_msg->REQ_METHOD); } else /* new branch added */ added_branches |= 1<<branch_ret; } } } if (success_branch<=0) { /* return always E_SEND for now * (the real reason could be: denied by onsend routes, blacklisted, * send failed or any of the errors listed before + dns failed * when attempting dns failover) */ ser_error=E_SEND; /* else return the last error (?) */ /* the caller should take care and delete the transaction */ replies_locked = 0; UNLOCK_REPLIES(t); return -1; } ser_error=0; /* clear branch send errors, we have overall success */ set_kr(REQ_FWDED); replies_locked = 0; UNLOCK_REPLIES(t); return 1; canceled: DBG("t_append_branches: cannot append branches to a canceled transaction\n"); /* reset processed branches */ clear_branches(); /* restore backup flags from initial env */ setbflagsval(0, backup_bflags); /* update message flags, if changed in branch route */ t->uas.request->flags = orig_msg->flags; /* if needed unlock transaction's replies */ if (likely(replies_locked)) { /* restore the number of outgoing branches * since new branches have not been completed */ t->nr_of_outgoings = outgoings; replies_locked = 0; UNLOCK_REPLIES(t); } ser_error=E_CANCELED; return -1; }
/* * Create a Contact header field from the dset * array */ char* print_dset(struct sip_msg* msg, int* len) { int cnt, i; unsigned int qlen; qvalue_t q; str uri; char* p, *qbuf; static char dset[MAX_REDIRECTION_LEN]; if (msg->new_uri.s) { cnt = 1; *len = msg->new_uri.len; if (ruri_q != Q_UNSPECIFIED) { *len += 1 + Q_PARAM_LEN + len_q(ruri_q); } } else { cnt = 0; *len = 0; } init_branch_iterator(); while ((uri.s = next_branch(&uri.len, &q, 0, 0, 0, 0))) { cnt++; *len += uri.len; if (q != Q_UNSPECIFIED) { *len += 1 + Q_PARAM_LEN + len_q(q); } } if (cnt == 0) return 0; *len += CONTACT_LEN + CRLF_LEN + (cnt - 1) * CONTACT_DELIM_LEN; if (*len + 1 > MAX_REDIRECTION_LEN) { LOG(L_ERR, "ERROR: redirection buffer length exceed\n"); return 0; } memcpy(dset, CONTACT, CONTACT_LEN); p = dset + CONTACT_LEN; if (msg->new_uri.s) { if (ruri_q != Q_UNSPECIFIED) { *p++ = '<'; } memcpy(p, msg->new_uri.s, msg->new_uri.len); p += msg->new_uri.len; if (ruri_q != Q_UNSPECIFIED) { memcpy(p, Q_PARAM, Q_PARAM_LEN); p += Q_PARAM_LEN; qbuf = q2str(ruri_q, &qlen); memcpy(p, qbuf, qlen); p += qlen; } i = 1; } else { i = 0; } init_branch_iterator(); while ((uri.s = next_branch(&uri.len, &q, 0, 0, 0, 0))) { if (i) { memcpy(p, CONTACT_DELIM, CONTACT_DELIM_LEN); p += CONTACT_DELIM_LEN; } if (q != Q_UNSPECIFIED) { *p++ = '<'; } memcpy(p, uri.s, uri.len); p += uri.len; if (q != Q_UNSPECIFIED) { memcpy(p, Q_PARAM, Q_PARAM_LEN); p += Q_PARAM_LEN; qbuf = q2str(q, &qlen); memcpy(p, qbuf, qlen); p += qlen; } i++; } memcpy(p, CRLF " ", CRLF_LEN + 1); return dset; }
/* function returns: * 1 - forward successful * -1 - error during forward */ int t_forward_nonack( struct cell *t, struct sip_msg* p_msg , struct proxy_l * proxy, int proto) { str backup_uri; int branch_ret, lowest_ret; str current_uri; branch_bm_t added_branches; int first_branch; int i, q; struct cell *t_invite; int success_branch; int try_new; str dst_uri; /* make -Wall happy */ current_uri.s=0; set_kr(REQ_FWDED); if (p_msg->REQ_METHOD==METHOD_CANCEL) { t_invite=t_lookupOriginalT( p_msg ); if (t_invite!=T_NULL_CELL) { e2e_cancel( p_msg, t, t_invite ); UNREF(t_invite); return 1; } } /* backup current uri ... add_uac changes it */ backup_uri = p_msg->new_uri; /* if no more specific error code is known, use this */ lowest_ret=E_BUG; /* branches added */ added_branches=0; /* branch to begin with */ first_branch=t->nr_of_outgoings; /* on first-time forwarding, use current uri, later only what is in additional branches (which may be continuously refilled */ if (first_branch==0) { try_new=1; branch_ret=add_uac( t, p_msg, GET_RURI(p_msg), GET_NEXT_HOP(p_msg), proxy, proto ); if (branch_ret>=0) added_branches |= 1<<branch_ret; else lowest_ret=branch_ret; } else try_new=0; init_branch_iterator(); while((current_uri.s=next_branch( ¤t_uri.len, &q, &dst_uri.s, &dst_uri.len))) { try_new++; branch_ret=add_uac( t, p_msg, ¤t_uri, (dst_uri.len) ? (&dst_uri) : ¤t_uri, proxy, proto); /* pick some of the errors in case things go wrong; note that picking lowest error is just as good as any other algorithm which picks any other negative branch result */ if (branch_ret>=0) added_branches |= 1<<branch_ret; else lowest_ret=branch_ret; } /* consume processed branches */ clear_branches(); /* restore original URI */ p_msg->new_uri=backup_uri; /* don't forget to clear all branches processed so far */ /* things went wrong ... no new branch has been fwd-ed at all */ if (added_branches==0) { if (try_new==0) { LOG(L_ERR, "ERROR: t_forward_nonack: no branched for forwarding\n"); return -1; } LOG(L_ERR, "ERROR: t_forward_nonack: failure to add branches\n"); return lowest_ret; } /* send them out now */ success_branch=0; for (i=first_branch; i<t->nr_of_outgoings; i++) { if (added_branches & (1<<i)) { if (SEND_BUFFER( &t->uac[i].request)==-1) { LOG(L_ERR, "ERROR: t_forward_nonack: sending request failed\n"); if (proxy) { proxy->errors++; proxy->ok=0; } } else { success_branch++; } start_retr( &t->uac[i].request ); } } if (success_branch<=0) { ser_error=E_SEND; return -1; } return 1; }
static int xl_get_branches(struct sip_msg *msg, str *res, str *hp, int hi, int hf) { str uri; qvalue_t q; int len, cnt, i; unsigned int qlen; char *p, *qbuf; if(msg==NULL || res==NULL) return -1; if(msg->first_line.type == SIP_REPLY) return xl_get_null(msg, res, hp, hi, hf); cnt = len = 0; init_branch_iterator(); while ((uri.s = next_branch(&uri.len, &q, 0, 0, 0))) { cnt++; len += uri.len; if (q != Q_UNSPECIFIED) { len += 1 + Q_PARAM_LEN + len_q(q); } } if (cnt == 0) return xl_get_empty(msg, res, hp, hi, hf); len += (cnt - 1) * XLOG_FIELD_DELIM_LEN; if (len + 1 > LOCAL_BUF_SIZE) { LOG(L_ERR, "ERROR:xl_get_branches: local buffer length exceeded\n"); return xl_get_null(msg, res, hp, hi, hf); } i = 0; p = local_buf; init_branch_iterator(); while ((uri.s = next_branch(&uri.len, &q, 0, 0, 0))) { if (i) { memcpy(p, XLOG_FIELD_DELIM, XLOG_FIELD_DELIM_LEN); p += XLOG_FIELD_DELIM_LEN; } if (q != Q_UNSPECIFIED) { *p++ = '<'; } memcpy(p, uri.s, uri.len); p += uri.len; if (q != Q_UNSPECIFIED) { memcpy(p, Q_PARAM, Q_PARAM_LEN); p += Q_PARAM_LEN; qbuf = q2str(q, &qlen); memcpy(p, qbuf, qlen); p += qlen; } i++; } res->s = &(local_buf[0]); res->len = len; return 0; }