From 52fa079b2b0d69f76faffa6f2daa4f38a71a3866 Mon Sep 17 00:00:00 2001 From: Michael Jerris Date: Wed, 11 Feb 2009 16:11:33 +0000 Subject: [PATCH] Tue Dec 16 16:19:37 CST 2008 Jarod Neuner * Early TLS Handshake and Verification tport_type_tls.c: * tport_tls_accept(): - Replaces tport_accept for incoming TLS connections. * tport_tls_connect(): - Replaces tport_base_connect() for outgoing TLS connections. tport_tls.c: * tls_t now use a memory home instead of malloc. * removed tls_check_hosts() * tls_connect(): - Replaces tport_base_connect for TLS connection setup. - Completes TLS handshake and verifies peer certificates. - Destroys suspect TLS connections before sending/receiving payload. - Populates a su_strlst_t with subjects from the peer certificate. tport.c: * tport_is_verified() - true if peer certificate validated successfully * tport_delivered_from_subjects() - Certificate subjects listed in the peer certificate. git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@11769 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- libs/sofia-sip/.update | 2 +- .../libsofia-sip-ua/tport/sofia-sip/tport.h | 9 + libs/sofia-sip/libsofia-sip-ua/tport/tport.c | 16 ++ .../libsofia-sip-ua/tport/tport_internal.h | 8 + .../libsofia-sip-ua/tport/tport_tls.c | 254 +++++++++++------- .../libsofia-sip-ua/tport/tport_tls.h | 16 +- .../libsofia-sip-ua/tport/tport_type_tls.c | 173 ++++++++++-- 7 files changed, 357 insertions(+), 121 deletions(-) diff --git a/libs/sofia-sip/.update b/libs/sofia-sip/.update index bca3fc7945..417ce8acd5 100644 --- a/libs/sofia-sip/.update +++ b/libs/sofia-sip/.update @@ -1 +1 @@ -Wed Feb 11 10:10:42 CST 2009 +Wed Feb 11 10:11:23 CST 2009 diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/sofia-sip/tport.h b/libs/sofia-sip/libsofia-sip-ua/tport/sofia-sip/tport.h index da957901fa..51f62ba3fc 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/sofia-sip/tport.h +++ b/libs/sofia-sip/libsofia-sip-ua/tport/sofia-sip/tport.h @@ -36,6 +36,9 @@ #ifndef SU_H #include #endif +#ifndef SU_STRLST_H +#include +#endif #ifndef SU_WAIT_H #include #endif @@ -267,6 +270,9 @@ TPORT_DLL int tport_is_tcp(tport_t const *self); /** Test if transport has TLS. */ TPORT_DLL int tport_has_tls(tport_t const *tport); +/** Test if transport provided a verified certificate chain (TLS only) */ +TPORT_DLL int tport_is_verified(tport_t const *tport); + /** Return true if transport is being updated. */ TPORT_DLL int tport_is_updating(tport_t const *self); @@ -332,6 +338,9 @@ TPORT_DLL tport_t *tport_delivered_by(tport_t const *tp, msg_t const *msg); TPORT_DLL int tport_delivered_from(tport_t *tp, msg_t const *msg, tp_name_t name[1]); +/** Return TLS Subjects provided by the source transport */ +TPORT_DLL su_strlst_t *tport_delivered_from_subjects(tport_t *tp, msg_t const *msg); + /** Check if transport named is already resolved */ TPORT_DLL int tport_name_is_resolved(tp_name_t const *); diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport.c b/libs/sofia-sip/libsofia-sip-ua/tport/tport.c index 4ca325ba18..5aecd6d932 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/tport.c +++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport.c @@ -268,7 +268,12 @@ int tport_has_ip6(tport_t const *self) int tport_has_tls(tport_t const *self) { return self && self->tp_pri->pri_has_tls; +} +/** Return true if transport certificate verified successfully */ +int tport_is_verified(tport_t const *self) +{ + return tport_has_tls(self) && self->tp_verified; } /** Return true if transport is being updated. */ @@ -3040,6 +3045,17 @@ int tport_delivered_from(tport_t *tp, msg_t const *msg, tp_name_t name[1]) } } +/** Return TLS Subjects provided by the source transport */ +su_strlst_t *tport_delivered_from_subjects(tport_t *tp, msg_t const *msg) +{ + if (tp && msg && msg == tp->tp_master->mr_delivery->d_msg) { + tport_t *tp_sec = tp->tp_master->mr_delivery->d_tport; + return (tp_sec) ? tp_sec->tp_subjects : NULL; + } + else + return NULL; +} + /** Return UDVM used to decompress the message. */ int tport_delivered_with_comp(tport_t *tp, msg_t const *msg, diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport_internal.h b/libs/sofia-sip/libsofia-sip-ua/tport/tport_internal.h index 7af7fff132..c4cf4a22e6 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/tport_internal.h +++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport_internal.h @@ -40,6 +40,7 @@ #endif #include +#include #ifndef MSG_ADDR_H #include @@ -155,6 +156,7 @@ struct tport_s { unsigned tp_has_stun_server:1; unsigned tp_trunc:1; unsigned tp_is_connected:1; /**< Connection is established */ + unsigned tp_verified:1; /**< Certificate Chain was verified */ unsigned:0; tport_t *tp_left, *tp_right, *tp_dad; /**< Links in tport tree */ @@ -177,6 +179,12 @@ struct tport_s { * or peer name (if secondary). */ + su_strlst_t *tp_subjects; /**< Transport Subjects. + * + * Subject Name(s) provided by the + * peer in a TLS connection (if secondary). + */ + #define tp_protoname tp_name->tpn_proto #define tp_canon tp_name->tpn_canon #define tp_host tp_name->tpn_host diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.c b/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.c index 8b6bf09f0d..ed18be820f 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.c +++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.c @@ -35,9 +35,11 @@ #include "config.h" #define OPENSSL_NO_KRB5 oh-no +#define SU_WAKEUP_ARG_T struct tport_s #include #include +#include #include #include @@ -61,18 +63,21 @@ #endif #include "tport_tls.h" -#include "tport_internal.h" char const tls_version[] = OPENSSL_VERSION_TEXT; -enum { tls_master, tls_slave }; +enum { tls_master = 0, tls_slave = 1}; struct tls_s { + su_home_t home[1]; SSL_CTX *ctx; SSL *con; BIO *bio_con; - int type; - int verified; + unsigned int type:1, + accept:1, + verify_outgoing:1, + verify_incoming:1, + verified:1; /* Receiving */ int read_events; @@ -85,7 +90,7 @@ struct tls_s { size_t write_buffer_len; /* Host names */ - char *hosts[TLS_MAX_HOSTS + 1]; + su_strlst_t *subject; }; enum { tls_buffer_size = 16384 }; @@ -122,10 +127,11 @@ void tls_log_errors(unsigned level, char const *s, unsigned long e) static tls_t *tls_create(int type) { - tls_t *tls = calloc(1, sizeof(*tls)); + tls_t *tls = su_home_new(sizeof(*tls)); + memset(((void *)tls) + sizeof(su_home_t), 0, sizeof(*tls) - sizeof(su_home_t)); if (tls) - tls->type = type; + tls->type = type == tls_master ? tls_master : tls_slave; return tls; } @@ -268,6 +274,8 @@ int tls_init_context(tls_t *tls, tls_issues_t const *ti) ti->verify_peer == 1 ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, tls_verify_cb); + tls->verify_incoming = tls->verify_outgoing = ti->verify_peer ? 1 : 0; + if (!SSL_CTX_set_cipher_list(tls->ctx, ti->cipher)) { SU_DEBUG_1(("%s: error setting cipher list\n", "tls_init_context")); tls_log_errors(1, "tls_init_context", 0); @@ -280,14 +288,9 @@ int tls_init_context(tls_t *tls, tls_issues_t const *ti) void tls_free(tls_t *tls) { - int k; - if (!tls) return; - if (tls->read_buffer) - free(tls->read_buffer), tls->read_buffer = NULL; - if (tls->con != NULL) SSL_shutdown(tls->con); @@ -297,12 +300,7 @@ void tls_free(tls_t *tls) if (tls->bio_con != NULL) BIO_free(tls->bio_con); - for (k = 0; k < TLS_MAX_HOSTS; k++) - if (tls->hosts[k]) { - free(tls->hosts[k]), tls->hosts[k] = NULL; - } - - free(tls); + su_home_unref(tls->home); } int tls_get_socket(tls_t *tls) @@ -369,9 +367,10 @@ tls_t *tls_clone(tls_t *master, int sock, int accept) if (tls) { tls->ctx = master->ctx; + tls->accept = accept ? 1 : 0; - if (!(tls->read_buffer = malloc(tls_buffer_size))) - free(tls), tls = NULL; + if (!(tls->read_buffer = su_alloc(tls->home, tls_buffer_size))) + su_home_unref(tls->home), tls = NULL; } if (!tls) return tls; @@ -389,14 +388,9 @@ tls_t *tls_clone(tls_t *master, int sock, int accept) } SSL_set_bio(tls->con, tls->bio_con, tls->bio_con); - if (accept) - SSL_set_accept_state(tls->con); - else - SSL_set_connect_state(tls->con); SSL_set_mode(tls->con, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); su_setblocking(sock, 0); - tls_read(tls); /* XXX - works only with non-blocking sockets */ return tls; } @@ -413,24 +407,12 @@ tls_t *tls_init_client(tls_t *master, int sock) return tls_clone(master, sock, accept = 0); } -static char *tls_strdup(char const *s) -{ - if (s) { - size_t len = strlen(s) + 1; - char *d = malloc(len); - if (d) - memcpy(d, s, len); - return d; - } - return NULL; -} - static int tls_post_connection_check(tls_t *tls) { X509 *cert; int extcount; - int k, i, j, error; + int i, j, error; if (!tls) return -1; @@ -440,8 +422,8 @@ int tls_post_connection_check(tls_t *tls) extcount = X509_get_ext_count(cert); - for (k = 0; k < TLS_MAX_HOSTS && tls->hosts[k]; k++) - ; + if (!tls->subject) + tls->subject = su_strlst_create(tls->home); /* Find matching subjectAltName.DNS */ for (i = 0; i < extcount; i++) { @@ -464,23 +446,18 @@ int tls_post_connection_check(tls_t *tls) for (j = 0; j < sk_CONF_VALUE_num(values); j++) { value = sk_CONF_VALUE_value(values, j); - if (strcmp(value->name, "DNS") == 0) { - if (k < TLS_MAX_HOSTS) { - tls->hosts[k] = tls_strdup(value->value); - k += tls->hosts[k] != NULL; - } - } + if (strcmp(value->name, "DNS") == 0) + su_strlst_dup_append(tls->subject, value->value); else if (strcmp(value->name, "URI") == 0) { - char const *uri = strchr(value->value, ':'); - if (uri ++ && k < TLS_MAX_HOSTS) { - tls->hosts[k] = tls_strdup(uri); - k += tls->hosts[k] != NULL; - } + char *uri = su_strlst_dup_append(tls->subject, value->value); + char const *url = strchr(uri, ':'); + if (url++) + su_strlst_append(tls->subject, url); } } } - if (k < TLS_MAX_HOSTS) { + { X509_NAME *subject; char name[256]; @@ -490,12 +467,12 @@ int tls_post_connection_check(tls_t *tls) name, sizeof name) > 0) { name[(sizeof name) - 1] = '\0'; - for (i = 0; tls->hosts[i]; i++) - if (strcasecmp(tls->hosts[i], name) == 0) + for (i = 0; i < su_strlst_len(tls->subject); i++) + if (strcasecmp(su_strlst_item(tls->subject, i), name) == 0) break; - if (i == k) - tls->hosts[k++] = tls_strdup(name); + if (i == su_strlst_len(tls->subject)) + su_strlst_dup_append(tls->subject, name); } } } @@ -507,33 +484,13 @@ int tls_post_connection_check(tls_t *tls) if (error == X509_V_OK) tls->verified = 1; + if (tls->accept && !tls->verify_incoming) + return X509_V_OK; + else if (!tls->accept && !tls->verify_outgoing) + return X509_V_OK; return error; } -int tls_check_hosts(tls_t *tls, char const *hosts[TLS_MAX_HOSTS]) -{ - int i, j; - - if (tls == NULL) { errno = EINVAL; return -1; } - if (!tls->verified) { errno = EAGAIN; return -1; } - - if (!hosts) - return 0; - - for (i = 0; hosts[i]; i++) { - for (j = 0; tls->hosts[j]; j++) { - if (strcasecmp(hosts[i], tls->hosts[j]) == 0) - break; - } - if (tls->hosts[j] == NULL) { - errno = EACCES; - return -1; - } - } - - return 0; -} - static int tls_error(tls_t *tls, int ret, char const *who, void *buf, int size) @@ -589,7 +546,7 @@ ssize_t tls_read(tls_t *tls) if (0) SU_DEBUG_1(("tls_read(%p) called on %s (events %u)\n", (void *)tls, - tls->type == tls_slave ? "server" : "client", + tls->accept ? "server" : "client", tls->read_events)); if (tls->read_buffer_len) @@ -601,19 +558,6 @@ ssize_t tls_read(tls_t *tls) if (ret <= 0) return tls_error(tls, ret, "tls_read: SSL_read", NULL, 0); - if (!tls->verified) { - int err = tls_post_connection_check(tls); - - if (err != X509_V_OK && - err != SSL_ERROR_SYSCALL && - err != SSL_ERROR_WANT_WRITE && - err != SSL_ERROR_WANT_READ) { - SU_DEBUG_1(( - "%s: server certificate doesn't verify\n", - "tls_read")); - } - } - return (ssize_t)(tls->read_buffer_len = ret); } @@ -694,13 +638,6 @@ ssize_t tls_write(tls_t *tls, void *buf, size_t size) tls->write_events = 0; - if (!tls->verified) { - if (tls_post_connection_check(tls) != X509_V_OK) { - SU_DEBUG_1(( - "tls_read: server certificate doesn't verify\n")); - } - } - ret = SSL_write(tls->con, buf, size); if (ret < 0) return tls_error(tls, ret, "tls_write: SSL_write", buf, size); @@ -748,3 +685,118 @@ int tls_events(tls_t const *tls, int mask) ((mask & SU_WAIT_IN) ? tls->read_events : 0) | ((mask & SU_WAIT_OUT) ? tls->write_events : 0); } + +int tls_connect(su_root_magic_t *magic, su_wait_t *w, tport_t *self) +{ + tport_master_t *mr = self->tp_master; + tport_tls_t *tlstp = (tport_tls_t *)self; + tls_t *tls; + int events = su_wait_events(w, self->tp_socket); + int error; + + SU_DEBUG_7(("%s(%p): events%s%s%s%s\n", __func__, (void *)self, + events & (SU_WAIT_CONNECT) ? " CONNECTING" : "", + events & SU_WAIT_IN ? " NEGOTIATING" : "", + events & SU_WAIT_ERR ? " ERROR" : "", + events & SU_WAIT_HUP ? " HANGUP" : "")); + +#if HAVE_POLL + assert(w->fd == self->tp_socket); +#endif + + if (events & SU_WAIT_ERR) + tport_error_event(self); + + if (events & SU_WAIT_HUP && !self->tp_closed) + tport_hup_event(self); + + if (self->tp_closed) + return 0; + + error = su_soerror(self->tp_socket); + if (error) { + tport_error_report(self, error, NULL); + return 0; + } + + if ((tls = tlstp->tlstp_context) == NULL) { + SU_DEBUG_3(("%s(%p): Error: no TLS context data for connected socket.\n", + __func__, tlstp)); + tport_close(self); + tport_set_secondary_timer(self); + return 0; + } + + if (self->tp_is_connected == 0) { + int ret, status; + + ret = tls->accept ? SSL_accept(tls->con) : SSL_connect(tls->con); + status = SSL_get_error(tls->con, ret); + + switch (status) { + case SSL_ERROR_WANT_READ: + /* OpenSSL is waiting for the peer to send handshake data */ + self->tp_events = SU_WAIT_IN | SU_WAIT_ERR | SU_WAIT_HUP; + su_root_eventmask(mr->mr_root, self->tp_index, + self->tp_socket, self->tp_events); + return 0; + + case SSL_ERROR_WANT_WRITE: + /* OpenSSL is waiting for the peer to receive handshake data */ + self->tp_events = SU_WAIT_IN | SU_WAIT_ERR | SU_WAIT_HUP | SU_WAIT_OUT; + su_root_eventmask(mr->mr_root, self->tp_index, + self->tp_socket, self->tp_events); + return 0; + + case SSL_ERROR_NONE: + /* TLS Handshake complete */ + if ( tls_post_connection_check(tls) == X509_V_OK ) { + su_wait_t wait[1] = {SU_WAIT_INIT}; + tport_master_t *mr = self->tp_master; + + su_root_deregister(mr->mr_root, self->tp_index); + self->tp_index = -1; + self->tp_events = SU_WAIT_IN | SU_WAIT_ERR | SU_WAIT_HUP; + + if ((su_wait_create(wait, self->tp_socket, self->tp_events) == -1) || + ((self->tp_index = su_root_register(mr->mr_root, wait, tport_wakeup, + self, 0)) == -1)) { + tport_close(self); + tport_set_secondary_timer(self); + return 0; + } + + tls->read_events = SU_WAIT_IN; + tls->write_events = 0; + self->tp_is_connected = 1; + self->tp_verified = tls->verified; + self->tp_subjects = tls->subject == NULL ? NULL : + su_strlst_dup(self->tp_home, tls->subject); + + if (tport_has_queued(self)) + tport_send_event(self); + else + tport_set_secondary_timer(self); + + return 0; + } + break; + + default: + { + char errbuf[64]; + ERR_error_string_n(status, errbuf, 64); + SU_DEBUG_3(("%s(%p): TLS setup failed (%s)\n", + __func__, self, errbuf)); + } + break; + } + } + + /* TLS Handshake Failed or Peer Certificate did not Verify */ + tport_close(self); + tport_set_secondary_timer(self); + + return 0; +} + diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.h b/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.h index f6fc2beaf3..caa8fe0398 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.h +++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport_tls.h @@ -39,6 +39,8 @@ #include #endif +#include "tport_internal.h" + SOFIA_BEGIN_DECLS #define TLS_MAX_HOSTS (16) @@ -64,6 +66,17 @@ typedef struct tls_issues_s { * used, it is 0. */ } tls_issues_t; +typedef struct tport_tls_s { + tport_t tlstp_tp[1]; + tls_t *tlstp_context; + char *tlstp_buffer; +} tport_tls_t; + +typedef struct tport_tls_primary_s { + tport_primary_t tlspri_pri[1]; + tls_t *tlspri_master; +} tport_tls_primary_t; + tls_t *tls_init_master(tls_issues_t *tls_issues); tls_t *tls_init_slave(tls_t *tls_master, int sock); tls_t *tls_init_client(tls_t *tls_master, int sock); @@ -74,11 +87,10 @@ void *tls_read_buffer(tls_t *tls, size_t N); int tls_want_read(tls_t *tls, int events); int tls_pending(tls_t const *tls); +int tls_connect(su_root_magic_t *magic, su_wait_t *w, tport_t *self); ssize_t tls_write(tls_t *tls, void *buf, size_t size); int tls_want_write(tls_t *tls, int events); -int tls_check_hosts(tls_t *tls, char const *hosts[TLS_MAX_HOSTS]); - int tls_events(tls_t const *tls, int flags); SOFIA_END_DECLS diff --git a/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_tls.c b/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_tls.c index a9432bcaab..c0bffdde13 100644 --- a/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_tls.c +++ b/libs/sofia-sip/libsofia-sip-ua/tport/tport_type_tls.c @@ -38,6 +38,8 @@ #include "config.h" +#define SU_WAKEUP_ARG_T struct tport_s + #include "tport_internal.h" #include @@ -89,19 +91,10 @@ static int tport_tls_events(tport_t *self, int events); static int tport_tls_recv(tport_t *self); static ssize_t tport_tls_send(tport_t const *self, msg_t *msg, msg_iovec_t iov[], size_t iovused); - -typedef struct -{ - tport_primary_t tlspri_pri[1]; - tls_t *tlspri_master; -} tport_tls_primary_t; - -typedef struct -{ - tport_t tlstp_tp[1]; - tls_t *tlstp_context; - char *tlstp_buffer; /**< 2k Buffer */ -} tport_tls_t; +static int tport_tls_accept(tport_primary_t *pri, int events); +static tport_t *tport_tls_connect(tport_primary_t *pri, su_addrinfo_t *ai, + tp_name_t const *tpn); +static void tport_tls_deliver(tport_t *self, msg_t *msg, su_time_t now); tport_vtable_t const tport_tls_vtable = { @@ -109,8 +102,8 @@ tport_vtable_t const tport_tls_vtable = sizeof (tport_tls_primary_t), tport_tls_init_primary, tport_tls_deinit_primary, - tport_accept, - NULL, + tport_tls_accept, + tport_tls_connect, sizeof (tport_tls_t), tport_tls_init_secondary, tport_tls_deinit_secondary, @@ -127,8 +120,8 @@ tport_vtable_t const tport_tls_client_vtable = sizeof (tport_tls_primary_t), tport_tls_init_client, tport_tls_deinit_primary, - tport_accept, - NULL, + tport_tls_accept, + tport_tls_connect, sizeof (tport_tls_t), tport_tls_init_secondary, tport_tls_deinit_secondary, @@ -527,3 +520,149 @@ ssize_t tport_tls_send(tport_t const *self, return size; } + +static +int tport_tls_accept(tport_primary_t *pri, int events) +{ + tport_t *self; + su_addrinfo_t ai[1]; + su_sockaddr_t su[1]; + socklen_t sulen = sizeof su; + su_socket_t s = INVALID_SOCKET, l = pri->pri_primary->tp_socket; + char const *reason = "accept"; + + if (events & SU_WAIT_ERR) + tport_error_event(pri->pri_primary); + + if (!(events & SU_WAIT_ACCEPT)) + return 0; + + memcpy(ai, pri->pri_primary->tp_addrinfo, sizeof ai); + ai->ai_canonname = NULL; + + s = accept(l, &su->su_sa, &sulen); + + if (s < 0) { + tport_error_report(pri->pri_primary, su_errno(), NULL); + return 0; + } + + ai->ai_addr = &su->su_sa, ai->ai_addrlen = sulen; + + /* Alloc a new transport object, then register socket events with it */ + if ((self = tport_alloc_secondary(pri, s, 1, &reason)) == NULL) { + SU_DEBUG_3(("%s(%p): incoming secondary on "TPN_FORMAT + " failed. reason = %s\n", __func__, pri, + TPN_ARGS(pri->pri_primary->tp_name), reason)); + return 0; + } + else { + tport_tls_t *tlstp = (tport_tls_t *)self; + tport_tls_primary_t *tlspri = (tport_tls_primary_t *)self->tp_pri; + int events = SU_WAIT_IN|SU_WAIT_ERR|SU_WAIT_HUP; + + SU_CANONIZE_SOCKADDR(su); + + if (/* Name this transport */ + tport_setname(self, pri->pri_protoname, ai, NULL) != -1 + /* Register this secondary */ + && + tport_register_secondary(self, tls_connect, events) != -1) { + + self->tp_conn_orient = 1; + self->tp_is_connected = 0; + + tlstp->tlstp_context = tls_init_slave(tlspri->tlspri_master, s); + + SU_DEBUG_5(("%s(%p): new connection from " TPN_FORMAT "\n", + __func__, (void *)self, TPN_ARGS(self->tp_name))); + + /* Return succesfully */ + return 0; + } + + /* Failure: shutdown socket, */ + tport_close(self); + tport_zap_secondary(self); + self = NULL; + } + + return 0; +} + +static +tport_t *tport_tls_connect(tport_primary_t *pri, + su_addrinfo_t *ai, + tp_name_t const *tpn) +{ + tport_t *self = NULL; + + su_socket_t s, server_socket; + int events = SU_WAIT_CONNECT | SU_WAIT_ERR; + + int err; + unsigned errlevel = 3; + char buf[TPORT_HOSTPORTSIZE]; + char const *what; + + s = su_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (s == INVALID_SOCKET) + goto sys_error; + + what = "tport_alloc_secondary"; + if ((self = tport_alloc_secondary(pri, s, 0, &what)) == NULL) + goto sys_error; + + self->tp_conn_orient = 1; + + if ((server_socket = pri->pri_primary->tp_socket) != INVALID_SOCKET) { + su_sockaddr_t susa; + socklen_t susalen = sizeof(susa); + + if (getsockname(server_socket, &susa.su_sa, &susalen) < 0) { + SU_DEBUG_3(("%s(%p): getsockname(): %s\n", + __func__, (void *)self, su_strerror(su_errno()))); + } else { + susa.su_port = 0; + if (bind(s, &susa.su_sa, susalen) < 0) { + SU_DEBUG_3(("%s(%p): bind(local-ip): %s\n", + __func__, (void *)self, su_strerror(su_errno()))); + } + } + } + + if (connect(s, ai->ai_addr, (socklen_t)(ai->ai_addrlen)) == SOCKET_ERROR) { + err = su_errno(); + if (!su_is_blocking(err)) + goto sys_error; + } + + if (tport_setname(self, tpn->tpn_proto, ai, tpn->tpn_canon) != -1 + && + tport_register_secondary(self, tls_connect, events) != -1) { + tport_tls_t *tlstp = (tport_tls_t *)self; + tport_tls_primary_t *tlspri = (tport_tls_primary_t *)self->tp_pri; + tlstp->tlstp_context = tls_init_client(tlspri->tlspri_master, s); + } + else + goto sys_error; + + SU_DEBUG_5(("%s(%p): connecting to " TPN_FORMAT "\n", + __func__, (void *)self, TPN_ARGS(self->tp_name))); + + tport_set_secondary_timer(self); + + return self; + +sys_error: + err = errno; + if (SU_LOG_LEVEL >= errlevel) + su_llog(tport_log, errlevel, "%s(%p): %s (pf=%d %s/%s): %s\n", + __func__, (void *)pri, what, ai->ai_family, tpn->tpn_proto, + tport_hostport(buf, sizeof(buf), (void *)ai->ai_addr, 2), + su_strerror(err)); + tport_zap_secondary(self); + su_seterrno(err); + return NULL; +} +