/*
 * $Id$
 *
 * message printing
 *
 * Copyright (C) 2001-2003 FhG Fokus
 *
 * 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
 *
 *
 * History:
 * ----------
 * 2003-01-27  next baby-step to removing ZT - PRESERVE_ZT (jiri)
 * 2003-02-13  build_uac_request uses proto (andrei)
 * 2003-02-28  scratchpad compatibility abandoned (jiri)
 * 2003-04-14  build_local no longer checks reply status as it
 *             is now called before reply status is updated to
 *             avoid late ACK sending (jiri)
 * 2003-10-02  added via_builder set host/port support (andrei)
 * 2004-02-11  FIFO/CANCEL + alignments (hash=f(callid,cseq)) (uli+jiri)
 * 2004-02-13: t->is_invite and t->local replaced with flags (bogdan)
 * 2006-04-21  build_uac_req, assemble_via use struct dest_info now;
 *              uri2sock replaced with uri2dst (andrei)
 * 2006-08-11  build_dlg_ack: use the first dns ip for which a send_sock
 *              is found (andrei)
 * 2007-03-15  build_dls_ack: removed next_hop and replaced by dst to avoid
 *               resolving nexthop twice (andrei)
 * 2007-05-28: build_local_reparse() is introdued: it uses the outgoing
 *             INVITE as a source to construct a CANCEL or ACK (Miklos)
 */

#include "defs.h"

#ifdef EXTRA_DEBUG
#include <assert.h>
#endif
#include "../../comp_defs.h"
#include "../../hash_func.h"
#include "../../globals.h"
#include "t_funcs.h"
#include "../../dprint.h"
#include "../../config.h"
#include "../../parser/parser_f.h"
#include "../../ut.h"
#include "../../parser/msg_parser.h"
#include "../../parser/contact/parse_contact.h"
#include "lw_parser.h"
#include "t_msgbuilder.h"
#include "uac.h"
#ifdef USE_DNS_FAILOVER
#include "../../dns_cache.h"
#include "../../cfg_core.h" /* cfg_get(core, core_cfg, use_dns_failover) */
#endif

/* convenience macros */
#define memapp(_d,_s,_len) \
	do{\
		memcpy((_d),(_s),(_len));\
		(_d) += (_len);\
	}while(0)

#define  append_mem_block(_d,_s,_len) \
		do{\
			memcpy((_d),(_s),(_len));\
			(_d) += (_len);\
		}while(0)

#define append_str(_p,_str) \
	do{  \
		memcpy((_p), (_str).s, (_str).len); \
		(_p)+=(_str).len;  \
 	} while(0)


/* Build a local request based on a previous request; main
   customers of this function are local ACK and local CANCEL
 */
char *build_local(struct cell *Trans,unsigned int branch,
	unsigned int *len, char *method, int method_len, str *to)
{
	char                *cancel_buf, *p, *via;
	unsigned int         via_len;
	struct hdr_field    *hdr;
	char branch_buf[MAX_BRANCH_PARAM_LEN];
	int branch_len;
	str branch_str;
	str via_id;
	struct hostport hp;

	/* init */
	via_id.s=0;
	via_id.len=0;

	/* method, separators, version: "CANCEL sip:p2@iptel.org SIP/2.0" */
	*len=SIP_VERSION_LEN + method_len + 2 /* spaces */ + CRLF_LEN;
	*len+=Trans->uac[branch].uri.len;

	/*via*/
	if (!t_calc_branch(Trans,  branch, 
		branch_buf, &branch_len ))
		goto error;
	branch_str.s=branch_buf;
	branch_str.len=branch_len;
	set_hostport(&hp, (is_local(Trans))?0:(Trans->uas.request));
#ifdef USE_TCP
	if (!is_local(Trans) && ((Trans->uas.request->rcv.proto==PROTO_TCP)
#ifdef USE_TLS
				|| (Trans->uas.request->rcv.proto==PROTO_TLS)
#endif /* USE_TLS */
		)){
		if ((via_id.s=id_builder(Trans->uas.request,
									(unsigned int*)&via_id.len))==0){
			LOG(L_ERR, "ERROR: build_local: id builder failed\n");
			/* try to continue without id */
		}
	}
#endif /* USE_TCP */
	via=via_builder(&via_len, &Trans->uac[branch].request.dst,
		&branch_str, via_id.s?&via_id:0 , &hp );
	
	/* via_id.s not needed anylonger => free it */
	if (via_id.s){
		pkg_free(via_id.s);
		via_id.s=0;
		via_id.len=0;
	}
	
	if (!via)
	{
		LOG(L_ERR, "ERROR: build_local: "
			"no via header got from builder\n");
		goto error;
	}
	*len+= via_len;
	/*headers*/
	*len+=Trans->from.len+Trans->callid.len+to->len+
		+Trans->cseq_n.len+1+method_len+CRLF_LEN; 


	/* copy'n'paste Route headers */
	if (!is_local(Trans)) {
		for ( hdr=Trans->uas.request->headers ; hdr ; hdr=hdr->next )
			 if (hdr->type==HDR_ROUTE_T)
				*len+=hdr->len;
	}

	/* User Agent */
	if (server_signature) {
		*len += USER_AGENT_LEN + CRLF_LEN;
	}
	/* Content Length, EoM */
	*len+=CONTENT_LENGTH_LEN+1 + CRLF_LEN + CRLF_LEN;

	cancel_buf=shm_malloc( *len+1 );
	if (!cancel_buf)
	{
		LOG(L_ERR, "ERROR: build_local: cannot allocate memory\n");
		goto error01;
	}
	p = cancel_buf;

	append_mem_block( p, method, method_len );
	append_mem_block( p, " ", 1 );
	append_str( p, Trans->uac[branch].uri );
	append_mem_block( p, " " SIP_VERSION CRLF, 1+SIP_VERSION_LEN+CRLF_LEN );

	/* insert our via */
	append_mem_block(p,via,via_len);

	/*other headers*/
	append_str( p, Trans->from );
	append_str( p, Trans->callid );
	append_str( p, *to );

	append_str( p, Trans->cseq_n );
	append_mem_block( p, " ", 1 );
	append_mem_block( p, method, method_len );
	append_mem_block( p, CRLF, CRLF_LEN );

	if (!is_local(Trans))  {
		for ( hdr=Trans->uas.request->headers ; hdr ; hdr=hdr->next )
			if(hdr->type==HDR_ROUTE_T) {
				append_mem_block(p, hdr->name.s, hdr->len );
			}
	}

	/* User Agent header */
	if (server_signature) {
		append_mem_block(p,USER_AGENT CRLF, USER_AGENT_LEN+CRLF_LEN );
	}
	/* Content Length, EoM */
	append_mem_block(p, CONTENT_LENGTH "0" CRLF CRLF ,
		CONTENT_LENGTH_LEN+1 + CRLF_LEN + CRLF_LEN);
	*p=0;

	pkg_free(via);
	return cancel_buf;
error01:
	pkg_free(via);
error:
	return NULL;
}

