Mercurial > hg > nginx
view src/core/ngx_proxy_protocol.c @ 9270:3d455e37abf8
Core: PID file writing synchronization.
Now, ngx_daemon() does not call exit() in the parent process immediately,
but instead waits for the child process to signal it actually started (and
wrote the PID file if configured to). This ensures that the PID file
already exists when the parent process exits.
To make sure that signal handlers won't cause unexpected logging in the
parent process if the child process dies (for example, due to errors when
writing the PID file), ngx_init_signals() is moved to the child process.
This resolves "PID file ... not readable (yet?) after start" and "Failed
to parse PID from file..." errors as observed with systemd.
Note that the errors observed are considered to be a bug in systemd, which
isn't able to work properly with traditional Unix daemons. Still, the
workaround is implemented to make sure there will be no OS vendor patches
trying to address this.
author | Maxim Dounin <mdounin@mdounin.ru> |
---|---|
date | Mon, 13 May 2024 06:13:22 +0300 |
parents | 42bc158a47ec |
children |
line wrap: on
line source
/* * Copyright (C) Roman Arutyunyan * Copyright (C) Nginx, Inc. */ #include <ngx_config.h> #include <ngx_core.h> #define NGX_PROXY_PROTOCOL_AF_INET 1 #define NGX_PROXY_PROTOCOL_AF_INET6 2 #define ngx_proxy_protocol_parse_uint16(p) \ ( ((uint16_t) (p)[0] << 8) \ + ( (p)[1]) ) #define ngx_proxy_protocol_parse_uint32(p) \ ( ((uint32_t) (p)[0] << 24) \ + ( (p)[1] << 16) \ + ( (p)[2] << 8) \ + ( (p)[3]) ) typedef struct { u_char signature[12]; u_char version_command; u_char family_transport; u_char len[2]; } ngx_proxy_protocol_header_t; typedef struct { u_char src_addr[4]; u_char dst_addr[4]; u_char src_port[2]; u_char dst_port[2]; } ngx_proxy_protocol_inet_addrs_t; typedef struct { u_char src_addr[16]; u_char dst_addr[16]; u_char src_port[2]; u_char dst_port[2]; } ngx_proxy_protocol_inet6_addrs_t; typedef struct { u_char type; u_char len[2]; } ngx_proxy_protocol_tlv_t; typedef struct { u_char client; u_char verify[4]; } ngx_proxy_protocol_tlv_ssl_t; typedef struct { ngx_str_t name; ngx_uint_t type; } ngx_proxy_protocol_tlv_entry_t; static u_char *ngx_proxy_protocol_read_addr(ngx_connection_t *c, u_char *p, u_char *last, ngx_str_t *addr); static u_char *ngx_proxy_protocol_read_port(u_char *p, u_char *last, in_port_t *port, u_char sep); static u_char *ngx_proxy_protocol_v2_read(ngx_connection_t *c, u_char *buf, u_char *last); static ngx_int_t ngx_proxy_protocol_lookup_tlv(ngx_connection_t *c, ngx_str_t *tlvs, ngx_uint_t type, ngx_str_t *value); static ngx_proxy_protocol_tlv_entry_t ngx_proxy_protocol_tlv_entries[] = { { ngx_string("alpn"), 0x01 }, { ngx_string("authority"), 0x02 }, { ngx_string("unique_id"), 0x05 }, { ngx_string("ssl"), 0x20 }, { ngx_string("netns"), 0x30 }, { ngx_null_string, 0x00 } }; static ngx_proxy_protocol_tlv_entry_t ngx_proxy_protocol_tlv_ssl_entries[] = { { ngx_string("version"), 0x21 }, { ngx_string("cn"), 0x22 }, { ngx_string("cipher"), 0x23 }, { ngx_string("sig_alg"), 0x24 }, { ngx_string("key_alg"), 0x25 }, { ngx_null_string, 0x00 } }; u_char * ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, u_char *last) { size_t len; u_char *p; ngx_proxy_protocol_t *pp; static const u_char signature[] = "\r\n\r\n\0\r\nQUIT\n"; p = buf; len = last - buf; if (len >= sizeof(ngx_proxy_protocol_header_t) && ngx_memcmp(p, signature, sizeof(signature) - 1) == 0) { return ngx_proxy_protocol_v2_read(c, buf, last); } if (len < 8 || ngx_strncmp(p, "PROXY ", 6) != 0) { goto invalid; } p += 6; len -= 6; if (len >= 7 && ngx_strncmp(p, "UNKNOWN", 7) == 0) { ngx_log_debug0(NGX_LOG_DEBUG_CORE, c->log, 0, "PROXY protocol unknown protocol"); p += 7; goto skip; } if (len < 5 || ngx_strncmp(p, "TCP", 3) != 0 || (p[3] != '4' && p[3] != '6') || p[4] != ' ') { goto invalid; } p += 5; pp = ngx_pcalloc(c->pool, sizeof(ngx_proxy_protocol_t)); if (pp == NULL) { return NULL; } p = ngx_proxy_protocol_read_addr(c, p, last, &pp->src_addr); if (p == NULL) { goto invalid; } p = ngx_proxy_protocol_read_addr(c, p, last, &pp->dst_addr); if (p == NULL) { goto invalid; } p = ngx_proxy_protocol_read_port(p, last, &pp->src_port, ' '); if (p == NULL) { goto invalid; } p = ngx_proxy_protocol_read_port(p, last, &pp->dst_port, CR); if (p == NULL) { goto invalid; } if (p == last) { goto invalid; } if (*p++ != LF) { goto invalid; } ngx_log_debug4(NGX_LOG_DEBUG_CORE, c->log, 0, "PROXY protocol src: %V %d, dst: %V %d", &pp->src_addr, pp->src_port, &pp->dst_addr, pp->dst_port); c->proxy_protocol = pp; return p; skip: for ( /* void */ ; p < last - 1; p++) { if (p[0] == CR && p[1] == LF) { return p + 2; } } invalid: for (p = buf; p < last; p++) { if (*p == CR || *p == LF) { break; } } ngx_log_error(NGX_LOG_ERR, c->log, 0, "broken header: \"%*s\"", (size_t) (p - buf), buf); return NULL; } static u_char * ngx_proxy_protocol_read_addr(ngx_connection_t *c, u_char *p, u_char *last, ngx_str_t *addr) { size_t len; u_char ch, *pos; pos = p; for ( ;; ) { if (p == last) { return NULL; } ch = *p++; if (ch == ' ') { break; } if (ch != ':' && ch != '.' && (ch < 'a' || ch > 'f') && (ch < 'A' || ch > 'F') && (ch < '0' || ch > '9')) { return NULL; } } len = p - pos - 1; addr->data = ngx_pnalloc(c->pool, len); if (addr->data == NULL) { return NULL; } ngx_memcpy(addr->data, pos, len); addr->len = len; return p; } static u_char * ngx_proxy_protocol_read_port(u_char *p, u_char *last, in_port_t *port, u_char sep) { size_t len; u_char *pos; ngx_int_t n; pos = p; for ( ;; ) { if (p == last) { return NULL; } if (*p++ == sep) { break; } } len = p - pos - 1; n = ngx_atoi(pos, len); if (n < 0 || n > 65535) { return NULL; } *port = (in_port_t) n; return p; } u_char * ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, u_char *last) { ngx_uint_t port, lport; if (last - buf < NGX_PROXY_PROTOCOL_V1_MAX_HEADER) { ngx_log_error(NGX_LOG_ALERT, c->log, 0, "too small buffer for PROXY protocol"); return NULL; } if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) { return NULL; } switch (c->sockaddr->sa_family) { case AF_INET: buf = ngx_cpymem(buf, "PROXY TCP4 ", sizeof("PROXY TCP4 ") - 1); break; #if (NGX_HAVE_INET6) case AF_INET6: buf = ngx_cpymem(buf, "PROXY TCP6 ", sizeof("PROXY TCP6 ") - 1); break; #endif default: return ngx_cpymem(buf, "PROXY UNKNOWN" CRLF, sizeof("PROXY UNKNOWN" CRLF) - 1); } buf += ngx_sock_ntop(c->sockaddr, c->socklen, buf, last - buf, 0); *buf++ = ' '; buf += ngx_sock_ntop(c->local_sockaddr, c->local_socklen, buf, last - buf, 0); port = ngx_inet_get_port(c->sockaddr); lport = ngx_inet_get_port(c->local_sockaddr); return ngx_slprintf(buf, last, " %ui %ui" CRLF, port, lport); } static u_char * ngx_proxy_protocol_v2_read(ngx_connection_t *c, u_char *buf, u_char *last) { u_char *end; size_t len; socklen_t socklen; ngx_uint_t version, command, family, transport; ngx_sockaddr_t src_sockaddr, dst_sockaddr; ngx_proxy_protocol_t *pp; ngx_proxy_protocol_header_t *header; ngx_proxy_protocol_inet_addrs_t *in; #if (NGX_HAVE_INET6) ngx_proxy_protocol_inet6_addrs_t *in6; #endif header = (ngx_proxy_protocol_header_t *) buf; buf += sizeof(ngx_proxy_protocol_header_t); version = header->version_command >> 4; if (version != 2) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "unknown PROXY protocol version: %ui", version); return NULL; } len = ngx_proxy_protocol_parse_uint16(header->len); if ((size_t) (last - buf) < len) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "header is too large"); return NULL; } end = buf + len; command = header->version_command & 0x0f; /* only PROXY is supported */ if (command != 1) { ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, "PROXY protocol v2 unsupported command %ui", command); return end; } transport = header->family_transport & 0x0f; /* only STREAM is supported */ if (transport != 1) { ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, "PROXY protocol v2 unsupported transport %ui", transport); return end; } pp = ngx_pcalloc(c->pool, sizeof(ngx_proxy_protocol_t)); if (pp == NULL) { return NULL; } family = header->family_transport >> 4; switch (family) { case NGX_PROXY_PROTOCOL_AF_INET: if ((size_t) (end - buf) < sizeof(ngx_proxy_protocol_inet_addrs_t)) { return NULL; } in = (ngx_proxy_protocol_inet_addrs_t *) buf; src_sockaddr.sockaddr_in.sin_family = AF_INET; src_sockaddr.sockaddr_in.sin_port = 0; ngx_memcpy(&src_sockaddr.sockaddr_in.sin_addr, in->src_addr, 4); dst_sockaddr.sockaddr_in.sin_family = AF_INET; dst_sockaddr.sockaddr_in.sin_port = 0; ngx_memcpy(&dst_sockaddr.sockaddr_in.sin_addr, in->dst_addr, 4); pp->src_port = ngx_proxy_protocol_parse_uint16(in->src_port); pp->dst_port = ngx_proxy_protocol_parse_uint16(in->dst_port); socklen = sizeof(struct sockaddr_in); buf += sizeof(ngx_proxy_protocol_inet_addrs_t); break; #if (NGX_HAVE_INET6) case NGX_PROXY_PROTOCOL_AF_INET6: if ((size_t) (end - buf) < sizeof(ngx_proxy_protocol_inet6_addrs_t)) { return NULL; } in6 = (ngx_proxy_protocol_inet6_addrs_t *) buf; src_sockaddr.sockaddr_in6.sin6_family = AF_INET6; src_sockaddr.sockaddr_in6.sin6_port = 0; ngx_memcpy(&src_sockaddr.sockaddr_in6.sin6_addr, in6->src_addr, 16); dst_sockaddr.sockaddr_in6.sin6_family = AF_INET6; dst_sockaddr.sockaddr_in6.sin6_port = 0; ngx_memcpy(&dst_sockaddr.sockaddr_in6.sin6_addr, in6->dst_addr, 16); pp->src_port = ngx_proxy_protocol_parse_uint16(in6->src_port); pp->dst_port = ngx_proxy_protocol_parse_uint16(in6->dst_port); socklen = sizeof(struct sockaddr_in6); buf += sizeof(ngx_proxy_protocol_inet6_addrs_t); break; #endif default: ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, "PROXY protocol v2 unsupported address family %ui", family); return end; } pp->src_addr.data = ngx_pnalloc(c->pool, NGX_SOCKADDR_STRLEN); if (pp->src_addr.data == NULL) { return NULL; } pp->src_addr.len = ngx_sock_ntop(&src_sockaddr.sockaddr, socklen, pp->src_addr.data, NGX_SOCKADDR_STRLEN, 0); pp->dst_addr.data = ngx_pnalloc(c->pool, NGX_SOCKADDR_STRLEN); if (pp->dst_addr.data == NULL) { return NULL; } pp->dst_addr.len = ngx_sock_ntop(&dst_sockaddr.sockaddr, socklen, pp->dst_addr.data, NGX_SOCKADDR_STRLEN, 0); ngx_log_debug4(NGX_LOG_DEBUG_CORE, c->log, 0, "PROXY protocol v2 src: %V %d, dst: %V %d", &pp->src_addr, pp->src_port, &pp->dst_addr, pp->dst_port); if (buf < end) { pp->tlvs.data = ngx_pnalloc(c->pool, end - buf); if (pp->tlvs.data == NULL) { return NULL; } ngx_memcpy(pp->tlvs.data, buf, end - buf); pp->tlvs.len = end - buf; } c->proxy_protocol = pp; return end; } ngx_int_t ngx_proxy_protocol_get_tlv(ngx_connection_t *c, ngx_str_t *name, ngx_str_t *value) { u_char *p; size_t n; uint32_t verify; ngx_str_t ssl, *tlvs; ngx_int_t rc, type; ngx_proxy_protocol_tlv_ssl_t *tlv_ssl; ngx_proxy_protocol_tlv_entry_t *te; if (c->proxy_protocol == NULL) { return NGX_DECLINED; } ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, "PROXY protocol v2 get tlv \"%V\"", name); te = ngx_proxy_protocol_tlv_entries; tlvs = &c->proxy_protocol->tlvs; p = name->data; n = name->len; if (n >= 4 && p[0] == 's' && p[1] == 's' && p[2] == 'l' && p[3] == '_') { rc = ngx_proxy_protocol_lookup_tlv(c, tlvs, 0x20, &ssl); if (rc != NGX_OK) { return rc; } if (ssl.len < sizeof(ngx_proxy_protocol_tlv_ssl_t)) { return NGX_ERROR; } p += 4; n -= 4; if (n == 6 && ngx_strncmp(p, "verify", 6) == 0) { tlv_ssl = (ngx_proxy_protocol_tlv_ssl_t *) ssl.data; verify = ngx_proxy_protocol_parse_uint32(tlv_ssl->verify); value->data = ngx_pnalloc(c->pool, NGX_INT32_LEN); if (value->data == NULL) { return NGX_ERROR; } value->len = ngx_sprintf(value->data, "%uD", verify) - value->data; return NGX_OK; } ssl.data += sizeof(ngx_proxy_protocol_tlv_ssl_t); ssl.len -= sizeof(ngx_proxy_protocol_tlv_ssl_t); te = ngx_proxy_protocol_tlv_ssl_entries; tlvs = &ssl; } if (n >= 2 && p[0] == '0' && p[1] == 'x') { type = ngx_hextoi(p + 2, n - 2); if (type == NGX_ERROR) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "invalid PROXY protocol TLV \"%V\"", name); return NGX_ERROR; } return ngx_proxy_protocol_lookup_tlv(c, tlvs, type, value); } for ( /* void */ ; te->type; te++) { if (te->name.len == n && ngx_strncmp(te->name.data, p, n) == 0) { return ngx_proxy_protocol_lookup_tlv(c, tlvs, te->type, value); } } ngx_log_error(NGX_LOG_ERR, c->log, 0, "unknown PROXY protocol TLV \"%V\"", name); return NGX_DECLINED; } static ngx_int_t ngx_proxy_protocol_lookup_tlv(ngx_connection_t *c, ngx_str_t *tlvs, ngx_uint_t type, ngx_str_t *value) { u_char *p; size_t n, len; ngx_proxy_protocol_tlv_t *tlv; ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, "PROXY protocol v2 lookup tlv:%02xi", type); p = tlvs->data; n = tlvs->len; while (n) { if (n < sizeof(ngx_proxy_protocol_tlv_t)) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "broken PROXY protocol TLV"); return NGX_ERROR; } tlv = (ngx_proxy_protocol_tlv_t *) p; len = ngx_proxy_protocol_parse_uint16(tlv->len); p += sizeof(ngx_proxy_protocol_tlv_t); n -= sizeof(ngx_proxy_protocol_tlv_t); if (n < len) { ngx_log_error(NGX_LOG_ERR, c->log, 0, "broken PROXY protocol TLV"); return NGX_ERROR; } if (tlv->type == type) { value->data = p; value->len = len; return NGX_OK; } p += len; n -= len; } return NGX_DECLINED; }