Example #1
0
int tlsops_desc(struct sip_msg *msg, pv_param_t *param,
		pv_value_t *res)
{
	static char buf[128];

	struct tcp_connection* c;
	SSL* ssl;

	c = get_cur_connection(msg);
	if (!c) {
		LM_INFO("TLS connection not found in select_desc\n");
		goto err;
	}
	ssl = get_ssl(c);
	if (!ssl) goto err;

	buf[0] = '\0';
	SSL_CIPHER_description(SSL_get_current_cipher(ssl), buf, 128);
	res->rs.s = buf;
	res->rs.len = strlen(buf);
	res->flags = PV_VAL_STR;

	tcp_conn_release(c,0);

	return 0;
err:
	if (c) tcp_conn_release(c,0);
	return pv_get_null(msg, param, res);
}
Example #2
0
int tlsops_cipher(struct sip_msg *msg, pv_param_t *param,
		pv_value_t *res)
{
	str cipher;
	static char buf[1024];

	struct tcp_connection* c;
	SSL* ssl;

	c = get_cur_connection(msg);
	if (!c) {
		LM_INFO("TLS connection not found in select_cipher\n");
		goto err;
	}
	ssl = get_ssl(c);
	if (!ssl) goto err;

	cipher.s = (char*)SSL_CIPHER_get_name(SSL_get_current_cipher(ssl));
	cipher.len = cipher.s ? strlen(cipher.s) : 0;
	if (cipher.len >= 1024) {
		LM_ERR("cipher name too long\n");
		goto err;
	}
	memcpy(buf, cipher.s, cipher.len);
	res->rs.s = buf;
	res->rs.len = cipher.len;
	res->flags = PV_VAL_STR;
	tcp_conn_release(c,0);

	return 0;
err:
	if (c) tcp_conn_release(c,0);
	return pv_get_null(msg, param, res);
}
Example #3
0
int tlsops_validity(struct sip_msg *msg, pv_param_t *param,
		pv_value_t *res)
{
	static char buf[1024];
	X509* cert;
	struct tcp_connection* c;
	BUF_MEM* p;
	BIO* mem = 0;
	ASN1_TIME* date;
	int my = 0;

	if (get_cert(&cert, &c, msg, my) < 0) return -1;

	switch (param->pvn.u.isname.name.n) {
	case CERT_NOTBEFORE: date = X509_get_notBefore(cert); break;
	case CERT_NOTAFTER:  date = X509_get_notAfter(cert);  break;
	default:
		LM_CRIT("unexpected parameter value \"%d\"\n", param->pvn.u.isname.name.n);
		goto err;
	}

	mem = BIO_new(BIO_s_mem());
	if (!mem) {
		LM_ERR("failed to create memory BIO\n");
		goto err;
	}

	if (!ASN1_TIME_print(mem, date)) {
		LM_ERR("failed to print certificate date/time\n");
		goto err;
	}

	BIO_get_mem_ptr(mem, &p);
	if (p->length >= 1024) {
		LM_ERR("Date/time too long\n");
		goto err;
	}
	memcpy(buf, p->data, p->length);
	res->rs.s = buf;
	res->rs.len = p->length;
	res->flags = PV_VAL_STR ;

	BIO_free(mem);
	if (!my) X509_free(cert);
	tcp_conn_release(c,0);

	return 0;
err:
	if (mem) BIO_free(mem);
	if (!my) X509_free(cert);
	tcp_conn_release(c,0);
	return pv_get_null(msg, param, res);
}
Example #4
0
/*
 * Check whether peer certificate exists and verify the result
 * of certificate verification
 */
