modules/tm/uac.c
caf80ae6
 /*
  * $Id$
  *
  * simple UAC for things such as SUBSCRIBE or SMS gateway;
  * no authentication and other UAC features -- just send
  * a message, retransmit and await a reply; forking is not
  * supported during client generation, in all other places
  * it is -- adding it should be simple
7dd0b342
  *
84d8e165
  * Copyright (C) 2001-2003 FhG Fokus
7dd0b342
  *
  * This file is part of ser, a free SIP server.
  *
  * ser is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version
  *
  * For a license to use the ser software under conditions
  * other than those described here, or to purchase support for this
  * software, please contact iptel.org by e-mail at the following addresses:
  *    info@iptel.org
  *
  * ser is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License 
  * along with this program; if not, write to the Free Software 
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
ff979952
  *
88e4bebb
  * History:
  * --------
8710b7ee
  *  2003-01-23  t_uac_dlg now uses get_out_socket (jiri)
  *  2003-01-27  fifo:t_uac_dlg completed (jiri)
  *  2003-01-29  scratchpad removed (jiri)
  *  2003-02-13  t_uac, t _uac_dlg, gethfblock, uri2proxy changed to use 
  *               proto & rb->dst (andrei)
  *  2003-02-27  FIFO/UAC now dumps reply -- good for CTD (jiri)
  *  2003-02-28  scratchpad compatibility abandoned (jiri)
19710544
  *  2003-03-01  kr set through a function now (jiri)
8710b7ee
  *  2003-03-19  replaced all mallocs/frees w/ pkg_malloc/pkg_free (andrei)
19710544
  *  2003-04-02  port_no_str does not contain a leading ':' anymore (andrei)
ffd3b034
  *  2003-07-08  appropriate log messages in check_params(...), 
  *               call calculate_hooks if next_hop==NULL in t_uac (dcm) 
9f4c52ce
  *  2003-10-24  updated to the new socket_info lists (andrei)
a70eb298
  *  2003-12-03  completion filed removed from transaction and uac callbacks
  *              merged in transaction callbacks as LOCAL_COMPLETED (bogdan)
c2ea965c
  *  2004-02-11  FIFO/CANCEL + alignments (hash=f(callid,cseq)) (uli+jiri)
0be6158b
  *  2004-02-13  t->is_invite, t->local, t->noisy_ctimer replaced (bogdan)
2be21e31
  *  2004-08-23  avp support in t_uac (bogdan)
bb050b70
  *  2005-12-16  t_uac will set the new_cell timers to the default values,
  *               fixes 0 fr_timer bug (andrei)
dcb59e67
  *  2006-08-11  t_uac uses dns failover until it finds a send socket (andrei)
9217a640
  *  2007-03-15  TMCB_ONSEND callbacks support added (andrei)
5c953757
  *  2007-03-23  TMCB_LOCAL_REQUEST_IN callbacks support (andrei)
0277f618
  *  2007-04-23  per dialog callbacks support (andrei)
c0ff60b6
  *  2007-06-01  support for per transaction different retransmissions intervals
  *              (andrei)
caf80ae6
  */
 
27c28ba1
 #include <string.h>
c5867ab3
 #include "../../mem/shm_mem.h"
caf80ae6
 #include "../../dprint.h"
c5867ab3
 #include "../../globals.h"
 #include "../../md5.h"
82802743
 #include "../../crc.h"
c5867ab3
 #include "../../ip_addr.h"
9f4c52ce
 #include "../../socket_info.h"
6aa46ec4
 #include "../../compiler_opt.h"
2b36d889
 #include "config.h"
caf80ae6
 #include "ut.h"
c5867ab3
 #include "h_table.h"
 #include "t_hooks.h"
 #include "t_funcs.h"
caf80ae6
 #include "t_msgbuilder.h"
c5867ab3
 #include "callid.h"
caf80ae6
 #include "uac.h"
351210a5
 #include "t_stats.h"
2cfcc6bb
 #ifdef USE_DNS_FAILOVER
 #include "../../dns_cache.h"
 #include "../../cfg_core.h" /* cfg_get(core, core_cfg, use_dns_failover) */
 #endif
8bae6347
 #ifdef WITH_EVENT_LOCAL_REQUEST
 #include "../../receive.h"
 #include "../../route.h"
 #include "../../action.h"
