/*
 * $Id: trans_layer.cpp 1713 2010-03-30 14:11:14Z rco $
 *
 * Copyright (C) 2007 Raphael Coeffic
 *
 * This file is part of SEMS, a free SIP media server.
 *
 * SEMS 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. This program is released under
 * the GPL with the additional exemption that compiling, linking,
 * and/or using OpenSSL is allowed.
 *
 * For a license to use the SEMS 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
 *
 * SEMS 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
 */

#include "trans_layer.h"
#include "sip_parser.h"
#include "trans_table.h"
#include "parse_cseq.h"
#include "parse_from_to.h"
#include "parse_route.h"
#include "parse_100rel.h"
#include "sip_trans.h"
#include "msg_fline.h"
#include "msg_hdrs.h"
#include "udp_trsp.h"
#include "resolver.h"
#include "log.h"

#include "wheeltimer.h"
#include "sip_timers.h"

#include "SipCtrlInterface.h"
#include "AmUtils.h"
#include "AmSipMsg.h"
#include "AmConfig.h"
#include "AmSipEvent.h"

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <assert.h>

#include <algorithm>

bool _trans_layer::accept_fr_without_totag = false;

_trans_layer::_trans_layer()
    : ua(NULL),
      transports()
{
}

_trans_layer::~_trans_layer()
{}


void _trans_layer::register_ua(sip_ua* ua)
{
    this->ua = ua;
}

void _trans_layer::register_transport(trsp_socket* trsp)
{
    transports.push_back(trsp);
}

void _trans_layer::clear_transports()
{
    transports.clear();
}


int _trans_layer::send_reply(trans_ticket* tt,
			     int reply_code, const cstring& reason,
			     const cstring& to_tag, const cstring& hdrs,
			     const cstring& body,
			     const cstring& _next_hop, unsigned short _next_port,
			     int out_interface)
{
    // Ref.: RFC 3261 8.2.6, 12.1.1
    //
    // Fields to copy (from RFC 3261):
    //  - From
    //  - Call-ID
    //  - CSeq
    //  - Vias (same order)
    //  - To (+ tag if not yet present in request)
    //  - (if a dialog is created) Record-Route
    //
    // Fields to generate (if INVITE transaction):
    //    - Contact
    //    - Route: copied from 
    // 
    // SHOULD be contained:
    //  - Allow, Supported
    //
    // MAY be contained:
    //  - Accept

    assert(tt);

    if (!tt->_bucket || !tt->_t) {
	ERROR("Invalid transaction ticket\n");
	return -1;
    }

    trans_bucket* bucket = tt->_bucket;
    sip_trans*    t = tt->_t;

    bucket->lock();
    if(!bucket->exist(t)){
	bucket->unlock();
	ERROR("Invalid transaction key: transaction does not exist (%p;%p)\n",bucket,t);
	return -1;
    }

    sip_msg* req = t->msg;
    assert(req);

    bool have_to_tag = false;
    int  reply_len   = status_line_len(reason);

    // add 'received' should be added
    // check if first Via has rport parameter

    assert(req->via1);
    assert(req->via_p1);

    unsigned int new_via1_len = copy_hdr_len(req->via1);
    string remote_ip_str = get_addr_str(((sockaddr_in*)&req->remote_ip)->sin_addr).c_str();
    new_via1_len += 10/*;received=*/ + remote_ip_str.length();

    // needed if rport parameter was present but empty
    string remote_port_str;
    if(req->via_p1->has_rport) {
	if(!req->via_p1->rport.len){
	    remote_port_str = int2str(ntohs(((sockaddr_in*)&req->remote_ip)->sin_port));
	    new_via1_len += remote_port_str.length() + 1/* "=<port number>" */;
	}
    }

    // copy necessary headers
    for(list<sip_header*>::iterator it = req->hdrs.begin();
	it != req->hdrs.end(); ++it) {

	assert((*it));
	switch((*it)->type){

	case sip_header::H_VIA:
	    // if first via, take the possibly modified one
	    if((*it) == req->via1)
		reply_len += new_via1_len;
	    else
		reply_len += copy_hdr_len(*it);
	    break;

	case sip_header::H_TO:
	    assert((*it)->p);
	    if(! ((sip_from_to*)(*it)->p)->tag.len ) {

		reply_len += 5/* ';tag=' */
		    + to_tag.len; 
	    }
	    else {
		// To-tag present in request
		have_to_tag = true;

		t->to_tag = ((sip_from_to*)(*it)->p)->tag;
	    }
	    reply_len += copy_hdr_len(*it);
	    break;

	case sip_header::H_FROM:
	case sip_header::H_CALL_ID:
	case sip_header::H_CSEQ:
	case sip_header::H_RECORD_ROUTE:
	    reply_len += copy_hdr_len(*it);
	    break;
	}
    }

    reply_len += hdrs.len;

    string c_len = int2str(body.len);
    reply_len += content_length_len((char*)c_len.c_str());

    if(body.len){
	
	reply_len += body.len;
    }

    reply_len += 2/*CRLF*/;
    
    // Allocate buffer for the reply
    //
    char* reply_buf = new char[reply_len];
    char* c = reply_buf;

    DBG("reply_len = %i\n",reply_len);

    status_line_wr(&c,reply_code,reason);

    for(list<sip_header*>::iterator it = req->hdrs.begin();
	it != req->hdrs.end(); ++it) {

	switch((*it)->type){

	case sip_header::H_VIA:
	    
	    if((*it) == req->via1) {// 1st Via
		
		// move this code to something like:
		// write_reply_via(old_via1,old_via_p1,remote_ip_str,remote_port_str)

		unsigned int len;

		memcpy(c,(*it)->name.s,(*it)->name.len);
		c += (*it)->name.len;
		
		*(c++) = ':';
		*(c++) = SP;
		
		if(req->via_p1->has_rport && !req->via_p1->rport.len){

		    // copy everything from the beginning up to the "rport" param:
		    len = (req->via_p1->rport.s + req->via_p1->rport.len) - req->via1->value.s;
		    memcpy(c,req->via1->value.s,len);
		    c += len;

		    // add '='
		    *(c++) = '=';

		    // add the remote port
		    memcpy(c,remote_port_str.c_str(),remote_port_str.length());
		    c += remote_port_str.length();

		    //copy up to the end of the first Via parm
		    len = req->via_p1->eop - (req->via_p1->rport.s + req->via_p1->rport.len);
		    memcpy(c,req->via_p1->rport.s + req->via_p1->rport.len, len);
		    c += len;
		}
		else {
		    //copy up to the end of the first Via parm
		    len = req->via_p1->eop - req->via1->value.s;
		    memcpy(c,req->via1->value.s,len);
		    c += len;
		}

		memcpy(c,";received=",10);
		c += 10;

		memcpy(c,remote_ip_str.c_str(),remote_ip_str.length());
		c += remote_ip_str.length();

		//copy the rest of the first Via header 
		len = req->via1->value.s + req->via1->value.len - req->via_p1->eop;
		memcpy(c,req->via_p1->eop,len);
		c += len;

		*(c++) = CR;
		*(c++) = LF;
	    }
	    else {
		copy_hdr_wr(&c,*it);
	    }
	    break;

	case sip_header::H_TO:
	    if(have_to_tag){
		copy_hdr_wr(&c,*it);
	    }
	    else {
		memcpy(c,(*it)->name.s,(*it)->name.len);
		c += (*it)->name.len;
		
		*(c++) = ':';
		*(c++) = SP;
		
		memcpy(c,(*it)->value.s,(*it)->value.len);
		c += (*it)->value.len;
		
		memcpy(c,";tag=",5);
		c += 5;

		t->to_tag.s = c;
		t->to_tag.len = to_tag.len;

		memcpy(c,to_tag.s,to_tag.len);
		c += to_tag.len;

		*(c++) = CR;
		*(c++) = LF;
	    }
	    break;

	case sip_header::H_FROM:
	case sip_header::H_CALL_ID:
	case sip_header::H_CSEQ:
	case sip_header::H_RECORD_ROUTE:
	    copy_hdr_wr(&c,*it);
	    break;
	}
    }

    if (hdrs.len) {
      memcpy(c,hdrs.s,hdrs.len);
      c += hdrs.len;
    }

    content_length_wr(&c,(char*)c_len.c_str());

    *c++ = CR;
    *c++ = LF;

    if(body.len){
	
	memcpy(c,body.s,body.len);
    }

    int err = -1;

    // TODO: Inspect topmost 'Via' and select proper addr (+resolve DNS names)
    // refs: RFC3261 18.2.2; RFC3581

    sockaddr_storage remote_ip;
    trsp_socket* local_socket = NULL;

    if (!_next_hop.len) {
	memcpy(&remote_ip,&req->remote_ip,sizeof(sockaddr_storage));
	local_socket = req->local_socket;

	if(req->via_p1->has_rport){

	    if(req->via_p1->rport_i){
		// use 'rport'
		((sockaddr_in*)&remote_ip)->sin_port = htons(req->via_p1->rport_i);
	    }
	    // else: use the source port from the replied request (from IP hdr)
	}
	else {
	
	    if(req->via_p1->port_i){
		// use port from 'sent-by' via address
		((sockaddr_in*)&remote_ip)->sin_port = htons(req->via_p1->port_i);
	    }
	    // else: use the source port from the replied request (from IP hdr)

	    // TODO: use destination address from 1st Via???
	}
    } else {
	DBG("setting next hop '%.*s:%u'\n",
	    _next_hop.len, _next_hop.s, _next_port ? _next_port : 5060);
	// use _next_hop:_next_port
	if (resolver::instance()->str2ip(_next_hop.s, &remote_ip,
					 (address_type)(IPv4 | IPv6)) != 1) {
	    ERROR("Invalid next_hop_ip '%.*s'\n", _next_hop.len, _next_hop.s);
	    delete [] reply_buf;
	    goto end;
	}
	// default port to 5060
	((sockaddr_in*)&remote_ip)->sin_port = htons(_next_port ? _next_port : 5060);
    }

    DBG("Sending to %s:%i <%.*s...>\n",
	get_addr_str(((sockaddr_in*)&remote_ip)->sin_addr).c_str(),
	ntohs(((sockaddr_in*)&remote_ip)->sin_port),
	50 /* preview - instead of p_msg->len */,reply_buf);

    // rco: should we overwrite the socket from the request in all cases???
    if((out_interface >= 0) && ((unsigned int)out_interface < transports.size())){
	local_socket = transports[out_interface];
    }
    else if(!local_socket) {
	local_socket = find_transport(&remote_ip);
	if(!local_socket){
	    ERROR("Could not find transport socket\n");
	    delete [] reply_buf;
	    goto end;
	}
    }

    err = local_socket->send(&remote_ip,reply_buf,reply_len);
    if(err < 0){
	delete [] reply_buf;
	goto end;
    }

    err = update_uas_reply(bucket,t,reply_code);
    if(err < 0){
	
	ERROR("Invalid state change\n");
	delete [] reply_buf;
    }
    else if(err != TS_TERMINATED) {
	if (t->retr_buf) 
		delete [] t->retr_buf;

	t->retr_buf = reply_buf;
	t->retr_len = reply_len;
	memcpy(&t->retr_addr,&remote_ip,sizeof(sockaddr_storage));
	t->retr_socket = local_socket;

	err = 0;
    }
    else {
	// Transaction has been deleted
	// -> should not happen, as we 
	//    now wait for 200 ACK
	delete [] reply_buf;
	err = 0;
    }
    
 end:
    bucket->unlock();
    return err;
}