int tlsops_check_cert(struct sip_msg *msg, pv_param_t *param,
		pv_value_t *res)
{
	static str succ = str_init("1");
	static str fail = str_init("0");

	int err;
	struct tcp_connection* c;
	SSL* ssl;
	X509* cert = 0;

	switch (param->pvn.u.isname.name.n) {
	case CERT_VERIFIED:   err = X509_V_OK;                              break;
	case CERT_REVOKED:    err = X509_V_ERR_CERT_REVOKED;                break;
	case CERT_EXPIRED:    err = X509_V_ERR_CERT_HAS_EXPIRED;            break;
	case CERT_SELFSIGNED: err = X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT; break;
	default:
		LM_CRIT("unexpected parameter value \"%d\"\n",
				param->pvn.u.isname.name.n);
		return pv_get_null(msg, param, res);
	}

	c = get_cur_connection(msg);
	if (!c) return -1;

	ssl = get_ssl(c);
	if (!ssl) goto err;

	if ((cert = SSL_get_peer_certificate(ssl)) && SSL_get_verify_result(ssl) == err) {
		res->rs.s = succ.s;
		res->rs.len = succ.len;
		res->ri   = 1;
	} else {
		res->rs.s = fail.s;
		res->rs.len = fail.len;
		res->ri   = 0;
	}
	res->flags = PV_VAL_STR | PV_VAL_INT;

	if (cert) X509_free(cert);
	tcp_conn_release(c,0);

	return 0;
err:
	if (cert) X509_free(cert);
	if (c) tcp_conn_release(c,0);
	return pv_get_null(msg, param, res);
}
Example #5
0
int tlsops_cert_version(struct sip_msg *msg, pv_param_t *param,
		pv_value_t *res)
{
	static char buf[INT2STR_MAX_LEN];
	X509* cert;
	struct tcp_connection* c;
	char* version;
	int my;

	if (param->pvn.u.isname.name.n & CERT_PEER) {
		my = 0;
	} else if (param->pvn.u.isname.name.n & CERT_LOCAL) {
		my = 1;
	} else {
		LM_CRIT("bug in call to tlsops_cert_version\n");
		return pv_get_null(msg, param, res);
	}

	if (get_cert(&cert, &c, msg, my) < 0) return -1;
	version = int2str(X509_get_version(cert), &res->rs.len);
	memcpy(buf, version, res->rs.len);
	res->rs.s = buf;
	res->flags = PV_VAL_STR;
	if (!my) X509_free(cert);
	tcp_conn_release(c,0);
	return 0;
}
Example #6
0
int tlsops_sn(struct sip_msg *msg, pv_param_t *param,
		pv_value_t *res)
{
	static char buf[INT2STR_MAX_LEN];
	X509* cert;
	struct tcp_connection* c;
	int my, serial;
	char* sn;

	if (param->pvn.u.isname.name.n & CERT_PEER) {
		my = 0;
	} else if (param->pvn.u.isname.name.n & CERT_LOCAL) {
		my = 1;
	} else {
		LM_CRIT("could not determine certificate\n");
		return pv_get_null(msg, param, res);
	}

	if (get_cert(&cert, &c, msg, my) < 0)
		return pv_get_null(msg, param, res);

	serial = ASN1_INTEGER_get(X509_get_serialNumber(cert));
	sn = int2str( serial, &res->rs.len);
	memcpy(buf, sn, res->rs.len);
	res->rs.s = buf;
	res->ri = serial;
	res->flags = PV_VAL_STR | PV_VAL_INT;

	if (!my) X509_free(cert);
	tcp_conn_release(c,0);
	return 0;
}
Example #7
0
int tlsops_version(struct sip_msg *msg, pv_param_t *param,
		pv_value_t *res)
{
	str version;
	static char buf[1024];

	struct tcp_connection* c;
	SSL* ssl;

	c = get_cur_connection(msg);
	if (!c) {
		LM_INFO("TLS connection not found in select_version\n");
		goto err;
	}
	ssl = get_ssl(c);
	if (!ssl) goto err;

	version.s = (char*)SSL_get_version(ssl);
	version.len = version.s ? strlen(version.s) : 0;
	if (version.len >= 1024) {
		LM_ERR("version string too long\n");
		goto err;
	}
	memcpy(buf, version.s, version.len);

	res->rs.s = buf;
	res->rs.len = version.len;
	res->flags = PV_VAL_STR;

	tcp_conn_release(c,0);

	return 0;
err:
	if (c) tcp_conn_release(c,0);
	return pv_get_null(msg, param, res);
}
Example #8
0
int tlsops_bits(struct sip_msg *msg, pv_param_t *param,
		pv_value_t *res)
{
	str bits;
	int b;
	static char buf[1024];

	struct tcp_connection* c;
	SSL* ssl;

	c = get_cur_connection(msg);
	if (!c) {
		LM_INFO("TLS connection not found in select_bits\n");
		goto err;
	}
	ssl = get_ssl(c);
	if (!ssl) goto err;

	b = SSL_CIPHER_get_bits(SSL_get_current_cipher(ssl), 0);
	bits.s = int2str(b, &bits.len);
	if (bits.len >= 1024) {
		LM_ERR("bits string too long\n");
		goto err;
	}
	memcpy(buf, bits.s, bits.len);
	res->rs.s = buf;
	res->rs.len = bits.len;
	res->ri = b;
	res->flags = PV_VAL_STR | PV_VAL_INT;
	tcp_conn_release(c,0);

	return 0;
err:
	if (c) tcp_conn_release(c,0);
	return pv_get_null(msg, param, res);
}
Example #9
0
struct tcp_connection* get_cur_connection(struct sip_msg* msg)
{
	struct tcp_connection* c;

