Пример #1
0
void
nict_rcv_23456xx (osip_transaction_t * nict, osip_event_t * evt)
{
  /* leave this answer to the core application */

  if (nict->last_response != NULL)
    {
      osip_message_free (nict->last_response);
    }
  nict->last_response = evt->sip;

  if (EVT_IS_RCV_STATUS_2XX (evt))
    __osip_message_callback (OSIP_NICT_STATUS_2XX_RECEIVED, nict,
                             nict->last_response);
  else if (MSG_IS_STATUS_3XX (nict->last_response))
    __osip_message_callback (OSIP_NICT_STATUS_3XX_RECEIVED, nict,
                             nict->last_response);
  else if (MSG_IS_STATUS_4XX (nict->last_response))
    __osip_message_callback (OSIP_NICT_STATUS_4XX_RECEIVED, nict,
                             nict->last_response);
  else if (MSG_IS_STATUS_5XX (nict->last_response))
    __osip_message_callback (OSIP_NICT_STATUS_5XX_RECEIVED, nict,
                             nict->last_response);
  else
    __osip_message_callback (OSIP_NICT_STATUS_6XX_RECEIVED, nict,
                             nict->last_response);

  if (nict->state != NICT_COMPLETED)    /* reset timer K */
    {
      osip_gettimeofday (&nict->nict_context->timer_k_start, NULL);
      add_gettimeofday (&nict->nict_context->timer_k_start,
                        nict->nict_context->timer_k_length);
    }
  __osip_transaction_set_state (nict, NICT_COMPLETED);
}
void
ist_snd_3456xx (osip_transaction_t * ist, osip_event_t * evt)
{
  int i;

  if (ist->last_response != NULL) {
    osip_message_free (ist->last_response);
  }
  ist->last_response = evt->sip;

  i = __osip_transaction_snd_xxx (ist, evt->sip);
  if (i != 0) {
    ist_handle_transport_error (ist, i);
    return;
  }
  else {
    if (MSG_IS_STATUS_3XX (ist->last_response))
      __osip_message_callback (OSIP_IST_STATUS_3XX_SENT, ist, ist->last_response);
    else if (MSG_IS_STATUS_4XX (ist->last_response))
      __osip_message_callback (OSIP_IST_STATUS_4XX_SENT, ist, ist->last_response);
    else if (MSG_IS_STATUS_5XX (ist->last_response))
      __osip_message_callback (OSIP_IST_STATUS_5XX_SENT, ist, ist->last_response);
    else
      __osip_message_callback (OSIP_IST_STATUS_6XX_SENT, ist, ist->last_response);
  }

  if (ist->ist_context->timer_g_length != -1) {
    osip_gettimeofday (&ist->ist_context->timer_g_start, NULL);
    add_gettimeofday (&ist->ist_context->timer_g_start, ist->ist_context->timer_g_length);
  }
  osip_gettimeofday (&ist->ist_context->timer_h_start, NULL);
  add_gettimeofday (&ist->ist_context->timer_h_start, ist->ist_context->timer_h_length);
  __osip_transaction_set_state (ist, IST_COMPLETED);
  return;
}
Пример #3
0
void
nist_snd_23456xx (osip_transaction_t * nist, osip_event_t * evt)
{
  int i;

  if (nist->last_response != NULL)
    {
      osip_message_free (nist->last_response);
    }
  nist->last_response = evt->sip;

  i = __osip_transaction_snd_xxx(nist, nist->last_response);
  if (i != 0)
    {
      nist_handle_transport_error (nist, i);
      return;
  } else
    {
      if (EVT_IS_SND_STATUS_2XX (evt))
        __osip_message_callback (OSIP_NIST_STATUS_2XX_SENT, nist,
                                 nist->last_response);
      else if (MSG_IS_STATUS_3XX (nist->last_response))
        __osip_message_callback (OSIP_NIST_STATUS_3XX_SENT, nist,
                                 nist->last_response);
      else if (MSG_IS_STATUS_4XX (nist->last_response))
        __osip_message_callback (OSIP_NIST_STATUS_4XX_SENT, nist,
                                 nist->last_response);
      else if (MSG_IS_STATUS_5XX (nist->last_response))
        __osip_message_callback (OSIP_NIST_STATUS_5XX_SENT, nist,
                                 nist->last_response);
      else
        __osip_message_callback (OSIP_NIST_STATUS_6XX_SENT, nist,
                                 nist->last_response);
    }

  if (nist->state != NIST_COMPLETED)    /* start J timer */
    {
      osip_gettimeofday (&nist->nist_context->timer_j_start, NULL);
      add_gettimeofday (&nist->nist_context->timer_j_start,
                        nist->nist_context->timer_j_length);
    }

  __osip_transaction_set_state (nist, NIST_COMPLETED);
}
Пример #4
0
/*
 * PROXY_RESPONSE
 *
 * RETURNS
 *	STS_SUCCESS on success
 *	STS_FAILURE on error
 * RFC3261
 *    Section 16.7: Proxy Behavior - Response Processing
 *    1.  Find the appropriate response context
 *    2.  Update timer C for provisional responses
 *    3.  Remove the topmost Via
 *    4.  Add the response to the response context
 *    5.  Check to see if this response should be forwarded immediately
 *    6.  When necessary, choose the best final response from the
 *        response context
 *    7.  Aggregate authorization header field values if necessary
 *    8.  Optionally rewrite Record-Route header field values
 *    9.  Forward the response
 *    10. Generate any necessary CANCEL requests 
 *
 */