43e7274c
 #include "../../onsend.h"
3cf20fed
 #include "t_lookup.h"
8bae6347
 #endif
1540fad1
 
c5867ab3
 #define FROM_TAG_LEN (MD5_LEN + 1 /* - */ + CRC16_LEN) /* length of FROM tags */
caf80ae6
 
5c037f80
 #ifdef WITH_EVENT_LOCAL_REQUEST
 /* where to go for the local request route ("tm:local-request") */
 int goto_on_local_req=-1; /* default disabled */
 #endif /* WITH_EVEN_LOCAL_REQuEST */
 
c5867ab3
 static char from_tag[FROM_TAG_LEN + 1];
caf80ae6
 
c5867ab3
 /*
  * Initialize UAC
  */
 int uac_init(void) 
 {
caf80ae6
 	str src[3];
9f4c52ce
 	struct socket_info *si;
caf80ae6
 
c5867ab3
 	if (RAND_MAX < TABLE_ENTRIES) {
82802743
 		LOG(L_WARN, "Warning: uac does not spread "
84d8e165
 		    "across the whole hash table\n");
82802743
 	}
9f4c52ce
 	/* on tcp/tls bind_address is 0 so try to get the first address we listen
 	 * on no matter the protocol */
 	si=bind_address?bind_address:get_first_socket();
 	if (si==0){
1d4c5c00
 		LOG(L_CRIT, "BUG: uac_init: null socket list\n");
9f4c52ce
 		return -1;
 	}
82802743
 
 	/* calculate the initial From tag */
c5867ab3
 	src[0].s = "Long live SER server";
 	src[0].len = strlen(src[0].s);
9f4c52ce
 	src[1].s = si->address_str.s;
c5867ab3
 	src[1].len = strlen(src[1].s);
9f4c52ce
 	src[2].s = si->port_no_str.s;
c5867ab3
 	src[2].len = strlen(src[2].s);
 
5da567f4
 	MD5StringArray(from_tag, src, 3);
c5867ab3
 	from_tag[MD5_LEN] = '-';
82802743
 	return 1;
caf80ae6
 }
 
 
c5867ab3
 /*
  * Generate a From tag
  */
 void generate_fromtag(str* tag, str* callid)
82802743
 {
c5867ab3
 	     /* calculate from tag from callid */
 	crcitt_string_array(&from_tag[MD5_LEN + 1], callid, 1);
 	tag->s = from_tag; 
 	tag->len = FROM_TAG_LEN;
caf80ae6
 }
 
1540fad1
 
c5867ab3
 /*
  * Check value of parameters
  */
2c2a937c
 static inline int check_params(uac_req_t *uac_r, str* to, str* from)
c5867ab3
 {
2c2a937c
 	if (!uac_r || !uac_r->method || !to || !from) {
c5867ab3
 		LOG(L_ERR, "check_params(): Invalid parameter value\n");
 		return -1;
1540fad1
 	}
 
2c2a937c
 	if (!uac_r->method->s || !uac_r->method->len) {
c5867ab3
 		LOG(L_ERR, "check_params(): Invalid request method\n");
 		return -2;
1540fad1
 	}
 
c5867ab3
 	if (!to->s || !to->len) {
ffd3b034
 		LOG(L_ERR, "check_params(): Invalid To URI\n");
c5867ab3
 		return -4;
 	}
1540fad1
 
c5867ab3
 	if (!from->s || !from->len) {
ffd3b034
 		LOG(L_ERR, "check_params(): Invalid From URI\n");
c5867ab3
 		return -5;
 	}
 	return 0;
1540fad1
 }
 
c2ea965c
 static inline unsigned int dlg2hash( dlg_t* dlg )
 {
 	str cseq_nr;
 	unsigned int hashid;
 
 	cseq_nr.s=int2str(dlg->loc_seq.value, &cseq_nr.len);
 	hashid=hash(dlg->id.call_id, cseq_nr);
 	DBG("DEBUG: dlg2hash: %d\n", hashid);
 	return hashid;
 }
27c28ba1
 
e67d9509
 
 /* 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) 
  */
2c2a937c
 static inline int t_uac_prepare(uac_req_t *uac_r, 
 		struct retr_buf **dst_req,
04436036
 		struct cell **dst_cell)