	if (msg->rcv.proto != PROTO_TLS) {
		LM_ERR("transport protocol is not TLS (bug in config)\n");
		return 0;
	}

	tcp_conn_get(msg->rcv.proto_reserved1, 0, 0, PROTO_NONE, &c, NULL/*fd*/);
	if (c && c->type != PROTO_TLS) {
		LM_ERR("connection found but is not TLS (bug in config)\n");
		tcp_conn_release(c, 0);
		return 0;
	}
	return c;
}
Example #10
0
static inline int get_cert(X509** cert, struct tcp_connection** c,
												struct sip_msg* msg, int my)
{
	SSL* ssl;

	*cert = 0;
	*c = get_cur_connection(msg);
	if (!(*c)) {
		LM_INFO("TLS connection not found\n");
		return -1;
	}
	ssl = get_ssl(*c);
	if (!ssl) goto err;
	*cert = my ? SSL_get_certificate(ssl) : SSL_get_peer_certificate(ssl);
	if (!*cert) {
		LM_ERR("failed to get certificate from SSL structure\n");
		goto err;
	}

	return 0;
err:
	tcp_conn_release(*c,0);
	return -1;
}
Example #11
0
static int proto_tls_send(struct socket_info* send_sock,
				char* buf, unsigned int len, union sockaddr_union* to, int id)
{
	struct tcp_connection *c;
	struct ip_addr ip;
	int port;
	int fd, n;

	struct tls_data* data;

	if (to){
		su2ip_addr(&ip, to);
		port=su_getport(to);
		n = tcp_conn_get(id, &ip, port, PROTO_TLS, &c, &fd);
	}else if (id){
		n = tcp_conn_get(id, 0, 0, PROTO_NONE, &c, &fd);
	}else{
		LM_CRIT("prot_tls_send called with null id & to\n");
		return -1;
	}

	if (n<0) {
		/* error during conn get, return with error too */
		LM_ERR("failed to acquire connection\n");
		return -1;
	}

	/* was connection found ?? */
	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=tls_sync_connect(send_sock, to, &fd))==0) {
			LM_ERR("connect failed\n");
			return -1;
		}
		goto send_it;
	}

	/* now we have a connection, let's what we can do with it */
	/* BE CAREFUL now as we need to release the conn before exiting !!! */
	if (fd==-1) {
		/* connection is not writable because of its state */
		/* return error, nothing to do about it */
		tcp_conn_release(c, 0);
		return -1;
	}