int _trans_layer::send_sl_reply(sip_msg* req, int reply_code, 
			       const cstring& reason, const cstring& hdrs, 
			       const cstring& body)
{
    // Ref.: RFC 3261 8.2.6, 12.1.1
    //
    // Fields to copy (from RFC 3261):
    //  - From
    //  - Call-ID
    //  - CSeq
    //  - Vias (same order)
    //  - To (+ tag if not yet present in request)
    //  - (if a dialog is created) Record-Route
    //
    // Fields to generate (if INVITE transaction):
    //    - Contact
    //    - Route: copied from 
    // 
    // SHOULD be contained:
    //  - Allow, Supported
    //
    // MAY be contained:
    //  - Accept

    assert(req);

    bool have_to_tag = false;
    int  reply_len   = status_line_len(reason);

    for(list<sip_header*>::iterator it = req->hdrs.begin();
	it != req->hdrs.end(); ++it) {

	assert(*it);
	switch((*it)->type){

	case sip_header::H_TO:

	    if((!(*it)->p) || (!((sip_from_to*)(*it)->p)->tag.len) ) {

		reply_len += 5/* ';tag=' */
		    + SL_TOTAG_LEN; 
	    }
	    else {
		// To-tag present in request
		have_to_tag = true;
	    }
	    // fall-through-trap
	case sip_header::H_FROM:
	case sip_header::H_CALL_ID:
	case sip_header::H_CSEQ:
	case sip_header::H_VIA:
	case sip_header::H_RECORD_ROUTE:
	    reply_len += copy_hdr_len(*it);
	    break;
	}
    }

    reply_len += hdrs.len;

    string c_len = int2str(body.len);
    reply_len += content_length_len((char*)c_len.c_str());

    if(body.len){
	
	reply_len += body.len;
    }

    reply_len += 2/*CRLF*/;
    
    // Allocate buffer for the reply
    //
    char* reply_buf = new char[reply_len];
    char* c = reply_buf;

    status_line_wr(&c,reply_code,reason);

    for(list<sip_header*>::iterator it = req->hdrs.begin();
	it != req->hdrs.end(); ++it) {

	switch((*it)->type){

	case sip_header::H_TO:

	    if(have_to_tag){
		copy_hdr_wr(&c,*it);
	    }
	    else {
		memcpy(c,(*it)->name.s,(*it)->name.len);
		c += (*it)->name.len;
		
		*(c++) = ':';
		*(c++) = SP;
		
		memcpy(c,(*it)->value.s,(*it)->value.len);
		c += (*it)->value.len;
	    
		memcpy(c,";tag=",5);
		c += 5;

		char to_tag[SL_TOTAG_LEN];
		compute_sl_to_tag(to_tag,req);
		memcpy(c,to_tag,SL_TOTAG_LEN);
		c += SL_TOTAG_LEN;

		*(c++) = CR;
		*(c++) = LF;
	    }
	    break;

	case sip_header::H_FROM:
	case sip_header::H_CALL_ID:
	case sip_header::H_CSEQ:
	case sip_header::H_VIA:
	case sip_header::H_RECORD_ROUTE:
	    copy_hdr_wr(&c,*it);
	    break;
	}
    }

    if (hdrs.len) {
	memcpy(c,hdrs.s,hdrs.len);
	c += hdrs.len;
    }

    content_length_wr(&c,(char*)c_len.c_str());

    *c++ = CR;
    *c++ = LF;

    if(body.len){
	
	memcpy(c,body.s,body.len);
    }

    assert(req->local_socket);

    int err = req->local_socket->send(&req->remote_ip,reply_buf,reply_len);
    delete [] reply_buf;

    return err;
}


