/* * 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); if ( c->proto_flags & F_TLS_DO_ACCEPT ) { ret = tls_update_fd(c, c->fd); if (!ret) ret = tls_accept(c, NULL); } else if ( c->proto_flags & F_TLS_DO_CONNECT ) { ret = tls_update_fd(c, c->fd); if (!ret) ret = tls_connect(c, NULL); } lock_release(&c->write_lock); return ret; }
/* * 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,struct tcp_req *r) { int bytes_free; int fd, read; fd = c->fd; bytes_free = TCP_BUF_SIZE - (int) (r->pos - r->buf); if (bytes_free == 0) { LM_ERR("TLS 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; }
/* * 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 */ LM_DBG("closing TLS connection\n"); tls_update_fd(c, fd); tls_shutdown(c); }
/* * 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, NULL); break; case S_CONN_CONNECT: ret = tls_update_fd(c, c->fd); if (!ret) ret = tls_connect(c, NULL); break; default: /* fall through */ break; } lock_release(&c->write_lock); return ret; }
static void tls_conn_clean(struct tcp_connection* c) { /* * runs within global tcp lock */ LM_DBG("entered\n"); if (c->extra_data) { tls_update_fd(c,c->s); tls_conn_shutdown(c); SSL_free((SSL *) c->extra_data); c->extra_data = 0; } }
/* * fixme: probably does not work correctly */ size_t tls_blocking_write(struct tcp_connection *c, int fd, const char *buf, size_t len) { #define MAX_SSL_RETRIES 32 int written, n; int timeout, retries; struct pollfd pf; pf.fd = fd; written = 0; retries = 0; if (c->state!=S_CONN_OK) { LM_ERR("TLS broken connection\n"); goto error; } lock_get(&c->write_lock); if (tls_update_fd(c, fd) < 0) goto error; timeout = tls_send_timeout; again: n = 0; pf.events = 0; if ( c->proto_flags & F_TLS_DO_ACCEPT ) { if (tls_accept(c, &(pf.events)) < 0) goto error; timeout = tls_handshake_timeout; } else if ( c->proto_flags & F_TLS_DO_CONNECT ) { if (tls_connect(c, &(pf.events)) < 0) goto error; timeout = tls_handshake_timeout; } else { n = tls_write(c, fd, buf, len, &(pf.events)); timeout = tls_send_timeout; } if (n < 0) { LM_ERR("TLS failed to send data\n"); goto error; } /* nothing happens */ if (n==0) { retries++; /* avoid looping */ if (retries==MAX_SSL_RETRIES) { LM_ERR("too many retries with no operation\n"); goto error; } } else { /* reset the retries if we succeded in doing something*/ retries = 0; } written += n; if (n < len) { /* * partial write */ buf += n; len -= n; } else { /* * successful full write */ lock_release(&c->write_lock); return written; } if (pf.events == 0) pf.events = POLLOUT; poll_loop: while (1) { n = poll(&pf, 1, timeout); if (n < 0) { if (errno == EINTR) continue; /* signal, ignore */ else if (errno != EAGAIN && errno != EWOULDBLOCK) { LM_ERR("TLS poll failed: %s [%d]\n",strerror(errno), errno); goto error; } else goto poll_loop; } else if (n == 0) { /* * timeout */ LM_ERR("TLS 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)) { LM_ERR("TLS 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: lock_release(&c->write_lock); return -1; }