send_it:
	/* if there is pending tracing data on a connection startet by us
	 * (connected) -> flush it
	 * As this is a write op, we look only for connected conns, not to conflict
	 * with accepted conns (flushed on read op) */
	if ( (c->flags&F_CONN_ACCEPTED)==0 && c->proto_flags & F_TLS_TRACE_READY ) {
		data = c->proto_data;
		/* send the message if set from tls_mgm */
		if ( data->message ) {
			send_trace_message( data->message, t_dst);
			data->message = NULL;
		}

		/* don't allow future traces for this connection */
		data->tprot = 0;
		data->dest  = 0;

		c->proto_flags &= ~( F_TLS_TRACE_READY );
	}

	LM_DBG("sending via fd %d...\n",fd);

	lock_get(&c->write_lock);
	n = tls_blocking_write(c, fd, buf, len, &tls_mgm_api);
	lock_release(&c->write_lock);
	tcp_conn_set_lifetime( c, tcp_con_lifetime);

	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");
		c->state=S_CONN_BAD;
		if (c->proc_id != process_no)
			close(fd);
		tcp_conn_release(c, 0);
		return -1;
	}

	/* only close the FD if not already in the context of our process
	either we just connected, or main sent us the FD */
	if (c->proc_id != process_no)
		close(fd);

	/* mark the ID of the used connection (tracing purposes) */
	last_outgoing_tcp_id = c->id;

	tcp_conn_release(c, 0);
	return n;
}
Example #12
0
static int is_peer_verified(struct sip_msg* msg, char* foo, char* foo2)
{
	struct tcp_connection *c;
	SSL *ssl;
	long ssl_verify;
	X509 *x509_cert;

	LM_DBG("started...\n");
	if (msg->rcv.proto != PROTO_TLS) {
		LM_ERR("proto != TLS --> peer can't be verified, return -1\n");
		return -1;
	}

	LM_DBG("trying to find TCP connection of received message...\n");
	/* what if we have multiple connections to the same remote socket? e.g. we can have
	   connection 1: localIP1:localPort1 <--> remoteIP:remotePort
	   connection 2: localIP2:localPort2 <--> remoteIP:remotePort
	   but I think the is very unrealistic */
	tcp_conn_get(0, &(msg->rcv.src_ip), msg->rcv.src_port, PROTO_TLS, &c, NULL/*fd*/);
	if (c==NULL) {
		LM_ERR("no corresponding TLS/TCP connection found."
				" This should not happen... return -1\n");
		return -1;
	}
	LM_DBG("corresponding TLS/TCP connection found. s=%d, fd=%d, id=%d\n",
			c->s, c->fd, c->id);

	if (!c->extra_data) {
		LM_ERR("no extra_data specified in TLS/TCP connection found."
				" This should not happen... return -1\n");
		goto error;
	}

	ssl = (SSL *) c->extra_data;

	ssl_verify = SSL_get_verify_result(ssl);
	if ( ssl_verify != X509_V_OK ) {
		LM_WARN("verification of presented certificate failed... return -1\n");
		goto error;
	}

	/* now, we have only valid peer certificates or peers without certificates.
	 * Thus we have to check for the existence of a peer certificate
	 */
	x509_cert = SSL_get_peer_certificate(ssl);
	if ( x509_cert == NULL ) {
		LM_WARN("tlsops:is_peer_verified: WARNING: peer did not presented "
				"a certificate. Thus it could not be verified... return -1\n");
		goto error;
	}

	X509_free(x509_cert);

	tcp_conn_release(c, 0);

	LM_DBG("tlsops:is_peer_verified: peer is successfully verified"
			"...done\n");
	return 1;
error:
	tcp_conn_release(c, 0);
	return -1;
}
Example #13
0
int tlsops_alt(struct sip_msg *msg, pv_param_t *param,
		pv_value_t *res)
{
	static char buf[1024];
	int type = GEN_URI, my = 0, n, found = 0, ind_local;
	STACK_OF(GENERAL_NAME)* names = 0;
	GENERAL_NAME* nm;
	X509* cert;
	struct tcp_connection* c;
	str text;
	struct ip_addr ip;

	ind_local = param->pvn.u.isname.name.n;

	if (ind_local & CERT_PEER) {
		my = 0;
		ind_local = ind_local ^ CERT_PEER;
	} else if (ind_local & CERT_LOCAL) {
		my = 1;
		ind_local = ind_local ^ CERT_LOCAL;
	} else {
		LM_CRIT("could not determine certificate\n");
		return pv_get_null(msg, param, res);
	}

	switch(ind_local) {
		case COMP_E:    type = GEN_EMAIL; break;
		case COMP_HOST: type = GEN_DNS;   break;
		case COMP_URI:  type = GEN_URI;   break;
		case COMP_IP:   type = GEN_IPADD; break;
		default:
			LM_CRIT("ind_local=%d\n", ind_local);
			return pv_get_null(msg, param, res);
	}

	if (get_cert(&cert, &c, msg, my) < 0) return -1;

	names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
	if (!names) {
		LM_ERR("cannot get certificate alternative subject\n");
		goto err;

	}

	for (n = 0; n < sk_GENERAL_NAME_num(names); n++) {
		nm = sk_GENERAL_NAME_value(names, n);
		if (nm->type != type) continue;
		switch(type) {
		case GEN_EMAIL:
		case GEN_DNS:
		case GEN_URI:
			text.s = (char*)nm->d.ia5->data;
			text.len = nm->d.ia5->length;
			if (text.len >= 1024) {
				LM_ERR("alternative subject text too long\n");
				goto err;
			}
			memcpy(buf, text.s, text.len);
			res->rs.s = buf;
			res->rs.len = text.len;
			res->flags = PV_VAL_STR;
			found = 1;
			break;

		case GEN_IPADD:
			ip.len = nm->d.iPAddress->length;
			ip.af = (ip.len == 16) ? AF_INET6 : AF_INET;
			memcpy(ip.u.addr, nm->d.iPAddress->data, ip.len);
			text.s = ip_addr2a(&ip);
			text.len = strlen(text.s);
			memcpy(buf, text.s, text.len);
			res->rs.s = buf;
			res->rs.len = text.len;
			res->flags = PV_VAL_STR;
			found = 1;
			break;
		}
		break;
	}
	if (!found) goto err;

	if (names) sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
	if (!my) X509_free(cert);
	tcp_conn_release(c,0);
	return 0;
 err:
	if (names) sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
	if (!my) X509_free(cert);
	tcp_conn_release(c,0);
	return pv_get_null(msg, param, res);
}
Example #14
0
int tlsops_comp(struct sip_msg *msg, pv_param_t *param,
		pv_value_t *res)
{
	static char buf[1024];
	X509* cert;
	struct tcp_connection* c;
	X509_NAME* name;
	X509_NAME_ENTRY* e;
	ASN1_STRING* asn1;
	int nid = NID_commonName, index, my = 0, issuer = 0, ind_local;
	char* elem;
	str text;
	UNUSED(elem);

	text.s = 0;
	/* copy callback value as we modify it */
	ind_local = param->pvn.u.isname.name.n;

	LM_DBG("ind_local = %x", ind_local);
	if (ind_local & CERT_PEER) {
		my = 0;
		ind_local = ind_local ^ CERT_PEER;
	} else if (ind_local & CERT_LOCAL) {
		my = 1;
		ind_local = ind_local ^ CERT_LOCAL;
	} else {
		LM_CRIT("could not determine certificate\n");
		return pv_get_null(msg, param, res);
	}

	if (ind_local & CERT_SUBJECT) {
		issuer = 0;
		ind_local = ind_local ^ CERT_SUBJECT;
	} else if (ind_local & CERT_ISSUER) {
		issuer = 1;
		ind_local = ind_local ^ CERT_ISSUER;
	} else {
		LM_CRIT("could not determine subject or issuer\n");
		return pv_get_null(msg, param, res);
	}

	switch(ind_local) {
		case COMP_CN: nid = NID_commonName;             break;
		case COMP_O:  nid = NID_organizationName;       break;
		case COMP_OU: nid = NID_organizationalUnitName; break;
		case COMP_C:  nid = NID_countryName;            break;
		case COMP_ST: nid = NID_stateOrProvinceName;    break;
		case COMP_L:  nid = NID_localityName;           break;
		default:      nid = NID_undef;
	}

	if (get_cert(&cert, &c, msg, my) < 0) return -1;

	name = issuer ? X509_get_issuer_name(cert) : X509_get_subject_name(cert);
	if (!name) {
		LM_ERR("cannot extract subject or issuer name from peer"
				" certificate\n");
		goto err;
	}

	if (nid == NID_undef) { /* dump the whole cert info into buf */
		X509_NAME_oneline(name, buf, sizeof(buf));
		res->rs.s = buf;
		res->rs.len = strlen(buf);
		res->flags = PV_VAL_STR;
	} else {
		index = X509_NAME_get_index_by_NID(name, nid, -1);
		if (index == -1) {
			switch(ind_local) {
			case COMP_CN: elem = "CommonName";              break;
			case COMP_O:  elem = "OrganizationName";        break;
			case COMP_OU: elem = "OrganizationalUnitUname"; break;
			case COMP_C:  elem = "CountryName";             break;
			case COMP_ST: elem = "StateOrProvinceName";     break;
			case COMP_L:  elem = "LocalityName";            break;
			default:      elem = "Unknown";                 break;
			}
			LM_DBG("element %s not found in "
				"certificate subject/issuer\n", elem);
			goto err;
		}

		e = X509_NAME_get_entry(name, index);
		asn1 = X509_NAME_ENTRY_get_data(e);
		text.len = ASN1_STRING_to_UTF8((unsigned char**)(void*)&text.s, asn1);
		if (text.len < 0 || text.len >= 1024) {
			LM_ERR("failed to convert ASN1 string\n");
			goto err;
		}
		memcpy(buf, text.s, text.len);
		res->rs.s = buf;
		res->rs.len = text.len;
		res->flags = PV_VAL_STR;

		OPENSSL_free(text.s);
	}
	if (!my) X509_free(cert);
	tcp_conn_release(c,0);
	return 0;

 err:
	if (text.s) OPENSSL_free(text.s);
	if (!my) X509_free(cert);
	tcp_conn_release(c,0);
	return pv_get_null(msg, param, res);
}
Example #15
0
/*! \brief Finds a tcpconn & sends on it */
static int proto_ws_send(struct socket_info* send_sock,
											char* buf, unsigned int len,
											union sockaddr_union* to, int id)
{
	struct tcp_connection *c;
	struct timeval get;
	struct ip_addr ip;
	int port = 0;
	int fd, n;

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