27c28ba1
 {
7a068ff3
 	struct dest_info dst;
c5867ab3
 	struct cell *new_cell;
 	struct retr_buf *request;
 	char* buf;
8bae6347
 	int buf_len, ret;
4d8a988d
 	unsigned int hi;
04436036
 	int is_ack;
c0ff60b6
 	ticks_t lifetime;
dcb59e67
 #ifdef USE_DNS_FAILOVER
 	struct dns_srv_handle dns_h;
 #endif
d65cdd3f
 	long nhtype;
8bae6347
 #ifdef WITH_EVENT_LOCAL_REQUEST
3cf20fed
 	struct cell *backup_t;
 	int backup_branch;
 	unsigned int backup_msgid;
8bae6347
 	static struct sip_msg lreq;
 	char *buf1;
 	int buf_len1;
 	int sflag_bk;
 	int backup_route_type;
 #endif
1d777cdc
 	snd_flags_t snd_flags;
1e83919b
 	tm_xlinks_t backup_xd;
 	tm_xdata_t local_xd;
ff0c03d4
 
 	ret=-1;
04436036
 	hi=0; /* make gcc happy */
2410e6eb
 	/*if (dst_req) *dst_req = NULL;*/
2c2a937c
 	is_ack = (((uac_r->method->len == 3) && (memcmp("ACK", uac_r->method->s, 3)==0)) ? 1 : 0);
ffd3b034
 	
 	/*** added by dcm 
 	 * - needed by external ua to send a request within a dlg
 	 */
d65cdd3f
 	if ((nhtype = w_calculate_hooks(uac_r->dialog)) < 0)
 		/* if err's returned, the message is incorrect */
ffd3b034
 		goto error2;
27c28ba1
 
2c2a937c
 	if (!uac_r->dialog->loc_seq.is_set) {
04436036
 		/* this is the first request in the dialog,
 		set cseq to default value now - Miklos */
2c2a937c
 		uac_r->dialog->loc_seq.value = DEFAULT_CSEQ;
 		uac_r->dialog->loc_seq.is_set = 1;
04436036
 	}
 
2c2a937c
 	DBG("DEBUG:tm:t_uac: next_hop=<%.*s>\n",uac_r->dialog->hooks.next_hop->len,
 			uac_r->dialog->hooks.next_hop->s);
934de3e1
 	/* new message => take the dialog send_socket if set, or the default
 	  send_socket if not*/
1d777cdc
 	SND_FLAGS_INIT(&snd_flags);
dcb59e67
 #ifdef USE_DNS_FAILOVER
2cfcc6bb
 	if (cfg_get(core, core_cfg, use_dns_failover)){
dcb59e67
 		dns_srv_handle_init(&dns_h);
1d777cdc
 		if ((uri2dst2(&dns_h, &dst, uac_r->dialog->send_sock, snd_flags,
934de3e1
 							uac_r->dialog->hooks.next_hop, PROTO_NONE)==0)
dcb59e67
 				|| (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{
1d777cdc
 		if ((uri2dst2(0, &dst, uac_r->dialog->send_sock, snd_flags,
934de3e1
 						uac_r->dialog->hooks.next_hop, PROTO_NONE)==0) ||
dcb59e67
 				(dst.send_sock==0)){
 			ser_error = E_NO_SOCKET;
 			ret=ser_error;
 			LOG(L_ERR, "t_uac: no socket found\n");
 			goto error2;
 		}
 	}
934de3e1
 #else /* USE_DNS_FAILOVER */
1d777cdc
 	if ((uri2dst2(&dst, uac_r->dialog->send_sock, snd_flags,
934de3e1
 					uac_r->dialog->hooks.next_hop, PROTO_NONE)==0) ||
7a068ff3
 			(dst.send_sock==0)){
 		ser_error = E_NO_SOCKET;
ff0c03d4
 		ret=ser_error;
c5867ab3
 		LOG(L_ERR, "t_uac: no socket found\n");
 		goto error2;
dcb59e67
 	}
934de3e1
 #endif /* USE_DNS_FAILOVER */
27c28ba1
 
1e83919b
 	/* 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);
27c28ba1
 	new_cell = build_cell(0); 
1e83919b
 	tm_xdata_replace(0, &backup_xd);
 
27c28ba1
 	if (!new_cell) {
ff0c03d4
 		ret=E_OUT_OF_MEM;
c5867ab3
 		LOG(L_ERR, "t_uac: short of cell shmem\n");
 		goto error2;
27c28ba1
 	}
2c2a937c
 	if (uac_r->method->len==INVITE_LEN && memcmp(uac_r->method->s, INVITE, INVITE_LEN)==0){
c0ff60b6
 		new_cell->flags |= T_IS_INVITE_FLAG;
2b36d889
 		new_cell->flags|=T_AUTO_INV_100 &
 				(!cfg_get(tm, tm_cfg, tm_auto_inv_100) -1);
d65cdd3f
 #ifdef WITH_AS_SUPPORT
 		if (uac_r->cb_flags & TMCB_DONT_ACK)
 			new_cell->flags |= T_NO_AUTO_ACK;
 #endif
2b36d889
 		lifetime=cfg_get(tm, tm_cfg, tm_max_inv_lifetime);
c0ff60b6
 	}else
2b36d889
 		lifetime=cfg_get(tm, tm_cfg, tm_max_noninv_lifetime);
c0ff60b6
 	new_cell->flags |= T_IS_LOCAL_FLAG;
bb050b70
 	/* 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 */
2b36d889
 	new_cell->fr_timeout=cfg_get(tm, tm_cfg, fr_timeout);
 	new_cell->fr_inv_timeout=cfg_get(tm, tm_cfg, fr_inv_timeout);
c0ff60b6
 	new_cell->end_of_life=get_ticks_raw()+lifetime;
 #ifdef TM_DIFF_RT_TIMEOUT
 	/* same as above for retransmission intervals */
a92001d4
 	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);
c0ff60b6
 #endif
27c28ba1
 
fde02f64
 	set_kr(REQ_FWDED);
0be6158b
 
c5867ab3
 	request = &new_cell->uac[0].request;
7a068ff3
 	request->dst = dst;
d65cdd3f
 	request->flags |= nhtype;
27c28ba1
 
04436036
 	if (!is_ack) {
e67d9509
 #ifdef TM_DEL_UNREF
 		INIT_REF(new_cell, 1); /* ref'ed only from the hash */
 #endif
2c2a937c
 		hi=dlg2hash(uac_r->dialog);
04436036
 		LOCK_HASH(hi);
 		insert_into_hash_table_unsafe(new_cell, hi);
 		UNLOCK_HASH(hi);
 	}
27c28ba1
 
2c2a937c
 	buf = build_uac_req(uac_r->method, uac_r->headers, uac_r->body, uac_r->dialog, 0, new_cell,
7a068ff3
 		&buf_len, &dst);
27c28ba1
 	if (!buf) {
c5867ab3
 		LOG(L_ERR, "t_uac: Error while building message\n");
ff0c03d4
 		ret=E_OUT_OF_MEM;
c5867ab3
 		goto error1;
27c28ba1
 	}
c5867ab3
 
8bae6347
 #ifdef WITH_EVENT_LOCAL_REQUEST
5c037f80
 	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)) {
8bae6347
 			/* fill some field in sip_msg */
5c037f80
 			if (unlikely(set_dst_uri(&lreq, uac_r->dialog->hooks.next_hop))) {
8bae6347
 				LM_ERR("failed to set dst_uri");
 				free_sip_msg(&lreq);
 			} else {
43e7274c
 				struct onsend_info onsnd_info;
 
8bae6347
 				lreq.force_send_socket = uac_r->dialog->send_sock;
ef6fed5b
 				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 */
8bae6347
 				sflag_bk = getsflags();
08798a6d
 				tm_xdata_swap(new_cell, &backup_xd, 0);
8bae6347
 
43e7274c
 				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;
 
8bae6347
 				/* run the route */
 				backup_route_type = get_route_type();
 				set_route_type(LOCAL_ROUTE);
3cf20fed
 				/* 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);
5c037f80
 				run_top_route(event_rt.rlist[goto_on_local_req], &lreq, 0);
3cf20fed
 				/* restore original environment */
 				set_t(backup_t, backup_branch);
 				global_msg_id=backup_msgid;
8bae6347
 				set_route_type( backup_route_type );
43e7274c
 				p_onsend=0;
8bae6347
 
 				/* restore original environment */
08798a6d
 				tm_xdata_swap(new_cell, &backup_xd, 1);
8bae6347
 				setsflagsval(sflag_bk);
 
5c037f80
 				if (unlikely(lreq.new_uri.s))
8bae6347
 				{
 					pkg_free(lreq.new_uri.s);
 					lreq.new_uri.s=0;
 					lreq.new_uri.len=0;
 				}
5c037f80
 				if (unlikely(lreq.dst_uri.s))
8bae6347
 				{
 					pkg_free(lreq.dst_uri.s);
 					lreq.dst_uri.s=0;
 					lreq.dst_uri.len=0;
 				}
 
5c037f80
 				if (unlikely(lreq.add_rm || lreq.body_lumps)) {
8bae6347
 					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);
0f6dc007
 					if (likely(buf1)){
8bae6347
 						shm_free(buf);
 						buf = buf1;
 						buf_len = buf_len1;
0f6dc007
 						/* a possible change of the method is not handled! */
8bae6347
 					}
 				}
0f6dc007
 				lreq.buf=0; /* covers the obsolete DYN_BUF */
 				free_sip_msg(&lreq);
8bae6347
 			}
 		}
 	}
 #endif
 
