#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, };