diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c09a2e..82253f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,8 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") find_package(libev REQUIRED) find_package(libtls REQUIRED) +set(CMAKE_BUILD_TYPE RelWithDebInfo) + include(AddAutoformatTarget) include(Makeheaders) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8fac055..476083b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,8 @@ add_executable(${PROJECT_NAME} - main.c - connection.c buffer.c + connection.c + http.c + main.c ) target_link_libraries(${PROJECT_NAME} libev::ev diff --git a/src/buffer.c b/src/buffer.c index 38198a4..b603c4e 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -1,4 +1,6 @@ #include +#include +#include #include #if INTERFACE @@ -11,7 +13,7 @@ struct Buffer { }; #define buffer_size(buffer) ((buffer)->size) -#define buffer_capacity(buffer) (sizeof(buffer)->data) +#define buffer_capacity(buffer) (sizeof((buffer)->data)) #define buffer_data(buffer) ((buffer)->data) #endif @@ -25,7 +27,25 @@ void buffer_provide(Buffer *buffer, size_t n) { buffer->size += n; } -size_t buffer_size_remaining(Buffer *buffer) { +int buffer_printf(Buffer *buffer, const char *format, ...) { + va_list ap; + int ret; + + va_start(ap, format); + ret = vsnprintf(buffer_data_remaining(buffer), buffer_size_remaining(buffer), + format, ap); + if (ret > 0) { + if ((size_t)ret < buffer_size_remaining(buffer)) { + buffer_provide(buffer, ret); + } else { + buffer_provide(buffer, buffer_size_remaining(buffer)); + } + } + va_end(ap); + return ret; +} + +size_t buffer_size_remaining(const Buffer *buffer) { return buffer_capacity(buffer) - buffer_size(buffer); } diff --git a/src/connection.c b/src/connection.c index c0b2ac6..4219965 100644 --- a/src/connection.c +++ b/src/connection.c @@ -1,5 +1,12 @@ +#include +#include + #include #include +#include +#include +#include +#include #include #if INTERFACE @@ -33,11 +40,69 @@ struct Connection { #endif -void connection_init(EV_P_ Connection *connection, struct tls_config *config) { +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); - ev_io_init(&connection->watcher, NULL, STDIN_FILENO, 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(); - tls_configure(connection->ctx, config); + 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) { @@ -61,6 +126,7 @@ static void connection_read_some_cb(EV_P_ ev_io *watcher, int revents) { 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); @@ -89,6 +155,7 @@ static void connection_write_some_cb(EV_P_ ev_io *watcher, int revents) { 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); @@ -96,8 +163,11 @@ static void connection_write_some_cb(EV_P_ ev_io *watcher, int revents) { } } -void connection_stop(EV_P_ Connection *connection) { +int connection_close(EV_P_ Connection *connection) { + int ret; + ev_io_stop(EV_A_ & connection->watcher); - close(connection->watcher.fd); + ret = close(connection->watcher.fd); tls_free(connection->ctx); + return ret; } diff --git a/src/http.c b/src/http.c new file mode 100644 index 0000000..22364c6 --- /dev/null +++ b/src/http.c @@ -0,0 +1,62 @@ +#include +#include + +#if INTERFACE + +struct Http { + Connection connection; +}; + +#endif + +int http_get(EV_P_ Http *http, const char *servername, const char *path) { + struct tls_config *config; + + config = tls_config_new(); + if (connection_open(EV_DEFAULT_ & http->connection, config, servername, + "https")) { + goto err_connection_open; + } + buffer_printf(connection_write_buf(&http->connection), + "GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", + path, servername); + connection_set_failure_cb(&http->connection, error_cb); + connection_set_cb(&http->connection, http_get_cb); + connection_write_some(EV_A_ & http->connection); + tls_config_free(config); + return 0; + +err_connection_open: + tls_config_free(config); + return -1; +} + +static void error_cb(EV_P_ Connection *connection) { + connection_close(EV_A_ connection); + ev_break(EV_A_ EVBREAK_ALL); +} + +static void http_get_cb(EV_P_ Connection *connection) { + if (buffer_size(connection_write_buf(connection))) { + connection_write_some(EV_A_ connection); + return; + } + connection_set_cb(connection, http_response_cb); + connection_read_some(EV_A_ connection); +} + +static void http_response_cb(EV_P_ Connection *connection) { + Buffer *read_buf; + size_t n; + + read_buf = connection_read_buf(connection); + n = buffer_size(read_buf); + if (n) { + fwrite(buffer_data(read_buf), n, 1, stdout); + buffer_consume(read_buf, n); + connection_read_some(EV_A_ connection); + return; + } + connection_close(EV_A_ connection); + ev_break(EV_A_ EVBREAK_ALL); +} diff --git a/src/main.c b/src/main.c index 19fd759..fa7b19f 100644 --- a/src/main.c +++ b/src/main.c @@ -3,14 +3,14 @@ #include int main(int argc, char **argv) { - struct tls_config *config; - Connection connection; + Http http; (void)argc, (void)argv; tls_init(); - config = tls_config_new(); - connection_init(EV_DEFAULT_ & connection, config); - tls_config_free(config); + if (http_get(EV_DEFAULT_ & http, "chat.heizhaus.org", + "/.well-known/matrix/client")) { + return 1; + } ev_run(EV_DEFAULT_UC_ 0); return 0; }