27c28ba1
 	new_cell->method.s = buf;
2c2a937c
 	new_cell->method.len = uac_r->method->len;
27c28ba1
 
 	request->buffer = buf;
c5867ab3
 	request->buffer_len = buf_len;
27c28ba1
 	new_cell->nr_of_outgoings++;
2c2a937c
 
 	/* 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. */
657f117b
 	if(uac_r->cb && insert_tmcb(&(new_cell->tmcb_hl), uac_r->cb_flags, 
 								*(uac_r->cb), uac_r->cbp, NULL)!=1){
2c2a937c
 		ret=E_OUT_OF_MEM; 
 		LOG(L_ERR, "t_uac: short of tmcb shmem\n");
 		goto error1;
 	}
5c953757
 	if (has_local_reqin_tmcbs())
 			run_local_reqin_callbacks(new_cell, 0, 0);
0277f618
 #ifdef DIALOG_CALLBACKS
2c2a937c
 	run_trans_dlg_callbacks(uac_r->dialog, new_cell, request);
0277f618
 #endif /* DIALOG_CALLBACKS */
2410e6eb
 	if (dst_req) *dst_req = request;
04436036
 	if (dst_cell) *dst_cell = new_cell;
e67d9509
 	else if(is_ack && dst_req==0){
 		free_cell(new_cell);
 	}
