174 lines
4.6 KiB
C
174 lines
4.6 KiB
C
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
|
|
#include <assert.h>
|
|
#include <connection.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <netdb.h>
|
|
#include <unistd.h>
|
|
|
|
#if INTERFACE
|
|
|
|
#include <ev.h>
|
|
#include <tls.h>
|
|
|
|
typedef void (*ConnectionCallback)(EV_P_ Connection *);
|
|
|
|
struct Connection {
|
|
ev_io watcher;
|
|
struct tls *ctx;
|
|
ConnectionCallback success_cb;
|
|
ConnectionCallback failure_cb;
|
|
Buffer read_buf;
|
|
Buffer write_buf;
|
|
};
|
|
|
|
#define connection_set_failure_cb(connection, callback) \
|
|
do { \
|
|
(connection)->failure_cb = callback; \
|
|
} while (0)
|
|
|
|
#define connection_set_cb(connection, callback) \
|
|
do { \
|
|
(connection)->success_cb = callback; \
|
|
} while (0)
|
|
|
|
#define connection_read_buf(connection) &((connection)->read_buf)
|
|
#define connection_write_buf(connection) &((connection)->write_buf)
|
|
|
|
#endif
|
|
|
|
int connection_open(EV_P_ Connection *connection, struct tls_config *config,
|
|
const char *servername, const char *serverport) {
|
|
struct addrinfo hints, *res, *res0;
|
|
int error, fd, tmp;
|
|
const char *cause;
|
|
|
|
(void)(EV_A_ 0);
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = AF_UNSPEC;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
error = getaddrinfo(servername, serverport, &hints, &res0);
|
|
if (error) {
|
|
warnx("getaddrinfo: %s", gai_strerror(error));
|
|
goto err_getaddrinfo;
|
|
}
|
|
for (res = res0; res; res = res->ai_next) {
|
|
fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
|
if (fd == -1) {
|
|
cause = "socket";
|
|
continue;
|
|
}
|
|
if (connect(fd, res->ai_addr, res->ai_addrlen) == -1) {
|
|
tmp = errno;
|
|
close(fd);
|
|
errno = tmp;
|
|
cause = "connect";
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
if (!fd) {
|
|
warn("%s", cause);
|
|
goto err_connect;
|
|
}
|
|
ev_io_init(&connection->watcher, NULL, fd, 0);
|
|
connection->ctx = tls_client();
|
|
if (!connection->ctx) {
|
|
warnx("tls_client failed");
|
|
goto err_tls_client;
|
|
}
|
|
if (tls_configure(connection->ctx, config)) {
|
|
warnx("tls_configure: %s", tls_error(connection->ctx));
|
|
goto err_tls_configure;
|
|
}
|
|
if (tls_connect_socket(connection->ctx, fd, servername)) {
|
|
warnx("tls_connect: %s", tls_error(connection->ctx));
|
|
goto err_tls_connect_socket;
|
|
}
|
|
freeaddrinfo(res0);
|
|
return 0;
|
|
|
|
err_tls_connect_socket:
|
|
err_tls_configure:
|
|
tls_free(connection->ctx);
|
|
|
|
err_tls_client:
|
|
close(fd);
|
|
|
|
err_connect:
|
|
freeaddrinfo(res0);
|
|
|
|
err_getaddrinfo:
|
|
return -1;
|
|
}
|
|
|
|
void connection_read_some(EV_P_ Connection *connection) {
|
|
assert(connection->success_cb && connection->failure_cb);
|
|
ev_set_cb(&connection->watcher, connection_read_some_cb);
|
|
ev_invoke(EV_A_ & connection->watcher, 0);
|
|
}
|
|
|
|
static void connection_read_some_cb(EV_P_ ev_io *watcher, int revents) {
|
|
Connection *connection = (Connection *)watcher;
|
|
ssize_t n;
|
|
|
|
(void)revents;
|
|
ev_io_stop(EV_A_ watcher);
|
|
n = tls_read(connection->ctx, buffer_data_remaining(&connection->read_buf),
|
|
buffer_size_remaining(&connection->read_buf));
|
|
if (n == TLS_WANT_POLLIN) {
|
|
ev_io_modify(watcher, EV_READ);
|
|
ev_io_start(EV_A_ watcher);
|
|
} else if (n == TLS_WANT_POLLOUT) {
|
|
ev_io_modify(watcher, EV_WRITE);
|
|
ev_io_start(EV_A_ watcher);
|
|
} else if (n == -1) {
|
|
warnx("tls_read: %s", tls_error(connection->ctx));
|
|
connection->failure_cb(EV_A_ connection);
|
|
} else {
|
|
buffer_provide(&connection->read_buf, n);
|
|
connection->success_cb(EV_A_ connection);
|
|
}
|
|
}
|
|
|
|
void connection_write_some(EV_P_ Connection *connection) {
|
|
assert(connection->success_cb && connection->failure_cb);
|
|
ev_set_cb(&connection->watcher, connection_write_some_cb);
|
|
ev_invoke(EV_A_ & connection->watcher, 0);
|
|
}
|
|
|
|
static void connection_write_some_cb(EV_P_ ev_io *watcher, int revents) {
|
|
Connection *connection = (Connection *)watcher;
|
|
ssize_t n;
|
|
|
|
(void)revents;
|
|
ev_io_stop(EV_A_ watcher);
|
|
n = tls_write(connection->ctx, buffer_data(&connection->write_buf),
|
|
buffer_size(&connection->write_buf));
|
|
if (n == TLS_WANT_POLLIN) {
|
|
ev_io_modify(watcher, EV_READ);
|
|
ev_io_start(EV_A_ watcher);
|
|
} else if (n == TLS_WANT_POLLOUT) {
|
|
ev_io_modify(watcher, EV_WRITE);
|
|
ev_io_start(EV_A_ watcher);
|
|
} else if (n == -1) {
|
|
warnx("tls_write: %s", tls_error(connection->ctx));
|
|
connection->failure_cb(EV_A_ connection);
|
|
} else {
|
|
buffer_consume(&connection->write_buf, n);
|
|
connection->success_cb(EV_A_ connection);
|
|
}
|
|
}
|
|
|
|
int connection_close(EV_P_ Connection *connection) {
|
|
int ret;
|
|
|
|
ev_io_stop(EV_A_ & connection->watcher);
|
|
ret = close(connection->watcher.fd);
|
|
tls_free(connection->ctx);
|
|
return ret;
|
|
}
|