static void prepare_strict_routing(sip_msg* msg, string& ext_uri_buffer)
{
    if(msg->route.empty())
	return;

    sip_header* fr = msg->route.front();
    sip_uri* route_uri = get_first_route_uri(fr);

    // Loose routing is used,
    // no need for further processing
    if(!route_uri || is_loose_route(route_uri))
	return;

    sip_route* route = (sip_route*)fr->p;
    sip_nameaddr* fr_na = route->elmts.front()->addr;
    cstring fr_na_addr = fr_na->addr;

    if(route->elmts.size() == 1){
	// remove current route header from message
 	msg->route.pop_front();

	list<sip_header*>::iterator h_it = 
	    std::find(msg->hdrs.begin(),msg->hdrs.end(),fr);

	if(h_it != msg->hdrs.end()) 
	    msg->hdrs.erase(h_it);

 	delete fr;
    }
    else if(route->elmts.size() > 1) {
	// remove first element from the list
	delete route->elmts.front();
	route->elmts.pop_front();

	// fetch the next route element
	route_elmt* nxt_re = route->elmts.front();

 	// adjust route header
 	fr->value.len = (fr->value.s + fr->value.len) - nxt_re->route.s;
 	fr->value.s   = nxt_re->route.s;
    }

    // copy r_uri at the end of the route set.
    // ext_uri_buffer must have the same scope as 'msg'
    ext_uri_buffer = "<" + c2stlstr(msg->u.request->ruri_str) + ">";
    msg->hdrs.push_back(new sip_header(0,"Route",stl2cstr(ext_uri_buffer)));

    // and replace the R-URI with the first route URI 
    msg->u.request->ruri_str = fr_na_addr;
}


//
// Ref. RFC 3261 "12.2.1.1 Generating the Request"
//
int _trans_layer::set_next_hop(sip_msg* msg, 
			       cstring* next_hop,
			       unsigned short* next_port)
{
    assert(msg);

    list<sip_header*>& route_hdrs = msg->route; 
    int err=0;

    if(!route_hdrs.empty()){
	
	sip_header* fr = route_hdrs.front();
	sip_uri* route_uri = get_first_route_uri(fr);
	if(route_uri == NULL) {
	    
	    DBG("Parsing 1st route uri failed\n");
	    return -1;
	}
	
	if (next_hop->len == 0) {
	    *next_hop  = route_uri->host;
	    if(route_uri->port_str.len)
		*next_port = route_uri->port;
	}
    }
    else {

	sip_uri parsed_r_uri;
	cstring& r_uri = msg->u.request->ruri_str;

	err = parse_uri(&parsed_r_uri,r_uri.s,r_uri.len);
	if(err < 0){
	    ERROR("Invalid Request URI\n");
	    return -1;
	}
	*next_hop  = parsed_r_uri.host;
	if(parsed_r_uri.port_str.len)
	    *next_port = parsed_r_uri.port;
    }

    DBG("next_hop:next_port is <%.*s:%u>\n", next_hop->len, next_hop->s, *next_port);
    
    return 0;
}


int _trans_layer::set_destination_ip(sip_msg* msg, cstring* next_hop, unsigned short next_port)
{

    string nh = c2stlstr(*next_hop);

    DBG("checking whether '%s' is IP address...\n", nh.c_str());
    if (resolver::instance()->str2ip(nh.c_str(), &(msg->remote_ip), IPv4) != 1) {

	// nh does NOT contain a valid IP address
    
	if(!next_port){
	    // no explicit port specified,
	    // try SRV first
	    if (AmConfig::DisableDNSSRV) {
		DBG("no port specified, but DNS SRV disabled (skipping).\n");
	    } else {
		string srv_name = "_sip._udp." + nh;

		DBG("no port specified, looking up SRV '%s'...\n", srv_name.c_str());

		if(!resolver::instance()->resolve_name(srv_name.c_str(),
						       &(msg->h_dns),
						       &(msg->remote_ip),IPv4)){
		    return 0;
		}

		DBG("no SRV record for %s",srv_name.c_str());
	    }
	}

	memset(&(msg->remote_ip),0,sizeof(sockaddr_storage));
	int err = resolver::instance()->resolve_name(nh.c_str(),
						     &(msg->h_dns),
						     &(msg->remote_ip),IPv4);
	if(err < 0){
	    ERROR("Unresolvable Request URI domain\n");
	    return -1;
	}
    }

    if(!((sockaddr_in*)&(msg->remote_ip))->sin_port) {
	if(!next_port)
	    next_port = 5060;
	((sockaddr_in*)&(msg->remote_ip))->sin_port = htons(next_port);
    }

    DBG("set destination to %s:%u\n", nh.c_str(),
	ntohs(((sockaddr_in*)&(msg->remote_ip))->sin_port));
    
    return 0;
}

static void translate_string(sip_msg* dst_msg, cstring& dst,
			     sip_msg* src_msg, cstring& src)
{
    dst.s = (char*)src.s + (dst_msg->buf - src_msg->buf);
    dst.len = src.len;
}

static void translate_hdr(sip_msg* dst_msg, sip_header*& dst, 
			  sip_msg* src_msg, sip_header* src)
{
    dst = new sip_header();
    dst_msg->hdrs.push_back(dst);
    dst->type = src->type;
    translate_string(dst_msg,dst->name,src_msg,src->name);
    translate_string(dst_msg,dst->value,src_msg,src->value);
    dst->p = NULL;
}

