From 6011ce6c7495e4e707bd33b12a7e22d66c710aad Mon Sep 17 00:00:00 2001 From: Jeremy Visser Date: Sun, 20 Dec 2020 22:30:15 +1100 Subject: [PATCH] PoorMan: Add dual-stack IPv6 support This change adds dual-stack IPv6 support to the PoorMan web server, which will listen on all available IPv4 and IPv6 addresses and respond to both. This change also does some necessary plumbing to support the output of nicely-formatted IPv6 addresses for request logging. Change-Id: I0ce7691222f0233e2e098d67e6293b9e58d7486d Reviewed-on: https://review.haiku-os.org/c/haiku/+/3539 Reviewed-by: Adrien Destugues --- src/apps/poorman/PoorManLogger.cpp | 7 +- src/apps/poorman/PoorManLogger.h | 5 +- src/apps/poorman/PoorManServer.cpp | 135 +++++++++++++++++---------- src/apps/poorman/PoorManWindow.cpp | 19 ++-- src/apps/poorman/libhttpd/libhttpd.c | 12 +-- 5 files changed, 103 insertions(+), 75 deletions(-) diff --git a/src/apps/poorman/PoorManLogger.cpp b/src/apps/poorman/PoorManLogger.cpp index 9d47475bac..2f8a5de451 100644 --- a/src/apps/poorman/PoorManLogger.cpp +++ b/src/apps/poorman/PoorManLogger.cpp @@ -17,10 +17,11 @@ #include "PoorManApplication.h" #include "PoorManWindow.h" +#include "libhttpd.h" void poorman_log(const char* msg, bool needTimeHeader, - in_addr_t addr, rgb_color color) + httpd_sockaddr* addr, rgb_color color) { time_t now = time(NULL); @@ -38,8 +39,8 @@ poorman_log(const char* msg, bool needTimeHeader, if(message.AddData("time_t", B_TIME_TYPE, &now, sizeof(time_t)) != B_OK) return; } - if(addr != INADDR_NONE) - message.AddData("in_addr_t", B_ANY_TYPE, &addr, sizeof(in_addr_t)); + if(addr != NULL) + message.AddString("addr", httpd_ntoa(addr)); if(color != BLACK) message.AddData("rgb_color", B_RGB_COLOR_TYPE, &color, sizeof(rgb_color)); diff --git a/src/apps/poorman/PoorManLogger.h b/src/apps/poorman/PoorManLogger.h index 961d5e6e61..ac42d3ee47 100644 --- a/src/apps/poorman/PoorManLogger.h +++ b/src/apps/poorman/PoorManLogger.h @@ -11,20 +11,21 @@ #include #include "constants.h" //for rgb_color BLACK +#include "libhttpd.h" //for httpd_sockaddr #ifdef __cplusplus extern "C" void poorman_log( const char* msg, bool needTimeHeader = true, - in_addr_t addr = INADDR_NONE, + httpd_sockaddr* addr = NULL, rgb_color color = BLACK ); #else //c version is for libhttpd void poorman_log( const char* msg, bool needTimeHeader, - in_addr_t addr, + httpd_sockaddr* addr, rgb_color color ); #endif diff --git a/src/apps/poorman/PoorManServer.cpp b/src/apps/poorman/PoorManServer.cpp index faace730e4..0b53dd8104 100644 --- a/src/apps/poorman/PoorManServer.cpp +++ b/src/apps/poorman/PoorManServer.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -85,7 +86,7 @@ PoorManServer::~PoorManServer() status_t PoorManServer::Run() { if (chdir(fHttpdServer->cwd) == -1) { - poorman_log("no web directory, can't start up.\n", false, INADDR_NONE, RED); + poorman_log("no web directory, can't start up.\n", false, NULL, RED); return B_ERROR; } @@ -95,7 +96,15 @@ status_t PoorManServer::Run() sa4.sa_in.sin_port = htons(80); sa4.sa_in.sin_addr.s_addr = htonl(INADDR_ANY); fHttpdServer->listen4_fd = httpd_initialize_listen_socket(&sa4); - if (fHttpdServer->listen4_fd == -1) + + httpd_sockaddr sa6; + memset(&sa6, 0, sizeof(httpd_sockaddr)); + sa6.sa_in.sin_family = AF_INET6; + sa6.sa_in.sin_port = htons(80); + sa6.sa_in.sin_addr.s_addr = htonl(INADDR_ANY); + fHttpdServer->listen6_fd = httpd_initialize_listen_socket(&sa6); + + if (fHttpdServer->listen4_fd == -1 && fHttpdServer->listen6_fd == -1) return B_ERROR; fListenerTid = spawn_thread( @@ -105,7 +114,7 @@ status_t PoorManServer::Run() static_cast(this) ); if (fListenerTid < B_OK) { - poorman_log("can't create listener thread.\n", false, INADDR_NONE, RED); + poorman_log("can't create listener thread.\n", false, NULL, RED); return B_ERROR; } fIsRunning = true; @@ -204,55 +213,79 @@ int32 PoorManServer::_Listener(void* data) thread_id tid; httpd_conn* hc; PoorManServer* s = static_cast(data); - + const int nfds = 2; + pollfd fds[nfds]; + + // N.B. these fds could be -1, which poll() should skip + memset(&fds, 0, sizeof(fds)); + fds[0].fd = s->fHttpdServer->listen4_fd; + fds[0].events = POLLIN; + fds[1].fd = s->fHttpdServer->listen6_fd; + fds[1].events = POLLIN; + while (s->fIsRunning) { - hc = new httpd_conn; - hc->initialized = 0; - PRINT(("calling httpd_get_conn()\n")); - retval = //accept(), blocked here - httpd_get_conn(s->fHttpdServer, s->fHttpdServer->listen4_fd, hc); - switch (retval) { - case GC_OK: - break; - case GC_FAIL: - httpd_destroy_conn(hc); - delete hc; - s->fIsRunning = false; - return -1; - case GC_NO_MORE: - //should not happen, since we have a blocking socket - httpd_destroy_conn(hc); - continue; - break; - default: - //shouldn't happen - continue; - break; + // Wait for listen4_fd or listen6_fd (or both!) to become ready: + retval = poll(fds, nfds, -1); + if (retval < 1) { + return -1; // fds no longer available } - if (s->fCurConns > s->fMaxConns) { - httpd_send_err(hc, 503, - httpd_err503title, (char *)"", httpd_err503form, (char *)""); - httpd_write_response(hc); - continue; - } - - tid = spawn_thread( - PoorManServer::_Worker, - "www connection", - B_NORMAL_PRIORITY, - static_cast(s) - ); - if (tid < B_OK) { - continue; - } - /*We don't check the return code here. - *As we can't kill a thread that doesn't receive the - *httpd_conn, we simply let it die itself. - */ - send_data(tid, 512, &hc, sizeof(httpd_conn*)); - atomic_add(&s->fCurConns, 1); - resume_thread(tid); + for (int fdi = 0; fdi < nfds; fdi++) { + if (fds[fdi].fd < 0) { + continue; // fd is disabled, e.g. ipv4-only + } + if ((fds[fdi].revents & POLLIN) != POLLIN) { + continue; // fd is unavailable, try next fd + } + + hc = new httpd_conn; + hc->initialized = 0; + + PRINT(("calling httpd_get_conn()\n")); + retval = httpd_get_conn(s->fHttpdServer, fds[fdi].fd, hc); + switch (retval) { + case GC_OK: + break; + case GC_FAIL: + httpd_destroy_conn(hc); + delete hc; + s->fIsRunning = false; + return -1; + case GC_NO_MORE: + //should not happen, since we have a blocking socket + httpd_destroy_conn(hc); + continue; + break; + default: + //shouldn't happen + continue; + break; + } + + if (s->fCurConns > s->fMaxConns) { + httpd_send_err(hc, 503, + httpd_err503title, (char *)"", httpd_err503form, (char *)""); + httpd_write_response(hc); + continue; + } + + tid = spawn_thread( + PoorManServer::_Worker, + "www connection", + B_NORMAL_PRIORITY, + static_cast(s) + ); + if (tid < B_OK) { + continue; + } + /*We don't check the return code here. + *As we can't kill a thread that doesn't receive the + *httpd_conn, we simply let it die itself. + */ + send_data(tid, 512, &hc, sizeof(httpd_conn*)); + atomic_add(&s->fCurConns, 1); + resume_thread(tid); + }//for }//while return 0; } @@ -374,7 +407,7 @@ status_t PoorManServer::_HandleGet(httpd_conn* hc) pthread_rwlock_unlock(&fWebDirLock); } log << '/' << hc->expnfilename << '\n'; - poorman_log(log.String(), true, hc->client_addr.sa_in.sin_addr.s_addr); + poorman_log(log.String(), true, &hc->client_addr); //send mime headers if (send(hc->conn_fd, hc->response, hc->responselen, 0) < 0) { @@ -398,7 +431,7 @@ status_t PoorManServer::_HandleGet(httpd_conn* hc) pthread_rwlock_unlock(&fWebDirLock); } log << '/' << hc->expnfilename << '\n'; - poorman_log(log.String(), true, hc->client_addr.sa_in.sin_addr.s_addr, RED); + poorman_log(log.String(), true, &hc->client_addr, RED); delete [] buf; return B_ERROR; } diff --git a/src/apps/poorman/PoorManWindow.cpp b/src/apps/poorman/PoorManWindow.cpp index caa639a029..9bfdc7fd32 100644 --- a/src/apps/poorman/PoorManWindow.cpp +++ b/src/apps/poorman/PoorManWindow.cpp @@ -214,7 +214,7 @@ PoorManWindow::MessageReceived(BMessage* message) break; time_t time; - in_addr_t address; + const char* address; rgb_color color; const void* pointer; ssize_t size; @@ -228,10 +228,8 @@ PoorManWindow::MessageReceived(BMessage* message) else time = *static_cast(pointer); - if (message->FindData("in_addr_t", B_ANY_TYPE, &pointer, &size) != B_OK) - address = INADDR_NONE; - else - address = *static_cast(pointer); + if (message->FindString("addr", &address) != B_OK) + address = NULL; if (message->FindData("rgb_color", B_RGB_COLOR_TYPE, &pointer, &size) != B_OK) color = BLACK; @@ -246,13 +244,8 @@ PoorManWindow::MessageReceived(BMessage* message) } } - if (address != INADDR_NONE) { - char addr[INET_ADDRSTRLEN]; - struct in_addr sin_addr; - sin_addr.s_addr = address; - if (inet_ntop(AF_INET, &sin_addr, addr, sizeof(addr)) != NULL) { - line << '(' << addr << ") "; - } + if (address != NULL) { + line << '(' << address << ") "; } line << msg; @@ -711,7 +704,7 @@ PoorManWindow::StartServer() fStatus = true; UpdateStatusLabelAndMenuItem(); - poorman_log(B_TRANSLATE("done.\n"), false, INADDR_NONE, GREEN); + poorman_log(B_TRANSLATE("done.\n"), false, NULL, GREEN); return B_OK; } diff --git a/src/apps/poorman/libhttpd/libhttpd.c b/src/apps/poorman/libhttpd/libhttpd.c index ab52cc43b1..11320efa3a 100644 --- a/src/apps/poorman/libhttpd/libhttpd.c +++ b/src/apps/poorman/libhttpd/libhttpd.c @@ -394,7 +394,7 @@ httpd_initialize_listen_socket( httpd_sockaddr* saP ) if ( listen_fd < 0 ) { // syslog( LOG_CRIT, "socket %.80s - %m", httpd_ntoa( saP ) ); - poorman_log("can't create socket.\n", false, INADDR_NONE, RED); + poorman_log("can't create socket.\n", false, NULL, RED); return -1; } (void) fcntl( listen_fd, F_SETFD, 1 ); @@ -411,7 +411,7 @@ httpd_initialize_listen_socket( httpd_sockaddr* saP ) { // syslog( // LOG_CRIT, "bind %.80s - %m", httpd_ntoa( saP ) ); - poorman_log("can't bind to socket.\n", false, INADDR_NONE, RED); + poorman_log("can't bind to socket.\n", false, NULL, RED); (void) close( listen_fd ); return -1; } @@ -420,7 +420,7 @@ httpd_initialize_listen_socket( httpd_sockaddr* saP ) if ( listen( listen_fd, LISTEN_BACKLOG ) < 0 ) { // syslog( LOG_CRIT, "listen - %m" ); - poorman_log("can't listen to socket.\n", false, INADDR_NONE, RED); + poorman_log("can't listen to socket.\n", false, NULL, RED); (void) close( listen_fd ); return -1; } @@ -2695,7 +2695,7 @@ ls( httpd_conn* hc ) { char logString[27+B_PATH_NAME_LENGTH+1]; sprintf(logString, "Error 404 File not found: %s\n", hc->decodedurl+1); - poorman_log(logString, true, hc->client_addr.sa_in.sin_addr.s_addr, RED); + poorman_log(logString, true, &hc->client_addr, RED); // syslog( LOG_ERR, "opendir %.80s - %m", hc->expnfilename ); httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl ); free(de); @@ -2735,7 +2735,7 @@ ls( httpd_conn* hc ) strcat(logString, "index file"); strcat(logString, ". Sending directory listing.\n"); - poorman_log(logString, true, hc->client_addr.sa_in.sin_addr.s_addr, BLACK); + poorman_log(logString, true, &hc->client_addr, BLACK); } send_mime( @@ -3049,7 +3049,7 @@ really_start_request( httpd_conn* hc, struct timeval* nowP ) { char logString[27+B_PATH_NAME_LENGTH+1]; sprintf(logString, "Error 404 File not found: %s\n", hc->decodedurl+1); - poorman_log(logString, true, hc->client_addr.sa_in.sin_addr.s_addr, RED); + poorman_log(logString, true, &hc->client_addr, RED); httpd_send_err( hc, 404, err404title, "", err404form, hc->encodedurl ); return -1; }