dcb59e67
 	
27c28ba1
 	return 1;
 
4d8a988d
  error1:
04436036
  	if (!is_ack) {
 		LOCK_HASH(hi);
 		remove_from_hash_table_unsafe(new_cell);
 		UNLOCK_HASH(hi);
e67d9509
 #ifdef TM_DEL_UNREF
 		UNREF_FREE(new_cell);
 	}else
 #else
04436036
 	}
e67d9509
 #endif
 		free_cell(new_cell);
a70eb298
 error2:
ff0c03d4
 	return ret;
27c28ba1
 }
 
2410e6eb
 /*
  * Prepare a message within a dialog
  */
2c2a937c
 int prepare_req_within(uac_req_t *uac_r,
 		struct retr_buf **dst_req)
2410e6eb
 {
2c2a937c
 	if (!uac_r || !uac_r->method || !uac_r->dialog) {
2410e6eb
 		LOG(L_ERR, "req_within: Invalid parameter value\n");
 		goto err;
 	}
 
2c2a937c
 	if (uac_r->dialog->state != DLG_CONFIRMED) {
2410e6eb
 		LOG(L_ERR, "req_within: Dialog is not confirmed yet\n");
 		goto err;
 	}
 
2c2a937c
 	if ((uac_r->method->len == 3) && (!memcmp("ACK", uac_r->method->s, 3))) goto send;
 	if ((uac_r->method->len == 6) && (!memcmp("CANCEL", uac_r->method->s, 6))) goto send;
 	uac_r->dialog->loc_seq.value++; /* Increment CSeq */
2410e6eb
  send:
2c2a937c
 	return t_uac_prepare(uac_r, dst_req, 0);
2410e6eb
 
  err:
 	/* if (cbp) shm_free(cbp); */
 	/* !! never free cbp here because if t_uac_prepare fails, cbp is not freed
 	 * and thus caller has no chance to discover if it is freed or not !! */
 	return -1;
 }
 
