src/core/ngx_proxy_protocol.c - nginx source code

Global variables defined

Data types defined

Functions defined

Macros defined

Source code


  1. /*
  2. * Copyright (C) Roman Arutyunyan
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>


  7. #define NGX_PROXY_PROTOCOL_AF_INET          1
  8. #define NGX_PROXY_PROTOCOL_AF_INET6         2


  9. #define ngx_proxy_protocol_parse_uint16(p)                                    \
  10.     ( ((uint16_t) (p)[0] << 8)                                                \
  11.     + (           (p)[1]) )

  12. #define ngx_proxy_protocol_parse_uint32(p)                                    \
  13.     ( ((uint32_t) (p)[0] << 24)                                               \
  14.     + (           (p)[1] << 16)                                               \
  15.     + (           (p)[2] << 8)                                                \
  16.     + (           (p)[3]) )


  17. typedef struct {
  18.     u_char                                  signature[12];
  19.     u_char                                  version_command;
  20.     u_char                                  family_transport;
  21.     u_char                                  len[2];
  22. } ngx_proxy_protocol_header_t;


  23. typedef struct {
  24.     u_char                                  src_addr[4];
  25.     u_char                                  dst_addr[4];
  26.     u_char                                  src_port[2];
  27.     u_char                                  dst_port[2];
  28. } ngx_proxy_protocol_inet_addrs_t;


  29. typedef struct {
  30.     u_char                                  src_addr[16];
  31.     u_char                                  dst_addr[16];
  32.     u_char                                  src_port[2];
  33.     u_char                                  dst_port[2];
  34. } ngx_proxy_protocol_inet6_addrs_t;


  35. typedef struct {
  36.     u_char                                  type;
  37.     u_char                                  len[2];
  38. } ngx_proxy_protocol_tlv_t;


  39. typedef struct {
  40.     u_char                                  client;
  41.     u_char                                  verify[4];
  42. } ngx_proxy_protocol_tlv_ssl_t;


  43. typedef struct {
  44.     ngx_str_t                               name;
  45.     ngx_uint_t                              type;
  46. } ngx_proxy_protocol_tlv_entry_t;


  47. static u_char *ngx_proxy_protocol_read_addr(ngx_connection_t *c, u_char *p,
  48.     u_char *last, ngx_str_t *addr);
  49. static u_char *ngx_proxy_protocol_read_port(u_char *p, u_char *last,
  50.     in_port_t *port, u_char sep);
  51. static u_char *ngx_proxy_protocol_v2_read(ngx_connection_t *c, u_char *buf,
  52.     u_char *last);
  53. static ngx_int_t ngx_proxy_protocol_lookup_tlv(ngx_connection_t *c,
  54.     ngx_str_t *tlvs, ngx_uint_t type, ngx_str_t *value);


  55. static ngx_proxy_protocol_tlv_entry_t  ngx_proxy_protocol_tlv_entries[] = {
  56.     { ngx_string("alpn"),       0x01 },
  57.     { ngx_string("authority"),  0x02 },
  58.     { ngx_string("unique_id"),  0x05 },
  59.     { ngx_string("ssl"),        0x20 },
  60.     { ngx_string("netns"),      0x30 },
  61.     { ngx_null_string,          0x00 }
  62. };


  63. static ngx_proxy_protocol_tlv_entry_t  ngx_proxy_protocol_tlv_ssl_entries[] = {
  64.     { ngx_string("version"),    0x21 },
  65.     { ngx_string("cn"),         0x22 },
  66.     { ngx_string("cipher"),     0x23 },
  67.     { ngx_string("sig_alg"),    0x24 },
  68.     { ngx_string("key_alg"),    0x25 },
  69.     { ngx_null_string,          0x00 }
  70. };


  71. u_char *
  72. ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, u_char *last)
  73. {
  74.     size_t                 len;
  75.     u_char                *p;
  76.     ngx_proxy_protocol_t  *pp;

  77.     static const u_char signature[] = "\r\n\r\n\0\r\nQUIT\n";

  78.     p = buf;
  79.     len = last - buf;

  80.     if (len >= sizeof(ngx_proxy_protocol_header_t)
  81.         && ngx_memcmp(p, signature, sizeof(signature) - 1) == 0)
  82.     {
  83.         return ngx_proxy_protocol_v2_read(c, buf, last);
  84.     }

  85.     if (len < 8 || ngx_strncmp(p, "PROXY ", 6) != 0) {
  86.         goto invalid;
  87.     }

  88.     p += 6;
  89.     len -= 6;

  90.     if (len >= 7 && ngx_strncmp(p, "UNKNOWN", 7) == 0) {
  91.         ngx_log_debug0(NGX_LOG_DEBUG_CORE, c->log, 0,
  92.                        "PROXY protocol unknown protocol");
  93.         p += 7;
  94.         goto skip;
  95.     }

  96.     if (len < 5 || ngx_strncmp(p, "TCP", 3) != 0
  97.         || (p[3] != '4' && p[3] != '6') || p[4] != ' ')
  98.     {
  99.         goto invalid;
  100.     }

  101.     p += 5;

  102.     pp = ngx_pcalloc(c->pool, sizeof(ngx_proxy_protocol_t));
  103.     if (pp == NULL) {
  104.         return NULL;
  105.     }

  106.     p = ngx_proxy_protocol_read_addr(c, p, last, &pp->src_addr);
  107.     if (p == NULL) {
  108.         goto invalid;
  109.     }

  110.     p = ngx_proxy_protocol_read_addr(c, p, last, &pp->dst_addr);
  111.     if (p == NULL) {
  112.         goto invalid;
  113.     }

  114.     p = ngx_proxy_protocol_read_port(p, last, &pp->src_port, ' ');
  115.     if (p == NULL) {
  116.         goto invalid;
  117.     }

  118.     p = ngx_proxy_protocol_read_port(p, last, &pp->dst_port, CR);
  119.     if (p == NULL) {
  120.         goto invalid;
  121.     }

  122.     if (p == last) {
  123.         goto invalid;
  124.     }

  125.     if (*p++ != LF) {
  126.         goto invalid;
  127.     }

  128.     ngx_log_debug4(NGX_LOG_DEBUG_CORE, c->log, 0,
  129.                    "PROXY protocol src: %V %d, dst: %V %d",
  130.                    &pp->src_addr, pp->src_port, &pp->dst_addr, pp->dst_port);

  131.     c->proxy_protocol = pp;

  132.     return p;

  133. skip:

  134.     for ( /* void */ ; p < last - 1; p++) {
  135.         if (p[0] == CR && p[1] == LF) {
  136.             return p + 2;
  137.         }
  138.     }

  139. invalid:

  140.     for (p = buf; p < last; p++) {
  141.         if (*p == CR || *p == LF) {
  142.             break;
  143.         }
  144.     }

  145.     ngx_log_error(NGX_LOG_ERR, c->log, 0,
  146.                   "broken header: \"%*s\"", (size_t) (p - buf), buf);

  147.     return NULL;
  148. }


  149. static u_char *
  150. ngx_proxy_protocol_read_addr(ngx_connection_t *c, u_char *p, u_char *last,
  151.     ngx_str_t *addr)
  152. {
  153.     size_t  len;
  154.     u_char  ch, *pos;

  155.     pos = p;

  156.     for ( ;; ) {
  157.         if (p == last) {
  158.             return NULL;
  159.         }

  160.         ch = *p++;

  161.         if (ch == ' ') {
  162.             break;
  163.         }

  164.         if (ch != ':' && ch != '.'
  165.             && (ch < 'a' || ch > 'f')
  166.             && (ch < 'A' || ch > 'F')
  167.             && (ch < '0' || ch > '9'))
  168.         {
  169.             return NULL;
  170.         }
  171.     }

  172.     len = p - pos - 1;

  173.     addr->data = ngx_pnalloc(c->pool, len);
  174.     if (addr->data == NULL) {
  175.         return NULL;
  176.     }

  177.     ngx_memcpy(addr->data, pos, len);
  178.     addr->len = len;

  179.     return p;
  180. }


  181. static u_char *
  182. ngx_proxy_protocol_read_port(u_char *p, u_char *last, in_port_t *port,
  183.     u_char sep)
  184. {
  185.     size_t      len;
  186.     u_char     *pos;
  187.     ngx_int_t   n;

  188.     pos = p;

  189.     for ( ;; ) {
  190.         if (p == last) {
  191.             return NULL;
  192.         }

  193.         if (*p++ == sep) {
  194.             break;
  195.         }
  196.     }

  197.     len = p - pos - 1;

  198.     n = ngx_atoi(pos, len);
  199.     if (n < 0 || n > 65535) {
  200.         return NULL;
  201.     }

  202.     *port = (in_port_t) n;

  203.     return p;
  204. }


  205. u_char *
  206. ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, u_char *last)
  207. {
  208.     ngx_uint_t  port, lport;

  209.     if (last - buf < NGX_PROXY_PROTOCOL_V1_MAX_HEADER) {
  210.         ngx_log_error(NGX_LOG_ALERT, c->log, 0,
  211.                       "too small buffer for PROXY protocol");
  212.         return NULL;
  213.     }

  214.     if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
  215.         return NULL;
  216.     }

  217.     switch (c->sockaddr->sa_family) {

  218.     case AF_INET:
  219.         buf = ngx_cpymem(buf, "PROXY TCP4 ", sizeof("PROXY TCP4 ") - 1);
  220.         break;

  221. #if (NGX_HAVE_INET6)
  222.     case AF_INET6:
  223.         buf = ngx_cpymem(buf, "PROXY TCP6 ", sizeof("PROXY TCP6 ") - 1);
  224.         break;
  225. #endif

  226.     default:
  227.         return ngx_cpymem(buf, "PROXY UNKNOWN" CRLF,
  228.                           sizeof("PROXY UNKNOWN" CRLF) - 1);
  229.     }

  230.     buf += ngx_sock_ntop(c->sockaddr, c->socklen, buf, last - buf, 0);

  231.     *buf++ = ' ';

  232.     buf += ngx_sock_ntop(c->local_sockaddr, c->local_socklen, buf, last - buf,
  233.                          0);

  234.     port = ngx_inet_get_port(c->sockaddr);
  235.     lport = ngx_inet_get_port(c->local_sockaddr);

  236.     return ngx_slprintf(buf, last, " %ui %ui" CRLF, port, lport);
  237. }


  238. static u_char *
  239. ngx_proxy_protocol_v2_read(ngx_connection_t *c, u_char *buf, u_char *last)
  240. {
  241.     u_char                             *end;
  242.     size_t                              len;
  243.     socklen_t                           socklen;
  244.     ngx_uint_t                          version, command, family, transport;
  245.     ngx_sockaddr_t                      src_sockaddr, dst_sockaddr;
  246.     ngx_proxy_protocol_t               *pp;
  247.     ngx_proxy_protocol_header_t        *header;
  248.     ngx_proxy_protocol_inet_addrs_t    *in;
  249. #if (NGX_HAVE_INET6)
  250.     ngx_proxy_protocol_inet6_addrs_t   *in6;
  251. #endif

  252.     header = (ngx_proxy_protocol_header_t *) buf;

  253.     buf += sizeof(ngx_proxy_protocol_header_t);

  254.     version = header->version_command >> 4;

  255.     if (version != 2) {
  256.         ngx_log_error(NGX_LOG_ERR, c->log, 0,
  257.                       "unknown PROXY protocol version: %ui", version);
  258.         return NULL;
  259.     }

  260.     len = ngx_proxy_protocol_parse_uint16(header->len);

  261.     if ((size_t) (last - buf) < len) {
  262.         ngx_log_error(NGX_LOG_ERR, c->log, 0, "header is too large");
  263.         return NULL;
  264.     }

  265.     end = buf + len;

  266.     command = header->version_command & 0x0f;

  267.     /* only PROXY is supported */
  268.     if (command != 1) {
  269.         ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
  270.                        "PROXY protocol v2 unsupported command %ui", command);
  271.         return end;
  272.     }

  273.     transport = header->family_transport & 0x0f;

  274.     /* only STREAM is supported */
  275.     if (transport != 1) {
  276.         ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
  277.                        "PROXY protocol v2 unsupported transport %ui",
  278.                        transport);
  279.         return end;
  280.     }

  281.     pp = ngx_pcalloc(c->pool, sizeof(ngx_proxy_protocol_t));
  282.     if (pp == NULL) {
  283.         return NULL;
  284.     }

  285.     family = header->family_transport >> 4;

  286.     switch (family) {

  287.     case NGX_PROXY_PROTOCOL_AF_INET:

  288.         if ((size_t) (end - buf) < sizeof(ngx_proxy_protocol_inet_addrs_t)) {
  289.             return NULL;
  290.         }

  291.         in = (ngx_proxy_protocol_inet_addrs_t *) buf;

  292.         src_sockaddr.sockaddr_in.sin_family = AF_INET;
  293.         src_sockaddr.sockaddr_in.sin_port = 0;
  294.         ngx_memcpy(&src_sockaddr.sockaddr_in.sin_addr, in->src_addr, 4);

  295.         dst_sockaddr.sockaddr_in.sin_family = AF_INET;
  296.         dst_sockaddr.sockaddr_in.sin_port = 0;
  297.         ngx_memcpy(&dst_sockaddr.sockaddr_in.sin_addr, in->dst_addr, 4);

  298.         pp->src_port = ngx_proxy_protocol_parse_uint16(in->src_port);
  299.         pp->dst_port = ngx_proxy_protocol_parse_uint16(in->dst_port);

  300.         socklen = sizeof(struct sockaddr_in);

  301.         buf += sizeof(ngx_proxy_protocol_inet_addrs_t);

  302.         break;

  303. #if (NGX_HAVE_INET6)

  304.     case NGX_PROXY_PROTOCOL_AF_INET6:

  305.         if ((size_t) (end - buf) < sizeof(ngx_proxy_protocol_inet6_addrs_t)) {
  306.             return NULL;
  307.         }

  308.         in6 = (ngx_proxy_protocol_inet6_addrs_t *) buf;

  309.         src_sockaddr.sockaddr_in6.sin6_family = AF_INET6;
  310.         src_sockaddr.sockaddr_in6.sin6_port = 0;
  311.         ngx_memcpy(&src_sockaddr.sockaddr_in6.sin6_addr, in6->src_addr, 16);

  312.         dst_sockaddr.sockaddr_in6.sin6_family = AF_INET6;
  313.         dst_sockaddr.sockaddr_in6.sin6_port = 0;
  314.         ngx_memcpy(&dst_sockaddr.sockaddr_in6.sin6_addr, in6->dst_addr, 16);

  315.         pp->src_port = ngx_proxy_protocol_parse_uint16(in6->src_port);
  316.         pp->dst_port = ngx_proxy_protocol_parse_uint16(in6->dst_port);

  317.         socklen = sizeof(struct sockaddr_in6);

  318.         buf += sizeof(ngx_proxy_protocol_inet6_addrs_t);

  319.         break;

  320. #endif

  321.     default:
  322.         ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
  323.                        "PROXY protocol v2 unsupported address family %ui",
  324.                        family);
  325.         return end;
  326.     }

  327.     pp->src_addr.data = ngx_pnalloc(c->pool, NGX_SOCKADDR_STRLEN);
  328.     if (pp->src_addr.data == NULL) {
  329.         return NULL;
  330.     }

  331.     pp->src_addr.len = ngx_sock_ntop(&src_sockaddr.sockaddr, socklen,
  332.                                      pp->src_addr.data, NGX_SOCKADDR_STRLEN, 0);

  333.     pp->dst_addr.data = ngx_pnalloc(c->pool, NGX_SOCKADDR_STRLEN);
  334.     if (pp->dst_addr.data == NULL) {
  335.         return NULL;
  336.     }

  337.     pp->dst_addr.len = ngx_sock_ntop(&dst_sockaddr.sockaddr, socklen,
  338.                                      pp->dst_addr.data, NGX_SOCKADDR_STRLEN, 0);

  339.     ngx_log_debug4(NGX_LOG_DEBUG_CORE, c->log, 0,
  340.                    "PROXY protocol v2 src: %V %d, dst: %V %d",
  341.                    &pp->src_addr, pp->src_port, &pp->dst_addr, pp->dst_port);

  342.     if (buf < end) {
  343.         pp->tlvs.data = ngx_pnalloc(c->pool, end - buf);
  344.         if (pp->tlvs.data == NULL) {
  345.             return NULL;
  346.         }

  347.         ngx_memcpy(pp->tlvs.data, buf, end - buf);
  348.         pp->tlvs.len = end - buf;
  349.     }

  350.     c->proxy_protocol = pp;

  351.     return end;
  352. }


  353. ngx_int_t
  354. ngx_proxy_protocol_get_tlv(ngx_connection_t *c, ngx_str_t *name,
  355.     ngx_str_t *value)
  356. {
  357.     u_char                          *p;
  358.     size_t                           n;
  359.     uint32_t                         verify;
  360.     ngx_str_t                        ssl, *tlvs;
  361.     ngx_int_t                        rc, type;
  362.     ngx_proxy_protocol_tlv_ssl_t    *tlv_ssl;
  363.     ngx_proxy_protocol_tlv_entry_t  *te;

  364.     if (c->proxy_protocol == NULL) {
  365.         return NGX_DECLINED;
  366.     }

  367.     ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
  368.                    "PROXY protocol v2 get tlv \"%V\"", name);

  369.     te = ngx_proxy_protocol_tlv_entries;
  370.     tlvs = &c->proxy_protocol->tlvs;

  371.     p = name->data;
  372.     n = name->len;

  373.     if (n >= 4 && p[0] == 's' && p[1] == 's' && p[2] == 'l' && p[3] == '_') {

  374.         rc = ngx_proxy_protocol_lookup_tlv(c, tlvs, 0x20, &ssl);
  375.         if (rc != NGX_OK) {
  376.             return rc;
  377.         }

  378.         if (ssl.len < sizeof(ngx_proxy_protocol_tlv_ssl_t)) {
  379.             return NGX_ERROR;
  380.         }

  381.         p += 4;
  382.         n -= 4;

  383.         if (n == 6 && ngx_strncmp(p, "verify", 6) == 0) {

  384.             tlv_ssl = (ngx_proxy_protocol_tlv_ssl_t *) ssl.data;
  385.             verify = ngx_proxy_protocol_parse_uint32(tlv_ssl->verify);

  386.             value->data = ngx_pnalloc(c->pool, NGX_INT32_LEN);
  387.             if (value->data == NULL) {
  388.                 return NGX_ERROR;
  389.             }

  390.             value->len = ngx_sprintf(value->data, "%uD", verify)
  391.                          - value->data;
  392.             return NGX_OK;
  393.         }

  394.         ssl.data += sizeof(ngx_proxy_protocol_tlv_ssl_t);
  395.         ssl.len -= sizeof(ngx_proxy_protocol_tlv_ssl_t);

  396.         te = ngx_proxy_protocol_tlv_ssl_entries;
  397.         tlvs = &ssl;
  398.     }

  399.     if (n >= 2 && p[0] == '0' && p[1] == 'x') {

  400.         type = ngx_hextoi(p + 2, n - 2);
  401.         if (type == NGX_ERROR) {
  402.             ngx_log_error(NGX_LOG_ERR, c->log, 0,
  403.                           "invalid PROXY protocol TLV \"%V\"", name);
  404.             return NGX_ERROR;
  405.         }

  406.         return ngx_proxy_protocol_lookup_tlv(c, tlvs, type, value);
  407.     }

  408.     for ( /* void */ ; te->type; te++) {
  409.         if (te->name.len == n && ngx_strncmp(te->name.data, p, n) == 0) {
  410.             return ngx_proxy_protocol_lookup_tlv(c, tlvs, te->type, value);
  411.         }
  412.     }

  413.     ngx_log_error(NGX_LOG_ERR, c->log, 0,
  414.                   "unknown PROXY protocol TLV \"%V\"", name);

  415.     return NGX_DECLINED;
  416. }


  417. static ngx_int_t
  418. ngx_proxy_protocol_lookup_tlv(ngx_connection_t *c, ngx_str_t *tlvs,
  419.     ngx_uint_t type, ngx_str_t *value)
  420. {
  421.     u_char                    *p;
  422.     size_t                     n, len;
  423.     ngx_proxy_protocol_tlv_t  *tlv;

  424.     ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
  425.                    "PROXY protocol v2 lookup tlv:%02xi", type);

  426.     p = tlvs->data;
  427.     n = tlvs->len;

  428.     while (n) {
  429.         if (n < sizeof(ngx_proxy_protocol_tlv_t)) {
  430.             ngx_log_error(NGX_LOG_ERR, c->log, 0, "broken PROXY protocol TLV");
  431.             return NGX_ERROR;
  432.         }

  433.         tlv = (ngx_proxy_protocol_tlv_t *) p;
  434.         len = ngx_proxy_protocol_parse_uint16(tlv->len);

  435.         p += sizeof(ngx_proxy_protocol_tlv_t);
  436.         n -= sizeof(ngx_proxy_protocol_tlv_t);

  437.         if (n < len) {
  438.             ngx_log_error(NGX_LOG_ERR, c->log, 0, "broken PROXY protocol TLV");
  439.             return NGX_ERROR;
  440.         }

  441.         if (tlv->type == type) {
  442.             value->data = p;
  443.             value->len = len;
  444.             return NGX_OK;
  445.         }

  446.         p += len;
  447.         n -= len;
  448.     }

  449.     return NGX_DECLINED;
  450. }