int proxy_response (sip_ticket_t *ticket) {
   int i;
   int sts;
   int type;
   struct in_addr sendto_addr;
   osip_via_t *via;
   int port;
   char *buffer;
   int buflen;
   osip_message_t *response;
   struct sockaddr_in *from;

   DEBUGC(DBCLASS_PROXY,"proxy_response");

   if (ticket==NULL) {
      ERROR("proxy_response: called with NULL ticket");
      return STS_FAILURE;
   }

   response=ticket->sipmsg;
   from=&ticket->from;

   /*
    * RFC 3261, Section 16.7 step 3
    * Proxy Behavior - Response Processing - Remove my Via header field value
    */
   /* remove my Via header line */
   sts = sip_del_myvia(ticket);
   if (sts == STS_FAILURE) {
      DEBUGC(DBCLASS_PROXY,"not addressed to my VIA, ignoring response");
      return STS_FAILURE;
   }

   /*
    * figure out if this is an request coming from the outside
    * world to one of our registered clients
    */

   /* Ahhrghh...... a response seems to have NO contact information... 
    * so let's take FROM instead...
    * the TO and FROM headers are EQUAL to the request - that means 
    * they are swapped in their meaning for a response...
    */

#if _OLD_DIRECTION_EVALUATION
   type = 0;
   for (i=0; i<URLMAP_SIZE; i++) {
      if (urlmap[i].active == 0) continue;

      /* incoming response ('from' == 'masq') || ('from' == 'reg') */
      if ((compare_url(response->from->url, urlmap[i].reg_url)==STS_SUCCESS) ||
          (compare_url(response->from->url, urlmap[i].masq_url)==STS_SUCCESS)) {
         type=RESTYP_INCOMING;
         DEBUGC(DBCLASS_PROXY,"incoming response for %s@%s from outbound",
	   response->from->url->username? response->from->url->username:"******",
	   response->from->url->host? response->from->url->host : "*NULL*");
	 break;
      }

      /* outgoing response ('to' == 'reg') || ('to' == 'masq' ) */
      if ((compare_url(response->to->url, urlmap[i].masq_url)==STS_SUCCESS) ||
          (compare_url(response->to->url, urlmap[i].reg_url)==STS_SUCCESS)){
         type=RESTYP_OUTGOING;
         DEBUGC(DBCLASS_PROXY,"outgoing response for %s@%s from inbound",
	        response->from->url->username ?
                   response->from->url->username : "******",
	        response->from->url->host ? 
                   response->from->url->host : "*NULL*");
	 break;
      }
   }
#else
   type = 0;
   /*
    * did I receive the telegram from a REGISTERED host?
    * -> it must be an OUTGOING response
    */
   for (i=0; i<URLMAP_SIZE; i++) {
      struct in_addr tmp_addr;
      if (urlmap[i].active == 0) continue;

      if (get_ip_by_host(urlmap[i].true_url->host, &tmp_addr) == STS_FAILURE) {
         DEBUGC(DBCLASS_PROXY, "proxy_response: cannot resolve host [%s]",
             urlmap[i].true_url);
      } else {
         DEBUGC(DBCLASS_PROXY, "proxy_response: reghost:%s ip:%s",
                urlmap[i].true_url->host, utils_inet_ntoa(from->sin_addr));
         if (memcmp(&tmp_addr, &from->sin_addr, sizeof(tmp_addr)) == 0) {
            type=RESTYP_OUTGOING;
	    break;
         }
      }
   }
   /*
    * is the telegram directed to an internal registered host?
    * -> it must be an INCOMING response
    */
   if (type == 0) {
      for (i=0; i<URLMAP_SIZE; i++) {
         if (urlmap[i].active == 0) continue;
         /* incoming response ('from' == 'masq') || ('from' == 'reg') */
         if ((compare_url(response->from->url, urlmap[i].reg_url)==STS_SUCCESS) ||
             (compare_url(response->from->url, urlmap[i].masq_url)==STS_SUCCESS)) {
            type=RESTYP_INCOMING;
	    break;
         }
      }
   }
/* &&&& Open Issue &&&&
   it has been seen with cross-provider calls that the FROM may be 'garbled'
   (e.g [email protected] for calls made sipphone -> FWD)
   How can we deal with this? Should I take into consideration the 'Via'
   headers? This is the only clue I have, pointing to the *real* UA.
   Maybe I should put in a 'siproxd' ftag value to recognize it as a header
   inserted by myself
*/
   if ((type == 0) && (!osip_list_eol(response->vias, 0))) {
      osip_via_t *via;
      struct in_addr addr_via, addr_myself;
      int port_via, port_ua;

      /* get the via address */
      via = (osip_via_t *) osip_list_get (response->vias, 0);
      DEBUGC(DBCLASS_PROXY, "proxy_response: check via [%s] for "
             "registered UA",via->host);
      sts=get_ip_by_host(via->host, &addr_via);
      if (sts == STS_FAILURE) {
         DEBUGC(DBCLASS_DNS, "proxy_response: cannot resolve VIA [%s]",
                via->host);
      } else {

         for (i=0; i<URLMAP_SIZE; i++) {
            if (urlmap[i].active == 0) continue;
            /* incoming response (1st via in list points to a registered UA) */
            sts=get_ip_by_host(urlmap[i].true_url->host, &addr_myself);
            if (sts == STS_FAILURE) {
               DEBUGC(DBCLASS_DNS, "proxy_response: cannot resolve "
                      "true_url [%s]", via->host);
               continue;
            }

            port_via=0;
            if (via->port) port_via=atoi(via->port);
            if (port_via <= 0) port_via=SIP_PORT;

            port_ua=0;
            if (urlmap[i].true_url->port)
               port_ua=atoi(urlmap[i].true_url->port);
            if (port_ua <= 0) port_ua=SIP_PORT;

            DEBUGC(DBCLASS_BABBLE, "proxy_response: checking for registered "
                   "host [%s:%i] <-> [%s:%i]",
                   urlmap[i].true_url->host, port_ua,
                   via->host, port_via);

            if ((memcmp(&addr_myself, &addr_via, sizeof(addr_myself))==0) &&
                (port_via == port_ua)) {
               type=RESTYP_INCOMING;
	       break;
            }
         }
      }
   }
    
#endif
   ticket->direction=type;

/*
 * ok, we got a response that we are allowed to process.
 */
   switch (type) {
  /*
   * from an external host to the internal masqueraded host
   */
   case RESTYP_INCOMING:
      DEBUGC(DBCLASS_PROXY,"incoming response for %s@%s from outbound",
	response->from->url->username? response->from->url->username:"******",
	response->from->url->host? response->from->url->host : "*NULL*");

      /*
       * Response for INVITE - deal with RTP data in body and
       *                       start RTP proxy stream(s). In case
       *                       of a negative answer, stop RTP stream
       */
      if (MSG_IS_RESPONSE_FOR(response,"INVITE")) {
         /* positive response, start RTP stream */
         if ((MSG_IS_STATUS_1XX(response)) || 
              (MSG_IS_STATUS_2XX(response))) {
            if (configuration.rtp_proxy_enable == 1) {
               sts = proxy_rewrite_invitation_body(response, DIR_INCOMING);
            }
         /* negative - stop a possibly started RTP stream */
         } else if ((MSG_IS_STATUS_4XX(response))  ||
                     (MSG_IS_STATUS_5XX(response)) ||
                     (MSG_IS_STATUS_6XX(response))) {
            rtp_stop_fwd(osip_message_get_call_id(response), DIR_INCOMING);
            rtp_stop_fwd(osip_message_get_call_id(response), DIR_OUTGOING);
         }
      } /* if INVITE */

      /*
       * Response for REGISTER - special handling of Contact header
       */
      if (MSG_IS_RESPONSE_FOR(response,"REGISTER")) {
         /*
          * REGISTER returns *my* Contact header information.
          * Rewrite Contact header back to represent the true address.
          * Other responses do return the Contact header of the sender.
          */
         sip_rewrite_contact(ticket, DIR_INCOMING);
      }

      /* 
       * Response for SUBSCRIBE
       *
       * HACK for Grandstream SIP phones (with newer firmware like 1.0.4.40):
       *   They send a SUBSCRIBE request to the registration server. In
       *   case of beeing registering directly to siproxd, this request of
       *   course will eventually be forwarded back to the same UA.
       *   Grandstream then does reply with an '202' response (A 202
       *   response merely indicates that the subscription has been
       *   understood, and that authorization may or may not have been
       *   granted), which then of course is forwarded back to the phone.
       *   Ans it seems that the Grandstream can *not* *handle* this
       *   response, as it immediately sends another SUBSCRIBE request.
       *   And this games goes on and on and on...
       *
       *   As a workaround we will transform any 202 response to a
       *   '404 unknown destination'
       *   
       */
{
      osip_header_t *ua_hdr=NULL;
      osip_message_get_user_agent(response, 0, &ua_hdr);
      if (ua_hdr && ua_hdr->hvalue &&
          (osip_strncasecmp(ua_hdr->hvalue,"grandstream", 11)==0) &&
          (MSG_IS_RESPONSE_FOR(response,"SUBSCRIBE")) &&
          (MSG_TEST_CODE(response, 202))) {
         DEBUGC(DBCLASS_PROXY, "proxy_response: Grandstream hack 202->404");
         response->status_code=404;
      }
}
      break;
   
  /*
   * from the internal masqueraded host to an external host
   */
   case RESTYP_OUTGOING:
      DEBUGC(DBCLASS_PROXY,"outgoing response for %s@%s from inbound",
	     response->from->url->username ?
                response->from->url->username : "******",
	     response->from->url->host ? 
                response->from->url->host : "*NULL*");

      /* rewrite Contact header to represent the masqued address */
      sip_rewrite_contact(ticket, DIR_OUTGOING);

      /*
       * If an 2xx OK or 1xx response, answer to an INVITE request,
       * rewrite body
       *
       * In case of a negative answer, stop RTP stream
       */
      if (MSG_IS_RESPONSE_FOR(response,"INVITE")) {
         /* positive response, start RTP stream */
         if ((MSG_IS_STATUS_1XX(response)) || 
              (MSG_IS_STATUS_2XX(response))) {
            /* This is an outgoing response, therefore an outgoing stream */
            sts = proxy_rewrite_invitation_body(response, DIR_OUTGOING);
         /* megative - stop a possibly started RTP stream */
         } else if ((MSG_IS_STATUS_4XX(response))  ||
                     (MSG_IS_STATUS_5XX(response)) ||
                     (MSG_IS_STATUS_6XX(response))) {
            rtp_stop_fwd(osip_message_get_call_id(response), DIR_INCOMING);
            rtp_stop_fwd(osip_message_get_call_id(response), DIR_OUTGOING);
         }
      } /* if INVITE */

      break;
   
   default:
      DEBUGC(DBCLASS_PROXY, "response from/to unregistered UA (%s@%s)",
	   response->from->url->username? response->from->url->username:"******",
	   response->from->url->host? response->from->url->host : "*NULL*");
      return STS_FAILURE;
   }

   /*
    * for ALL incoming response include my Record-Route header.
    * The local UA will probably send its answer to the topmost 
    * Route Header (8.1.2 of RFC3261)
    */
    if (type == RESTYP_INCOMING) {
       DEBUGC(DBCLASS_PROXY,"Adding my Record-Route");
       route_add_recordroute(ticket);
    } else {
       /*
        * outgoing packets must not have my record route header, as
        * this likely will contain a private IP address (my inbound).
        */
       DEBUGC(DBCLASS_PROXY,"Purging Record-Routes (outgoing packet)");
       route_purge_recordroute(ticket);
    }

   /*
    * Determine Next-Hop Address
    */
/*&&&& priority probably should be:
 * 1) Route header
 * 2) fixed outbound proxy
 * 3) SIP URI
 */
   /*
    * check if we need to send to an outbound proxy
    */
   if ((type == RESTYP_OUTGOING) &&
       (sip_find_outbound_proxy(ticket, &sendto_addr, &port) == STS_SUCCESS)) {
      DEBUGC(DBCLASS_PROXY, "proxy_response: have outbound proxy %s:%i",
             utils_inet_ntoa(sendto_addr), port);
   /*
    * Route present?
    * If so, fetch address from topmost Route: header and remove it.
    */
   } else if ((type == RESTYP_OUTGOING) && 
              (response->routes && !osip_list_eol(response->routes, 0))) {
      sts=route_determine_nexthop(ticket, &sendto_addr, &port);
      if (sts == STS_FAILURE) {
         DEBUGC(DBCLASS_PROXY, "proxy_response: route_determine_nexthop failed");
         return STS_FAILURE;
      }
      DEBUGC(DBCLASS_PROXY, "proxy_response: have Route header to %s:%i",
             utils_inet_ntoa(sendto_addr), port);
   } else {
      /* get target address and port from VIA header */
      via = (osip_via_t *) osip_list_get (response->vias, 0);
      if (via == NULL) {
         ERROR("proxy_response: list_get via failed");
         return STS_FAILURE;
      }

      sts = get_ip_by_host(via->host, &sendto_addr);
      if (sts == STS_FAILURE) {
         DEBUGC(DBCLASS_PROXY, "proxy_response: cannot resolve VIA [%s]",
                via->host);
         return STS_FAILURE;
      }

      if (via->port) {
         port=atoi(via->port);
      } else {
         port=SIP_PORT;
      }
   }

   sts = sip_message_to_str(response, &buffer, &buflen);
   if (sts != 0) {
      ERROR("proxy_response: sip_message_to_str failed");
      return STS_FAILURE;
   }

   sipsock_send(sendto_addr, port, ticket->protocol,
                buffer, buflen); 
   osip_free (buffer);
   return STS_SUCCESS;
}
Пример #5
0
void
nist_snd_23456xx (osip_transaction_t * nist, osip_event_t * evt)
{
  int i;
  osip_via_t *via;
  osip_t *osip = (osip_t *) nist->config;

  if (nist->last_response != NULL)
    {
      osip_message_free (nist->last_response);
    }
  nist->last_response = evt->sip;

  via = (osip_via_t *) osip_list_get (nist->last_response->vias, 0);
  if (via)
    {
      char *host;
      int port;
      osip_generic_param_t *maddr;
      osip_generic_param_t *received;
      osip_generic_param_t *rport;
      osip_via_param_get_byname (via, "maddr", &maddr);
      osip_via_param_get_byname (via, "received", &received);
      osip_via_param_get_byname (via, "rport", &rport);
      /* 1: user should not use the provided information
         (host and port) if they are using a reliable
         transport. Instead, they should use the already
         open socket attached to this transaction. */
      /* 2: check maddr and multicast usage */
      if (maddr != NULL)
	host = maddr->gvalue;
      /* we should check if this is a multicast address and use
         set the "ttl" in this case. (this must be done in the
         UDP message (not at the SIP layer) */
      else if (received != NULL)
	host = received->gvalue;
      else
	host = via->host;

      if (rport == NULL || rport->gvalue == NULL)
	{
	  if (via->port != NULL)
	    port = osip_atoi (via->port);
	  else
	    port = 5060;
	}
      else
	port = osip_atoi (rport->gvalue);
      i = osip->cb_send_message (nist, nist->last_response, host,
				 port, nist->out_socket);
    }
  else
    i = -1;
  if (i != 0)
    {
      nist_handle_transport_error (nist, i);
      return;
    }
  else
    {
      if (EVT_IS_SND_STATUS_2XX (evt))
	__osip_message_callback (OSIP_NIST_STATUS_2XX_SENT, nist, nist->last_response);
      else if (MSG_IS_STATUS_3XX (nist->last_response))
	__osip_message_callback (OSIP_NIST_STATUS_3XX_SENT, nist, nist->last_response);
      else if (MSG_IS_STATUS_4XX (nist->last_response))
	__osip_message_callback (OSIP_NIST_STATUS_4XX_SENT, nist, nist->last_response);
      else if (MSG_IS_STATUS_5XX (nist->last_response))
	__osip_message_callback (OSIP_NIST_STATUS_5XX_SENT, nist, nist->last_response);
      else
	__osip_message_callback (OSIP_NIST_STATUS_6XX_SENT, nist, nist->last_response);
    }

  if (nist->state != NIST_COMPLETED)	/* start J timer */
    nist->nist_context->timer_j_start = time (NULL);

  __osip_transaction_set_state (nist, NIST_COMPLETED);
}
Пример #6
0
int GB_handle_messages (GB_CONNECT_STATE *gb_cons)
{
	osip_event_t * osip_event = NULL;
	int ret = -1;
	int isDownLoad = 0;
	
	ret = gb_sip_messages_parse(&osip_event,gb_cons->buffer,gb_cons->datasize);

	if(ret == 0)
	{
		switch(osip_event->type)
		{
			// 请求消息
			case RCV_REQINVITE:
			{
				GB_handle_RCV_REQINVITE(gb_cons, osip_event, &isDownLoad);
			}
			break;
			case RCV_REQACK:
			{
				GB_handle_RCV_REQACK(gb_cons, osip_event);
			}
			break;
			case RCV_REQUEST:
			{
				GB_handle_RCV_REQUEST(gb_cons, osip_event);
			}
			break;

			// 响应消息
			case RCV_STATUS_1XX:
			{
				GB_handle_RCV_STATUS_1XX(gb_cons, osip_event);
			}
			break;
			case RCV_STATUS_2XX:
			{
				GB_handle_RCV_STATUS_2XX(gb_cons, osip_event);
			}
			break;
			case RCV_STATUS_3456XX:
			{
				GB_Record_Node *record = NULL;
				int index = -1;
				
				record = GB_Find_Record_Node_by_Call_ID(gb_cons,osip_event->sip->call_id ,&index);
				if(record)
				{
					if(record->info)
						SN_FREE(record->info);
					GB_Remove_Record_Node(gb_cons, index);
				}
	
				if(MSG_IS_STATUS_3XX(osip_event->sip))
				{
					GB_handle_RCV_STATUS_3XX(gb_cons, osip_event);
				}
				else if(MSG_IS_STATUS_4XX(osip_event->sip))
				{
					GB_handle_RCV_STATUS_4XX(gb_cons, osip_event);
				}
				else if(MSG_IS_STATUS_5XX(osip_event->sip))
				{
					GB_handle_RCV_STATUS_5XX(gb_cons, osip_event);
				}
				else if(MSG_IS_STATUS_6XX(osip_event->sip))
				{
					GB_handle_RCV_STATUS_6XX(gb_cons, osip_event);
				}
				else
				{
					
				}
			}
			break;
			default:
			{
				TRACE(SCI_TRACE_NORMAL,MOD_GB,"\n%s\n",gb_cons->buffer);
			}
			break;
			
		}
	}


	if(osip_event != NULL && isDownLoad == 0)  // 若是下载,则先不释放osip_event
	{
		gb_sip_free(osip_event);
		osip_event = NULL;
	}
	
	return 0;
}
Пример #7
0
void ict_rcv_3456xx(osip_transaction_t * ict, osip_event_t * evt)
{
	osip_route_t *route;
	int i;
	osip_t *osip = (osip_t *) ict->config;

	/* leave this answer to the core application */

	if (ict->last_response != NULL)
		osip_message_free(ict->last_response);

	ict->last_response = evt->sip;
	if (ict->state != ICT_COMPLETED) {	/* not a retransmission */
		/* automatic handling of ack! */
		osip_message_t *ack = ict_create_ack(ict, evt->sip);

		ict->ack = ack;

		if (ict->ack == NULL) {
			__osip_transaction_set_state(ict, ICT_TERMINATED);
			__osip_kill_transaction_callback(OSIP_ICT_KILL_TRANSACTION, ict);
			return;
		}

		/* reset ict->ict_context->destination only if
		   it is not yet set. */
		if (ict->ict_context->destination == NULL) {
			osip_message_get_route(ack, 0, &route);
			if (route != NULL && route->url != NULL) {
				osip_uri_param_t *lr_param;

				osip_uri_uparam_get_byname(route->url, "lr", &lr_param);
				if (lr_param == NULL) {
					/* using uncompliant proxy: destination is the request-uri */
					route = NULL;
				}
			}

			if (route != NULL && route->url != NULL) {
				int port = 5060;

				if (route->url->port != NULL)
					port = osip_atoi(route->url->port);
				osip_ict_set_destination(ict->ict_context,
										 osip_strdup(route->url->host), port);
			} else {
				int port = 5060;
				/* search for maddr parameter */
				osip_uri_param_t *maddr_param = NULL;

				port = 5060;
				if (ack->req_uri->port != NULL)
					port = osip_atoi(ack->req_uri->port);

				osip_uri_uparam_get_byname(ack->req_uri, "maddr", &maddr_param);
				if (maddr_param != NULL && maddr_param->gvalue != NULL)
					osip_ict_set_destination(ict->ict_context,
											 osip_strdup(maddr_param->gvalue),
											 port);
				else
					osip_ict_set_destination(ict->ict_context,
											 osip_strdup(ack->req_uri->host),
											 port);
			}
		}
		i = osip->cb_send_message(ict, ack, ict->ict_context->destination,
								  ict->ict_context->port, ict->out_socket);
		if (i != 0) {
			ict_handle_transport_error(ict, i);
			return;
		}
		if (MSG_IS_STATUS_3XX(evt->sip))
			__osip_message_callback(OSIP_ICT_STATUS_3XX_RECEIVED, ict, evt->sip);
		else if (MSG_IS_STATUS_4XX(evt->sip))
			__osip_message_callback(OSIP_ICT_STATUS_4XX_RECEIVED, ict, evt->sip);
		else if (MSG_IS_STATUS_5XX(evt->sip))
			__osip_message_callback(OSIP_ICT_STATUS_5XX_RECEIVED, ict, evt->sip);
		else
			__osip_message_callback(OSIP_ICT_STATUS_6XX_RECEIVED, ict, evt->sip);

		__osip_message_callback(OSIP_ICT_ACK_SENT, ict, evt->sip);
	}

	/* start timer D (length is set to MAX (64*DEFAULT_T1 or 32000) */
	osip_gettimeofday(&ict->ict_context->timer_d_start, NULL);
	add_gettimeofday(&ict->ict_context->timer_d_start,
					 ict->ict_context->timer_d_length);
	__osip_transaction_set_state(ict, ICT_COMPLETED);
}