void _trans_layer::timeout(trans_bucket* bucket, sip_trans* t)
{
    t->reset_all_timers();
    t->state = TS_TERMINATED;

    // send 408 to 'ua'
    sip_msg* req = t->msg;
    sip_msg  msg(req->buf,req->len);

    msg.type = SIP_REPLY;
    msg.u.reply = new sip_reply();

    msg.u.reply->code = 408;
    msg.u.reply->reason = cstring("Timeout");

    translate_hdr(&msg,msg.from, req,req->from);
    msg.from->p = new sip_from_to();
    parse_from_to((sip_from_to*)msg.from->p,
		  msg.from->value.s,msg.from->value.len);

    translate_hdr(&msg,msg.to, req,req->to);
    msg.to->p = new sip_from_to();
    parse_from_to((sip_from_to*)msg.to->p,
		  msg.to->value.s,msg.to->value.len);

    translate_hdr(&msg,msg.cseq, req,req->cseq);
    msg.cseq->p = new sip_cseq();
    parse_cseq((sip_cseq*)msg.cseq->p,
	       msg.cseq->value.s,msg.cseq->value.len);

    translate_hdr(&msg,msg.callid, req,req->callid);

    bucket->remove(t);
    bucket->unlock();

    ua->handle_sip_reply(&msg);
}

int _trans_layer::send_request(sip_msg* msg, trans_ticket* tt,
			       const cstring& _next_hop, unsigned short _next_port,
			       int out_interface)
{
    // Request-URI
    // To
    // From
    // Call-ID
    // CSeq
    // Max-Forwards
    // Via
    // Contact
    // Supported / Require
    // Content-Length / Content-Type

    assert(msg);
    assert(tt);

    cstring next_hop;
    unsigned short next_port=0;

    if (!_next_hop.len) {
	if(set_next_hop(msg,&next_hop,&next_port) < 0){
	    DBG("set_next_hop failed\n");
	    return -1;
	}
    } else {
	next_hop = _next_hop;
	next_port = _next_port ? _next_port : 5060;
    }

    string uri_buffer; // must have the same scope as 'msg'
    prepare_strict_routing(msg,uri_buffer);

    if(set_destination_ip(msg,&next_hop,next_port) < 0){
     	DBG("set_destination_ip failed\n");
     	return -1;
    }

    // rco: should we overwrite the socket from the request in all cases???
    if((out_interface >= 0) && ((unsigned int)out_interface < transports.size())){
	msg->local_socket = transports[out_interface];
    }
    // no socket yet, find one
    else if(!msg->local_socket) {
	trsp_socket* s = find_transport(&msg->remote_ip);
	if(!s){
	    ERROR("could not find a transport socket (dest: '%.*s')\n",
		  next_hop.len,next_hop.s);
	    return -1;
	}

	msg->local_socket = s;
    }

    tt->_bucket = 0;
    tt->_t = 0;

    if(!msg->u.request->ruri_str.len ||
       !msg->u.request->method_str.len) {
	
	ERROR("empty method name or R-URI");
	return -1;
    }
    else {
	DBG("send_request to R-URI <%.*s>",msg->u.request->ruri_str.len,msg->u.request->ruri_str.s);
    }

    int request_len = request_line_len(msg->u.request->method_str,
				       msg->u.request->ruri_str);

    char branch_buf[BRANCH_BUF_LEN];
    compute_branch(branch_buf,msg->callid->value,msg->cseq->value);
    cstring branch(branch_buf,BRANCH_BUF_LEN);
    
    string via(msg->local_socket->get_ip());
    if(msg->local_socket->get_port() != 5060)
	via += ":" + int2str(msg->local_socket->get_port());

    // add 'rport' parameter defaultwise? yes, for now
    request_len += via_len(stl2cstr(via),branch,true);

    request_len += copy_hdrs_len(msg->hdrs);

    string content_len = int2str(msg->body.len);

    request_len += content_length_len(stl2cstr(content_len));
    request_len += 2/* CRLF end-of-headers*/;

    if(msg->body.len){
	request_len += msg->body.len;
    }

    // Allocate new message
    sip_msg* p_msg = new sip_msg();
    p_msg->buf = new char[request_len+1];
    p_msg->len = request_len;

    // generate it
    char* c = p_msg->buf;
    request_line_wr(&c,msg->u.request->method_str,
		    msg->u.request->ruri_str);

    via_wr(&c,stl2cstr(via),branch,true);
    copy_hdrs_wr(&c,msg->hdrs);

    content_length_wr(&c,stl2cstr(content_len));

    *c++ = CR;
    *c++ = LF;

    if(msg->body.len){
	memcpy(c,msg->body.s,msg->body.len);

	c += msg->body.len;
    }
    *c++ = '\0';

    // and parse it
    char* err_msg=0;
    if(parse_sip_msg(p_msg,err_msg)){
	ERROR("Parser failed on generated request\n");
	ERROR("Message was: <%.*s>\n",p_msg->len,p_msg->buf);
	delete p_msg;
	return MALFORMED_SIP_MSG;
    }

    // copy msg->remote_ip
    memcpy(&p_msg->remote_ip,&msg->remote_ip,sizeof(sockaddr_storage));
    p_msg->local_socket = msg->local_socket;

    DBG("Sending to %s:%i <%.*s...>\n",
	get_addr_str(((sockaddr_in*)&p_msg->remote_ip)->sin_addr).c_str(),
	ntohs(((sockaddr_in*)&p_msg->remote_ip)->sin_port),
	50 /* preview - instead of p_msg->len */,p_msg->buf);

    tt->_bucket = get_trans_bucket(p_msg->callid->value,
				   get_cseq(p_msg)->num_str);
    tt->_bucket->lock();
    
    int send_err = p_msg->send();
    if(send_err < 0){
	ERROR("Error from transport layer\n");
	delete p_msg;
    }
    else {
	DBG("update_uac_request tt->_t =%p\n", tt->_t);
	send_err = update_uac_request(tt->_bucket,tt->_t,p_msg);
	if(send_err < 0){
	    ERROR("Could not update UAC state for request.\n");
	    delete p_msg;
	}
    }

    tt->_bucket->unlock();
    
    return send_err;
}

