Ejemplo n.º 1
0
/* forwards a request to dst
 * parameters:
 *   msg       - sip msg
 *   dst       - destination name, if non-null it will be resolved and
 *               send_info updated with the ip/port. Even if dst is non
 *               null send_info must contain the protocol and if a non
 *               default port or non srv. lookup is desired, the port must
 *               be !=0 
 *   port      - used only if dst!=0 (else the port in send_info->to is used)
 *   send_info - value/result partially filled dest_info structure:
 *                 - send_info->proto and comp are used
 *                 - send_info->to will be filled (dns)
 *                 - send_info->send_flags is filled from the message
 *                 - if the send_socket member is null, a send_socket will be 
 *                   chosen automatically
 * WARNING: don't forget to zero-fill all the  unused members (a non-zero 
 * random id along with proto==PROTO_TCP can have bad consequences, same for
 *   a bogus send_socket value)
 */
int forward_request(struct sip_msg* msg, str* dst, unsigned short port,
							struct dest_info* send_info)
{
	unsigned int len;
	char* buf;
	char md5[MD5_LEN];
	struct socket_info* orig_send_sock; /* initial send_sock */
	int ret;
	struct ip_addr ip; /* debugging only */
	char proto;
#ifdef USE_DNS_FAILOVER
	struct socket_info* prev_send_sock;
	int err;
	struct dns_srv_handle dns_srv_h;
	
	prev_send_sock=0;
	err=0;
#endif
	
	
	buf=0;
	orig_send_sock=send_info->send_sock;
	proto=send_info->proto;
	ret=0;