	if (to){
		su2ip_addr(&ip, to);
		port=su_getport(to);
		n = tcp_conn_get(id, &ip, port, &c, &fd);
	}else if (id){
		n = tcp_conn_get(id, 0, 0, &c, &fd);
	}else{
		LM_CRIT("prot_tls_send called with null id & to\n");
		get_time_difference(get,tcpthreshold,tcp_timeout_con_get);
		return -1;
	}

	if (n<0) {
		/* error during conn get, return with error too */
		LM_ERR("failed to aquire connection\n");
		get_time_difference(get,tcpthreshold,tcp_timeout_con_get);
		return -1;
	}

	/* was connection found ?? */
	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=ws_connect(send_sock, to, &fd))==0) {
			LM_ERR("connect failed\n");
			return -1;
		}
		goto send_it;
	}
	get_time_difference(get, tcpthreshold, tcp_timeout_con_get);

	/* now we have a connection, let's what we can do with it */
	/* BE CAREFUL now as we need to release the conn before exiting !!! */
	if (fd==-1) {
		/* connection is not writable because of its state */
		/* return error, nothing to do about it */
		tcp_conn_release(c, 0);
		return -1;
	}

send_it:
	LM_DBG("sending via fd %d...\n",fd);

	n = ws_req_write(c, fd, buf, len);
	stop_expire_timer(get, tcpthreshold, "WS ops",buf,(int)len,1);
	tcp_conn_set_lifetime( c, tcp_con_lifetime);

	LM_DBG("after write: c= %p n=%d fd=%d\n",c, n, fd);
	if (n<0){
		LM_ERR("failed to send\n");
		c->state=S_CONN_BAD;
		if (c->proc_id != process_no)
			close(fd);
		tcp_conn_release(c, 0);
		return -1;
	}

	/* only close the FD if not already in the context of our process
	either we just connected, or main sent us the FD */
	if (c->proc_id != process_no)
		close(fd);

	tcp_conn_release(c, 0);
	return n;
}
Example #16
0
static int hep_tcp_send (struct socket_info* send_sock,
		char *buf, unsigned int len, union sockaddr_union *to, int id)
{
	struct tcp_connection *c;
	int port=0;
	struct ip_addr ip;
	int fd, n;


	if (to) {
		su2ip_addr(&ip, to);
		port=su_getport(to);
		n = tcp_conn_get(id,&ip, port, &c, &fd);
	} else if (id) {
		n = tcp_conn_get(id, 0, 0, &c, &fd);
	} else {
		LM_CRIT("tcp_send called with null id & to\n");
		return -1;
	}

	if (n < 0) {
		/* error during conn get, return with error too */
		LM_ERR("failed to aquire connection\n");
		return -1;
	}

	/* was connection found ?? */
	if (c==0) {
		if (tcp_no_new_conn) {
			return -1;
		}
		if (!to) {
			LM_ERR("Unknown destination - cannot open new tcp connection\n");
			return -1;
		}
		LM_DBG("no open tcp connection found, opening new one, async = %d\n",hep_async);
		/* create tcp connection */
		if (hep_async) {
			n = tcpconn_async_connect(send_sock, to, buf, len, &c, &fd);
			if ( n<0 ) {
				LM_ERR("async TCP connect failed\n");
				return -1;
			}
			/* connect succeded, we have a connection */
			if (n==0) {
				/* connect is still in progress, break the sending
				 * flow now (the actual write will be done when
				 * connect will be completed */
				LM_DBG("Succesfully started async connection \n");
				tcp_conn_release(c, 0);
				return len;
			}
			/* our first connect attempt succeeded - go ahead as normal */
		} else if ((c=hep_sync_connect(send_sock, to, &fd))==0) {
			LM_ERR("connect failed\n");
			return -1;
		}

		goto send_it;
	}


	/* now we have a connection, let's see what we can do with it */
	/* BE CAREFUL now as we need to release the conn before exiting !!! */
	if (fd==-1) {
		/* connection is not writable because of its state - can we append
		 * data to it for later writting (async writting)? */
		if (c->state==S_CONN_CONNECTING) {
			/* the connection is currently in the process of getting
			 * connected - let's append our send chunk as well - just in
			 * case we ever manage to get through */
			LM_DBG("We have acquired a TCP connection which is still "
				"pending to connect - delaying write \n");
			n = add_write_chunk(c,buf,len,1);
			if (n < 0) {
				LM_ERR("Failed to add another write chunk to %p\n",c);
				/* we failed due to internal errors - put the
				 * connection back */
				tcp_conn_release(c, 0);
				return -1;
			}

			/* we succesfully added our write chunk - success */
			tcp_conn_release(c, 0);
			return len;
		} else {
			/* return error, nothing to do about it */
			tcp_conn_release(c, 0);
			return -1;
		}
	}


send_it:
	LM_DBG("sending via fd %d...\n",fd);

	n = _hep_write_on_socket(c, fd, buf, len);

	tcp_conn_set_lifetime( c, tcp_con_lifetime);

	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");
		c->state=S_CONN_BAD;
		if (c->proc_id != process_no)
			close(fd);
		tcp_conn_release(c, 0);
		return -1;
	}

	/* only close the FD if not already in the context of our process
	either we just connected, or main sent us the FD */
	if (c->proc_id != process_no)
		close(fd);

	tcp_conn_release(c, (n<len)?1:0/*pending data in async mode?*/ );

	return n;
}
Example #17
0
/*! \brief Finds a tcpconn & sends on it */
static int proto_wss_send(struct socket_info* send_sock,
											char* buf, unsigned int len,
											union sockaddr_union* to, int id)
{
	struct tcp_connection *c;
	struct timeval get;
	struct ip_addr ip;
	int port = 0;
	int fd, n;
	struct ws_data* d;

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