04436036
 static inline void send_prepared_request_impl(struct retr_buf *request, int retransmit)
2410e6eb
 {
 	if (SEND_BUFFER(request) == -1) {
 		LOG(L_ERR, "t_uac: Attempt to send to precreated request failed\n");
 	}
6aa46ec4
 	else if (unlikely(has_tran_tmcbs(request->my_T, TMCB_REQUEST_SENT)))
9217a640
 		/* we don't know the method here */
68017b5d
 			run_trans_callbacks_with_buf(TMCB_REQUEST_SENT, request, 0, 0,
 			TMCB_LOCAL_F);
2410e6eb
 	
04436036
 	if (retransmit && (start_retr(request)!=0))
2410e6eb
 		LOG(L_CRIT, "BUG: t_uac: failed to start retr. for %p\n", request);
 }
 
 void send_prepared_request(struct retr_buf *request)
 {
04436036
 	send_prepared_request_impl(request, 1 /* retransmit */);
2410e6eb
 }
 
 /*
  * Send a request using data from the dialog structure
  */
2c2a937c
 int t_uac(uac_req_t *uac_r)
2410e6eb
 {
d65cdd3f
 	return t_uac_with_ids(uac_r, NULL, NULL);
2410e6eb
 }
 
04436036
 /*
  * Send a request using data from the dialog structure
  * ret_index and ret_label will identify the new cell
  */
2c2a937c
 int t_uac_with_ids(uac_req_t *uac_r,
04436036
 	unsigned int *ret_index, unsigned int *ret_label)
 {
 	struct retr_buf *request;
 	struct cell *cell;
 	int ret;
 	int is_ack;
 
2c2a937c
 	ret = t_uac_prepare(uac_r, &request, &cell);
04436036
 	if (ret < 0) return ret;
2c2a937c
 	is_ack = (uac_r->method->len == 3) && (memcmp("ACK", uac_r->method->s, 3)==0) ? 1 : 0;
04436036
 	send_prepared_request_impl(request, !is_ack /* retransmit */);
 	if (is_ack) {
 		if (cell) free_cell(cell);
 		if (ret_index && ret_label)
 			*ret_index = *ret_label = 0;
 	} else {
 		if (ret_index && ret_label) {
 			*ret_index = cell->hash_index;
 			*ret_label = cell->label;
 		}
 	}
 	return ret;
 }
27c28ba1
 
