/* introduce a new uac to transaction; returns its branch id (>=0) or error (<0); it doesn't send a message yet -- a reply to it might interfere with the processes of adding multiple branches */ static int add_uac( struct cell *t, struct sip_msg *request, str *uri, str* next_hop, unsigned int bflags, str* path, struct proxy_l *proxy) { unsigned short branch; struct sip_msg_body *body_clone=NO_BODY_CLONE_MARKER; int do_free_proxy; int ret; branch=t->nr_of_outgoings; if (branch==MAX_BRANCHES) { LM_ERR("maximum number of branches exceeded\n"); ret=E_CFG; goto error; } /* check existing buffer -- rewriting should never occur */ if (t->uac[branch].request.buffer.s) { LM_CRIT("buffer rewrite attempt\n"); ret=ser_error=E_BUG; goto error; } /* set proper RURI to request to reflect the branch */ request->new_uri=*uri; request->parsed_uri_ok=0; request->dst_uri=*next_hop; request->path_vec=*path; request->ruri_bflags=bflags; if ( pre_print_uac_request( t, branch, request, &body_clone)!= 0 ) { ret = -1; goto error01; } /* check DNS resolution */ if (proxy){ do_free_proxy = 0; }else { proxy=uri2proxy( request->dst_uri.len ? &request->dst_uri:&request->new_uri, request->force_send_socket ? request->force_send_socket->proto : PROTO_NONE ); if (proxy==0) { ret=E_BAD_ADDRESS; goto error01; } do_free_proxy = 1; } msg_callback_process(request, REQ_PRE_FORWARD, (void *)proxy); if ( !(t->flags&T_NO_DNS_FAILOVER_FLAG) ) { t->uac[branch].proxy = shm_clone_proxy( proxy , do_free_proxy ); if (t->uac[branch].proxy==NULL) { ret = E_OUT_OF_MEM; goto error02; } } /* use the first address */ hostent2su( &t->uac[branch].request.dst.to, &proxy->host, proxy->addr_idx, proxy->port ? proxy->port:SIP_PORT); t->uac[branch].request.dst.proto = proxy->proto; /* do print of the uac request */ if ( update_uac_dst( request, &t->uac[branch] )!=0) { ret = ser_error; goto error02; } /* things went well, move ahead */ t->uac[branch].uri.s=t->uac[branch].request.buffer.s+ request->first_line.u.request.method.len+1; t->uac[branch].uri.len=request->new_uri.len; t->uac[branch].br_flags = request->ruri_bflags; t->uac[branch].added_rr = count_local_rr( request ); t->nr_of_outgoings++; /* done! */ ret=branch; error02: if(do_free_proxy) { free_proxy( proxy ); pkg_free( proxy ); } error01: post_print_uac_request( request, uri, next_hop, body_clone); if (ret < 0) { /* destroy all the bavps added, the path vector and the destination, * since this branch will never be properly added to * the UAC list, otherwise we'll have memory leaks - razvanc */ clean_branch(t->uac[branch]); memset(&t->uac[branch], 0, sizeof(t->uac[branch])); init_branch(&t->uac[branch], branch, t->wait_tl.set, t); } error: return ret; }
/* * Send a request using data from the dialog structure */ int t_uac(str* method, str* headers, str* body, dlg_t* dialog, transaction_cb cb, void* cbp,release_tmcb_param release_func) { union sockaddr_union to_su, new_to_su; struct cell *new_cell; struct cell *backup_cell; struct retr_buf *request; static struct sip_msg *req; struct usr_avp **backup; char *buf, *buf1; int buf_len, buf_len1; int ret, flags, sflag_bk; int backup_route_type; int sip_msg_len; unsigned int hi; struct socket_info *new_send_sock; str h_to, h_from, h_cseq, h_callid; struct proxy_l *proxy, *new_proxy; unsigned short dst_changed; ret=-1; /*** added by dcm * - needed by external ua to send a request within a dlg */ if(!dialog->hooks.next_hop && w_calculate_hooks(dialog)<0) goto error3; if(dialog->obp.s) dialog->hooks.next_hop = &dialog->obp; LM_DBG("next_hop=<%.*s>\n",dialog->hooks.next_hop->len, dialog->hooks.next_hop->s); /* calculate the socket corresponding to next hop */ proxy = uri2proxy( dialog->hooks.next_hop, dialog->send_sock ? dialog->send_sock->proto : PROTO_NONE ); if (proxy==0) { ret=E_BAD_ADDRESS; goto error3; } /* use the first address */ hostent2su( &to_su, &proxy->host, proxy->addr_idx, proxy->port ? proxy->port:SIP_PORT); /* check/discover the send socket */ if (dialog->send_sock) { /* if already set, the protocol of send sock must have the the same type as the proto required by destination URI */ if (proxy->proto != dialog->send_sock->proto) dialog->send_sock = NULL; } if (dialog->send_sock==NULL) { /* get the send socket */ dialog->send_sock = get_send_socket( NULL/*msg*/, &to_su, proxy->proto); if (!dialog->send_sock) { LM_ERR("no corresponding socket for af %d\n", to_su.s.sa_family); ser_error = E_NO_SOCKET; goto error2; } } LM_DBG("sending socket is %.*s \n", dialog->send_sock->name.len,dialog->send_sock->name.s); /* ***** Create TRANSACTION and all related ***** */ new_cell = build_cell( NULL/*msg*/, 1/*full UAS clone*/); if (!new_cell) { ret=E_OUT_OF_MEM; LM_ERR("short of cell shmem\n"); goto error2; } /* pass the transaction flags from dialog to transaction */ new_cell->flags |= dialog->T_flags; /* add the callback the transaction for LOCAL_COMPLETED event */ flags = TMCB_LOCAL_COMPLETED; /* Add also TMCB_LOCAL_RESPONSE_OUT if provisional replies are desired */ if (pass_provisional_replies || pass_provisional(new_cell)) flags |= TMCB_LOCAL_RESPONSE_OUT; if(cb && insert_tmcb(&(new_cell->tmcb_hl),flags,cb,cbp,release_func)!=1){ ret=E_OUT_OF_MEM; LM_ERR("short of tmcb shmem\n"); goto error2; } if (method->len==INVITE_LEN && memcmp(method->s, INVITE, INVITE_LEN)==0) new_cell->flags |= T_IS_INVITE_FLAG; new_cell->flags |= T_IS_LOCAL_FLAG; request = &new_cell->uac[0].request; if (dialog->forced_to_su.s.sa_family == AF_UNSPEC) request->dst.to = to_su; else request->dst.to = dialog->forced_to_su; request->dst.send_sock = dialog->send_sock; request->dst.proto = dialog->send_sock->proto; request->dst.proto_reserved1 = 0; hi=dlg2hash(dialog); LOCK_HASH(hi); insert_into_hash_table_unsafe(new_cell, hi); UNLOCK_HASH(hi); /* copy AVPs into transaction */ new_cell->user_avps = dialog->avps; dialog->avps = NULL; /* ***** Create the message buffer ***** */ buf = build_uac_req(method, headers, body, dialog, 0, new_cell, &buf_len); if (!buf) { LM_ERR("failed to build message\n"); ret=E_OUT_OF_MEM; goto error1; } if (local_rlist.a) { LM_DBG("building sip_msg from buffer\n"); req = buf_to_sip_msg(buf, buf_len, dialog); if (req==NULL) { LM_ERR("failed to build sip_msg from buffer\n"); } else { /* set this transaction as active one */ backup_cell = get_t(); set_t( new_cell ); /* set transaction AVP list */ backup = set_avp_list( &new_cell->user_avps ); /* backup script flags */ sflag_bk = getsflags(); /* disable parallel forking */ set_dset_state( 0 /*disable*/); /* run the route */ swap_route_type( backup_route_type, LOCAL_ROUTE); run_top_route( local_rlist.a, req); set_route_type( backup_route_type ); /* transfer current message context back to t */ new_cell->uac[0].br_flags = getb0flags(req); /* restore the prevoius active transaction */ set_t( backup_cell ); set_dset_state( 1 /*enable*/); setsflagsval(sflag_bk); set_avp_list( backup ); /* check for changes - if none, do not regenerate the buffer */ dst_changed = 1; if (req->new_uri.s || req->force_send_socket!=dialog->send_sock || req->dst_uri.len != dialog->hooks.next_hop->len || memcmp(req->dst_uri.s,dialog->hooks.next_hop->s,req->dst_uri.len) || (dst_changed=0)==0 || req->add_rm || req->body_lumps){ new_send_sock = NULL; /* do we also need to change the destination? */ if (dst_changed) { /* calculate the socket corresponding to next hop */ new_proxy = uri2proxy( req->dst_uri.s ? &(req->dst_uri) : &req->new_uri, PROTO_NONE ); if (new_proxy==0) goto abort_update; /* use the first address */ hostent2su( &new_to_su, &new_proxy->host, new_proxy->addr_idx, new_proxy->port ? new_proxy->port:SIP_PORT); /* get the send socket */ new_send_sock = get_send_socket( req, &new_to_su, new_proxy->proto); if (!new_send_sock) { free_proxy( new_proxy ); pkg_free( new_proxy ); LM_ERR("no socket found for the new destination\n"); goto abort_update; } } /* if interface change, we need to re-build the via */ if (new_send_sock && new_send_sock != dialog->send_sock) { LM_DBG("Interface change in local route -> " "rebuilding via\n"); if (!del_lump(req,req->h_via1->name.s - req->buf, req->h_via1->len,0)) { LM_ERR("Failed to remove initial via \n"); goto abort_update; } memcpy(req->add_to_branch_s,req->via1->branch->value.s, req->via1->branch->value.len); req->add_to_branch_len = req->via1->branch->value.len; /* update also info about new destination and send sock */ dialog->send_sock = new_send_sock; free_proxy( proxy ); pkg_free( proxy ); proxy = new_proxy; request->dst.send_sock = new_send_sock; request->dst.proto = new_send_sock->proto; request->dst.proto_reserved1 = 0; /* build the shm buffer now */ set_init_lump_flags(LUMPFLAG_BRANCH); buf1 = build_req_buf_from_sip_req(req, (unsigned int*)&buf_len1, new_send_sock, new_send_sock->proto, MSG_TRANS_SHM_FLAG); reset_init_lump_flags(); del_flaged_lumps( &req->add_rm, LUMPFLAG_BRANCH); } else { LM_DBG("Change in local route -> rebuilding buffer\n"); /* build the shm buffer now */ buf1 = build_req_buf_from_sip_req(req, (unsigned int*)&buf_len1, dialog->send_sock, dialog->send_sock->proto, MSG_TRANS_SHM_FLAG|MSG_TRANS_NOVIA_FLAG); /* now as it used, hide the original VIA header */ del_lump(req,req->h_via1->name.s - req->buf, req->h_via1->len, 0); } if (!buf1) { LM_ERR("no more shm mem\n"); /* keep original buffer */ goto abort_update; } /* update shortcuts */ if(!req->add_rm && !req->new_uri.s) { /* headers are not affected, simply tranlate */ new_cell->from.s = new_cell->from.s - buf + buf1; new_cell->to.s = new_cell->to.s - buf + buf1; new_cell->callid.s = new_cell->callid.s - buf + buf1; new_cell->cseq_n.s = new_cell->cseq_n.s - buf + buf1; } else { /* use heavy artilery :D */ if (extract_ftc_hdrs( buf1, buf_len1, &h_from, &h_to, &h_cseq, &h_callid)!=0 ) { LM_ERR("failed to update shortcut pointers\n"); shm_free(buf1); goto abort_update; } new_cell->from = h_from; new_cell->to = h_to; new_cell->callid = h_callid; new_cell->cseq_n = h_cseq; } /* here we rely on how build_uac_req() builds the first line */ new_cell->uac[0].uri.s = buf1 + req->first_line.u.request.method.len + 1; new_cell->uac[0].uri.len = GET_RURI(req)->len; /* update also info about new destination and send sock */ if (new_send_sock) request->dst.to = new_to_su; shm_free(buf); buf = buf1; buf_len = buf_len1; /* use new buffer */ } else { /* no changes over the message, buffer is already generated, just hide the original VIA for potential further branches */ del_lump(req,req->h_via1->name.s-req->buf,req->h_via1->len,0); } abort_update: /* save the SIP message into transaction */ new_cell->uas.request = sip_msg_cloner( req, &sip_msg_len, 1); if (new_cell->uas.request==NULL) { /* reset any T triggering */ new_cell->on_negative = 0; new_cell->on_reply = 0; } else { new_cell->uas.end_request= ((char*)new_cell->uas.request)+sip_msg_len; } /* no parallel support in UAC transactions */ new_cell->on_branch = 0; free_sip_msg(req); } } /* for DNS based failover, copy the DNS proxy into transaction */ if (!disable_dns_failover) { new_cell->uac[0].proxy = shm_clone_proxy( proxy, 1/*do_free*/); if (new_cell->uac[0].proxy==NULL) LM_ERR("failed to store DNS info -> no DNS based failover\n"); } new_cell->method.s = buf; new_cell->method.len = method->len; request->buffer.s = buf; request->buffer.len = buf_len; new_cell->nr_of_outgoings++; if(last_localT) { *last_localT = new_cell; REF_UNSAFE(new_cell); } if (SEND_BUFFER(request) == -1) { LM_ERR("attempt to send to '%.*s' failed\n", dialog->hooks.next_hop->len, dialog->hooks.next_hop->s); } if (method->len==ACK_LEN && memcmp(method->s, ACK, ACK_LEN)==0 ) { t_release_transaction(new_cell); } else { start_retr(request); } free_proxy( proxy ); pkg_free( proxy ); return 1; error1: LOCK_HASH(hi); remove_from_hash_table_unsafe(new_cell); UNLOCK_HASH(hi); free_cell(new_cell); error2: free_proxy( proxy ); pkg_free( proxy ); error3: return ret; }
/* introduce a new uac to transaction; returns its branch id (>=0) or error (<0); it doesn't send a message yet -- a reply to it might interfere with the processes of adding multiple branches */ static int add_uac( struct cell *t, struct sip_msg *request, str *uri, str* next_hop, str* path, struct proxy_l *proxy) { unsigned short branch; int do_free_proxy; int ret; branch=t->nr_of_outgoings; if (branch==MAX_BRANCHES) { LM_ERR("maximum number of branches exceeded\n"); ret=E_CFG; goto error; } /* check existing buffer -- rewriting should never occur */ if (t->uac[branch].request.buffer.s) { LM_CRIT("buffer rewrite attempt\n"); ret=ser_error=E_BUG; goto error; } /* set proper RURI to request to reflect the branch */ request->new_uri=*uri; request->parsed_uri_ok=0; request->dst_uri=*next_hop; request->path_vec=*path; if ( pre_print_uac_request( t, branch, request)!= 0 ) { ret = -1; goto error01; } /* check DNS resolution */ if (proxy){ do_free_proxy = 0; }else { proxy=uri2proxy( request->dst_uri.len ? &request->dst_uri:&request->new_uri, PROTO_NONE ); if (proxy==0) { ret=E_BAD_ADDRESS; goto error01; } do_free_proxy = 1; } msg_callback_process(request, REQ_PRE_FORWARD, (void *)proxy); if ( !(t->flags&T_NO_DNS_FAILOVER_FLAG) ) { t->uac[branch].proxy = shm_clone_proxy( proxy , do_free_proxy ); if (t->uac[branch].proxy==NULL) { ret = E_OUT_OF_MEM; goto error02; } } /* use the first address */ hostent2su( &t->uac[branch].request.dst.to, &proxy->host, proxy->addr_idx, proxy->port ? proxy->port:SIP_PORT); t->uac[branch].request.dst.proto = proxy->proto; if ( update_uac_dst( request, &t->uac[branch] )!=0) { ret = ser_error; goto error02; } /* things went well, move ahead */ t->uac[branch].uri.s=t->uac[branch].request.buffer.s+ request->first_line.u.request.method.len+1; t->uac[branch].uri.len=request->new_uri.len; t->uac[branch].br_flags = getb0flags(); t->uac[branch].added_rr = count_local_rr( request ); t->nr_of_outgoings++; /* done! */ ret=branch; error02: if(do_free_proxy) { free_proxy( proxy ); pkg_free( proxy ); } error01: post_print_uac_request( request, uri, next_hop); error: return ret; }