	if(dst){
#ifdef USE_DNS_FAILOVER
		if (cfg_get(core, core_cfg, use_dns_failover)){
			dns_srv_handle_init(&dns_srv_h);
			err=dns_sip_resolve2su(&dns_srv_h, &send_info->to, dst, port,
									&proto, dns_flags);
			if (err!=0){
				LOG(L_ERR, "ERROR: forward_request: resolving \"%.*s\""
						" failed: %s [%d]\n", dst->len, ZSW(dst->s),
						dns_strerror(err), err);
				ret=E_BAD_ADDRESS;
				goto error;
			}
		}else
#endif
		if (sip_hostport2su(&send_info->to, dst, port, &proto)<0){
			LOG(L_ERR, "ERROR: forward_request: bad host name %.*s,"
						" dropping packet\n", dst->len, ZSW(dst->s));
			ret=E_BAD_ADDRESS;
			goto error;
		}
	}/* dst */
	send_info->send_flags=msg->fwd_send_flags;
	/* calculate branch for outbound request;  if syn_branch is turned off,
	   calculate is from transaction key, i.e., as an md5 of From/To/CallID/
	   CSeq exactly the same way as TM does; good for reboot -- than messages
	   belonging to transaction lost due to reboot will still be forwarded
	   with the same branch parameter and will be match-able downstream
	
	   if it is turned on, we don't care about reboot; we simply put a simple
	   value in there; better for performance
	*/
	if (syn_branch ) {
	        memcpy(msg->add_to_branch_s, "z9hG4bKcydzigwkX", 16);
		msg->add_to_branch_len=16;
	} else {
		if (!char_msg_val( msg, md5 )) 	{ /* parses transaction key */
			LOG(L_ERR, "ERROR: forward_request: char_msg_val failed\n");
			ret=E_UNSPEC;
			goto error;
		}
		msg->hash_index=hash( msg->callid->body, get_cseq(msg)->number);
		if (!branch_builder( msg->hash_index, 0, md5, 0 /* 0-th branch */,
					msg->add_to_branch_s, &msg->add_to_branch_len )) {
			LOG(L_ERR, "ERROR: forward_request: branch_builder failed\n");
			ret=E_UNSPEC;
			goto error;
		}
	}
	/* try to send the message until success or all the ips are exhausted
	 *  (if dns lookup is performed && the dns cache used ) */
#ifdef USE_DNS_FAILOVER
	do{
#endif
		if (orig_send_sock==0) /* no forced send_sock => find it **/
			send_info->send_sock=get_send_socket(msg, &send_info->to, proto);
		if (send_info->send_sock==0){
			LOG(L_ERR, "forward_req: ERROR: cannot forward to af %d, proto %d "
						"no corresponding listening socket\n",
						send_info->to.s.sa_family, proto);
			ret=ser_error=E_NO_SOCKET;
#ifdef USE_DNS_FAILOVER
			/* continue, maybe we find a socket for some other ip */
			continue;
#else
			goto error;
#endif
		}
	
#ifdef USE_DNS_FAILOVER
		if (prev_send_sock!=send_info->send_sock){
			/* rebuild the message only if the send_sock changed */
			prev_send_sock=send_info->send_sock;
#endif
			if (buf) pkg_free(buf);
			send_info->proto=proto;
			buf = build_req_buf_from_sip_req(msg, &len, send_info, 0);
			if (!buf){
				LOG(L_ERR, "ERROR: forward_request: building failed\n");
				ret=E_OUT_OF_MEM; /* most probable */
				goto error;
			}
#ifdef USE_DNS_FAILOVER
		}
#endif
		 /* send it! */
		DBG("Sending:\n%.*s.\n", (int)len, buf);
		DBG("orig. len=%d, new_len=%d, proto=%d\n",
				msg->len, len, send_info->proto );
	
		if (run_onsend(msg, send_info, buf, len)==0){
			su2ip_addr(&ip, &send_info->to);
			LOG(L_INFO, "forward_request: request to %s:%d(%d) dropped"
					" (onsend_route)\n", ip_addr2a(&ip),
						su_getport(&send_info->to), send_info->proto);
			ser_error=E_OK; /* no error */
			ret=E_ADM_PROHIBITED;
#ifdef USE_DNS_FAILOVER
			continue; /* try another ip */
#else
			goto error; /* error ? */
#endif
		}
#ifdef USE_DST_BLACKLIST
		if (cfg_get(core, core_cfg, use_dst_blacklist)){
			if (dst_is_blacklisted(send_info, msg)){
				su2ip_addr(&ip, &send_info->to);
				LOG(L_DBG, "DEBUG: blacklisted destination:%s:%d (%d)\n",
							ip_addr2a(&ip), su_getport(&send_info->to),
							send_info->proto);
				ret=ser_error=E_SEND;
#ifdef USE_DNS_FAILOVER
				continue; /* try another ip */
#else
				goto error;
#endif
			}
		}
#endif
		if (msg_send(send_info, buf, len)<0){
			ret=ser_error=E_SEND;
#ifdef USE_DST_BLACKLIST
			(void)dst_blacklist_add(BLST_ERR_SEND, send_info, msg);
#endif
#ifdef USE_DNS_FAILOVER
			continue; /* try another ip */
#else
			goto error;
#endif
		}else{
			ret=ser_error=E_OK;
			/* sent requests stats */
			STATS_TX_REQUEST(  msg->first_line.u.request.method_value );
			/* exit succcesfully */
			goto end;
		}
#ifdef USE_DNS_FAILOVER
	}while(dst && cfg_get(core, core_cfg, use_dns_failover) &&
			dns_srv_handle_next(&dns_srv_h, err) && 
			((err=dns_sip_resolve2su(&dns_srv_h, &send_info->to, dst, port,
										&proto, dns_flags))==0));
	if ((err!=0) && (err!=-E_DNS_EOR)){
		LOG(L_ERR, "ERROR:  resolving %.*s host name in uri"
							" failed: %s [%d] (dropping packet)\n",
									dst->len, ZSW(dst->s),
									dns_strerror(err), err);
		ret=ser_error=E_BAD_ADDRESS;
		goto error;
	}
#endif
	
error:
	STATS_TX_DROPS;
end:
#ifdef USE_DNS_FAILOVER
	if (dst && cfg_get(core, core_cfg, use_dns_failover)){
				dns_srv_handle_put(&dns_srv_h);
	}
#endif
	if (buf) pkg_free(buf);
	/* received_buf & line_buf will be freed in receive_msg by free_lump_list*/
#if defined STATS_REQ_FWD_OK || defined STATS_REQ_FWD_DROP
	if(ret==0)
		STATS_REQ_FWD_OK();
	else
		STATS_REQ_FWD_DROP();
#endif /* STATS_REQ_FWD_* */
	return ret;
}
Ejemplo n.º 2
0
inline static void final_response_handler(
		struct retr_buf *r_buf, struct cell *t)
{
	int silent;
#ifdef USE_DNS_FAILOVER
	/*int i;
	int added_branches;
	*/
	int branch_ret;
	int prev_branch;
	ticks_t now;
#endif

#ifdef EXTRA_DEBUG
	if(t->flags & T_IN_AGONY) {
		LM_ERR("transaction %p scheduled for deletion and"
			   " called from FR timer (flags %x)\n",
				t, t->flags);
		abort();
	}
#endif
	/* FR for local cancels.... */
	if(r_buf->rbtype == TYPE_LOCAL_CANCEL) {
#ifdef TIMER_DEBUG
		LM_DBG("stop retr for local cancel\n");
#endif
		return;
	}
	/* FR for replies (negative INVITE replies) */
	if(r_buf->rbtype > 0) {
#ifdef EXTRA_DEBUG
		if(t->uas.request->REQ_METHOD != METHOD_INVITE || t->uas.status < 200) {
			LM_CRIT("BUG - unknown type reply buffer\n");
			abort();
		}
#endif
		put_on_wait(t);
		return;
	};