/* Re-parsing version of build_local() function:
 * it builds a local CANCEL or ACK (for non-200 response) request based on
 * the previous INVITE which was sent out.
 *
 * Can not be used to build other type of requests!
 */
char *build_local_reparse(struct cell *Trans,unsigned int branch,
	unsigned int *len, char *method, int method_len, str *to)
{
	char	*invite_buf, *invite_buf_end;
	char	*cancel_buf;
	char	*s, *s1, *d;	/* source and destination buffers */
	short	invite_len;
	enum _hdr_types_t	hf_type;
	int	first_via, to_len;

	invite_buf = Trans->uac[branch].request.buffer;
	invite_len = Trans->uac[branch].request.buffer_len;

	if (!invite_buf || !invite_len) {
		LOG(L_ERR, "ERROR: build_local_reparse: INVITE is missing\n");
		goto error;
	}
	if ((*invite_buf != 'I') && (*invite_buf != 'i')) {
		LOG(L_ERR, "ERROR: build_local_reparse: trying to call build_local_reparse() for a non-INVITE request?\n");
		goto error;
	}
	invite_buf_end = invite_buf + invite_len;
	s = invite_buf;

	/* Allocate memory for the new message.
	The new request will be smaller than the INVITE, so the same size is enough.
	I just extend it with the length of new To HF to be sure.
	Ugly, but we avoid lots of checks and memory allocations this way */
	to_len = to ? to->len : 0;
	cancel_buf = shm_malloc(sizeof(char)*(invite_len + to_len));
	if (!cancel_buf)
	{
		LOG(L_ERR, "ERROR: build_local_reparse: cannot allocate shared memory\n");
		goto error;
	}
	d = cancel_buf;

	/* method name + space */
	append_mem_block(d, method, method_len);
	*d = ' ';
	d++;
	/* skip "INVITE " and copy the rest of the line including CRLF */
	s += 7;
	s1 = s;
	s = eat_line(s, invite_buf_end - s);
	append_mem_block(d, s1, s - s1);

	/* check every header field name,
	we must exclude and modify some of the headers */
	first_via = 1;
	while (s < invite_buf_end) {
		s1 = s;
		if ((*s == '\n') || (*s == '\r')) {
			/* end of SIP msg */
			hf_type = HDR_EOH_T;
		} else {
			/* parse HF name */
			s = lw_get_hf_name(s, invite_buf_end,
						&hf_type);
		}

		switch(hf_type) {
			case HDR_CSEQ_T:
				/* find the method name and replace it */
				while ((s < invite_buf_end)
					&& ((*s == ':') || (*s == ' ') || (*s == '\t') || ((*s >= '0') && (*s <= '9')))
					) s++;
				append_mem_block(d, s1, s - s1);
				append_mem_block(d, method, method_len);
				append_mem_block(d, CRLF, CRLF_LEN);
				s = lw_next_line(s, invite_buf_end);
				break;

			case HDR_VIA_T:
				s = lw_next_line(s, invite_buf_end);
				if (first_via) {
					/* copy hf */
					append_mem_block(d, s1, s - s1);
					first_via = 0;
				} /* else skip this line, we need olny the first via */
				break;

			case HDR_TO_T:
				if (to_len == 0) {
					/* there is no To tag required, just copy paste the header */
					s = lw_next_line(s, invite_buf_end);
					append_mem_block(d, s1, s - s1);
				} else {
					/* use the given To HF instead of the original one */
					append_mem_block(d, to->s, to->len);
					/* move the pointer to the next line */
					s = lw_next_line(s, invite_buf_end);
				}
				break;

			case HDR_FROM_T:
			case HDR_CALLID_T:
			case HDR_ROUTE_T:
			case HDR_MAXFORWARDS_T:
				/* copy hf */
				s = lw_next_line(s, invite_buf_end);
				append_mem_block(d, s1, s - s1);
				break;

			case HDR_REQUIRE_T:
			case HDR_PROXYREQUIRE_T:
				/* skip this line */
				s = lw_next_line(s, invite_buf_end);
				break;

			case HDR_CONTENTLENGTH_T:
				/* copy hf name with 0 value */
				append_mem_block(d, s1, s - s1);
				append_mem_block(d, ": 0" CRLF, 3 + CRLF_LEN);
				/* move the pointer to the next line */
				s = lw_next_line(s, invite_buf_end);
				break;

			case HDR_EOH_T:
				/* end of SIP message found */
				append_mem_block(d, CRLF, CRLF_LEN);
				*len = d - cancel_buf;
				/* LOG(L_DBG, "DBG: build_local: %.*s\n", *len, cancel_buf); */
				return cancel_buf;

			default:
				s = lw_next_line(s, invite_buf_end);

				if (cfg_get(tm, tm_cfg, ac_extra_hdrs).len
				&& (s1 + cfg_get(tm, tm_cfg, ac_extra_hdrs).len < invite_buf_end)
				&& (strncasecmp(s1,
						cfg_get(tm, tm_cfg, ac_extra_hdrs).s,
						cfg_get(tm, tm_cfg, ac_extra_hdrs).len) == 0)
				) {
					append_mem_block(d, s1, s - s1);
				} /* else skip this line */
				break;
		}
	}

	/* HDR_EOH_T was not found in the buffer, the message is corrupt */
	LOG(L_ERR, "ERROR: build_local_reparse: HDR_EOH_T was not found\n");

	shm_free(cancel_buf);
error:
	LOG(L_ERR, "ERROR: build_local_reparse: cannot build %.*s request\n", method_len, method);
	return NULL;

}