int _trans_layer::cancel(trans_ticket* tt)
{
    assert(tt);
    assert(tt->_bucket && tt->_t);

    trans_bucket* bucket = tt->_bucket;
    sip_trans*    t = tt->_t;

    bucket->lock();
    if(!bucket->exist(t)){
	DBG("No transaction to cancel: wrong key or finally replied\n");
	bucket->unlock();
	return 0;
    }

    sip_msg* req = t->msg;
    
    // RFC 3261 says: SHOULD NOT be sent for other request
    // than INVITE.
    if(req->u.request->method != sip_request::INVITE){
	bucket->unlock();
	ERROR("Trying to cancel a non-INVITE request (we SHOULD NOT do that)\n");
	return -1;
    }
    
    switch(t->state){
    case TS_CALLING:
	// do not send a request:
	// just remove the transaction
	bucket->remove(t);
	bucket->unlock();
	return 0;

    case TS_COMPLETED:
	// final reply has been sent, but still no ACK:
	// do nothing!!!

	// TODO: switch to TS_CANCELLING??
	// this would allow for sending the BYE as soon
	// we get an ACK or a timeout (STIMER_H)...
	bucket->unlock();
	return 0;
    }

    cstring cancel_str("CANCEL");

    int request_len = request_line_len(cancel_str,
				       req->u.request->ruri_str);

    char branch_buf[BRANCH_BUF_LEN];
    compute_branch(branch_buf,req->callid->value,get_cseq(req)->num_str);
    cstring branch(branch_buf,BRANCH_BUF_LEN);
    
    string via(req->local_socket->get_ip());
    if(req->local_socket->get_port() != 5060)
	via += ":" + int2str(req->local_socket->get_port());

    //TODO: add 'rport' parameter by default?

    request_len += copy_hdr_len(req->via1);

    request_len += copy_hdr_len(req->to)
	+ copy_hdr_len(req->from)
	+ copy_hdr_len(req->callid)
	+ cseq_len(get_cseq(req)->num_str,cancel_str)
	+ copy_hdrs_len(req->route)
	+ copy_hdrs_len(req->contacts);

    request_len += 2/* CRLF end-of-headers*/;

    // Allocate new message
    sip_msg* p_msg = new sip_msg();
    p_msg->buf = new char[request_len+1];
    p_msg->len = request_len;

    // generate it
    char* c = p_msg->buf;
    request_line_wr(&c,cancel_str,
		    req->u.request->ruri_str);

    copy_hdr_wr(&c,req->via1);
    copy_hdr_wr(&c,req->to);
    copy_hdr_wr(&c,req->from);
    copy_hdr_wr(&c,req->callid);
    cseq_wr(&c,get_cseq(req)->num_str,cancel_str);
    copy_hdrs_wr(&c,req->route);
    copy_hdrs_wr(&c,req->contacts);

    *c++ = CR;
    *c++ = LF;
    *c   = '\0';

    // and parse it
    char* err_msg=0;
    if(parse_sip_msg(p_msg,err_msg)){
	ERROR("Parser failed on generated request\n");
	ERROR("Message was: <%.*s>\n",p_msg->len,p_msg->buf);
	delete p_msg;
	return MALFORMED_SIP_MSG;
    }

    memcpy(&p_msg->remote_ip,&req->remote_ip,sizeof(sockaddr_storage));
    p_msg->local_socket = req->local_socket;

    DBG("Sending to %s:%i:\n<%.*s>\n",
	get_addr_str(((sockaddr_in*)&p_msg->remote_ip)->sin_addr).c_str(),
	ntohs(((sockaddr_in*)&p_msg->remote_ip)->sin_port),
	p_msg->len,p_msg->buf);

    trans_bucket* n_bucket = get_trans_bucket(p_msg->callid->value,
					      get_cseq(p_msg)->num_str);
    
    if(bucket != n_bucket)
	n_bucket->lock();

    int send_err = p_msg->send();
    if(send_err < 0){
	ERROR("Error from transport layer\n");
	delete p_msg;
    }
    else {

	sip_trans* t=NULL;
	send_err = update_uac_request(bucket,t,p_msg);
	if(send_err<0){
	    ERROR("Could not update state for UAC transaction\n");
	    delete p_msg;
	}
    }

    if(bucket != n_bucket)
	n_bucket->unlock();
    
    bucket->unlock();
    return send_err;
}


void _trans_layer::received_msg(sip_msg* msg)
{
#define DROP_MSG \
          delete msg;\
          return

    char* err_msg=0;
    int err = parse_sip_msg(msg,err_msg);

    if(err){
	DBG("parse_sip_msg returned %i\n",err);

	if(!err_msg){
	    err_msg = (char*)"unknown parsing error";
	}

	DBG("parsing error: %s\n",err_msg);

	DBG("Message was: \"%.*s\"\n",msg->len,msg->buf);

	if((err != MALFORMED_FLINE)
	   && (msg->type == SIP_REQUEST)
	   && (msg->u.request->method != sip_request::ACK)){

	    send_sl_reply(msg,400,cstring(err_msg),
			  cstring(),cstring());
	}

	DROP_MSG;
    }
    
    assert(msg->callid && get_cseq(msg));
#if 0
    unsigned int  h;
    if (msg->rack) { /* it's a PRACK */
        h = hash(msg->callid->value, get_rack(msg)->cseq_str);
        DBG("### RACK: <%.*s> <%.*s>\n", 
            msg->callid->value.len, msg->callid->value.s,
            get_rack(msg)->cseq_str.len, get_rack(msg)->cseq_str.s);
    } else {
        h = hash(msg->callid->value, get_cseq(msg)->num_str);
        DBG("### XXXX: <%.*s> <%.*s>\n", 
            msg->callid->value.len, msg->callid->value.s,
            get_cseq(msg)->num_str.len, get_cseq(msg)->num_str.s);
    }
#else
    unsigned int h = hash(msg->callid->value, get_cseq(msg)->num_str);
#endif
    trans_bucket* bucket = get_trans_bucket(h);
    sip_trans* t = NULL;

    bucket->lock();

    switch(msg->type){
    case SIP_REQUEST: 
	
	if((t = bucket->match_request(msg,TT_UAS)) != NULL){
	    if(msg->u.request->method != t->msg->u.request->method){
		
		// ACK matched INVITE transaction
		DBG("ACK matched INVITE transaction %p\n", t);
		
		err = update_uas_request(bucket,t,msg);
		DBG("update_uas_request(bucket,t=%p,msg) = %i\n",t, err);
		if(err<0){
		    DBG("trans_layer::update_uas_trans() failed!\n");
		    // Anyway, there is nothing we can do...
		}
		else {
		
		    // do not touch the transaction anymore:
		    // it could have been deleted !!!
		       
		    // should we forward the ACK to SEMS-App upstream? Yes
		    bucket->unlock();
		    
		    if(err == TS_REMOVED) {
			//  let's pass the request to
			//  the UA, iff it was a 200-ACK
			assert(ua);
			DBG("Passing ACK to the UA.\n");
			ua->handle_sip_request(trans_ticket(), // dummy
					       msg);
		    }
		    else {
			DBG("Absorbing non-200-ACK\n");
		    }

		    DROP_MSG;
		}
	    }
	    else {
		DBG("Found retransmission\n");
		t->retransmit(); // retransmit reply
	    }
	}
	else {
             unsigned inv_h;
             trans_bucket* inv_bucket;
             sip_trans* inv_t;
 
             switch (msg->u.request->method) {
                 case sip_request::ACK:
                     // non-2xx ACK??? drop!
                     break;
 
                 case sip_request::PRACK:
                     bucket->unlock();
                     if (! msg->rack) {
                       send_sl_reply(msg, 400, 
                           cstring("Missing valid RSeq header"),
                           cstring(),cstring());
                       DROP_MSG;
                     }
                     /* match INVITE transaction, cool off the 1xx timers */
                     inv_h = hash(msg->callid->value, get_rack(msg)->cseq_str);
                     inv_bucket = get_trans_bucket(inv_h);
                     inv_bucket->lock();
                     if((inv_t = inv_bucket->match_1xx_prack(msg)) != NULL) {
                         assert(msg->u.request->method != 
                             inv_t->msg->u.request->method);
                         err = update_uas_request(inv_bucket,inv_t,msg);
                         DBG("update_uas_request(bucket,t,msg) = %i\n",err);
                     }
                     inv_bucket->unlock();
                     bucket->lock();
                     // no break
 
                 default:
                     // New transaction
                     t = bucket->add_trans(msg, TT_UAS);
 
                     bucket->unlock();
 
                     //  let's pass the request to
                     //  the UA. 
                     assert(ua);
                     ua->handle_sip_request(trans_ticket(t,bucket),msg);
 
                     // forget the msg: it will be
                     // owned by the new transaction
                     return;
             }
	}
	break;
    
    case SIP_REPLY:

	if((t = bucket->match_reply(msg)) != NULL){

	    // Reply matched UAC transaction
	    
	    DBG("Reply matched an existing transaction\n");
	    int res = update_uac_reply(bucket,t,msg);
	    if(res < 0){
		ERROR("update_uac_trans() failed, so what happens now???\n");
		break;
	    }
	    if (res) {
		bucket->unlock();
		ua->handle_sip_reply(msg);
		DROP_MSG;
		//return; - part of DROP_MSG
	    }

	    // do not touch the transaction anymore:
	    // it could have been deleted !!!
	}
	else {
	    DBG("Reply did NOT match any existing transaction...\n");
	    DBG("reply code = %i\n",msg->u.reply->code);
	    if( (msg->u.reply->code >= 200) &&
	        (msg->u.reply->code <  300) ) {
		
		bucket->unlock();
		
		// pass to UA
		//assert(ua);
		//ua->handle_sip_reply(msg);
		
		DROP_MSG;
	    }
	}
	break;

    default:
	ERROR("Got unknown message type: Bug?\n");
	break;
    }

    // unlock_drop:
    bucket->unlock();
    DROP_MSG;
}


