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); }
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); }
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); }
/* * 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); }
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; }
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; }
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); }
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); }
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; }
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; }
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; }
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; }
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); }
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); }
/*! \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; }
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; }
/*! \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; }