From 1390e77b0742a9fd38ca9986f76f04a7ffab16a4 Mon Sep 17 00:00:00 2001 From: "Rafael G. Martins" Date: Sun, 5 Mar 2017 01:38:12 +0100 Subject: runserver: implemented ipv6 support --- src/blogc-runserver/httpd.c | 136 +++++++++++++++++++++++++++++++------------- src/blogc-runserver/httpd.h | 2 +- src/blogc-runserver/main.c | 14 ++--- 3 files changed, 105 insertions(+), 47 deletions(-) (limited to 'src') diff --git a/src/blogc-runserver/httpd.c b/src/blogc-runserver/httpd.c index d95278d..e4eb986 100644 --- a/src/blogc-runserver/httpd.c +++ b/src/blogc-runserver/httpd.c @@ -13,7 +13,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -209,12 +211,74 @@ point0: } +static char* +br_httpd_get_ip(int af, const struct sockaddr *addr) +{ + char host[INET6_ADDRSTRLEN]; + if (af == AF_INET6) { + struct sockaddr_in6 *a = (struct sockaddr_in6*) addr; + inet_ntop(af, &(a->sin6_addr), host, INET6_ADDRSTRLEN); + } + else { + struct sockaddr_in *a = (struct sockaddr_in*) addr; + inet_ntop(af, &(a->sin_addr), host, INET6_ADDRSTRLEN); + } + return bc_strdup(host); +} + + +static u_int16_t +br_httpd_get_port(int af, const struct sockaddr *addr) +{ + in_port_t port = 0; + if (af == AF_INET6) { + struct sockaddr_in6 *a = (struct sockaddr_in6*) addr; + port = a->sin6_port; + } + else { + struct sockaddr_in *a = (struct sockaddr_in*) addr; + port = a->sin_port; + } + return ntohs(port); +} + + int -br_httpd_run(const char *host, unsigned short port, const char *docroot, +br_httpd_run(const char *host, const char *port, const char *docroot, size_t max_threads) { - if (port == 0) { - fprintf(stderr, "Invalid port: 0\n"); + int err; + struct addrinfo *result; + struct addrinfo hints = { + .ai_family = AF_UNSPEC, + .ai_socktype = SOCK_STREAM, + .ai_flags = AI_PASSIVE, + .ai_protocol = 0, + .ai_canonname = NULL, + .ai_addr = NULL, + .ai_next = NULL, + }; + if (0 != (err = getaddrinfo(host, port, &hints, &result))) { + fprintf(stderr, "Failed to get host:port info: %s\n", + gai_strerror(err)); + } + + struct addrinfo *f = NULL; + int server_socket; + for (struct addrinfo *rp = result; rp != NULL; rp = rp->ai_next) { + server_socket = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (server_socket == -1) { + continue; + } + if (0 == bind(server_socket, rp->ai_addr, rp->ai_addrlen)) { + f = rp; + break; + } + close(server_socket); + } + + if (f == NULL) { + fprintf(stderr, "Failed to open server socket: %s:%s\n", host, port); return 3; } @@ -222,62 +286,55 @@ br_httpd_run(const char *host, unsigned short port, const char *docroot, for (size_t i = 0; i < max_threads; i++) threads[i].initialized = false; - int server_socket = socket(AF_INET, SOCK_STREAM, 0); - if (server_socket == -1) { - fprintf(stderr, "Failed to open server socket: %s\n", strerror(errno)); - return 3; - } - int rv = 0; int value = 1; - if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(int)) < 0) { + if (0 > setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(int))) { fprintf(stderr, "Failed to set socket option: %s\n", strerror(errno)); rv = 3; goto cleanup; } - struct sockaddr_in server_addr; - memset(&server_addr, 0, sizeof(struct sockaddr_in)); - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(port); - if ((server_addr.sin_addr.s_addr = inet_addr(host)) == -1) { - fprintf(stderr, "Invalid server listen address: %s\n", host); - rv = 3; - goto cleanup; - } - - if ((bind(server_socket, (struct sockaddr*) &server_addr, - sizeof(struct sockaddr_in))) == -1) - { - fprintf(stderr, "Failed to bind to server socket (%s:%d): %s\n", - host, port, strerror(errno)); - rv = 3; - goto cleanup; - } - - if (listen(server_socket, LISTEN_BACKLOG) == -1) { + if (-1 == listen(server_socket, LISTEN_BACKLOG)) { fprintf(stderr, "Failed to listen to server socket: %s\n", strerror(errno)); rv = 3; goto cleanup; } - fprintf(stderr, " * Running on http://%s", host); - if (port != 80) - fprintf(stderr, ":%hu", port); + char *final_host = br_httpd_get_ip(f->ai_family, f->ai_addr); + u_int16_t final_port = br_httpd_get_port(f->ai_family, f->ai_addr); + fprintf(stderr, " * Running on http://"); + if (f->ai_family == AF_INET6) + fprintf(stderr, "[%s]", final_host); + else + fprintf(stderr, "%s", final_host); + if (final_port != 80) + fprintf(stderr, ":%d", final_port); fprintf(stderr, "/ (max threads: %zu)\n" "\n" "WARNING!!! This is a development server, DO NOT RUN IT IN PRODUCTION!\n" "\n", max_threads); - - socklen_t len = sizeof(struct sockaddr_in); + free(final_host); size_t current_thread = 0; + struct sockaddr_in6 addr6; + struct sockaddr_in addr; + + socklen_t addrlen; + struct sockaddr *client_addr = NULL; + + if (f->ai_family == AF_INET6) { + addrlen = sizeof(addr6); + client_addr = (struct sockaddr*) &addr6; + } + else { + addrlen = sizeof(addr); + client_addr = (struct sockaddr*) &addr; + } + while (1) { - struct sockaddr_in client_addr; - int client_socket = accept(server_socket, - (struct sockaddr *) &client_addr, &len); + int client_socket = accept(server_socket, client_addr, &addrlen); if (client_socket == -1) { fprintf(stderr, "Failed to accept connection: %s\n", strerror(errno)); rv = 3; @@ -287,7 +344,7 @@ br_httpd_run(const char *host, unsigned short port, const char *docroot, request_data_t *arg = malloc(sizeof(request_data_t)); arg->thread_id = current_thread; arg->socket = client_socket; - arg->ip = bc_strdup(inet_ntoa(client_addr.sin_addr)); + arg->ip = br_httpd_get_ip(f->ai_family, client_addr); arg->docroot = docroot; if (threads[current_thread].initialized) { @@ -311,6 +368,7 @@ br_httpd_run(const char *host, unsigned short port, const char *docroot, } cleanup: + freeaddrinfo(result); close(server_socket); return rv; } diff --git a/src/blogc-runserver/httpd.h b/src/blogc-runserver/httpd.h index 2bf5a89..cca6508 100644 --- a/src/blogc-runserver/httpd.h +++ b/src/blogc-runserver/httpd.h @@ -9,7 +9,7 @@ #ifndef _HTTPD_H #define _HTTPD_H -int br_httpd_run(const char *host, unsigned short port, const char *docroot, +int br_httpd_run(const char *host, const char *port, const char *docroot, size_t max_threads); #endif /* _HTTPD_H */ diff --git a/src/blogc-runserver/main.c b/src/blogc-runserver/main.c index 4624527..0eef9f1 100644 --- a/src/blogc-runserver/main.c +++ b/src/blogc-runserver/main.c @@ -58,8 +58,8 @@ main(int argc, char **argv) int rv = 0; char *host = NULL; + char *port = NULL; char *docroot = NULL; - unsigned short port = 8080; size_t max_threads = 20; char *ptr; char *endptr; @@ -83,13 +83,9 @@ main(int argc, char **argv) break; case 'p': if (argv[i][2] != '\0') - ptr = argv[i] + 2; + port = bc_strdup(argv[i] + 2); else - ptr = argv[++i]; - port = strtoul(ptr, &endptr, 10); - if (*ptr != '\0' && *endptr != '\0') - fprintf(stderr, "blogc-runserver: warning: invalid value " - "for -p argument: %s. using %hu instead\n", ptr, port); + port = bc_strdup(argv[++i]); break; case 'm': if (argv[i][2] != '\0') @@ -141,10 +137,14 @@ main(int argc, char **argv) if (host == NULL) host = bc_strdup("127.0.0.1"); + if (port == NULL) + port = bc_strdup("8080"); + rv = br_httpd_run(host, port, docroot, max_threads); cleanup: free(host); + free(port); free(docroot); return rv; -- cgit v1.2.3-18-g5258