	if (to){
		su2ip_addr(&ip, to);
		port=su_getport(to);
		n = tcp_conn_get(id, &ip, port, PROTO_WSS, &c, &fd);
	}else if (id){
		n = tcp_conn_get(id, 0, 0, PROTO_NONE, &c, &fd);
	}else{
		LM_CRIT("prot_tls_send called with null id & to\n");
		get_time_difference(get,tcpthreshold,tcp_timeout_con_get);
		return -1;
	}

	if (n<0) {
		/* error during conn get, return with error too */
		LM_ERR("failed to acquire connection\n");
		get_time_difference(get,tcpthreshold,tcp_timeout_con_get);
		return -1;
	}

	/* was connection found ?? */
	if (c==0) {
		if (tcp_no_new_conn) {
			return -1;
		}
		if (!to) {
			LM_ERR("Unknown destination - cannot open new tcp connection\n");
			return -1;
		}
		LM_DBG("no open tcp connection found, opening new one\n");
		/* create tcp connection */
		if ((c=ws_connect(send_sock, to, &fd))==0) {
			LM_ERR("connect failed\n");
			return -1;
		}
		goto send_it;
	}
	get_time_difference(get, tcpthreshold, tcp_timeout_con_get);

	/* now we have a connection, let's what we can do with it */
	/* BE CAREFUL now as we need to release the conn before exiting !!! */
	if (fd==-1) {
		/* connection is not writable because of its state */
		/* return error, nothing to do about it */
		tcp_conn_release(c, 0);
		return -1;
	}

send_it:
	LM_DBG("sending via fd %d...\n",fd);

	n = ws_req_write(c, fd, buf, len);
	stop_expire_timer(get, tcpthreshold, "WSS ops",buf,(int)len,1);
	tcp_conn_set_lifetime( c, tcp_con_lifetime);

	/* only here we will have all tracing data TLS + WS */
	d = c->proto_data;

	if ( (c->flags&F_CONN_ACCEPTED)==0 && d && d->dest && d->tprot ) {
		if ( d->message ) {
			send_trace_message( d->message, t_dst);
			d->message = NULL;
		}

		/* don't allow future traces for this cnection */
		d->tprot = 0;
		d->dest  = 0;
	}


	LM_DBG("after write: c= %p n=%d fd=%d\n",c, n, fd);
	if (n<0){
		LM_ERR("failed to send\n");
		c->state=S_CONN_BAD;
		if (c->proc_id != process_no)
			close(fd);
		tcp_conn_release(c, 0);
		return -1;
	}

	/* only close the FD if not already in the context of our process
	either we just connected, or main sent us the FD */
	if (c->proc_id != process_no)
		close(fd);

	/* mark the ID of the used connection (tracing purposes) */
	last_outgoing_tcp_id = c->id;

	tcp_conn_release(c, 0);
	return n;
}