int _trans_layer::update_uac_reply(trans_bucket* bucket, sip_trans* t, sip_msg* msg)
{
    assert(msg->type == SIP_REPLY);

    cstring to_tag;
    int     reply_code = msg->u.reply->code;

    DBG("update_uac_reply(reply code = %i, trans=%p)\n",msg->u.reply->code, t);

    if(reply_code < 200){

	// Provisional reply
	switch(t->state){

	case TS_CALLING:
	    t->clear_timer(STIMER_A);
	    t->clear_timer(STIMER_M);
	    t->clear_timer(STIMER_B);
	    // fall through trap

	case TS_TRYING:
	    t->state = TS_PROCEEDING;
	    // fall through trap

	case TS_PROCEEDING:
	    if(t->msg->u.request->method != sip_request::CANCEL) {
		if(t->msg->u.request->method == sip_request::INVITE) {
		    t->reset_timer(STIMER_C, C_TIMER, bucket->get_id());
		}
		goto pass_reply;
	    }
	    else
		goto end;

	case TS_COMPLETED:
	default:
	    goto end;
	}
    }
    
    to_tag = ((sip_from_to*)msg->to->p)->tag;
    if((t->msg->u.request->method != sip_request::CANCEL) && 
       (reply_code < 300) &&
       !to_tag.len){
	if (!trans_layer::accept_fr_without_totag) {
	    DBG("To-tag missing in final reply (see sems.conf?)\n");
	    return -1;
	}
    }
    
    if(t->msg->u.request->method == sip_request::INVITE){
    
	if(reply_code >= 300){
	    
	    // Final error reply
	    switch(t->state){
		
	    case TS_CALLING:

		t->clear_timer(STIMER_A);
		t->clear_timer(STIMER_M);
		t->clear_timer(STIMER_B);

	    case TS_PROCEEDING:
	
		t->clear_timer(STIMER_C);

		t->state = TS_COMPLETED;
		send_non_200_ack(msg,t);
		t->reset_timer(STIMER_D, D_TIMER, bucket->get_id());
		
		goto pass_reply;
		
	    case TS_COMPLETED:
		// generate a new non-200 ACK
		send_non_200_ack(msg,t);
	    default:
		goto end;
	    }
	} 
	else {
	    
	    DBG("Positive final reply to INVITE transaction (state=%i)\n",t->state);

	    // Positive final reply to INVITE transaction
	    switch(t->state){
		
	    case TS_CALLING:
	    case TS_PROCEEDING:

		// TODO:
		//  we should take care of 200 ACK re-transmissions
		//    - on first reply:
		//      - save to-tag.
		//    - compare to-tag on subsequent replies.
		//      - (if different): 
		//        - (generate new 200 ACK based on reply).
		//        - (send BYE (check for existing UAC trans)).
		//      - else:
		//        - re-transmit ACK.

		t->state  = TS_TERMINATED_200;
		t->clear_timer(STIMER_A);
		t->clear_timer(STIMER_M);
		t->clear_timer(STIMER_B);

		// Timer B & C share the same slot,
		// so it would pretty useless to clear
		// the same timer slote another time ;-)
		//t->clear_timer(STIMER_C);

		t->reset_timer(STIMER_L, L_TIMER, bucket->get_id());

		if (t->to_tag.len==0) {
			t->to_tag.s = new char[to_tag.len];
			t->to_tag.len = to_tag.len;
			memcpy((void*)t->to_tag.s,to_tag.s,to_tag.len);
		}
		
		goto pass_reply;
		
	    case TS_TERMINATED_200:
		
		if( (to_tag.len != t->to_tag.len) ||
		    (memcmp(to_tag.s,t->to_tag.s,to_tag.len) != 0) ){

		    // TODO: 
		    //   (this should be implemented in the UA)
		    //   we should also send a 200 ACK here,
		    //   but this would mean that we should
		    //   also be sending a BYE to quit
		    //   this dialog...
		    //
		    DBG("Received 200 reply with different To-tag as the previous one.\n");
		    goto end;
		}

		DBG("Received 200 reply retransmission\n");
		t->retransmit();
		goto end;

	    default:
		goto end;
	    }
	}
    }
    else { // non-INVITE transaction

	// Final reply
	switch(t->state){
	    
	case TS_TRYING:
	case TS_CALLING:
	case TS_PROCEEDING:
	    
	    t->state = TS_COMPLETED;
	
	    t->clear_timer(STIMER_E);
	    t->clear_timer(STIMER_M);
	    t->clear_timer(STIMER_F);
	    t->reset_timer(STIMER_K, K_TIMER, bucket->get_id());
	    
	    if(t->msg->u.request->method != sip_request::CANCEL)
		goto pass_reply;
	    else
		goto end;

	case TS_COMPLETED:
	    // Absorb reply retransmission (only if UDP)
	    goto end;
	    
	default:
	    goto end;
	}
    }

 pass_reply:
    return 1;
 end:
    return 0;
}