d65cdd3f
 #ifdef WITH_AS_SUPPORT
 struct retr_buf *local_ack_rb(sip_msg_t *rpl_2xx, struct cell *trans,
 					unsigned int branch, str *hdrs, str *body)
 {
 	struct retr_buf *lack;
 	unsigned int buf_len;
 	char *buffer;
 	struct dest_info dst;
 
 	buf_len = (unsigned)sizeof(struct retr_buf);
 	if (! (buffer = build_dlg_ack(rpl_2xx, trans, branch, hdrs, body, 
 			&buf_len, &dst))) {
 		return 0;
 	} else {
 		/* 'buffer' now points into a contiguous chunk of memory with enough
 		 * room to hold both the retr. buffer and the string raw buffer: it
 		 * points to the begining of the string buffer; we iterate back to get
 		 * the begining of the space for the retr. buffer. */
 		lack = &((struct retr_buf *)buffer)[-1];
 		lack->buffer = buffer;
 		lack->buffer_len = buf_len;
 		lack->dst = dst;
 	}
 
 	/* TODO: need next 2? */
 	lack->activ_type = TYPE_LOCAL_ACK;
 	lack->my_T = trans;
 
 	return lack;
 }
 
 void free_local_ack(struct retr_buf *lack)
 {
 	shm_free(lack);
 }
 
 void free_local_ack_unsafe(struct retr_buf *lack)
 {
 	shm_free_unsafe(lack);
 }
 
 /**
  * @return: 
  * 	0: success
  * 	-1: internal error
  * 	-2: insane call :)
  */
 int ack_local_uac(struct cell *trans, str *hdrs, str *body)
 {
 	struct retr_buf *local_ack, *old_lack;
 	int ret;
311d365b
 	struct tmcb_params onsend_params;
d65cdd3f
 
 	/* sanity checks */
 
 #ifdef EXTRA_DEBUG
 	if (! trans) {
 		BUG("no transaction to ACK.\n");
 		abort();
 	}
 #endif
 
 #define RET_INVALID \
 		ret = -2; \
 		goto fin
 
 	if (! is_local(trans)) {
 		ERR("trying to ACK non local transaction (T@%p).\n", trans);
 		RET_INVALID;
 	}
 	if (! is_invite(trans)) {
 		ERR("trying to ACK non INVITE local transaction (T@%p).\n", trans);
 		RET_INVALID;
 	}
 	if (! trans->uac[0].reply) {
 		ERR("trying to ACK un-completed INVITE transaction (T@%p).\n", trans);
 		RET_INVALID;
 	}
 
 	if (! (trans->flags & T_NO_AUTO_ACK)) {
 		ERR("trying to ACK an auto-ACK transaction (T@%p).\n", trans);
 		RET_INVALID;
 	}
 	if (trans->uac[0].local_ack) {
 		ERR("trying to rebuild ACK retransmission buffer (T@%p).\n", trans);
 		RET_INVALID;
 	}
 
 	/* looks sane: build the retransmission buffer */
 
 	if (! (local_ack = local_ack_rb(trans->uac[0].reply, trans, /*branch*/0, 
 			hdrs, body))) {
 		ERR("failed to build ACK retransmission buffer");
 		RET_INVALID;
 	} else {
 		/* set the new buffer, but only if not already set (conc. invok.) */
 		if ((old_lack = (struct retr_buf *)atomic_cmpxchg_long(
 				(void *)&trans->uac[0].local_ack, 0, (long)local_ack))) {
 			/* buffer already set: deny current attempt */
 			ERR("concurrent ACKing for local INVITE detected (T@%p).\n",trans);
 			free_local_ack(local_ack);
 			RET_INVALID;
 		}
 	}
 
 	if (msg_send(&local_ack->dst, local_ack->buffer, local_ack->buffer_len)<0){
 		/* hopefully will succeed on next 2xx retransmission */
 		ERR("failed to send local ACK (T@%p).\n", trans);
 		ret = -1;
 		goto fin;
 	}
 	else {
311d365b
 		INIT_TMCB_ONSEND_PARAMS(onsend_params, 0, 0, &trans->uac[0].request,
 								&local_ack->dst,
 								local_ack->buffer, local_ack->buffer_len,
 								TMCB_LOCAL_F, 0 /* branch */, TYPE_LOCAL_ACK);
68017b5d
 		run_trans_callbacks_off_params(TMCB_REQUEST_SENT, trans, &onsend_params);
d65cdd3f
 	}
 
 	ret = 0;
 fin:
 	/* TODO: ugly! */
 	/* FIXME: the T had been obtain by t_lookup_ident()'ing for it, so, it is
 	 * ref-counted. The t_unref() can not be used, as it requests a valid SIP
 	 * message (all available might be the reply, but if AS goes wrong and
 	 * tries to ACK before the final reply is received, we still have to
 	 * lookup the T to find this out). */
 	UNREF( trans );
 	return ret;
 
 #undef RET_INVALID
 }
 #endif /* WITH_AS_SUPPORT */
 
 
c5867ab3
 /*
  * Send a message within a dialog
  */
2c2a937c
 int req_within(uac_req_t *uac_r)
caf80ae6
 {
2c2a937c
 	if (!uac_r || !uac_r->method || !uac_r->dialog) {
c5867ab3
 		LOG(L_ERR, "req_within: Invalid parameter value\n");
 		goto err;
1c507b72
 	}
 
2c2a937c
 	if ((uac_r->method->len == 3) && (!memcmp("ACK", uac_r->method->s, 3))) goto send;
 	if ((uac_r->method->len == 6) && (!memcmp("CANCEL", uac_r->method->s, 6))) goto send;
 	uac_r->dialog->loc_seq.value++; /* Increment CSeq */
c5867ab3
  send:
2c2a937c
 	return t_uac(uac_r);
1540fad1
 
c5867ab3
  err:
2c2a937c
 	/* callback parameter must be freed outside of tm module
 	if (cbp) shm_free(cbp); */
c5867ab3
 	return -1;
1540fad1
 }
 
 
