libpolluxd/px_connection.c

163 lines
4.2 KiB
C

#include <px_common.h>
#include <px_connection.h>
#include <unistd.h>
#include <px_log.h>
#include <px_listener.h>
#include <errno.h>
#include <openssl/err.h>
static void px_connection_init(px_connection_t* conn) {
memset(conn, 0, sizeof(*conn));
conn->sock = -1;
}
int px_connection_read(px_connection_t* conn, char* buf, size_t buflen, size_t* rb) {
// TODO replace this with some BIO marshalling for finer control over what is
// happening, and get rid of need for ignoring SIGPIPE
int r;
while ((r = SSL_read_ex(conn->ssl, buf, buflen, rb)) != 1) {
int err = SSL_get_error(conn->ssl, r);
if (err == SSL_ERROR_WANT_READ)
continue;
break;;
}
return r;
}
int px_connection_write(px_connection_t* conn, char const* buf, size_t buflen, size_t* wb) {
int r;
while ((r = SSL_write_ex(conn->ssl, buf, buflen, wb)) != 1) {
int err = SSL_get_error(conn->ssl, r);
if (err == SSL_ERROR_WANT_WRITE)
continue;
break;
}
return r;
}
void px_connection_shutdown(px_connection_t* conn) {
if (conn->ssl) {
SSL_shutdown(conn->ssl);
SSL_free(conn->ssl);
conn->ssl = NULL;
}
free(conn->client_addr);
conn->client_addr = 0;
if (conn->sock >= 0) {
close(conn->sock);
conn->sock = -1;
}
}
static void px_connection_destroy(px_connection_t* conn) {
if (!conn)
return;
px_connection_shutdown(conn);
}
void px_connection_delete(px_connection_t* conn) {
px_connection_destroy(conn);
free(conn);
}
//typedef void(*px_req_callback)(px_listener_t* lsn, px_request_t* req, void* data);
//void px_listener_callback_serve_file(px_listener_t* lsn, px_request_t* req, void* data);
// return: timeout, ok, error?
px_connection_t* px_connection_accept(px_listener_t* lsn) {
int r;
int client_sock = -1;
struct sockaddr* client_ai = NULL;
socklen_t client_ai_len = 0;
SSL* ssl = NULL;
// assertion checks
px_log_assert(lsn->ssl_ctx != NULL, "lsn ssl context not defined");
px_log_assert(lsn->bind_addr->sa_family == AF_INET, // TODO remove once IPv6 is supported
"unsupported sa_family for lsn connection");
if (lsn->bind_addr->sa_family == AF_INET) { // AF_INET
client_ai = (struct sockaddr*)PX_NEW(struct sockaddr_in);
if (!client_ai) {
int e = errno;
px_log_error("could not allocate sockaddr for client connection: %d, %s", e, strerror(e));
errno = e;
goto ERR;
}
client_ai_len = sizeof(struct sockaddr_in);
} // TODO else AF_INET6
// TODO else AF_UNIX
// TODO select/poll for available connection
{
socklen_t orig_client_ai_len = client_ai_len;
client_sock = accept(lsn->sock, client_ai, &client_ai_len);
if (client_sock < 0) {
int e = errno;
px_log_error("could not accept(): %d, %s", e, strerror(e));
errno = e;
goto ERR;
}
if (client_ai_len > orig_client_ai_len) {
// this shouldn't happen? but good to check
px_log_warn("client addrinfo greater than allocated structure: %d > %d",
client_ai_len,
orig_client_ai_len);
client_ai_len = orig_client_ai_len;
}
}
ssl = SSL_new(lsn->ssl_ctx);
if (SSL_set_fd(ssl, client_sock) != 1) {
px_log_error("could not set fd on ssl object: %s", ERR_reason_error_string(ERR_get_error()));
errno = EBADF;
goto ERR;
}
r = SSL_accept(ssl);
if (r <= 0) { // failure, no connection or abandoned connection
px_log_error("could not accept connection on ssl: %d, %s", r, ERR_reason_error_string(ERR_get_error()));
errno = ECONNABORTED;
goto ERR;
}
//px_log_debug("accepted connection");
px_connection_t* conn = PX_NEW_UNINITIALIZED(px_connection_t);
if (!conn) {
int e = errno;
px_log_error("could not allocate connection structure: %d, %s", e, strerror(e));
errno = e;
goto ERR;
}
px_connection_init(conn);
conn->listener = lsn;
conn->sock = client_sock;
conn->ssl = ssl;
conn->client_addr = client_ai;
conn->client_addr_len = client_ai_len;
return conn;
ERR:
{
int saved_errno = errno;
if (ssl) {
SSL_shutdown(ssl);
SSL_free(ssl);
}
free(client_ai);
if (client_sock >= 0)
close(client_sock);
errno = saved_errno;
}
return NULL;
}