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 <[email protected]>
This commit is contained in:
Jeremy Visser
2020-12-23 10:48:38 +00:00
committed by Adrien Destugues
parent da93a24811
commit 6011ce6c74
5 changed files with 103 additions and 75 deletions
+4 -3
View File
@@ -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));
+3 -2
View File
@@ -11,20 +11,21 @@
#include <netinet/in.h>
#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
+84 -51
View File
@@ -15,6 +15,7 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <poll.h>
#include <File.h>
#include <Debug.h>
@@ -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<void*>(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<PoorManServer*>(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<void*>(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<void*>(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;
}
+6 -13
View File
@@ -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<const time_t*>(pointer);
if (message->FindData("in_addr_t", B_ANY_TYPE, &pointer, &size) != B_OK)
address = INADDR_NONE;
else
address = *static_cast<const in_addr_t*>(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;
}
+6 -6
View File
@@ -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;
}