typedef struct rte {
	rr_t* ptr;
	/* 'ptr' above doesn't point to a mem chunk linked to a sip_msg, so it
	 * won't be free'd along with it => it must be free'd "manually" */
	int free_rr;
	struct rte* next;
} rte_t;

  	 
static inline void free_rte_list(struct rte* list)
{
	struct rte* ptr;
	
	while(list) {
		ptr = list;
		list = list->next;
		if (ptr->free_rr)
			free_rr(&ptr->ptr);
		pkg_free(ptr);
	}
}


static inline int calc_routeset_len(struct rte* list, str* contact)
{
	struct rte* ptr;
	int ret;
	
	if (list || contact) {
		ret = ROUTE_PREFIX_LEN + CRLF_LEN;
	} else {
		return 0;
	}
	
	ptr = list;
	while(ptr) {
		if (ptr != list) {
			ret += ROUTE_SEPARATOR_LEN;
		}
		ret += ptr->ptr->len;
		ptr = ptr->next;
	}
	
	if (contact) {
		if (list) ret += ROUTE_SEPARATOR_LEN;
		ret += 2 + contact->len;
	}
	
	return ret;
}


     /*
      * Print the route set
      */
static inline char* print_rs(char* p, struct rte* list, str* contact)
{
	struct rte* ptr;
	
	if (list || contact) {
		memapp(p, ROUTE_PREFIX, ROUTE_PREFIX_LEN);
	} else {
		return p;
	}
	
	ptr = list;
	while(ptr) {
		if (ptr != list) {
			memapp(p, ROUTE_SEPARATOR, ROUTE_SEPARATOR_LEN);
		}
		
		memapp(p, ptr->ptr->nameaddr.name.s, ptr->ptr->len);
		ptr = ptr->next;
	}
	
	if (contact) {
		if (list) memapp(p, ROUTE_SEPARATOR, ROUTE_SEPARATOR_LEN);
		*p++ = '<';
		append_str(p, *contact);
		*p++ = '>';
	}
	
	memapp(p, CRLF, CRLF_LEN);
	return p;
}


     /*
      * Parse Contact header field body and extract URI
      * Does not parse headers !
      */
static inline int get_contact_uri(struct sip_msg* msg, str* uri)
{
	contact_t* c;
	
	uri->len = 0;
	if (!msg->contact) return 1;
	
	if (parse_contact(msg->contact) < 0) {
		LOG(L_ERR, "get_contact_uri: Error while parsing Contact body\n");
		return -1;
	}
	
	c = ((contact_body_t*)msg->contact->parsed)->contacts;
	
	if (!c) {
		LOG(L_ERR, "get_contact_uri: Empty body or * contact\n");
		return -2;
	}
	
	*uri = c->uri;
	return 0;
}

/**
 * Extract route set from the message (out of Record-Route, if reply, OR
 * Route, if request).
 * The route set is returned into the "UAC-format" (keep order for Rs, reverse
 * RRs).
 */
static inline int get_uac_rs(sip_msg_t *msg, int is_req, struct rte **rtset)
{
	struct hdr_field* ptr;
	rr_t *p, *new_p;
	struct rte *t, *head, *old_head;

	head = 0;
	for (ptr = is_req ? msg->route : msg->record_route; ptr; ptr = ptr->next) {
		switch (ptr->type) {
			case HDR_RECORDROUTE_T:
				if (is_req)
					continue;
				break;
			case HDR_ROUTE_T:
				if (! is_req)
					continue;
				break;
			default:
				continue;
		}
		if (parse_rr(ptr) < 0) {
			ERR("failed to parse Record-/Route HF (%d).\n", ptr->type);
			goto err;
		}
			
		p = (rr_t*)ptr->parsed;
		while(p) {
			if (! (t = (struct rte*)pkg_malloc(sizeof(struct rte)))) {
				ERR("out of pkg mem (asked for: %zd).\n", sizeof(struct rte));
				goto err;
			}
			if (is_req) {
				/* in case of requests, the sip_msg structure is free'd before
				 * rte list is evaluated => must do a copy of it */
				if (duplicate_rr(&new_p, p) < 0) {
					pkg_free(t);
					ERR("failed to duplicate RR");
					goto err;
				}
				t->ptr = new_p;
			} else {
				t->ptr = p;
			}
			t->free_rr = is_req;
			t->next = head;
			head = t;
			p = p->next;
		}
	}

	if (is_req) {
		/* harvesting the R/RR HF above inserts at head, which suites RRs (as
		 * they must be reversed, anyway), but not Rs => reverse once more */
		old_head = head;
		head = 0;
		while (old_head) {
			t = old_head;
			old_head = old_head->next;
			t->next = head;
			head = t;
		}
	}

	*rtset = head;
	return 0;
err:
	free_rte_list(head);
	return -1;
}


static inline unsigned short uri2port(const struct sip_uri *puri)
{
	if (puri->port.s) {
		return puri->port_no;
	} else switch (puri->type) {
		case SIP_URI_T:
		case TEL_URI_T:
			if (puri->transport_val.len == sizeof("TLS") - 1) {
				unsigned trans;
				trans = puri->transport_val.s[0] | 0x20; trans <<= 8;
				trans |= puri->transport_val.s[1] | 0x20; trans <<= 8;
				trans |= puri->transport_val.s[2] | 0x20;
				if (trans == 0x746C73) /* t l s */
					return SIPS_PORT;
			}
			return SIP_PORT;
		case SIPS_URI_T:
		case TELS_URI_T:
			return SIPS_PORT;
		default:
			BUG("unexpected URI type %d.\n", puri->type);
	}
	return 0;
}

