commit c17b7a2201250eaa9aac012b0f5de6e70365e60e Author: Jeltz Date: Sat Jun 12 00:43:17 2021 +0200 Initial commit diff --git a/mod_unix_socket.c b/mod_unix_socket.c new file mode 100644 index 0000000..7f2a2f5 --- /dev/null +++ b/mod_unix_socket.c @@ -0,0 +1,170 @@ +#include +#include "httpd.h" +#include "http_config.h" +#include "http_protocol.h" +#include "ap_config.h" +#include "ap_listen.h" +#include "apr_strings.h" +#include "http_log.h" + +static ap_listen_rec * +alloc_unix_listener (process_rec * process, const char *path, + const char *proto) +{ + struct sockaddr_un sun = { + .sun_family = AF_UNIX, + }; + apr_os_sock_info_t si; + ap_listen_rec *rec; + apr_status_t rv; + int one = 1; + int fd; + + apr_cpystrn (sun.sun_path, path, sizeof (sun.sun_path)); + + rec = apr_palloc (process->pool, sizeof (*rec)); + rec->protocol = apr_pstrdup (process->pool, proto); + rec->accept_func = NULL; + rec->next = NULL; + rec->active = 1; + + if (unlink (sun.sun_path)) + { + ap_log_perror (APLOG_MARK, APLOG_CRIT, APR_EGENERAL, process->pool, + APLOGNO (10100) "unlink failed: %s", strerror (errno)); + return NULL; + } + + fd = socket (AF_UNIX, SOCK_STREAM, 0); + if (fd == -1) + { + ap_log_perror (APLOG_MARK, APLOG_CRIT, APR_EGENERAL, process->pool, + APLOGNO (10101) "socket failed: %s", strerror (errno)); + return NULL; + } + + if (setsockopt (fd, SOL_SOCKET, SO_REUSEPORT, + (void *) &one, sizeof (one)) < 0) + { + ap_log_perror (APLOG_MARK, APLOG_CRIT, APR_EGENERAL, process->pool, + APLOGNO (10102) "setsockopt failed: %s", + strerror (errno)); + close (fd); + return NULL; + } + + /* apr_socket_bind() is not used because apr_sockaddr_t is not compatible + * with sockaddr_un + */ + if (bind (fd, (struct sockaddr *) &sun, sizeof (sun))) + { + ap_log_perror (APLOG_MARK, APLOG_CRIT, APR_EGENERAL, process->pool, + APLOGNO (10103) "bind failed: %s", strerror (errno)); + close (fd); + return NULL; + } + + /* FIXME (ugly hack): configure fake socket information to allow + * ap_setup_listeners() to set the protocol of servers. + * I don't know if it interacts well with other parts of the codebase + * (I haven't even checked properly the whole server/listen.c file!) + */ + si.os_sock = &fd; + si.remote = NULL; + si.local = NULL; + si.family = APR_INET; + si.type = SOCK_STREAM; + si.protocol = APR_PROTO_TCP; + + rv = apr_os_sock_make (&rec->sd, &si, process->pool); + if (rv != APR_SUCCESS) + { + ap_log_perror (APLOG_MARK, APLOG_CRIT, APR_EGENERAL, process->pool, + APLOGNO (10104) "apr_os_sock_make failed (%d)", fd); + close (fd); + return NULL; + } + + rv = apr_socket_listen (rec->sd, 10); + if (rv != APR_SUCCESS) + { + ap_log_perror (APLOG_MARK, APLOG_CRIT, rv, process->pool, + APLOGNO (10105) "apr_socket_listen failed (%d)", fd); + return NULL; + } + + rv = apr_socket_addr_get (&rec->bind_addr, APR_LOCAL, rec->sd); + if (rv != APR_SUCCESS) + { + ap_log_perror (APLOG_MARK, APLOG_CRIT, rv, process->pool, + APLOGNO (10106) "apr_socket_addr_get failed (%d)", fd); + return NULL; + } + + /* TODO: implement useful parts of make_sock (server/listen.c) */ + + return rec; +} + + +static const char * +set_listen_path (cmd_parms * cmd, void *cfg, const char *path, + const char *proto) +{ + ap_listen_rec *last; + ap_listen_rec *new; + apr_status_t rv; + const char *err; + + err = ap_check_cmd_context (cmd, GLOBAL_ONLY); + if (err) + { + return err; + } + + if (strcmp (proto, "http") && strcmp (proto, "https")) + { + return "Proto must be either 'http' or 'https'"; + } + + new = alloc_unix_listener (cmd->server->process, path, proto); + if (!new) + { + return "Failed to listen on Unix socket"; + } + + last = ap_listeners; + + while (last && last->next) + { + last = last->next; + } + + if (!last) + { + ap_listeners = new; + } + else + { + last->next = new; + } + + return NULL; +} + +static const command_rec directives[] = { + AP_INIT_TAKE2 ("ListenUnixPath", set_listen_path, NULL, RSRC_CONF, + "Listen to Unix socket"), + {NULL}, +}; + + +module AP_MODULE_DECLARE_DATA unix_socket_module = { + STANDARD20_MODULE_STUFF, + NULL, + NULL, + NULL, + NULL, + directives, + NULL, +};