163 lines
4.2 KiB
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;
|
|
}
|
|
|