/**
 * Evaluate if next hop is a strict or loose router, by looking at the
 * retr. buffer of the original INVITE.
 * Assumes:
 * 	orig_inv is a parsed SIP message;
 * 	rtset is not NULL.
 * @return:
 * 	F_RB_NH_LOOSE : next hop was loose router;
 * 	F_RB_NH_STRICT: nh is strict;
 * 	0 on error.
 */
static unsigned long nhop_type(sip_msg_t *orig_inv, rte_t *rtset,
		const struct dest_info *dst_inv, str *contact)
{
	struct sip_uri puri, topr_uri, lastr_uri, inv_ruri, cont_uri;
	struct ip_addr *uri_ia;
	union sockaddr_union uri_sau;
	unsigned int uri_port, dst_port, inv_port, cont_port, lastr_port;
	rte_t *last_r;
#ifdef TM_LOC_ACK_DO_REV_DNS
	struct ip_addr ia;
	struct hostent *he;
	char **alias;
#endif

#define PARSE_URI(_str_, _uri_) \
	do { \
		/* parse_uri() 0z the puri */ \
		if (parse_uri((_str_)->s, \
				(_str_)->len, _uri_) < 0) { \
			ERR("failed to parse route body '%.*s'.\n", STR_FMT(_str_)); \
			return 0; \
		} \
	} while (0)

#define HAS_LR(_rte_) \
	({ \
		PARSE_URI(&(_rte_)->ptr->nameaddr.uri, &puri); \
		puri.lr.s; \
	})

#define URI_PORT(_puri_, _port) \
	do { \
		if (! (_port = uri2port(_puri_))) \
			return 0; \
	} while (0)

	/* examine the easy/fast & positive cases foremost */

	/* [1] check if 1st route lacks ;lr */
	DEBUG("checking lack of ';lr' in 1st route.\n");
	if (! HAS_LR(rtset))
		return F_RB_NH_STRICT;
	topr_uri = puri; /* save 1st route's URI */

	/* [2] check if last route shows ;lr */
	DEBUG("checking presence of ';lr' in last route.\n");
	for (last_r = rtset; last_r->next; last_r = last_r->next)
		/* scroll down to last route */
		;
	if (HAS_LR(last_r))
		return F_RB_NH_LOOSE;

	/* [3] 1st route has ;lr -> check if the destination of original INV
	 * equals the address provided by this route; if does -> loose */
	DEBUG("checking INVITE's destination against its first route.\n");
	URI_PORT(&topr_uri, uri_port);
	if (! (dst_port = su_getport((void *)&dst_inv->to)))
		return 0; /* not really expected */
	if (dst_port != uri_port)
		return F_RB_NH_STRICT;
	/* if 1st route contains an IP address, comparing it against .dst */
	if ((uri_ia = str2ip(&topr_uri.host))
#ifdef USE_IPV6
			|| (uri_ia = str2ip6(&topr_uri.host))
#endif
			) {
		/* we have an IP address in route -> comparison can go swiftly */
		if (init_su(&uri_sau, uri_ia, uri_port) < 0)
			return 0; /* not really expected */
		if (su_cmp(&uri_sau, (void *)&dst_inv->to))
			/* ;lr and sent there */
			return F_RB_NH_LOOSE;
		else
			/* ;lr and NOT sent there (probably sent to RURI address) */
			return F_RB_NH_STRICT;
	} else {
		/*if 1st route contains a name, rev resolve the .dst and compare*/
		INFO("Failed to decode string '%.*s' in route set element as IP "
				"address. Trying name resolution.\n",STR_FMT(&topr_uri.host));

	/* TODO: alternatively, rev name and compare against dest. IP.  */
#ifdef TM_LOC_ACK_DO_REV_DNS
		ia.af = 0;
		su2ip_addr(&ia, (void *)&dst_inv->to);
		if (! ia.af)
			return 0; /* not really expected */
		if ((he = rev_resolvehost(&ia))) {
			if ((strlen(he->h_name) == topr_uri.host.len) &&
					(memcmp(he->h_name, topr_uri.host.s, 
							topr_uri.host.len) == 0))
				return F_RB_NH_LOOSE;
			for (alias = he->h_aliases; *alias; alias ++)
				if ((strlen(*alias) == topr_uri.host.len) &&
						(memcmp(*alias, topr_uri.host.s, 
								topr_uri.host.len) == 0))
					return F_RB_NH_LOOSE;
			return F_RB_NH_STRICT;
		} else {
			INFO("failed to resolve address '%s' to a name.\n", 
					ip_addr2a(&ia));
		}
#endif
	}

	WARN("failed to establish with certainty the type of next hop; trying an"
			" educated guess.\n");

	/* [4] compare (possibly updated) remote target to original RURI; if
	 * equal, a strict router's address wasn't filled in as RURI -> loose */
	DEBUG("checking remote target against INVITE's RURI.\n");
	PARSE_URI(contact, &cont_uri);
	PARSE_URI(GET_RURI(orig_inv), &inv_ruri);
	URI_PORT(&cont_uri, cont_port);
	URI_PORT(&inv_ruri, inv_port);
	if ((cont_port == inv_port) && (cont_uri.host.len == inv_ruri.host.len) &&
			(memcmp(cont_uri.host.s, inv_ruri.host.s, cont_uri.host.len) == 0))
		return F_RB_NH_LOOSE;

	/* [5] compare (possibly updated) remote target to last route; if equal, 
	 * strict router's address might have been filled as RURI and remote
	 * target appended to route set -> strict */
	DEBUG("checking remote target against INVITE's last route.\n");
	PARSE_URI(&last_r->ptr->nameaddr.uri, &lastr_uri);
	URI_PORT(&lastr_uri, lastr_port);
	if ((cont_port == lastr_port) && 
			(cont_uri.host.len == lastr_uri.host.len) &&
			(memcmp(cont_uri.host.s, lastr_uri.host.s, 
					lastr_uri.host.len) == 0))
		return F_RB_NH_STRICT;

	WARN("failed to establish the type of next hop; assuming loose router.\n");
	return F_RB_NH_LOOSE;

#undef PARSE_URI
#undef HAS_LR
#undef URI_PORT
}