c5867ab3
 /*
  * Send an initial request that will start a dialog
2c2a937c
  * WARNING: writes uac_r->dialog
c5867ab3
  */
b93149c7
 int req_outside(uac_req_t *uac_r, str* ruri, str* to, str* from, str *next_hop)
ff979952
 {
c5867ab3
 	str callid, fromtag;
ff979952
 
2c2a937c
 	if (check_params(uac_r, to, from) < 0) goto err;
c5867ab3
 	
 	generate_callid(&callid);
 	generate_fromtag(&fromtag, &callid);
ff979952
 
2c2a937c
 	if (new_dlg_uac(&callid, &fromtag, DEFAULT_CSEQ, from, to, &uac_r->dialog) < 0) {
c5867ab3
 		LOG(L_ERR, "req_outside(): Error while creating new dialog\n");
 		goto err;
ff979952
 	}
 
b93149c7
 	if (ruri) {
 		uac_r->dialog->rem_target.s = ruri->s;
 		uac_r->dialog->rem_target.len = ruri->len;
 		/* hooks will be set from w_calculate_hooks */
 	}
 
 	if (next_hop) uac_r->dialog->dst_uri = *next_hop;
 	w_calculate_hooks(uac_r->dialog);
 
2c2a937c
 	return t_uac(uac_r);
ff979952
 
c5867ab3
  err:
2c2a937c
 	/* callback parameter must be freed outside of tm module
 	if (cbp) shm_free(cbp); */
c5867ab3
 	return -1;
 }
ff979952
 
 
c5867ab3
 /*
  * Send a transactional request, no dialogs involved
2c2a937c
  * WARNING: writes uac_r->dialog
c5867ab3
  */
2c2a937c
 int request(uac_req_t *uac_r, str* ruri, str* to, str* from, str *next_hop)
c5867ab3
 {
 	str callid, fromtag;
 	dlg_t* dialog;
 	int res;
ff979952
 
2c2a937c
 	if (check_params(uac_r, to, from) < 0) goto err;
ff979952
 
121c5a49
 	if (uac_r->callid == NULL || uac_r->callid->len <= 0)
 	    generate_callid(&callid);
 	else
 	    callid = *uac_r->callid;
c5867ab3
 	generate_fromtag(&fromtag, &callid);
ff979952
 
c5867ab3
 	if (new_dlg_uac(&callid, &fromtag, DEFAULT_CSEQ, from, to, &dialog) < 0) {
84d8e165
 		LOG(L_ERR, "request(): Error while creating temporary dialog\n");
c5867ab3
 		goto err;
1540fad1
 	}
ff979952
 
c5867ab3
 	if (ruri) {
 		dialog->rem_target.s = ruri->s;
 		dialog->rem_target.len = ruri->len;
4143f8e7
 		/* hooks will be set from w_calculate_hooks */
ff979952
 	}
4143f8e7
 
 	if (next_hop) dialog->dst_uri = *next_hop;
b380a7f8
 	w_calculate_hooks(dialog);
ff979952
 
4143f8e7
 	/* WARNING:
 	 * to be clean it should be called 
 	 *   set_dlg_target(dialog, ruri, next_hop);
 	 * which sets both uris if given [but it duplicates them in shm!]
 	 *
 	 * but in this case the _ruri parameter in set_dlg_target
 	 * must be optional (it is needed now) and following hacks
 	 *   dialog->rem_target.s = 0;
 	 *   dialog->dst_uri.s = 0;
 	 * before freeing dialog here must be removed
 	 */
2c2a937c
 	uac_r->dialog = dialog;
 	res = t_uac(uac_r);
03d4fa7e
 	dialog->rem_target.s = 0;
4143f8e7
 	dialog->dst_uri.s = 0;
c5867ab3
 	free_dlg(dialog);
2c2a937c
 	uac_r->dialog = 0;
c5867ab3
 	return res;
1540fad1
 
c5867ab3
  err:
2c2a937c
 	/* callback parameter must be freed outside of tm module
 	if (cp) shm_free(cp); */
c5867ab3
 	return -1;
ff979952
 }