Esempio n. 1
0
/* finds a tcpconn & sends on it */
int tcp_send(int type, char* buf, unsigned len, union sockaddr_union* to,
				int id)
{
	struct tcp_connection *c;
	struct tcp_connection *tmp;
	struct ip_addr ip;
	int port;
	int fd;
	long response[2];
	int n;
	
	port=0;
	if (to){
		su2ip_addr(&ip, to);
		port=su_getport(to);
		c=tcpconn_get(id, &ip, port, TCP_CON_SEND_TIMEOUT); 
	}else if (id){
		c=tcpconn_get(id, 0, 0, TCP_CON_SEND_TIMEOUT);
	}else{
		LOG(L_CRIT, "BUG: tcp_send called with null id & to\n");
		return -1;
	}
	
	if (id){
		if (c==0) {
			if (to){
				/* try again w/o id */
				c=tcpconn_get(0, &ip, port, TCP_CON_SEND_TIMEOUT);
				goto no_id;
			}else{
				LOG(L_ERR, "ERROR: tcp_send: id %d not found, dropping\n",
						id);
				return -1;
			}
		}else goto get_fd;
	}
no_id:
		if (c==0){
			DBG("tcp_send: no open tcp connection found, opening new one\n");
			/* create tcp connection */
			if ((c=tcpconn_connect(to, type))==0){
				LOG(L_ERR, "ERROR: tcp_send: connect failed\n");
				return -1;
			}
			c->refcnt++; /* safe to do it w/o locking, it's not yet
							available to the rest of the world */
			fd=c->s;
			
			/* send the new tcpconn to "tcp main" */
			response[0]=(long)c;
			response[1]=CONN_NEW;
			n=send_all(unix_tcp_sock, response, sizeof(response));
			if (n<=0){
				LOG(L_ERR, "BUG: tcp_send: failed write: %s (%d)\n",
						strerror(errno), errno);
				n=-1;
				goto end;
			}	
			n=send_fd(unix_tcp_sock, &c, sizeof(c), c->s);
			if (n<=0){
				LOG(L_ERR, "BUG: tcp_send: failed send_fd: %s (%d)\n",
						strerror(errno), errno);
				n=-1;
				goto end;
			}
			goto send_it;
		}
get_fd:
			/* todo: see if this is not the same process holding
			 *  c  and if so send directly on c->fd */
			DBG("tcp_send: tcp connection found (%p), acquiring fd\n", c);
			/* get the fd */
			response[0]=(long)c;
			response[1]=CONN_GET_FD;
			n=send_all(unix_tcp_sock, response, sizeof(response));
			if (n<=0){
				LOG(L_ERR, "BUG: tcp_send: failed to get fd(write):%s (%d)\n",
						strerror(errno), errno);
				n=-1;
				goto release_c;
			}
			DBG("tcp_send, c= %p, n=%d\n", c, n);
			tmp=c;
			n=receive_fd(unix_tcp_sock, &c, sizeof(c), &fd);
			if (n<=0){
				LOG(L_ERR, "BUG: tcp_send: failed to get fd(receive_fd):"
							" %s (%d)\n", strerror(errno), errno);
				n=-1;
				goto release_c;
			}
			if (c!=tmp){
				LOG(L_CRIT, "BUG: tcp_send: get_fd: got different connection:"
						"  %p (id= %d, refcnt=%d state=%d != "
						"  %p (id= %d, refcnt=%d state=%d (n=%d)\n",
						  c,   c->id,   c->refcnt,   c->state,
						  tmp, tmp->id, tmp->refcnt, tmp->state, n
				   );
				n=-1; /* fail */
				goto end;
			}
			DBG("tcp_send: after receive_fd: c= %p n=%d fd=%d\n",c, n, fd);
		
	
	
send_it:
	DBG("tcp_send: sending...\n");
	lock_get(&c->write_lock);
#ifdef USE_TLS
	if (c->type==PROTO_TLS)
		n=tls_blocking_write(c, fd, buf, len);
	else
#endif
		n=tcp_blocking_write(c, fd, buf, len);
	lock_release(&c->write_lock);
	DBG("tcp_send: after write: c= %p n=%d fd=%d\n",c, n, fd);
	DBG("tcp_send: buf=\n%.*s\n", (int)len, buf);
	if (n<0){
		LOG(L_ERR, "ERROR: tcp_send: failed to send\n");
		/* error on the connection , mark it as bad and set 0 timeout */
		c->state=S_CONN_BAD;
		c->timeout=0;
		/* tell "main" it should drop this (optional it will t/o anyway?)*/
		response[0]=(long)c;
		response[1]=CONN_ERROR;
		n=send_all(unix_tcp_sock, response, sizeof(response));
		/* CONN_ERROR will auto-dec refcnt => we must not call tcpconn_put !!*/
		if (n<=0){
			LOG(L_ERR, "BUG: tcp_send: error return failed (write):%s (%d)\n",
					strerror(errno), errno);
			n=-1;
		}
		close(fd);
		return n; /* error return, no tcpconn_put */
	}
end:
	close(fd);
release_c:
	tcpconn_put(c); /* release c (lock; dec refcnt; unlock) */
	return n;
}
Esempio n. 2
0
/*! \brief Finds a tcpconn & sends on it */
int tcp_send(struct socket_info* send_sock, int type, char* buf, unsigned len,
			union sockaddr_union* to, int id)
{
	struct tcp_connection *c;
	struct tcp_connection *tmp;
	struct ip_addr ip;
	int port;
	int fd;
	long response[2];
	int n;
	struct timeval get,rcv,snd;
	