/**
 * Evaluates the routing elements in locally originated request or reply to
 * locally originated request.
 * If original INVITE was in-dialog (had to-tag), it uses the
 * routes present there (b/c the 2xx for it does not have a RR set, normally).
 * Otherwise, use the reply (b/c the INVITE does not have yet the complete 
 * route set).
 *
 * @return: negative for failure; out params:
 *  - list: route set;
 *  - ruri: RURI to be used in ACK;
 *  - nexthop: where to first send the ACK.
 *
 *  NOTE: assumes rpl's parsed to EOF!
 *
 */
static int eval_uac_routing(sip_msg_t *rpl, const struct retr_buf *inv_rb, 
		str* contact, struct rte **list, str *ruri, str *next_hop)
{
	sip_msg_t orig_inv, *sipmsg; /* reparse original INVITE */
	rte_t *t, *prev_t, *rtset = NULL;
	int is_req;
	struct sip_uri puri;
	static size_t chklen;
	int ret = -1;
	
	/* parse the retr. buffer */
	memset(&orig_inv, 0, sizeof(struct sip_msg));
	orig_inv.buf = inv_rb->buffer;
	orig_inv.len = inv_rb->buffer_len;
	DEBUG("reparsing retransmission buffer of original INVITE:\n%.*s\n",
			orig_inv.len, orig_inv.buf);
	if (parse_msg(orig_inv.buf, orig_inv.len, &orig_inv) != 0) {
		ERR("failed to parse retr buffer (weird!): \n%.*s\n", orig_inv.len,
				orig_inv.buf);
		return -1;
	}

	/* check if we need to look at request or reply */
	if ((parse_headers(&orig_inv, HDR_TO_F, 0) < 0) || (! orig_inv.to)) {
		/* the bug is at message assembly */
		BUG("failed to parse INVITE retr. buffer and/or extract 'To' HF:"
				"\n%.*s\n", orig_inv.len, orig_inv.buf);
		goto end;
	}
	if (((struct to_body *)orig_inv.to->parsed)->tag_value.len) {
		DEBUG("building ACK for in-dialog INVITE (using RS in orig. INV.)\n");
		if (parse_headers(&orig_inv, HDR_EOH_F, 0) < 0) {
			BUG("failed to parse INVITE retr. buffer to EOH:"
					"\n%.*s\n", orig_inv.len, orig_inv.buf);
			goto end;
		}
		sipmsg = &orig_inv;
		is_req = 1;
	} else {
		DEBUG("building ACK for out-of-dialog INVITE (using RS in RR set).\n");
		sipmsg = rpl;
		is_req = 0;
	}

	/* extract the route set */
	if (get_uac_rs(sipmsg, is_req, &rtset) < 0) {
		ERR("failed to extract route set.\n");
		goto end;
	}

	if (! rtset) { /* No routes */
		*ruri = *contact;
		*next_hop = *contact;
	} else if (! is_req) { /* out of dialog req. */
		if (parse_uri(rtset->ptr->nameaddr.uri.s, rtset->ptr->nameaddr.uri.len,
				&puri) < 0) {
			ERR("failed to parse first route in set.\n");
			goto end;
		}
		
		if (puri.lr.s) { /* Next hop is loose router */
			*ruri = *contact;
			*next_hop = rtset->ptr->nameaddr.uri;
		} else { /* Next hop is strict router */
			*ruri = rtset->ptr->nameaddr.uri;
			*next_hop = *ruri;
			/* consume first route, b/c it will be put in RURI */
			t = rtset;
			rtset = rtset->next;
			pkg_free(t);
		}
	} else {
		unsigned long route_flags = inv_rb->flags;
		DEBUG("UAC rb flags: 0x%x.\n", (unsigned int)route_flags);
eval_flags:
		switch (route_flags & (F_RB_NH_LOOSE|F_RB_NH_STRICT)) {
		case 0:
			WARN("calculate_hooks() not called when built the local UAC of "
					"in-dialog request, or called with empty route set.\n");
			/* try to figure out what kind of hop is the next one
			 * (strict/loose) by reading the original invite */
			if ((route_flags = nhop_type(&orig_inv, rtset, &inv_rb->dst, 
					contact))) {
				DEBUG("original request's next hop type evaluated to: 0x%x.\n",
						(unsigned int)route_flags);
				goto eval_flags;
			} else {
				ERR("failed to establish what kind of router the next "
						"hop is.\n");
				goto end;
			}
			break;
		case F_RB_NH_LOOSE:
			*ruri = *contact;
			*next_hop = rtset->ptr->nameaddr.uri;
			break;
		case F_RB_NH_STRICT:
			/* find ptr to last route body that contains the (possibly) old 
			 * remote target 
			 */
			for (t = rtset, prev_t = t; t->next; prev_t = t, t = t->next)
				;
			if ((t->ptr->len == contact->len) && 
					(memcmp(t->ptr->nameaddr.name.s, contact->s, 
							contact->len) == 0)){
				/* the remote target didn't update -> keep the whole route set,
				 * including the last entry */
				/* do nothing */
			} else {
				/* trash last entry and replace with new remote target */
				free_rte_list(t);
				/* compact the rr_t struct along with rte. this way, free'ing
				 * it can be done along with rte chunk, independent of Route
				 * header parser's allocator (using pkg/shm) */
				chklen = sizeof(struct rte) + sizeof(rr_t);
				if (! (t = (struct rte *)pkg_malloc(chklen))) {
					ERR("out of pkg memory (%zd required)\n", chklen);
					goto end;
				}
				/* this way, .free_rr is also set to 0 (!!!) */
				memset(t, 0, chklen); 
				((rr_t *)&t[1])->nameaddr.name = *contact;
				((rr_t *)&t[1])->len = contact->len;
				/* chain the new route elem in set */
				if (prev_t == rtset)
				 	/*there is only one elem in route set: the remote target*/
					rtset = t;
				else
					prev_t->next = t;
			}

			*ruri = *GET_RURI(&orig_inv); /* reuse original RURI */
			*next_hop = *ruri;
			break;
		default:
			/* probably a mem corruption */
			BUG("next hop of original request marked as both loose and strict"
					" router (buffer: %.*s).\n", inv_rb->buffer_len, 
					inv_rb->buffer);
#ifdef EXTRA_DEBUG
			abort();
#else
			goto end;
#endif
		}
	}

	*list = rtset;
	/* all went well */
	ret = 0;
end:
	free_sip_msg(&orig_inv);
	if (ret < 0)
		free_rte_list(rtset);
	return ret;
}

     /*
      * The function creates an ACK to 200 OK. Route set will be created
      * and parsed and the dst parameter will contain the destination to which 
	  * the request should be send. The function is used by tm when it 
	  * generates local ACK to 200 OK (on behalf of applications using uac)
      */