int _trans_layer::update_uac_request(trans_bucket* bucket, sip_trans*& t, sip_msg* msg)
{
    if(msg->u.request->method != sip_request::ACK){
	t = bucket->add_trans(msg,TT_UAC);

	DBG("update_uac_request(t=%p)\n", t);
    }
    else {
	// 200 ACK
	t = bucket->match_request(msg,TT_UAC);
	if(t == NULL){
	    DBG("While sending 200 ACK: no matching transaction\n");
	    return -1;
	}
	DBG("update_uac_request(200 ACK, t=%p)\n", t);
	// clear old retransmission buffer
	delete [] t->retr_buf;
	
	// transfer the message buffer 
	// to the transaction (incl. ownership)
	t->retr_buf = msg->buf;
	t->retr_len = msg->len;
	msg->buf = NULL;
	msg->len = 0;
	
	// copy destination address
	memcpy(&t->retr_addr,&msg->remote_ip,sizeof(sockaddr_storage));
	t->retr_socket = msg->local_socket;

	// remove the message;
	delete msg;

	return 0;
    }

    switch(msg->u.request->method){

    case sip_request::INVITE:
	// if transport == UDP
	t->reset_timer(STIMER_A,A_TIMER,bucket->get_id());
	// for any transport type
	t->reset_timer(STIMER_B,B_TIMER,bucket->get_id());
	break;
    
    default:
	// if transport == UDP
	t->reset_timer(STIMER_E,E_TIMER,bucket->get_id());
	// for any transport type
	t->reset_timer(STIMER_F,F_TIMER,bucket->get_id());
	break;
    }

    if(!msg->h_dns.eoip()){ // if transport == UDP
	t->reset_timer(STIMER_M,M_TIMER,bucket->get_id());
    }

    return 0;
}

int _trans_layer::update_uas_reply(trans_bucket* bucket, sip_trans* t, int reply_code)
{
    DBG("update_uas_reply(t=%p)\n", t);

    if(t->reply_status >= 200){
	ERROR("Transaction has already been closed with a final reply\n");
	return -1;
    }

    t->reply_status = reply_code;

    if(t->reply_status >= 300){

	// error reply
	t->state = TS_COMPLETED;
	    
	if(t->msg->u.request->method == sip_request::INVITE){
	    t->reset_timer(STIMER_G,G_TIMER,bucket->get_id());
	    t->reset_timer(STIMER_H,H_TIMER,bucket->get_id());
	}
	else {
	    // 64*T1_TIMER if UDP / 0 if !UDP
	    t->reset_timer(STIMER_J,J_TIMER,bucket->get_id()); 
	}
    }
    else if(t->reply_status >= 200) {

	if(t->msg->u.request->method == sip_request::INVITE){

	    // final reply

	    //bucket->remove_trans(t);
	    //return TS_TERMINATED;

	    //
	    // In this stack, the transaction layer
	    // takes care of re-transmiting the 200 reply
	    // in a UAS INVITE transaction. The code above
	    // is commented out and shows the behavior as
	    // required by the RFC.
	    //
	    t->state = TS_TERMINATED_200;
	    t->reset_timer(STIMER_G,G_TIMER,bucket->get_id());
	    t->reset_timer(STIMER_H,H_TIMER,bucket->get_id());

	}
	else {
	    t->state = TS_COMPLETED;
	    // Only for unreliable transports.
	    t->reset_timer(STIMER_J,J_TIMER,bucket->get_id()); 
	}
    }
    else {
	// provisional reply
        if (t->last_rseq) {
            t->state = TS_PROCEEDING_REL;
            // see above notes, for 2xx replies; the same applies for
            // 1xx/PRACK
	    t->reset_timer(STIMER_G,G_TIMER,bucket->get_id());
	    t->reset_timer(STIMER_H,H_TIMER,bucket->get_id());
        } else {
	    t->state = TS_PROCEEDING;
        }
    }
	
    return t->state;
}

int _trans_layer::update_uas_request(trans_bucket* bucket, sip_trans* t, sip_msg* msg)
{
    DBG("update_uas_request(t=%p)\n", t);
    int method = msg->u.request->method;

    if(method != sip_request::ACK &&
       method != sip_request::PRACK) {

	ERROR("Bug? Recvd non PR-/ACK request for existing UAS transact.!?\n");
	return -1;
    }

    switch(t->state){

    case TS_PROCEEDING:
	// ACK or PRACK after non-reliable 1xx???
	return -1;

    case TS_PROCEEDING_REL:
	if(method == sip_request::PRACK) {
	    // stop retransmissions
	    t->clear_timer(STIMER_G);
	    t->clear_timer(STIMER_H);
	}
        return t->state;
	    
    case TS_COMPLETED: // non-2xx-ACK
	if(method != sip_request::ACK) return -1;
	t->state = TS_CONFIRMED;

	t->clear_timer(STIMER_G);
	t->clear_timer(STIMER_H);

	t->reset_timer(STIMER_I,I_TIMER,bucket->get_id());
	
	// drop through
    case TS_CONFIRMED:
	return t->state;
	    
    case TS_TERMINATED_200: // 2xx-ACK
	if(method != sip_request::ACK) return -1;
	// remove transaction
	bucket->remove(t);
	return TS_REMOVED;
	    
    default:
	DBG("Bug? Unknown state at this point: %i\n",t->state);
	break;
    }

    return -1;
}

void _trans_layer::send_non_200_ack(sip_msg* reply, sip_trans* t)
{
    sip_msg* inv = t->msg;
    
    cstring method("ACK",3);
    int ack_len = request_line_len(method,inv->u.request->ruri_str);
    
    ack_len += copy_hdr_len(inv->via1)
	+ copy_hdr_len(inv->from)
	+ copy_hdr_len(reply->to)
	+ copy_hdr_len(inv->callid);
    
    ack_len += cseq_len(get_cseq(inv)->num_str,method);
    ack_len += 2/* EoH CRLF */;

    if(!inv->route.empty())
 	ack_len += copy_hdrs_len(inv->route);
    
    char* ack_buf = new char [ack_len];
    char* c = ack_buf;

    request_line_wr(&c,method,inv->u.request->ruri_str);
    
    copy_hdr_wr(&c,inv->via1);

    if(!inv->route.empty())
	 copy_hdrs_wr(&c,inv->route);

    copy_hdr_wr(&c,inv->from);
    copy_hdr_wr(&c,reply->to);
    copy_hdr_wr(&c,inv->callid);
    
    cseq_wr(&c,get_cseq(inv)->num_str,method);
    
    *c++ = CR;
    *c++ = LF;

    DBG("About to send ACK\n");

    assert(inv->local_socket);
    int send_err = inv->local_socket->send(&inv->remote_ip,ack_buf,ack_len);
    if(send_err < 0){
	ERROR("Error from transport layer\n");
    }
    delete[] ack_buf;

}

