/*
* $Id: tls_server.c 1782 2007-03-09 13:04:51Z bogdan_iancu $
*
* Copyright (C) 2001-2003 FhG Fokus
* Copyright (C) 2004,2005 Free Software Foundation, Inc.
* Copyright (C) 2006 enum.at
*
* This file is part of openser, a free SIP server.
*
* openser is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version
*
* openser is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "../dprint.h"
#include "tls_server.h"
#include "tls_config.h"
#include "tls_init.h"
#include "tls_domain.h"
#include "../ip_addr.h"
#include <sys/poll.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include "../mem/shm_mem.h"
#include "../timer.h"
#include "../usr_avp.h"
#include "../ut.h"
/*
* Open questions:
*
* - what would happen when select exits, connection is passed
* to reader to perform read, but another process would acquire
* the same connection meanwhile, performs a write and finishes
* accept/connect on behalf of the reader process, thus the
* reader process would have nothing to read ? (resolved)
*
* - What happens if SSL_accept or SSL_connect gets called on
* already established connection (c->S_CONN_OK) ? We could
* save some locking provided that the functions do not screw
* up the connection (in tcp_fix_read_conn we would not have
* to lock before the switch).
*
* - tls_blocking_write needs fixing..
*
* - we need to protect ctx by a lock -- it is in shared memory
* and may be accessed simultaneously
*/
/*
* Update ssl structure with new fd
*/
static int
tls_update_fd(struct tcp_connection *c, int fd)
{
/*
* must be run from within a lock
*/
SSL *ssl;
ssl = (SSL *) c->extra_data;
if (!SSL_set_fd(ssl, fd)) {
LOG(L_ERR, "tls_update_fd: Error while assigning socket to ssl\n");
return -1;
}
DBG("tls_update_fd: New fd is %d\n", fd);
return 0;
}
/*
* dump ssl error stack
*/
void
tls_print_errstack(void)
{
int code;
while ((code = ERR_get_error())) {
LOG(L_ERR, "tls_error: %s\n", ERR_error_string(code, 0));
}
}
static void tls_dump_cert_info(char* s, X509* cert)
{
char* subj;
char* issuer;
subj = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
issuer = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
DBG("%s subject:%s\n", s ? s : "", subj);
DBG("%s issuer: %s\n", s ? s : "", issuer);
OPENSSL_free(subj);
OPENSSL_free(issuer);
}
static void tls_dump_verification_failure(long verification_result)
{
switch(verification_result) {
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
DBG("verification failure: unable to get issuer certificate\n");
break;
case X509_V_ERR_UNABLE_TO_GET_CRL:
DBG("verification failure: unable to get certificate CRL\n");
break;
case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
DBG("verification failure: unable to decrypt certificate's signature\n");
break;
case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
DBG("verification failure: unable to decrypt CRL's signature\n");
break;
case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
DBG("verification failure: unable to decode issuer public key\n");
break;
case X509_V_ERR_CERT_SIGNATURE_FAILURE:
DBG("verification failure: certificate signature failure\n");
break;
case X509_V_ERR_CRL_SIGNATURE_FAILURE:
DBG("verification failure: CRL signature failure\n");
break;
case X509_V_ERR_CERT_NOT_YET_VALID:
DBG("verification failure: certificate is not yet valid\n");
break;
case X509_V_ERR_CERT_HAS_EXPIRED:
DBG("verification failure: certificate has expired\n");
break;
case X509_V_ERR_CRL_NOT_YET_VALID:
DBG("verification failure: CRL is not yet valid\n");
break;
case X509_V_ERR_CRL_HAS_EXPIRED:
DBG("verification failure: CRL has expired\n");
break;
case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
DBG("verification failure: format error in certificate's notBefore field\n");
break;
case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
DBG("verification failure: format error in certificate's notAfter field\n");
break;
case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
DBG("verification failure: format error in CRL's lastUpdate field\n");
break;
case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
DBG("verification failure: format error in CRL's nextUpdate field\n");
break;
case X509_V_ERR_OUT_OF_MEM:
DBG("verification failure: out of memory\n");
break;
case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
DBG("verification failure: self signed certificate\n");
break;
case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
DBG("verification failure: self signed certificate in certificate chain\n");
break;
case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
DBG("verification failure: unable to get local issuer certificate\n");
break;
case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
DBG("verification failure: unable to verify the first certificate\n");
break;
case X509_V_ERR_CERT_CHAIN_TOO_LONG:
DBG("verification failure: certificate chain too long\n");
break;
case X509_V_ERR_CERT_REVOKED:
DBG("verification failure: certificate revoked\n");
break;
case X509_V_ERR_INVALID_CA:
DBG("verification failure: invalid CA certificate\n");
break;
case X509_V_ERR_PATH_LENGTH_EXCEEDED:
DBG("verification failure: path length constraint exceeded\n");
break;
case X509_V_ERR_INVALID_PURPOSE:
DBG("verification failure: unsupported certificate purpose\n");
break;
case X509_V_ERR_CERT_UNTRUSTED:
DBG("verification failure: certificate not trusted\n");
break;
case X509_V_ERR_CERT_REJECTED:
DBG("verification failure: certificate rejected\n");
break;
case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
DBG("verification failure: subject issuer mismatch\n");
break;
case X509_V_ERR_AKID_SKID_MISMATCH:
DBG("verification failure: authority and subject key identifier mismatch\n");
break;
case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
DBG("verification failure: authority and issuer serial number mismatch\n");
break;
case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
DBG("verification failure: key usage does not include certificate signing\n");
break;
case X509_V_ERR_APPLICATION_VERIFICATION:
DBG("verification failure: application verification failure\n");
break;
}
}
/*
* Wrapper around SSL_accept, returns -1 on error, 0 on success
*/
static int
tls_accept(struct tcp_connection *c)
{
int ret, err;
SSL *ssl;
X509* cert;
if (c->state != S_CONN_ACCEPT) {
LOG(L_ERR,
"tcp_accept: Invalid connection state (bug in TLS code)\n");
return -1;
}
ssl = (SSL *) c->extra_data;
#ifndef OPENSSL_NO_KRB5
if ( ssl->kssl_ctx==NULL )
ssl->kssl_ctx = kssl_ctx_new( );
#endif
ret = SSL_accept(ssl);
#ifndef OPENSSL_NO_KRB5
if ( ssl->kssl_ctx ) {
kssl_ctx_free( ssl->kssl_ctx );
ssl->kssl_ctx = 0;
}
#endif
if (ret > 0) {
DBG("tls_accept: TLS handshake successful\n");
c->state = S_CONN_OK;
DBG("tls_accept: new connection from %s:%d using %s %s %d\n",
ip_addr2a(&c->rcv.src_ip), c->rcv.src_port,
SSL_get_cipher_version(ssl), SSL_get_cipher_name(ssl),
SSL_get_cipher_bits(ssl, 0)
);
DBG("tls_accept: local socket: %s:%d\n",
ip_addr2a(&c->rcv.dst_ip), c->rcv.dst_port
);
cert = SSL_get_peer_certificate(ssl);
if (cert != 0) {
tls_dump_cert_info("tls_accept: client certificate", cert);
if (SSL_get_verify_result(ssl) != X509_V_OK) {
LOG(L_WARN, "WARNING: tls_accept: client certificate "
"verification failed!!!\n");
tls_dump_verification_failure(SSL_get_verify_result(ssl));
}
X509_free(cert);
} else {
LOG(L_INFO, "tls_accept: client did not present a certificate\n");
}
cert = SSL_get_certificate(ssl);
if (cert != 0) {
tls_dump_cert_info("tls_accept: local (server) certificate", cert);
} else {
/* this should not happen, servers always present a cert */
LOG(L_ERR, "tls_accept: ERRROR: local TLS server domain has no "
"certificate\n");
}
return 0;
} else {
err = SSL_get_error(ssl, ret);
switch (err) {
case SSL_ERROR_ZERO_RETURN:
DBG("tls_accept: SSH handshake failed cleanly\n");
c->state = S_CONN_BAD;
return -1;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
/*
* nothing to do here
*/
return 0;
default:
LOG(L_ERR, "tls_accept: Error in SSL:\n");
c->state = S_CONN_BAD;
tls_print_errstack();
return -1;
}
}
LOG(L_ERR, "tls_accept: Bug in tls_accept\n");
return -1;
}
/*
* wrapper around SSL_connect, returns 0 on success, -1 on error
*/
static int
tls_connect(struct tcp_connection *c)
{
int ret, err;
SSL *ssl;
X509* cert;
if (c->state != S_CONN_CONNECT) {
LOG(L_ERR,
"tls_connect: Invalid connection state (bug in TLS code)\n");
return -1;
}
ssl = (SSL *) c->extra_data;
ret = SSL_connect(ssl);
if (ret > 0) {
DBG("tls_connect: SSL/TLS connect successuful\n");
c->state = S_CONN_OK;
DBG("tls_connect: new connection to %s:%d using %s %s %d\n",
ip_addr2a(&c->rcv.src_ip), c->rcv.src_port,
SSL_get_cipher_version(ssl), SSL_get_cipher_name(ssl),
SSL_get_cipher_bits(ssl, 0)
);
DBG("tls_connect: sending socket: %s:%d \n",
ip_addr2a(&c->rcv.dst_ip), c->rcv.dst_port
);
cert = SSL_get_peer_certificate(ssl);
if (cert != 0) {
tls_dump_cert_info("tls_connect: server certificate", cert);
if (SSL_get_verify_result(ssl) != X509_V_OK) {
LOG(L_WARN, "WARNING: tls_connect: server certificate "
"verification failed!!!\n");
tls_dump_verification_failure(SSL_get_verify_result(ssl));
}
X509_free(cert);
} else {
/* this should not happen, servers always present a cert */
LOG(L_ERR, "tls_connect: ERRROR: server did not present a "
"certificate\n");
}
cert = SSL_get_certificate(ssl);
if (cert != 0) {
tls_dump_cert_info("tls_connect: local (client) certificate",
cert);
} else {
LOG(L_INFO, "tls_connect: local TLS client domain does not have "
"a certificate\n");
}
return 0;
} else {
err = SSL_get_error(ssl, ret);
switch (err) {
case SSL_ERROR_ZERO_RETURN:
DBG("tls_connect: SSL_connect failed cleanly\n");
c->state = S_CONN_BAD;
return -1;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
/*
* nothing to do here
*/
return 0;
default:
LOG(L_ERR, "tls_connect: Error in SSL:\n");
c->state = S_CONN_BAD;
tls_print_errstack();
return -1;
}
}
LOG(L_ERR, "tls_connect: Bug in tls_connect\n");
return -1;
}
/*
* wrapper around SSL_shutdown, returns -1 on error, 0 on success
*/
static int
tls_shutdown(struct tcp_connection *c)
{
int ret,
err;
SSL *ssl;
/*
* we do not implement full ssl shutdown
*/
ssl = (SSL *) c->extra_data;
if (ssl == 0) {
LOG(L_ERR, "tls_shutdown: No ssl data\n");
return -1;
}
ret = SSL_shutdown(ssl);
if (ret == 1) {
DBG("tls_shutdown: Shutdown successful\n");
return 0;
} else if (ret == 0) {
DBG("tls_shutdown: First phase of 2-way handshake completed "
"succesfuly\n");
return 0;
} else {
err = SSL_get_error(ssl, ret);
switch (err) {
case SSL_ERROR_ZERO_RETURN:
c->state = S_CONN_EOF;
return 0;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
c->state = S_CONN_EOF;
return 0;
default:
LOG(L_ERR, "tls_shutdown: Error in SSL:\n");
c->state = S_CONN_BAD;
tls_print_errstack();
return -1;
}
}
LOG(L_ERR, "tls_shutdown: Bug in tls_shutdown\n");
return -1;
}
/*
* Wrapper around SSL_write, returns number of bytes written on success, *
* -1 on error, 0 when it would block
*/
static int
tls_write(struct tcp_connection *c, int fd, const void *buf, size_t len)
{
int ret,
err;
/*
* runs within write lock, no need to lock here
*/
SSL *ssl;
ssl = (SSL *) c->extra_data;
ret = SSL_write(ssl, buf, len);
if (ret > 0) {
DBG("tls_write: Write was successful (%d bytes)\n", ret);
return ret;
} else {
err = SSL_get_error(ssl, ret);
switch (err) {
case SSL_ERROR_ZERO_RETURN:
DBG("tls_write: Connection closed cleanly\n");
c->state = S_CONN_EOF;
return -1;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
return 0;
default:
LOG(L_ERR, "tls_write: Error in SSL:\n");
c->state = S_CONN_BAD;
tls_print_errstack();
return -1;
}
}
LOG(L_ERR, "tls_write: Bug in tls_write\n");
return -1;
}
/*
* Wrapper around SSL_read
*/
/*
* returns number of bytes read, 0 on eof and transits into S_CONN_EOF, -1
* on error
*/
static int
_tls_read(struct tcp_connection *c, void *buf, size_t len)
{
int ret,
err;
SSL *ssl;
ssl = c->extra_data;
ret = SSL_read(ssl, buf, len);
if (ret > 0) {
DBG("_tls_read: %d bytes read\n", ret);
return ret;
} else {
err = SSL_get_error(ssl, ret);
switch (err) {
case SSL_ERROR_ZERO_RETURN:
DBG("_tls_read: Connection closed cleanly\n");
/*
* mark end of file
*/
c->state = S_CONN_EOF;
return 0;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
return 0;
default:
LOG(L_ERR, "_tls_read: Error in SSL:\n");
c->state = S_CONN_BAD;
tls_print_errstack();
return -1;
}
}
LOG(L_ERR, "_tls_read: Bug in _tls_read\n");
return -1;
}
/*
* Called when new tcp connection is accepted or connected, create ssl
* data structures here, there is no need to acquire any lock, because the
* connection is being created by a new process and on other process has
* access to it yet, this is called before adding the tcp_connection
* structure into the hash
*/
int
tls_tcpconn_init(struct tcp_connection *c, int sock)
{
struct tls_domain *dom;
struct usr_avp *avp;
int_str val;
int_str avp_tlscdom_name;
unsigned short avp_tlscdom_name_type;
// we use integer name avp, configured via openser.cfg
avp_tlscdom_name.n = tls_client_domain_avp;
avp_tlscdom_name_type = 0;
/*
* new connection within a single process, no lock necessary
*/
DBG("tls_tcpconn_init: Entered: Creating a whole new ssl connection\n");
/*
* do everything tcpconn_new wouldn't do when TLS
*/
c->type = PROTO_TLS;
c->rcv.proto = PROTO_TLS;
c->flags = 0;
c->timeout = get_ticks() + DEFAULT_TCP_CONNECTION_LIFETIME;
if (c->state == S_CONN_ACCEPT) {
DBG("tls_tcpconn_init: Looking up socket based TLS server "
"domain [%s:%d]\n", ip_addr2a(&c->rcv.dst_ip), c->rcv.dst_port);
dom = tls_find_server_domain(&c->rcv.dst_ip, c->rcv.dst_port);
if (dom) {
DBG("tls_tcpconn_init: Found socket based TLS server domain "
"[%s:%d]\n", ip_addr2a(&dom->addr), dom->port);
c->extra_data = SSL_new(dom->ctx);
} else {
LOG(L_ERR,"tls_tcpconn_init: ERROR: no TLS server domain found\n");
return -1;
}
} else if (c->state == S_CONN_CONNECT) {
avp = NULL;
if (avp_tlscdom_name.n) {
avp = search_first_avp(avp_tlscdom_name_type, avp_tlscdom_name,
&val, 0);
} else {
DBG("tls_tcpconn_init: name based TLS client domains are"
" disabled\n");
}
if (!avp) {
DBG("tls_tcpconn_init: no TLS client doman AVP set, looking "
"for socket based TLS client domain\n");
dom = tls_find_client_domain(&c->rcv.src_ip, c->rcv.src_port);
if (dom) {
DBG("tls_tcpconn_init: Found socket based TLS client domain "
"[%s:%d]\n", ip_addr2a(&dom->addr), dom->port);
c->extra_data = SSL_new(dom->ctx);
} else {
LOG(L_ERR,"tls_tcpconn_init: ERROR: no TLS client "
"domain found\n");
return -1;
}
} else {
DBG("tls_tcpconn_init: TLS client domain AVP found = '%.*s'\n",
val.s.len, ZSW(val.s.s));
dom = tls_find_client_domain_name(val.s);
if (dom) {
DBG("tls_tcpconn_init: Found name based TLS client domain "
"'%.*s'\n", val.s.len, ZSW(val.s.s));
c->extra_data = SSL_new(dom->ctx);
} else {
DBG("tls_tcpconn_init: No name based TLS client domain found, "
"trying socket based TLS client domains\n");
dom = tls_find_client_domain(&c->rcv.src_ip, c->rcv.src_port);
if (dom) {
DBG("tls_tcpconn_init: Found socket based TLS client "
"domain [%s:%d]\n",
ip_addr2a(&dom->addr), dom->port);
c->extra_data = SSL_new(dom->ctx);
} else {
LOG(L_ERR,"tls_tcpconn_init: ERROR: no TLS client "
"domain found\n");
return -1;
}
}
}
} else {
LOG(L_ERR,"ERROR:tls_tcpconn_init: Invalid connection "
"state (bug in TCP code)\n");
return -1;
}
if (!c->extra_data) {
LOG(L_ERR,"ERROR:tls_tcpconn_init: failed to create SSL structure\n");
return -1;
}
#ifndef OPENSSL_NO_KRB5
if ( ((SSL *)c->extra_data)->kssl_ctx ) {
kssl_ctx_free( ((SSL *)c->extra_data)->kssl_ctx );
((SSL *)c->extra_data)->kssl_ctx = 0;
}
#endif
if (c->state == S_CONN_ACCEPT) {
DBG("tls_tcpconn_init: Setting in ACCEPT mode (server)\n");
SSL_set_accept_state((SSL *) c->extra_data);
} else if (c->state == S_CONN_CONNECT) {
DBG("tls_tcpconn_init: Setting in CONNECT mode (client)\n");
SSL_set_connect_state((SSL *) c->extra_data);
}
return 0;
}
/*
* clean the extra data upon connection shut down
*/
void
tls_tcpconn_clean(struct tcp_connection *c)
{
/*
* runs within global tcp lock
*/
DBG("tls_tcpconn_clean: Entered\n");
if (c->extra_data) {
SSL_free((SSL *) c->extra_data);
c->extra_data = 0;
}
}
/*
* perform one-way shutdown, do not wait fro notify from the remote peer
*/
void
tls_close(struct tcp_connection *c, int fd)
{
/*
* runs within global tcp lock
*/
DBG("tls_close: Closing SSL connection\n");
tls_update_fd(c, fd);
tls_shutdown(c);
}
/*
* This is shamelessly stolen tsend_stream from tsend.c
*/
/*
* fixme: probably does not work correctly
*/
size_t
tls_blocking_write(struct tcp_connection *c, int fd, const char *buf,
size_t len)
{
int written,
n;
int timeout;
struct pollfd pf;
pf.fd = fd;
pf.events = POLLOUT | POLLIN; /* we need both because of ssl
* library */
/* DBG("tls_blocking_write: Entered\n"); //noisy */
written = 0;
if (tls_update_fd(c, fd) < 0)
goto error;
timeout = tls_send_timeout;
again:
n = 0;
switch (c->state) {
case S_CONN_ACCEPT:
if (tls_accept(c) < 0)
goto error;
timeout = tls_handshake_timeout * 1000;
break;
case S_CONN_CONNECT:
if (tls_connect(c) < 0)
goto error;
timeout = tls_handshake_timeout * 1000;
break;
case S_CONN_OK:
n = tls_write(c, fd, buf, len);
timeout = tls_send_timeout * 1000;
break;
default:
LOG(L_ERR, "tls_blocking_write: Broken connection\n");
goto error;
}
if (n < 0) {
LOG(L_ERR, "tls_blocking_write: failed to send data\n");
goto error;
}
written += n;
if (n < len) {
/*
* partial write
*/
buf += n;
len -= n;
} else {
/*
* successful full write
*/
return written;
}
poll_loop:
while (1) {
/*
* keep tls_send_timeout in seconds to be compatible with
* tcp_send_timeout
*/
n = poll(&pf, 1, timeout);
if (n < 0) {
if (errno == EINTR)
continue; /* signal, ignore */
else if (errno != EAGAIN && errno != EWOULDBLOCK) {
LOG(L_ERR, "tls_blocking_write: poll failed: %s [%d]\n",
strerror(errno), errno);
goto error;
} else
goto poll_loop;
} else if (n == 0) {
/*
* timeout
*/
LOG(L_ERR, "tls_blocking_write: send timeout (%d)\n", timeout);
goto error;
}
if (pf.revents & POLLOUT || pf.revents & POLLIN) {
/*
* we can read or write again
*/
goto again;
} else if (pf.revents & (POLLERR | POLLHUP | POLLNVAL)) {
LOG(L_ERR, "tls_blocking_write: bad poll flags %x\n",
pf.revents);
goto error;
}
/*
* if POLLPRI or other non-harmful events happened, continue (
* although poll should never signal them since we're not
* interested in them => we should never reach this point)
*/
}
error:
return -1;
}
/*
* called only when a connection is in S_CONN_OK, we do not have to care
* about accepting or connecting here, each modification of ssl data
* structures has to be protected, another process might ask for the same
* connection and attempt write to it which would result in updating the
* ssl structures
*/
size_t
tls_read(struct tcp_connection * c)
{
/*
* no lock acquired
*/
/*
* shamelessly stolen from tcp_read
*/
int bytes_free;
struct tcp_req *r;
int fd,
read;
r = &c->req;
fd = c->fd;
bytes_free = TCP_BUF_SIZE - (int) (r->pos - r->buf);
if (bytes_free == 0) {
LOG(L_ERR, "tls_read: buffer overrun, dropping\n");
r->error = TCP_REQ_OVERRUN;
return -1;
}
/*
* ssl structures may be accessed from several processes, we need to
* protect each access and modification by a lock
*/
lock_get(&c->write_lock);
tls_update_fd(c, fd);
read = _tls_read(c, r->pos, bytes_free);
lock_release(&c->write_lock);
if (read > 0)
r->pos += read;
return read;
}
/*
* called before tls_read, the this function should attempt tls_accept or
* tls_connect depending on the state of the connection, if this function
* does not transit a connection into S_CONN_OK then tcp layer would not
* call tcp_read
*/
int
tls_fix_read_conn(struct tcp_connection *c)
{
/*
* no lock acquired
*/
int ret;
ret = 0;
/*
* We have to acquire the lock before testing c->state, otherwise a
* writer could modify the structure if it gets preempted and has
* something to write
*/
lock_get(&c->write_lock);
switch (c->state) {
case S_CONN_ACCEPT:
ret = tls_update_fd(c, c->fd);
if (!ret)
ret = tls_accept(c);
break;
case S_CONN_CONNECT:
ret = tls_update_fd(c, c->fd);
if (!ret)
ret = tls_connect(c);
break;
default: /* fall through */
break;
}
lock_release(&c->write_lock);
return ret;
}
syntax highlighted by Code2HTML, v. 0.9.1