char *build_dlg_ack(struct sip_msg* rpl, struct cell *Trans, 
					unsigned int branch, str *hdrs, str *body,
					unsigned int *len, struct dest_info* dst)
{
	char *req_buf, *p, *via;
	unsigned int via_len;
	char branch_buf[MAX_BRANCH_PARAM_LEN];
	int branch_len;
	str branch_str;
	struct hostport hp;
	struct rte* list;
	str contact, ruri, *cont;
	str next_hop;
	str body_len;
	str _to, *to = &_to;
#ifdef USE_DNS_FAILOVER
	struct dns_srv_handle dns_h;
#endif
#ifdef WITH_AS_SUPPORT
	/* With AS support, TM allows for external modules to generate building of
	 * the ACK; in this case, the ACK's retransmission buffer is built once
	 * and kept in memory (to help when retransmitted 2xx are received and ACK
	 * must be resent).
	 * Allocation of the string raw buffer that holds the ACK is piggy-backed
	 * with allocation of the retransmission buffer (since both have the same
	 * life-cycle): both the string buffer and retransm. buffer are placed 
	 * into the same allocated chunk of memory (retr. buffer first, string 
	 * buffer follows).In this case, the 'len' param is used as in-out 
	 * parameter: 'in' to give the extra space needed by the retr. buffer,
	 * 'out' to return the lenght of the allocated string buffer.
	 */
	unsigned offset = *len;
#endif
	
	if (parse_headers(rpl, HDR_EOH_F, 0) == -1 || !rpl->to) {
		ERR("Error while parsing headers.\n");
		return 0;
	} else {
		_to.s = rpl->to->name.s;
		_to.len = rpl->to->len;
	}
	
	if (get_contact_uri(rpl, &contact) < 0) {
		return 0;
	}
	
	if (eval_uac_routing(rpl, &Trans->uac[branch].request, &contact, 
			&list, &ruri, &next_hop) < 0) {
		ERR("failed to evaluate routing elements.\n");
		return 0;
	}
	DEBUG("ACK RURI: `%.*s', NH: `%.*s'.\n", STR_FMT(&ruri), 
			STR_FMT(&next_hop));

	if ((contact.s != ruri.s) || (contact.len != ruri.len)) {
		     /* contact != ruri means that the next
		      * hop is a strict router, cont will be non-zero
		      * and print_routeset will append it at the end
		      * of the route set
		      */
		cont = &contact;
	} else {
		     /* Next hop is a loose router, nothing to append */
		cont = 0;
	}
	
	     /* method, separators, version: "ACK sip:p2@iptel.org SIP/2.0" */
	*len = SIP_VERSION_LEN + ACK_LEN + 2 /* spaces */ + CRLF_LEN;
	*len += ruri.len;
	
	
	 /* via */
#ifdef USE_DNS_FAILOVER
	if (cfg_get(core, core_cfg, use_dns_failover)){
		dns_srv_handle_init(&dns_h);
		if ((uri2dst(&dns_h , dst, rpl, &next_hop, PROTO_NONE)==0) ||
				(dst->send_sock==0)){
			dns_srv_handle_put(&dns_h);
			LOG(L_ERR, "build_dlg_ack: no socket found\n");
			goto error;
		}
		dns_srv_handle_put(&dns_h); /* not needed any more */
	}else{
		if ((uri2dst(0 , dst, rpl, &next_hop, PROTO_NONE)==0) ||
				(dst->send_sock==0)){
			LOG(L_ERR, "build_dlg_ack: no socket found\n");
			goto error;
		}
	}
#else
	if ( (uri2dst( dst, rpl, &next_hop, PROTO_NONE)==0) ||
			(dst->send_sock==0)){
			LOG(L_ERR, "build_dlg_ack: no socket found\n");
		goto error;
	}
#endif
	
	if (!t_calc_branch(Trans,  branch, branch_buf, &branch_len)) goto error;
	branch_str.s = branch_buf;
	branch_str.len = branch_len;
	set_hostport(&hp, 0);
	via = via_builder(&via_len, dst, &branch_str, 0, &hp);
	if (!via) {
		LOG(L_ERR, "build_dlg_ack: No via header got from builder\n");
		goto error;
	}
	*len+= via_len;
	
	     /*headers*/
	*len += Trans->from.len + Trans->callid.len + to->len + Trans->cseq_n.len + 1 + ACK_LEN + CRLF_LEN;
	
	     /* copy'n'paste Route headers */
	
	*len += calc_routeset_len(list, cont);
	
	     /* User Agent */
	if (server_signature) *len += USER_AGENT_LEN + CRLF_LEN;
		/* extra headers */
	if (hdrs)
		*len += hdrs->len;
		/* body */
	if (body) {
		body_len.s = int2str(body->len, &body_len.len);
		*len += body->len;
	} else {
		body_len.len = 0;
		body_len.s = NULL; /*4gcc*/
		*len += 1; /* for the (Cont-Len:) `0' */
	}
	     /* Content Length, EoM */
	*len += CONTENT_LENGTH_LEN + body_len.len + CRLF_LEN + CRLF_LEN;

#if WITH_AS_SUPPORT
	req_buf = shm_malloc(offset + *len + 1);
	req_buf += offset;
#else
	req_buf = shm_malloc(*len + 1);
#endif
	if (!req_buf) {
		ERR("Cannot allocate memory (%u+1)\n", *len);
		goto error01;
	}
	p = req_buf;
	
	append_mem_block( p, ACK, ACK_LEN );
	append_mem_block( p, " ", 1 );
	append_str(p, ruri);
	append_mem_block( p, " " SIP_VERSION CRLF, 1 + SIP_VERSION_LEN + CRLF_LEN);
  	 
	     /* insert our via */
	append_mem_block(p, via, via_len);
	
	     /*other headers*/
	append_str(p, Trans->from);
	append_str(p, Trans->callid);
	append_str(p, *to);
	
	append_str(p, Trans->cseq_n);
	append_mem_block( p, " ", 1 );
	append_mem_block( p, ACK, ACK_LEN);
	append_mem_block(p, CRLF, CRLF_LEN);
	
	     /* Routeset */
	p = print_rs(p, list, cont);
	
	     /* User Agent header */
	if (server_signature) {
		append_mem_block(p, USER_AGENT CRLF, USER_AGENT_LEN + CRLF_LEN);
	}
	
	/* extra headers */
	if (hdrs)
		append_mem_block(p, hdrs->s, hdrs->len);
	
	     /* Content Length, EoH, (body) */
	if (body) {
		append_mem_block(p, CONTENT_LENGTH, CONTENT_LENGTH_LEN);
		append_mem_block(p, body_len.s, body_len.len);
		append_mem_block(p, /*end crr. header*/CRLF /*EoH*/CRLF, CRLF_LEN + 
				CRLF_LEN);
		append_mem_block(p, body->s, body->len);
	} else {
		append_mem_block(p, CONTENT_LENGTH "0" CRLF CRLF, 
				CONTENT_LENGTH_LEN + 1 + CRLF_LEN + CRLF_LEN);
	}

	/* EoM */
	*p = 0;
	
	pkg_free(via);
	free_rte_list(list);
	return req_buf;
	
 error01:
	pkg_free(via);
 error:
	free_rte_list(list);
	return 0;
}


