[core] sketch support for abstract sockets

(experimental; untested)

Note: abstract sockets do not require filesystem access and can not be
protected using filesystem permissions; abstract sockets are accessible
by any process in the same network namespace on the same machine.

Abstract sockets can be passed to lighttpd via systemd socket activation
mechanism, via xinetd, or any other process which creates an abstract
socket and passes it to lighttpd.  Abstract sockets can also be
configured in lighttpd.conf using a backslash-escaped double-quoted
string, where CTL and chars with high bit set are backslash-escaped into
"\\xFF", with "\\x" followed by two-byte hex encoding (e.g. "FF") for
each escaped char, e.g. "\\x00abstract-socket"
master
Glenn Strauss 9 months ago
parent 609f9209c8
commit 14bfa016f1

@ -292,7 +292,8 @@ configparser_parse_condition(config_t * const ctx, const buffer * const obj_tag,
else if (COMP_SERVER_SOCKET == dc->comp) {
/*(redundant with parsing in network.c; not actually required here)*/
if (rvalue->ptr[0] != ':' /*(network.c special-cases ":" and "[]")*/
&& !(rvalue->ptr[0] == '[' && rvalue->ptr[1] == ']')) {
&& !(rvalue->ptr[0] == '[' && rvalue->ptr[1] == ']')
&& !(rvalue->ptr[0] == '/' || rvalue->ptr[0] == '\\')) { /*(UDS)*/
if (http_request_host_normalize(rvalue, 0)) {
fprintf(stderr, "invalid IP addr: %s\n", rvalue->ptr);
ctx->ok = 0;

@ -76,8 +76,10 @@ static handler_t network_server_handle_fdevent(void *context, int revents) {
network_accept_tcp_nagle_disable(fd);
#ifdef HAVE_SYS_UN_H /*(see sock_addr.h)*/
else if (addrlen <= 2) /*(AF_UNIX if !nagle_disable)*/
memcpy(addr.un.sun_path, srv_socket->srv_token->ptr,
buffer_clen(srv_socket->srv_token)+1);
memcpy(addr.un.sun_path, srv_socket->addr.un.sun_path,
srv_socket->srv_token_colon < sizeof(addr.un.sun_path)
? (size_t)srv_socket->srv_token_colon+1 /*(+1 for '\0')*/
: sizeof(addr.un.sun_path));/*(escaped len might be longer)*/
#endif
connection *con = connection_accepted(srv, srv_socket, &addr, fd);
@ -103,9 +105,103 @@ static handler_t network_server_handle_fdevent(void *context, int revents) {
return HANDLER_GO_ON;
}
static void network_host_normalize_addr_str(buffer *host, sock_addr *addr) {
#ifdef HAVE_SYS_UN_H
/* abstract socket (Linux, QNX?, Windows 10?) */
__attribute_cold__
static void
network_abstract_socket_enc (buffer * const restrict abstract,
unsigned char * const restrict sun_path,
const uint32_t len)
{
/*(strings needing encoding are expected to be short;
* code not written for performance)*/
buffer_clear(abstract);
uint32_t n = 0;
for (uint32_t i = 0; i < len; ++i)
n += (sun_path[i]-20 < 107) ? 1 : 4;
char *s = buffer_extend(abstract, n);
for (uint32_t i = 0; i < len; ++i) {
/* (sun_path[i] >= 20 && sun_path[i] < 127 && sun_path[i] != '\\') */
if (sun_path[i]-20 < 107 && sun_path[i] != '\\')
*s++ = ((char *)sun_path)[i];
else {
s[0] = '\\';
s[1] = 'x';
s[2] = "0123456789abcdef"[sun_path[i] >> 4];
s[3] = "0123456789abcdef"[sun_path[i] & 0xF];
s += 4;
}
}
}
__attribute_cold__
static uint32_t
network_abstract_socket_dec (const buffer * const restrict abstract,
char * const restrict sun_path,
const uint32_t plen)
{
/*(strings expected to begin with "\\x00")*/
/*(strings needing decoding are expected to be short;
* code not written for performance)*/
const char *s = abstract->ptr;
uint32_t n = 0;
for (int hi, lo; *s && n < plen; ++n) {
if (s[0] != '\\')
sun_path[n] = *s++;
else if (s[1] == 'x'
&& (hi = hex2int(s[2])) != 0xFF
&& (lo = hex2int(s[3])) != 0xFF) {
sun_path[n] = (char)((hi << 4) | lo);
s += 4;
}
else
break;
}
return *s == '\0' ? n : 0;
}
__attribute_cold__
static int
network_abstract_socket_parse (const buffer *abstract,
sock_addr *addr, socklen_t *addr_len,
log_error_st *errh)
{
/* abstract socket (Linux, QNX?, Windows 10?) */
/*assert(*addr_len >= sizeof(struct sockaddr_un));*/
memset(addr, 0, sizeof(struct sockaddr_un));
addr->un.sun_family = AF_UNIX;
uint32_t len =
(uint32_t)(*addr_len - offsetof(struct sockaddr_un, sun_path));
if (len > sizeof(addr->un.sun_path))
len = sizeof(addr->un.sun_path);
len = network_abstract_socket_dec(abstract, addr->un.sun_path, len);
if (len) {
*addr_len = offsetof(struct sockaddr_un, sun_path) + len;
return 0;
}
else {
log_error(errh, __FILE__, __LINE__,
"abstract unix socket filename invalid encoding or too long: %s",
abstract->ptr);
return -1;
}
}
#endif /* HAVE_SYS_UN_H */
static void network_host_normalize_addr_str(buffer *host, sock_addr *addr, socklen_t addr_len) {
buffer_clear(host);
sock_addr_stringify_append_buffer(host, addr);
#ifdef HAVE_SYS_UN_H
if (AF_UNIX == sock_addr_get_family(addr) && 0 == buffer_clen(host))
network_abstract_socket_enc(host, (unsigned char *)addr->un.sun_path,
(uint32_t)(addr_len
- offsetof(struct sockaddr_un, sun_path)));
#else
UNUSED(addr_len);
#endif
}
static int network_host_parse_addr(server *srv, sock_addr *addr, socklen_t *addr_len, buffer *host, int use_ipv6) {
@ -120,14 +216,16 @@ static int network_host_parse_addr(server *srv, sock_addr *addr, socklen_t *addr
return -1;
}
h = host->ptr;
if (h[0] == '/') {
if (h[0] == '/' || h[0] == '\\') {
#ifdef HAVE_SYS_UN_H
if (h[0] == '\\' && h[1] == 'x' && h[2] == '0' && h[3] == '0')
return network_abstract_socket_parse(host,addr,addr_len,srv->errh);
return (1 ==
sock_addr_from_str_hints(addr,addr_len,h,AF_UNIX,0,srv->errh))
? 0
: -1;
#else
log_error(srv, __FILE__, __LINE__,
log_error(srv->errh, __FILE__, __LINE__,
"ERROR: Unix Domain sockets are not supported.");
return -1;
#endif
@ -246,6 +344,18 @@ static uint8_t network_srv_token_colon (const buffer * const b) {
return colon ? (uint8_t)(colon - p) : (uint8_t)buffer_clen(b);
}
static void network_srv_socket_init_token (server_socket * const srv_socket, const buffer * const token) {
buffer * const srv_token = srv_socket->srv_token = buffer_init();
buffer_copy_buffer(srv_token, token);
#ifdef HAVE_SYS_UN_H
/*(srv_socket->addr must have been initialized by caller)*/
if (AF_UNIX == sock_addr_get_family(&srv_socket->addr))
srv_socket->srv_token_colon = buffer_clen(srv_token);
else
#endif
srv_socket->srv_token_colon = network_srv_token_colon(srv_token);
}
static int network_server_init(server *srv, network_socket_config *s, buffer *host_token, size_t sidx, int stdin_fd) {
server_socket *srv_socket;
const char *host;
@ -305,7 +415,7 @@ static int network_server_init(server *srv, network_socket_config *s, buffer *ho
}
#endif
network_host_normalize_addr_str(host_token, &addr);
network_host_normalize_addr_str(host_token, &addr, addr_len);
host = host_token->ptr;
if (srv->srvconf.preflight_check) {
@ -326,10 +436,7 @@ static int network_server_init(server *srv, network_socket_config *s, buffer *ho
srv_socket->sidx = sidx;
srv_socket->is_ssl = s->ssl_enabled;
srv_socket->srv = srv;
buffer_copy_buffer((srv_socket->srv_token = buffer_init()), host_token);
srv_socket->srv_token_colon =
network_srv_token_colon(srv_socket->srv_token);
network_srv_socket_init_token(srv_socket, host_token);
network_srv_sockets_append(srv, srv_socket);
if (srv->sockets_disabled) { /* lighttpd -1 (one-shot mode) */
@ -371,7 +478,7 @@ static int network_server_init(server *srv, network_socket_config *s, buffer *ho
/* connect failed */
switch(errno) {
case ECONNREFUSED:
unlink(host);
if (*host == '/') unlink(host);
break;
case ENOENT:
break;
@ -426,6 +533,7 @@ static int network_server_init(server *srv, network_socket_config *s, buffer *ho
return -1;
}
#ifdef HAVE_SYS_UN_H
if (-1 != stdin_fd) { } else
if (AF_UNIX == family && s->socket_perms) {
mode_t m = 0;
@ -433,12 +541,13 @@ static int network_server_init(server *srv, network_socket_config *s, buffer *ho
m <<= 3;
m |= (*str - '0');
}
if (0 != m && -1 == chmod(host, m)) {
if (0 != m && *host == '/' && -1 == chmod(host, m)) {
log_perror(srv->errh, __FILE__, __LINE__,
"chmod(\"%s\", %s)", host, s->socket_perms->ptr);
return -1;
}
}
#endif
if (-1 != stdin_fd) { } else
if (-1 == listen(srv_socket->fd, s->listen_backlog)) {
@ -570,7 +679,7 @@ static int network_socket_activation_nfds(server *srv, network_socket_config *s,
"socket activation getsockname()");
break;
}
network_host_normalize_addr_str(host, &addr);
network_host_normalize_addr_str(host, &addr, addr_len);
rc = network_server_init(srv, s, host, 0, fd);
if (0 != rc) break;
srv->srv_sockets.ptr[srv->srv_sockets.used-1]->sidx = (unsigned short)~0u;
@ -720,7 +829,7 @@ int network_init(server *srv, int stdin_fd) {
rc = network_host_parse_addr(srv, &addr, &addr_len,
host_token, p->conf.use_ipv6);
if (0 != rc) break;
network_host_normalize_addr_str(host_token, &addr);
network_host_normalize_addr_str(host_token, &addr, addr_len);
}
}
if (0 != rc) break;
@ -739,7 +848,7 @@ int network_init(server *srv, int stdin_fd) {
if (srv->srvconf.bindhost)
buffer_copy_buffer(b, srv->srvconf.bindhost);
/*(skip adding port if unix socket path)*/
if (!b->ptr || b->ptr[0] != '/') {
if (!b->ptr || (b->ptr[0] != '/' && b->ptr[0] != '\\')) {
buffer_append_string_len(b, CONST_STR_LEN(":"));
buffer_append_int(b, srv->srvconf.port);
}
@ -764,11 +873,8 @@ int network_init(server *srv, int stdin_fd) {
force_assert(NULL != srv_socket);
memcpy(srv_socket, srv->srv_sockets_inherited.ptr[i],
sizeof(server_socket));
const buffer * const srv_token = srv_socket->srv_token;
buffer_copy_buffer((srv_socket->srv_token = buffer_init()),
srv_token);
srv_socket->srv_token_colon =
network_srv_token_colon(srv_socket->srv_token);
/*(note: re-inits srv_socket->srv_token to new buffer ptr)*/
network_srv_socket_init_token(srv_socket,srv_socket->srv_token);
network_srv_sockets_append(srv, srv_socket);
}
}

Loading…
Cancel
Save