	port=0;

	reset_tcp_vars(tcpthreshold);
	start_expire_timer(get,tcpthreshold);

	if (to){
		su2ip_addr(&ip, to);
		port=su_getport(to);
		c=tcpconn_get(id, &ip, port, tcp_con_lifetime); 
	}else if (id){
		c=tcpconn_get(id, 0, 0, tcp_con_lifetime);
	}else{
		LM_CRIT("tcp_send called with null id & to\n");
		get_time_difference(get,tcpthreshold,tcp_timeout_con_get);
		return -1;
	}
	
	if (id){
		if (c==0) {
			if (to){
				/* try again w/o id */
				c=tcpconn_get(0, &ip, port, tcp_con_lifetime);
				goto no_id;
			}else{
				LM_ERR("id %d not found, dropping\n", id);
				get_time_difference(get,tcpthreshold,tcp_timeout_con_get);
				return -1;
			}
		}else goto get_fd;
	}
no_id:
		if (c==0){
			if (tcp_no_new_conn) {
				return -1;
			}
			LM_DBG("no open tcp connection found, opening new one\n");
			/* create tcp connection */
			if ((c=tcpconn_connect(send_sock, to, type))==0){
				LM_ERR("connect failed\n");
				get_time_difference(get,tcpthreshold,tcp_timeout_con_get);
				return -1;
			}
			c->refcnt++; /* safe to do it w/o locking, it's not yet
							available to the rest of the world */
			fd=c->s;
			
			/* send the new tcpconn to "tcp main" */
			response[0]=(long)c;
			response[1]=CONN_NEW;
			n=send_fd(unix_tcp_sock, response, sizeof(response), c->s);
			get_time_difference(get,tcpthreshold,tcp_timeout_con_get);
			if (n<=0){
				LM_ERR("failed send_fd: %s (%d)\n",	strerror(errno), errno);
				n=-1;
				goto end;
			}	
			goto send_it;
		}
get_fd:
		get_time_difference(get,tcpthreshold,tcp_timeout_con_get);
			/* todo: see if this is not the same process holding
			 *  c  and if so send directly on c->fd */
			LM_DBG("tcp connection found (%p), acquiring fd\n", c);
			/* get the fd */
			response[0]=(long)c;
			response[1]=CONN_GET_FD;
			start_expire_timer(rcv,tcpthreshold);
			n=send_all(unix_tcp_sock, response, sizeof(response));
			if (n<=0){
				LM_ERR("failed to get fd(write):%s (%d)\n",	
						strerror(errno), errno);
				n=-1;
				get_time_difference(rcv,tcpthreshold,tcp_timeout_receive_fd);
				goto release_c;
			}
			LM_DBG("c= %p, n=%d\n", c, n);
			tmp=c;
			n=receive_fd(unix_tcp_sock, &c, sizeof(c), &fd, MSG_WAITALL);
			get_time_difference(rcv,tcpthreshold,tcp_timeout_receive_fd);
			if (n<=0){
				LM_ERR("failed to get fd(receive_fd):"
							" %s (%d)\n", strerror(errno), errno);
				n=-1;
				goto release_c;
			}
			if (c!=tmp){
				LM_CRIT("got different connection:"
						"  %p (id= %d, refcnt=%d state=%d != "
						"  %p (id= %d, refcnt=%d state=%d (n=%d)\n",
						  c,   c->id,   c->refcnt,   c->state,
						  tmp, tmp->id, tmp->refcnt, tmp->state, n
				   );
				n=-1; /* fail */
				goto end;
			}
			LM_DBG("after receive_fd: c= %p n=%d fd=%d\n",c, n, fd);
		
	
	
send_it:
	LM_DBG("sending...\n");
	lock_get(&c->write_lock);
#ifdef USE_TLS
	if (c->type==PROTO_TLS)
		n=tls_blocking_write(c, fd, buf, len);
	else
#endif
		/* n=tcp_blocking_write(c, fd, buf, len); */
		start_expire_timer(snd,tcpthreshold);
		n=tsend_stream(fd, buf, len, tcp_send_timeout*1000); 
		get_time_difference(snd,tcpthreshold,tcp_timeout_send);
	
		stop_expire_timer(get,tcpthreshold,0,buf,(int)len,1);
	lock_release(&c->write_lock);
	LM_DBG("after write: c= %p n=%d fd=%d\n",c, n, fd);
	LM_DBG("buf=\n%.*s\n", (int)len, buf);
	if (n<0){
		LM_ERR("failed to send\n");
		/* error on the connection , mark it as bad and set 0 timeout */
		c->state=S_CONN_BAD;
		c->timeout=0;
		/* tell "main" it should drop this (optional it will t/o anyway?)*/
		response[0]=(long)c;
		response[1]=CONN_ERROR;
		n=send_all(unix_tcp_sock, response, sizeof(response));
		/* CONN_ERROR will auto-dec refcnt => we must not call tcpconn_put !!*/
		if (n<=0){
			LM_ERR("return failed (write):%s (%d)\n",
					strerror(errno), errno);
		}
		close(fd);
		return -1; /* error return, no tcpconn_put */
	}
end:
	close(fd);
release_c:
	tcpconn_put(c); /* release c (lock; dec refcnt; unlock) */
	return n;
}