/*
 * Convert length of body into asciiz
 */
static inline int print_content_length(str* dest, str* body)
{
	static char content_length[10];
	int len;
	int b_len;
	char* tmp;

	     /* Print Content-Length */
	b_len=body?body->len:0;
	tmp = int2str(b_len, &len);
	if (len >= sizeof(content_length)) {
		LOG(L_ERR, "ERROR: print_content_length: content_len too big\n");
		dest->s = 0;
		dest->len = 0;
		return -1;
	}
	memcpy(content_length, tmp, len); 
	dest->s = content_length;
	dest->len = len;
	return 0;
}


/*
 * Convert CSeq number into asciiz
 */
static inline int print_cseq_num(str* _s, dlg_t* _d)
{
	static char cseq[INT2STR_MAX_LEN];
	char* tmp;
	int len;

	tmp = int2str(_d->loc_seq.value, &len);
	if (len > sizeof(cseq)) {
		LOG(L_ERR, "print_cseq_num: cseq too big\n");
		return -1;
	}
	
	memcpy(cseq, tmp, len);
	_s->s = cseq;
	_s->len = len;
	return 0;
}


/*
 * Create Via header
 */
static inline int assemble_via(str* dest, struct cell* t, 
								struct dest_info* dst, int branch)
{
	static char branch_buf[MAX_BRANCH_PARAM_LEN];
	char* via;
	int len;
	unsigned int via_len;
	str branch_str;
	struct hostport hp;

	if (!t_calc_branch(t, branch, branch_buf, &len)) {
		LOG(L_ERR, "ERROR: assemble_via: branch calculation failed\n");
		return -1;
	}
	
	branch_str.s = branch_buf;
	branch_str.len = len;

#ifdef XL_DEBUG
	printf("!!!proto: %d\n", sock->proto);
#endif

	set_hostport(&hp, 0);
	via = via_builder(&via_len, dst, &branch_str, 0, &hp);
	if (!via) {
		LOG(L_ERR, "assemble_via: via building failed\n");
		return -2;
	}
	
	dest->s = via;
	dest->len = via_len;
	return 0;
}


/*
 * Print Request-URI
 */
static inline char* print_request_uri(char* w, str* method, dlg_t* dialog, struct cell* t, int branch)
{
	memapp(w, method->s, method->len); 
	memapp(w, " ", 1); 

	t->uac[branch].uri.s = w; 
	t->uac[branch].uri.len = dialog->hooks.request_uri->len;

	memapp(w, dialog->hooks.request_uri->s, dialog->hooks.request_uri->len); 
	memapp(w, " " SIP_VERSION CRLF, 1 + SIP_VERSION_LEN + CRLF_LEN);

	return w;
}


/*
 * Print To header field
 */
static inline char* print_to(char* w, dlg_t* dialog, struct cell* t)
{
	t->to.s = w;
	t->to.len = TO_LEN + dialog->rem_uri.len + CRLF_LEN;

	memapp(w, TO, TO_LEN);
	memapp(w, dialog->rem_uri.s, dialog->rem_uri.len);

	if (dialog->id.rem_tag.len) {
		t->to.len += TOTAG_LEN + dialog->id.rem_tag.len ;
		memapp(w, TOTAG, TOTAG_LEN);
		memapp(w, dialog->id.rem_tag.s, dialog->id.rem_tag.len);
	}

	memapp(w, CRLF, CRLF_LEN);
	return w;
}


/*
 * Print From header field
 */