	/* lock reply processing to determine how to proceed reliably */
	LOCK_REPLIES(t);
	/* now it can be only a request retransmission buffer;
	   try if you can simply discard the local transaction
	   state without compellingly removing it from the
	   world */
	silent =
			/* don't go silent if disallowed globally ... */
			cfg_get(tm, tm_cfg, noisy_ctimer) == 0
			/* ... or for this particular transaction */
			&& has_noisy_ctimer(t) == 0
			/* not for UACs */
			&& !is_local(t)
			/* invites only */
			&& is_invite(t)
			/* parallel forking does not allow silent state discarding */
			&& t->nr_of_outgoings == 1
			/* on_negativ reply handler not installed -- serial forking
		 * could occur otherwise */
			&& t->on_failure == 0
			/* the same for FAILURE callbacks */
			&& !has_tran_tmcbs(t, TMCB_ON_FAILURE_RO | TMCB_ON_FAILURE)
			/* something received -- we will not be silent on error */
			&& t->uac[r_buf->branch].last_received == 0;

	if(silent) {
		UNLOCK_REPLIES(t);
#ifdef EXTRA_DEBUG
		LM_DBG("transaction silently dropped (%p), branch %d, last_received %d\n",
				t, r_buf->branch, t->uac[r_buf->branch].last_received);
#endif
		put_on_wait(t);
		return;
	}
#ifdef EXTRA_DEBUG
	LM_DBG("stop retr. and send CANCEL (%p)\n", t);
#endif
	if((r_buf->branch < sr_dst_max_branches)
			&& /* r_buf->branch is always >=0 */
			(t->uac[r_buf->branch].last_received == 0)
			&& (t->uac[r_buf->branch].request.buffer
					   != NULL) /* not a blind UAC */
			) {
/* no reply received */
#ifdef USE_DST_BLACKLIST
		if(r_buf->my_T && r_buf->my_T->uas.request
				&& (r_buf->my_T->uas.request->REQ_METHOD
						   & cfg_get(tm, tm_cfg, tm_blst_methods_add)))
			dst_blacklist_add(
					BLST_ERR_TIMEOUT, &r_buf->dst, r_buf->my_T->uas.request);
#endif
#ifdef USE_DNS_FAILOVER
		/* if this is an invite, the destination resolves to more ips, and
		 *  it still hasn't passed more than fr_inv_timeout since we
		 *  started, add another branch/uac */
		if(cfg_get(core, core_cfg, use_dns_failover)) {
			now = get_ticks_raw();
			if((s_ticks_t)(t->end_of_life - now) > 0) {
				branch_ret = add_uac_dns_fallback(
						t, t->uas.request, &t->uac[r_buf->branch], 0);
				prev_branch = -1;
				while((branch_ret >= 0) && (branch_ret != prev_branch)) {
					prev_branch = branch_ret;
					branch_ret =
							t_send_branch(t, branch_ret, t->uas.request, 0, 0);
				}
			}
		}
#endif
	}
	fake_reply(t, r_buf->branch, 408);
}