void _trans_layer::timer_expired(timer* t, trans_bucket* bucket, sip_trans* tr)
{
    int n = t->type >> 16;
    int type = t->type & 0xFFFF;

    switch(type){

    case STIMER_A:  // Calling: (re-)send INV

	n++;
	tr->msg->send();
	tr->reset_timer((n<<16) | type, A_TIMER<<n, bucket->get_id());
	break;
	
    case STIMER_B:  // Calling: -> Terminated

	tr->clear_timer(STIMER_B);
	if(tr->state == TS_CALLING) {
	    DBG("Transaction timeout!\n");
	    // unlocks the bucket
	    timeout(bucket,tr);
	    return;
	}
	else {
	    DBG("Transaction timeout timer hit while state=0x%x",tr->state);
	}
	break;

    case STIMER_C: // Proceeding -> Terminated
	
	// Note: remember well, we first set timer C
	//       after the first provisional reply.
	tr->clear_timer(STIMER_C);
	if(tr->state != TS_PROCEEDING)
	    break; // shouldn't happen

	bucket->unlock();

	{
	    // send CANCEL
	    trans_ticket tt(tr,bucket);
	    cancel(&tt);
	    
	    // Now remove the transaction
	    bucket->lock();
	    if(bucket->exist(tr)) {
		// unlocks the bucket
		timeout(bucket,tr);
		return;
	    }
	}
	break;

    case STIMER_F:  // Trying/Proceeding: terminate transaction
	
	tr->clear_timer(STIMER_F);

	switch(tr->state) {

	case TS_TRYING:
	case TS_PROCEEDING:
	    DBG("Transaction timeout!\n");
	    // unlocks the bucket
	    timeout(bucket,tr);
	    return;
	}
	break;

    case STIMER_H:  // PENDING_REL, Completed: -> Terminated
        if(tr->type == TT_UAS) { // TODO: can timer _H fire for TT_UAC??
          bool handled;

          switch(tr->state) {
          case TS_PROCEEDING_REL: // missing PRACK for rel-1xx
            assert(tr->retr_len);
            tr->clear_timer(type); // stop retransmissions
            //signal timeout to UA
            ua->handle_reply_timeout(AmSipTimeoutEvent::noPRACK, tr, bucket);
                //tr->msg, tr->retr_buf, tr->retr_len); //signal timeout to UA
            handled = true; // let the UA [3..6]xx, if it wishes so
            break;

          case TS_TERMINATED_200: // missing ACK for (locally replied) 200
          case TS_COMPLETED: // missing ACK for (locally replied) [3..6]xx
            tr->clear_timer(type);
            ////ua->timer_expired(tr,STIMER_H);
            ua->handle_reply_timeout(AmSipTimeoutEvent::noACK, tr);
            bucket->remove(tr);
            handled = true;
            break;

          default: 
            handled = false;
          }

          if (handled)
            break;
        }

    case STIMER_D:  // Completed: -> Terminated
    case STIMER_K:  // Completed: terminate transaction
    case STIMER_J:  // Completed: -> Terminated
    case STIMER_I:  // Confirmed: -> Terminated
    case STIMER_L:  // Terminated_200 -> Terminated
	
	// TODO:
	//  - check if the UA has sent the ACK.
	//    else, send ACK & BYE.

	tr->clear_timer(type);
	bucket->remove(tr);
	break;

    case STIMER_E:  // Trying/Proceeding: (re-)send request
    case STIMER_G:  { // Completed: (re-)send response

	n++; // re-transmission counter

	//
	// in this stack, the transaction layer
	// takes care of re-transmiting the 200 reply
	// in a UAS INVITE transaction.
	//
	if(tr->type == TT_UAS){
	    
	    // re-transmit reply to INV
	    tr->retransmit();
	}
	else {

	    // re-transmit request
	    tr->msg->send();
	}

	unsigned int retr_timer = (type == STIMER_E) ?
	    E_TIMER << n : G_TIMER << n;

	if(retr_timer<<n > T2_TIMER) {
	    tr->reset_timer((n<<16) | type, T2_TIMER, bucket->get_id());
	}
	else {
	    tr->reset_timer((n<<16) | type, retr_timer, bucket->get_id());
	}
    } break;

    case STIMER_M:
	{
	    sockaddr_storage sa;
	    memset(&sa,0,sizeof(sockaddr_storage));

	    // get the next ip
	    if(tr->msg->h_dns.next_ip(&sa) < 0){
		tr->clear_timer(STIMER_M);
		break;
	    }

	    //If a SRV record is involved, the port number
	    // should have been set by h_dns.next_ip(...).
	    if(!((sockaddr_in*)&sa)->sin_port){
		//Else, we copy the old port number
		((sockaddr_in*)&sa)->sin_port = ((sockaddr_in*)&tr->msg->remote_ip)->sin_port;
	    }

	    // copy the new address back
	    memcpy(&tr->msg->remote_ip,&sa,sizeof(sockaddr_storage));

	    // create new branch tag
	    compute_branch((char*)(tr->msg->via_p1->branch.s+MAGIC_BRANCH_LEN),
			   tr->msg->callid->value,tr->msg->cseq->value);

	    // and re-send
	    tr->msg->send();

	    // reset counter for timer A & E
	    timer* A_E_timer = tr->get_timer(STIMER_A);
	    tr->reset_timer(A_E_timer->type & 0xFFFF,A_TIMER,bucket->get_id());

	    if(!tr->msg->h_dns.eoip())
		tr->reset_timer(STIMER_M,M_TIMER,bucket->get_id());
	    else
		tr->clear_timer(STIMER_M);
	}
	break;

    default:
	ERROR("Invalid timer type %i\n",t->type);
	break;
    }

    bucket->unlock();
}

/**
 * Tries to find a registered transport socket
 * suitable for sending to the destination supplied.
 */
trsp_socket* _trans_layer::find_transport(sockaddr_storage* remote_ip)
{
    if(transports.size() == 0)
	return NULL;

    if(transports.size() == 1)
	return transports[0];
    
  int temp_sock = socket(remote_ip->ss_family, SOCK_DGRAM, 0 );
  if (temp_sock == -1) {
    ERROR( "ERROR: socket() failed: %s\n",
	strerror(errno));
    return NULL;
  }

  sockaddr_storage from;

  socklen_t    len=sizeof(from);
  trsp_socket* tsock=NULL;

  if (connect(temp_sock, (sockaddr*)remote_ip, 
	      remote_ip->ss_family == AF_INET ? 
	      sizeof(sockaddr_in) : sizeof(sockaddr_in6))==-1) {

      ERROR("connect failed: %s\n",
	    strerror(errno));
      goto error;
  }

  if (getsockname(temp_sock, (sockaddr*)&from, &len)==-1) {
      ERROR("getsockname failed: %s\n",
	    strerror(errno));
      goto error;
  }
  close(temp_sock);

  // TODO:
  //  - if no exact IP match, try with matching the interface

  for(vector<trsp_socket*>::iterator it = transports.begin();
      it != transports.end(); ++it) {

      if((*it)->match_addr(&from)){
	  tsock = *it;
	  break;
      }
  }

  return tsock;

 error:
  close(temp_sock);
  return NULL;
}

/** EMACS **
 * Local variables:
 * mode: c++
 * c-basic-offset: 4
 * End:
 */