static inline char* print_from(char* w, dlg_t* dialog, struct cell* t)
{
	t->from.s = w;
	t->from.len = FROM_LEN + dialog->loc_uri.len + CRLF_LEN;

	memapp(w, FROM, FROM_LEN);
	memapp(w, dialog->loc_uri.s, dialog->loc_uri.len);

	if (dialog->id.loc_tag.len) {
		t->from.len += FROMTAG_LEN + dialog->id.loc_tag.len;
		memapp(w, FROMTAG, FROMTAG_LEN);
		memapp(w, dialog->id.loc_tag.s, dialog->id.loc_tag.len);
	}

	memapp(w, CRLF, CRLF_LEN);
	return w;
}


/*
 * Print CSeq header field
 */
char* print_cseq_mini(char* target, str* cseq, str* method) {
	memapp(target, CSEQ, CSEQ_LEN);
	memapp(target, cseq->s, cseq->len);
	memapp(target, " ", 1);
	memapp(target, method->s, method->len);
	return target;
}

static inline char* print_cseq(char* w, str* cseq, str* method, struct cell* t)
{
	t->cseq_n.s = w; 
	/* don't include method name and CRLF -- subsequent
	 * local requests ACK/CANCEL will add their own */
	t->cseq_n.len = CSEQ_LEN + cseq->len; 
	w = print_cseq_mini(w, cseq, method);
	return w;
}

/*
 * Print Call-ID header field
 * created an extra function for pure header field creation, that is used by t_cancel for 
 * t_uac_cancel FIFO function.
 */
char* print_callid_mini(char* target, str callid) {
	memapp(target, CALLID, CALLID_LEN);
	memapp(target, callid.s, callid.len);
	memapp(target, CRLF, CRLF_LEN);
	return target;
}

static inline char* print_callid(char* w, dlg_t* dialog, struct cell* t)
{
	/* begins with CRLF, not included in t->callid, don`t know why...?!? */
	memapp(w, CRLF, CRLF_LEN);
	t->callid.s = w;
	t->callid.len = CALLID_LEN + dialog->id.call_id.len + CRLF_LEN;

	w = print_callid_mini(w, dialog->id.call_id);
	return w;
}


/*
 * Create a request
 */
char* build_uac_req(str* method, str* headers, str* body, dlg_t* dialog, int branch, 
			struct cell *t, int* len, struct dest_info* dst)
{
	char* buf, *w;
	str content_length, cseq, via;

	if (!method || !dialog) {
		LOG(L_ERR, "build_uac_req(): Invalid parameter value\n");
		return 0;
	}
	if (print_content_length(&content_length, body) < 0) {
		LOG(L_ERR, "build_uac_req(): Error while printing content-length\n");
		return 0;
	}
	if (print_cseq_num(&cseq, dialog) < 0) {
		LOG(L_ERR, "build_uac_req(): Error while printing CSeq number\n");
		return 0;
	}
	*len = method->len + 1 + dialog->hooks.request_uri->len + 1 + SIP_VERSION_LEN + CRLF_LEN;

	if (assemble_via(&via, t, dst, branch) < 0) {
		LOG(L_ERR, "build_uac_req(): Error while assembling Via\n");
		return 0;
	}
	*len += via.len;

	*len += TO_LEN + dialog->rem_uri.len
		+ (dialog->id.rem_tag.len ? (TOTAG_LEN + dialog->id.rem_tag.len) : 0) + CRLF_LEN;    /* To */
	*len += FROM_LEN + dialog->loc_uri.len
		+ (dialog->id.loc_tag.len ? (FROMTAG_LEN + dialog->id.loc_tag.len) : 0) + CRLF_LEN;  /* From */
	*len += CALLID_LEN + dialog->id.call_id.len + CRLF_LEN;                                      /* Call-ID */
	*len += CSEQ_LEN + cseq.len + 1 + method->len + CRLF_LEN;                                    /* CSeq */
	*len += calculate_routeset_length(dialog);                                                   /* Route set */
	*len += CONTENT_LENGTH_LEN + content_length.len + CRLF_LEN; /* Content-
																	 Length */
	*len += (server_signature ? (USER_AGENT_LEN + CRLF_LEN) : 0);                                /* Signature */
	*len += (headers ? headers->len : 0);                                                        /* Additional headers */
	*len += (body ? body->len : 0);                                                              /* Message body */
	*len += CRLF_LEN;                                                                            /* End of Header */

	buf = shm_malloc(*len + 1);
	if (!buf) {
		LOG(L_ERR, "build_uac_req(): no shmem\n");
		goto error;
	}
	
	w = buf;

	w = print_request_uri(w, method, dialog, t, branch);  /* Request-URI */
	memapp(w, via.s, via.len);                            /* Top-most Via */
	w = print_to(w, dialog, t);                           /* To */
	w = print_from(w, dialog, t);                         /* From */
	w = print_cseq(w, &cseq, method, t);                  /* CSeq */
	w = print_callid(w, dialog, t);                       /* Call-ID */
	w = print_routeset(w, dialog);                        /* Route set */

     /* Content-Length */
	memapp(w, CONTENT_LENGTH, CONTENT_LENGTH_LEN);
	memapp(w, content_length.s, content_length.len);
	memapp(w, CRLF, CRLF_LEN);
	
	     /* Server signature */
	if (server_signature) memapp(w, USER_AGENT CRLF, USER_AGENT_LEN + CRLF_LEN);
	if (headers) memapp(w, headers->s, headers->len);
	memapp(w, CRLF, CRLF_LEN);
     	if (body) memapp(w, body->s, body->len);

#ifdef EXTRA_DEBUG
	assert(w-buf == *len);
#endif

	pkg_free(via.s);
	return buf;

 error:
	pkg_free(via.s);
	return 0;
}


int t_calc_branch(struct cell *t, 
	int b, char *branch, int *branch_len)
{
	return syn_branch ?
		branch_builder( t->hash_index,
			t->label, 0,
			b, branch, branch_len )
		: branch_builder( t->hash_index,
			0, t->md5,
			b, branch, branch_len );
}

