]> git.kaiwu.me - nginx.git/commitdiff
Core: PROXY protocol v2 write support
authorSergey Kandaurov <pluknet@nginx.com>
Mon, 10 Aug 2026 14:10:44 +0000 (18:10 +0400)
committerSergey Kandaurov <s.kandaurov@f5.com>
Tue, 11 Aug 2026 19:39:53 +0000 (23:39 +0400)
Add ngx_proxy_protocol_v2_write() that writes the fixed 16-byte
PPv2 header and address block, and appends a list of TLVs.

An address block is written using IPv4/IPv6 mixed-family promotion:
the highest family present on either side (IPv6 > IPv4) is used for
the header, and the other address is promoted into it.  Both v4-mapped
addresses are demoted to IPv4.

When PROXY protocol v2 is configured and the downstream connection
is SSL, standard PROXY protocol v2 TLVs are populated:

    PP2_TYPE_ALPN              from SSL_get0_alpn_selected()
    PP2_TYPE_AUTHORITY         from SSL_get_servername()
    PP2_SUBTYPE_SSL_VERSION    from SSL_get_version()
    PP2_SUBTYPE_SSL_CIPHER     from SSL_get_cipher_name()
    PP2_SUBTYPE_SSL_CN         from client cert subject CN
    PP2_SUBTYPE_SSL_SIG_ALG    from server certificate
    PP2_SUBTYPE_SSL_KEY_ALG    from server certificate

The CRC32c TLV is written last and covers the entire header in place.

Co-authored-by: Vadim Zhestikov <v.zhestikov@f5.com>
src/core/ngx_proxy_protocol.c
src/core/ngx_proxy_protocol.h
src/event/ngx_event_openssl.h

index 2ab09bc665696ceae9b3b54cb8c7ec9caf935fd9..d1ff171bc7376757dfbe55378cc2d55bc9be9fdc 100644 (file)
@@ -9,9 +9,12 @@
 #include <ngx_core.h>
 
 
+#define NGX_PROXY_PROTOCOL_V2_MAX_HEADER    52
+
 #define NGX_PROXY_PROTOCOL_CMD_LOCAL        0
 #define NGX_PROXY_PROTOCOL_CMD_PROXY        1
 
+#define NGX_PROXY_PROTOCOL_AF_UNSPEC        0
 #define NGX_PROXY_PROTOCOL_AF_INET          1
 #define NGX_PROXY_PROTOCOL_AF_INET6         2
 
 #define NGX_PROXY_PROTOCOL_TLV_SSL_KEY_ALG  0x25
 #define NGX_PROXY_PROTOCOL_TLV_NETNS        0x30
 
+#define NGX_PROXY_PROTOCOL_V2_CLIENT_SSL          0x01
+#define NGX_PROXY_PROTOCOL_V2_CLIENT_CERT_CONN    0x02
+#define NGX_PROXY_PROTOCOL_V2_CLIENT_CERT_SESS    0x04
+
 
 #define ngx_proxy_protocol_parse_uint16(p)                                    \
     ( ((uint16_t) (p)[0] << 8)                                                \
@@ -85,6 +92,12 @@ typedef struct {
 } ngx_proxy_protocol_tlv_entry_t;
 
 
+typedef struct {
+    ngx_uint_t                              type;
+    ngx_str_t                               value;
+} ngx_proxy_protocol_tlv_value_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,
@@ -93,6 +106,31 @@ 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);
+#if (NGX_OPENSSL)
+static ngx_int_t ngx_proxy_protocol_v2_eval_ssl(ngx_connection_t *c,
+    ngx_array_t *tlvs, ngx_array_t *ssl_tlvs, ngx_uint_t *client,
+    uint32_t *verify);
+static ngx_int_t ngx_proxy_protocol_v2_authority(ngx_connection_t *c,
+    ngx_str_t *out);
+static ngx_int_t ngx_proxy_protocol_v2_alpn(ngx_connection_t *c,
+    ngx_str_t *out);
+static ngx_int_t ngx_proxy_protocol_v2_ssl_sub(ngx_connection_t *c,
+    ngx_uint_t type, ngx_str_t *out);
+#endif
+static u_char *ngx_proxy_protocol_v2_write_header(ngx_connection_t *c,
+    u_char *buf, u_char *last);
+static u_char ngx_proxy_protocol_v2_family(ngx_uint_t family);
+static void ngx_proxy_protocol_v2_write_ipv4(struct sockaddr *sa, u_char *addr,
+    u_char *port);
+static void ngx_proxy_protocol_v2_write_ipv6(struct sockaddr *sa, u_char *addr,
+    u_char *port);
+static u_char *ngx_proxy_protocol_v2_write_tlv(ngx_connection_t *c, u_char *p,
+    u_char *last, ngx_uint_t type, ngx_str_t *value);
+static u_char *ngx_proxy_protocol_v2_write_ssl(ngx_connection_t *c, u_char *p,
+    u_char *last, ngx_array_t *ssl_tlvs, ngx_uint_t client, uint32_t verify);
+static void ngx_proxy_protocol_v2_set_len(u_char *buf, u_char *p);
+static u_char *ngx_proxy_protocol_v2_write_crc32c(ngx_connection_t *c,
+    u_char *buf, u_char *p, u_char *last);
 
 
 static ngx_proxy_protocol_tlv_entry_t  ngx_proxy_protocol_tlv_entries[] = {
@@ -115,6 +153,9 @@ static ngx_proxy_protocol_tlv_entry_t  ngx_proxy_protocol_tlv_ssl_entries[] = {
 };
 
 
+static const u_char  ngx_proxy_protocol_signature[] = "\r\n\r\n\0\r\nQUIT\n";
+
+
 u_char *
 ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, u_char *last)
 {
@@ -122,13 +163,13 @@ ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, u_char *last)
     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)
+        && ngx_memcmp(p, ngx_proxy_protocol_signature,
+                      sizeof(ngx_proxy_protocol_signature) - 1)
+           == 0)
     {
         return ngx_proxy_protocol_v2_read(c, buf, last);
     }
@@ -632,3 +673,667 @@ ngx_proxy_protocol_lookup_tlv(ngx_connection_t *c, ngx_str_t *tlvs,
 
     return NGX_DECLINED;
 }
+
+
+u_char *
+ngx_proxy_protocol_v2_write(ngx_connection_t *c, u_char *buf, u_char *last,
+    ngx_array_t *tlvs /* reserved */ )
+{
+    u_char                          *p;
+    uint32_t                         verify;
+    ngx_uint_t                       i, client;
+    ngx_array_t                      base_tlvs, ssl_tlvs;
+    ngx_proxy_protocol_tlv_value_t  *tlv;
+
+    client = 0;
+    verify = 0;
+
+    ngx_memzero(&base_tlvs, sizeof(ngx_array_t));
+    ngx_memzero(&ssl_tlvs, sizeof(ngx_array_t));
+
+#if (NGX_OPENSSL)
+
+    if (c->ssl != NULL) {
+        if (ngx_array_init(&base_tlvs, c->pool, 2,
+                           sizeof(ngx_proxy_protocol_tlv_value_t))
+            != NGX_OK)
+        {
+            return NULL;
+        }
+
+        if (ngx_array_init(&ssl_tlvs, c->pool, 5,
+                           sizeof(ngx_proxy_protocol_tlv_value_t))
+            != NGX_OK)
+        {
+            return NULL;
+        }
+
+        if (ngx_proxy_protocol_v2_eval_ssl(c, &base_tlvs, &ssl_tlvs, &client,
+                                           &verify)
+            != NGX_OK)
+        {
+            return NULL;
+        }
+    }
+
+#endif
+
+    p = ngx_proxy_protocol_v2_write_header(c, buf, last);
+    if (p == NULL) {
+        return NULL;
+    }
+
+    tlv = base_tlvs.elts;
+
+    for (i = 0; i < base_tlvs.nelts; i++) {
+        p = ngx_proxy_protocol_v2_write_tlv(c, p, last, tlv[i].type,
+                                           &tlv[i].value);
+        if (p == NULL) {
+            return NULL;
+        }
+    }
+
+    if (client) {
+        p = ngx_proxy_protocol_v2_write_ssl(c, p, last, &ssl_tlvs, client,
+                                            verify);
+        if (p == NULL) {
+            return NULL;
+        }
+    }
+
+    ngx_proxy_protocol_v2_set_len(buf,
+                                  p + sizeof(ngx_proxy_protocol_tlv_t) + 4);
+
+    return ngx_proxy_protocol_v2_write_crc32c(c, buf, p, last);
+}
+
+
+#if (NGX_OPENSSL)
+
+static ngx_int_t
+ngx_proxy_protocol_v2_eval_ssl(ngx_connection_t *c, ngx_array_t *tlvs,
+    ngx_array_t *ssl_tlvs, ngx_uint_t *client, uint32_t *verify)
+{
+    X509                            *cert;
+    long                             n;
+    ngx_int_t                        rc;
+    ngx_str_t                        value;
+    ngx_uint_t                       type;
+    const char                      *s;
+    ngx_proxy_protocol_tlv_value_t  *tlv;
+
+    if (ngx_proxy_protocol_v2_authority(c, &value) == NGX_OK) {
+        tlv = ngx_array_push(tlvs);
+        if (tlv == NULL) {
+            return NGX_ERROR;
+        }
+
+        tlv->type = NGX_PROXY_PROTOCOL_TLV_AUTHORITY;
+        tlv->value = value;
+    }
+
+    if (ngx_proxy_protocol_v2_alpn(c, &value) == NGX_OK) {
+        tlv = ngx_array_push(tlvs);
+        if (tlv == NULL) {
+            return NGX_ERROR;
+        }
+
+        tlv->type = NGX_PROXY_PROTOCOL_TLV_ALPN;
+        tlv->value = value;
+    }
+
+    for (type = NGX_PROXY_PROTOCOL_TLV_SSL_VERSION;
+         type <= NGX_PROXY_PROTOCOL_TLV_SSL_KEY_ALG;
+         type++)
+    {
+        rc = ngx_proxy_protocol_v2_ssl_sub(c, type, &value);
+
+        if (rc == NGX_ERROR) {
+            return NGX_ERROR;
+        }
+
+        if (rc != NGX_OK) {
+            continue;
+        }
+
+        tlv = ngx_array_push(ssl_tlvs);
+        if (tlv == NULL) {
+            return NGX_ERROR;
+        }
+
+        tlv->type = type;
+        tlv->value = value;
+    }
+
+    *client = NGX_PROXY_PROTOCOL_V2_CLIENT_SSL;
+    *verify = 1;  /* X509_V_ERR_UNSPECIFIED */
+
+    cert = SSL_get_peer_certificate(c->ssl->connection);
+
+    if (cert != NULL) {
+        X509_free(cert);
+
+        *client |= NGX_PROXY_PROTOCOL_V2_CLIENT_CERT_SESS;
+
+        if (!SSL_session_reused(c->ssl->connection)) {
+            *client |= NGX_PROXY_PROTOCOL_V2_CLIENT_CERT_CONN;
+        }
+
+        n = SSL_get_verify_result(c->ssl->connection);
+
+        if (n == X509_V_OK) {
+
+            if (ngx_ssl_ocsp_get_status(c, &s) != NGX_OK) {
+                n = X509_V_ERR_CERT_REVOKED;
+            }
+        }
+
+        *verify = (uint32_t) n;
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_proxy_protocol_v2_authority(ngx_connection_t *c, ngx_str_t *out)
+{
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+
+    const char  *sni;
+
+    sni = SSL_get_servername(c->ssl->connection, TLSEXT_NAMETYPE_host_name);
+    if (sni == NULL) {
+        return NGX_DECLINED;
+    }
+
+    out->data = (u_char *) sni;
+    out->len = ngx_strlen(sni);
+    return NGX_OK;
+
+#else
+    return NGX_DECLINED;
+#endif
+}
+
+
+static ngx_int_t
+ngx_proxy_protocol_v2_alpn(ngx_connection_t *c, ngx_str_t *out)
+{
+#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
+
+    unsigned int          alpnlen;
+    const unsigned char  *alpn;
+
+    SSL_get0_alpn_selected(c->ssl->connection, &alpn, &alpnlen);
+    if (alpn == NULL || alpnlen == 0) {
+        return NGX_DECLINED;
+    }
+
+    out->data = (u_char *) alpn;
+    out->len = alpnlen;
+    return NGX_OK;
+
+#else
+    return NGX_DECLINED;
+#endif
+}
+
+
+static ngx_int_t
+ngx_proxy_protocol_v2_ssl_sub(ngx_connection_t *c, ngx_uint_t type,
+    ngx_str_t *out)
+{
+    int               i, len;
+    X509             *cert;
+    u_char           *alg, *s;
+    EVP_PKEY         *pkey;
+#if (OPENSSL_VERSION_NUMBER >= 0x40000000L)
+    const
+#endif
+    X509_NAME        *subject;
+#if (OPENSSL_VERSION_NUMBER >= 0x40000000L)
+    const
+#endif
+    X509_NAME_ENTRY  *entry;
+
+    switch (type) {
+
+    case NGX_PROXY_PROTOCOL_TLV_SSL_VERSION:
+        out->data = (u_char *) SSL_get_version(c->ssl->connection);
+        out->len = ngx_strlen(out->data);
+        return NGX_OK;
+
+    case NGX_PROXY_PROTOCOL_TLV_SSL_CIPHER:
+        out->data = (u_char *) SSL_get_cipher_name(c->ssl->connection);
+        out->len = ngx_strlen(out->data);
+        return NGX_OK;
+
+    case NGX_PROXY_PROTOCOL_TLV_SSL_CN:
+        cert = SSL_get_peer_certificate(c->ssl->connection);
+        if (cert == NULL) {
+            return NGX_DECLINED;
+        }
+
+        subject = X509_get_subject_name(cert);
+        if (subject == NULL) {
+            X509_free(cert);
+            return NGX_ERROR;
+        }
+
+        i = X509_NAME_get_index_by_NID(subject, NID_commonName, -1);
+        if (i < 0) {
+            X509_free(cert);
+            return NGX_DECLINED;
+        }
+
+        entry = X509_NAME_get_entry(subject, i);
+
+        len = ASN1_STRING_to_UTF8(&s, X509_NAME_ENTRY_get_data(entry));
+        if (len < 0) {
+            ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,
+                          "ASN1_STRING_to_UTF8() failed");
+            X509_free(cert);
+            return NGX_ERROR;
+        }
+
+        out->len = len;
+        out->data = ngx_pnalloc(c->pool, len);
+        if (out->data == NULL) {
+            OPENSSL_free(s);
+            X509_free(cert);
+            return NGX_ERROR;
+        }
+
+        ngx_memcpy(out->data, s, len);
+
+        OPENSSL_free(s);
+        X509_free(cert);
+        return NGX_OK;
+
+    case NGX_PROXY_PROTOCOL_TLV_SSL_SIG_ALG:
+        cert = SSL_get_certificate(c->ssl->connection);
+
+        out->data = (u_char *) OBJ_nid2sn(X509_get_signature_nid(cert));
+        out->len = ngx_strlen(out->data);
+        return NGX_OK;
+
+    case NGX_PROXY_PROTOCOL_TLV_SSL_KEY_ALG:
+        cert = SSL_get_certificate(c->ssl->connection);
+
+        pkey = X509_get_pubkey(cert);
+        if (pkey == NULL) {
+            return NGX_ERROR;
+        }
+
+        switch (EVP_PKEY_base_id(pkey)) {
+
+        case EVP_PKEY_RSA:
+#ifdef EVP_PKEY_RSA_PSS
+        case EVP_PKEY_RSA_PSS:
+#endif
+            alg = (u_char *) "RSA";
+            break;
+
+        case EVP_PKEY_EC:
+            alg = (u_char *) "EC";
+            break;
+
+        case EVP_PKEY_DSA:
+            alg = (u_char *) "DSA";
+            break;
+
+        default:
+            EVP_PKEY_free(pkey);
+            return NGX_DECLINED;
+        }
+
+        out->len = sizeof("RSA16384") - 1;
+        out->data = ngx_pnalloc(c->pool, out->len);
+        if (out->data == NULL) {
+            EVP_PKEY_free(pkey);
+            return NGX_ERROR;
+        }
+
+        out->len = ngx_snprintf(out->data, out->len, "%s%d",
+                                alg, EVP_PKEY_bits(pkey))
+                   - out->data;
+
+        EVP_PKEY_free(pkey);
+
+        return NGX_OK;
+    }
+
+    return NGX_DECLINED;
+}
+
+#endif
+
+
+static u_char *
+ngx_proxy_protocol_v2_write_header(ngx_connection_t *c, u_char *buf,
+    u_char *last)
+{
+    u_char                            *p, src_af, dst_af,
+                                       family, command, transport;
+    ngx_uint_t                         src_fam, dst_fam;
+    ngx_proxy_protocol_header_t       *header;
+    ngx_proxy_protocol_inet_addrs_t   *in;
+#if (NGX_HAVE_INET6)
+    ngx_uint_t                         port, lport;
+    struct in6_addr                   *src6, *dst6;
+    ngx_proxy_protocol_inet6_addrs_t  *in6;
+#endif
+
+    if (last - buf < NGX_PROXY_PROTOCOL_V2_MAX_HEADER) {
+        ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+                      "too small buffer for PROXY protocol v2");
+        return NULL;
+    }
+
+    if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
+        return NULL;
+    }
+
+    /* the length field is set by the caller once all TLVs are written */
+
+    header = (ngx_proxy_protocol_header_t *) buf;
+
+    ngx_memcpy(header->signature, ngx_proxy_protocol_signature,
+               sizeof(ngx_proxy_protocol_signature) - 1);
+
+    p = buf + sizeof(ngx_proxy_protocol_header_t);
+
+    src_fam = c->sockaddr->sa_family;
+    dst_fam = c->local_sockaddr->sa_family;
+
+    src_af = ngx_proxy_protocol_v2_family(src_fam);
+    dst_af = ngx_proxy_protocol_v2_family(dst_fam);
+
+    /* promote to the highest address family present on either side */
+
+    if (src_af == NGX_PROXY_PROTOCOL_AF_UNSPEC
+        || dst_af == NGX_PROXY_PROTOCOL_AF_UNSPEC)
+    {
+        command = NGX_PROXY_PROTOCOL_CMD_LOCAL;
+        family = NGX_PROXY_PROTOCOL_AF_UNSPEC;
+        transport = NGX_PROXY_PROTOCOL_TYPE_UNSPEC;
+
+    } else {
+        command = NGX_PROXY_PROTOCOL_CMD_PROXY;
+        family = ngx_max(src_af, dst_af);
+
+        switch (c->type) {
+        case SOCK_STREAM:
+            transport = NGX_PROXY_PROTOCOL_TYPE_STREAM;
+            break;
+        case SOCK_DGRAM:
+            transport = NGX_PROXY_PROTOCOL_TYPE_DGRAM;
+            break;
+        default:
+            transport = NGX_PROXY_PROTOCOL_TYPE_UNSPEC;
+            break;
+        }
+    }
+
+    header->version_command = 0x20 | command;
+    header->family_transport = (family << 4) | transport;
+
+    switch (family) {
+
+    case NGX_PROXY_PROTOCOL_AF_INET:
+
+        in = (ngx_proxy_protocol_inet_addrs_t *) p;
+
+        ngx_proxy_protocol_v2_write_ipv4(c->sockaddr,
+                                         in->src_addr, in->src_port);
+        ngx_proxy_protocol_v2_write_ipv4(c->local_sockaddr,
+                                         in->dst_addr, in->dst_port);
+
+        p += sizeof(ngx_proxy_protocol_inet_addrs_t);
+
+        break;
+
+#if (NGX_HAVE_INET6)
+
+    case NGX_PROXY_PROTOCOL_AF_INET6:
+
+        if (src_fam == AF_INET6 && dst_fam == AF_INET6) {
+            src6 = &((struct sockaddr_in6 *) c->sockaddr)->sin6_addr;
+            dst6 = &((struct sockaddr_in6 *) c->local_sockaddr)->sin6_addr;
+
+            if (IN6_IS_ADDR_V4MAPPED(src6) && IN6_IS_ADDR_V4MAPPED(dst6)) {
+
+                /* both v4-mapped: demote to AF_INET */
+
+                header->family_transport = (NGX_PROXY_PROTOCOL_AF_INET << 4)
+                                           | transport;
+
+                in = (ngx_proxy_protocol_inet_addrs_t *) p;
+
+                ngx_memcpy(in->src_addr, src6->s6_addr + 12, 4);
+                ngx_memcpy(in->dst_addr, dst6->s6_addr + 12, 4);
+
+                port  = ngx_inet_get_port(c->sockaddr);
+                lport = ngx_inet_get_port(c->local_sockaddr);
+
+                in->src_port[0] = (u_char) (port >> 8);
+                in->src_port[1] = (u_char)  port;
+                in->dst_port[0] = (u_char) (lport >> 8);
+                in->dst_port[1] = (u_char)  lport;
+
+                p += sizeof(ngx_proxy_protocol_inet_addrs_t);
+                break;
+            }
+        }
+
+        in6 = (ngx_proxy_protocol_inet6_addrs_t *) p;
+
+        ngx_proxy_protocol_v2_write_ipv6(c->sockaddr,
+                                         in6->src_addr, in6->src_port);
+        ngx_proxy_protocol_v2_write_ipv6(c->local_sockaddr,
+                                         in6->dst_addr, in6->dst_port);
+
+        p += sizeof(ngx_proxy_protocol_inet6_addrs_t);
+
+        break;
+
+#endif
+
+    default:  /* NGX_PROXY_PROTOCOL_AF_UNSPEC */
+        break;
+    }
+
+    return p;
+}
+
+
+static u_char
+ngx_proxy_protocol_v2_family(ngx_uint_t family)
+{
+    switch (family) {
+
+    case AF_INET:
+        return NGX_PROXY_PROTOCOL_AF_INET;
+
+#if (NGX_HAVE_INET6)
+    case AF_INET6:
+        return NGX_PROXY_PROTOCOL_AF_INET6;
+#endif
+
+    default:
+        return NGX_PROXY_PROTOCOL_AF_UNSPEC;
+    }
+}
+
+
+static void
+ngx_proxy_protocol_v2_write_ipv4(struct sockaddr *sa, u_char *addr,
+    u_char *port)
+{
+    struct sockaddr_in  *sin;
+
+    sin = (struct sockaddr_in *) sa;
+    ngx_memcpy(addr, &sin->sin_addr, 4);
+    ngx_memcpy(port, &sin->sin_port, 2);
+}
+
+
+#if (NGX_HAVE_INET6)
+
+static void
+ngx_proxy_protocol_v2_write_ipv6(struct sockaddr *sa, u_char *addr,
+    u_char *port)
+{
+    struct sockaddr_in   *sin;
+    struct sockaddr_in6  *sin6;
+
+    /* IPv4-mapped prefix: 80 zero bits + 16 one bits */
+    static const u_char  mapped[]   = { 0x00, 0x00, 0x00, 0x00,
+                                        0x00, 0x00, 0x00, 0x00,
+                                        0x00, 0x00, 0xff, 0xff };
+
+    if (sa->sa_family == AF_INET6) {
+        sin6 = (struct sockaddr_in6 *) sa;
+        ngx_memcpy(addr, &sin6->sin6_addr, 16);
+        ngx_memcpy(port, &sin6->sin6_port, 2);
+
+    } else {
+        /* promote AF_INET to ::ffff:a.b.c.d */
+        sin = (struct sockaddr_in *) sa;
+        ngx_memcpy(addr, mapped, sizeof(mapped));
+        ngx_memcpy(addr + sizeof(mapped), &sin->sin_addr, 4);
+        ngx_memcpy(port, &sin->sin_port, 2);
+    }
+}
+
+#endif
+
+
+static u_char *
+ngx_proxy_protocol_v2_write_tlv(ngx_connection_t *c, u_char *p, u_char *last,
+    ngx_uint_t type, ngx_str_t *value)
+{
+    ngx_proxy_protocol_tlv_t  *tlv;
+
+    if ((size_t) (last - p) < sizeof(ngx_proxy_protocol_tlv_t) + value->len) {
+        ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+                      "too small buffer for PROXY protocol v2 TLV");
+        return NULL;
+    }
+
+    tlv = (ngx_proxy_protocol_tlv_t *) p;
+    tlv->type = (u_char) type;
+    tlv->len[0] = (u_char) (value->len >> 8);
+    tlv->len[1] = (u_char)  value->len;
+
+    p += sizeof(ngx_proxy_protocol_tlv_t);
+
+    return ngx_cpymem(p, value->data, value->len);
+}
+
+
+static u_char *
+ngx_proxy_protocol_v2_write_ssl(ngx_connection_t *c, u_char *p, u_char *last,
+    ngx_array_t *ssl_tlvs, ngx_uint_t client, uint32_t verify)
+{
+    u_char                          *start;
+    uint16_t                         len;
+    ngx_uint_t                       i;
+    ngx_proxy_protocol_tlv_t        *tlv;
+    ngx_proxy_protocol_tlv_ssl_t    *tlv_ssl;
+    ngx_proxy_protocol_tlv_value_t  *sub;
+
+    if ((size_t) (last - p) < sizeof(ngx_proxy_protocol_tlv_t)
+                              + sizeof(ngx_proxy_protocol_tlv_ssl_t))
+    {
+        ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+                      "too small buffer for PROXY protocol v2 SSL TLV");
+        return NULL;
+    }
+
+    start = p;
+    p += sizeof(ngx_proxy_protocol_tlv_t);
+
+    tlv_ssl = (ngx_proxy_protocol_tlv_ssl_t *) p;
+    tlv_ssl->client = (u_char) client;
+    tlv_ssl->verify[0] = (u_char) (verify >> 24);
+    tlv_ssl->verify[1] = (u_char) (verify >> 16);
+    tlv_ssl->verify[2] = (u_char) (verify >> 8);
+    tlv_ssl->verify[3] = (u_char)  verify;
+
+    p += sizeof(ngx_proxy_protocol_tlv_ssl_t);
+
+    sub = ssl_tlvs->elts;
+
+    for (i = 0; i < ssl_tlvs->nelts; i++) {
+        p = ngx_proxy_protocol_v2_write_tlv(c, p, last, sub[i].type,
+                                            &sub[i].value);
+        if (p == NULL) {
+            return NULL;
+        }
+    }
+
+    len = (uint16_t) (p - start - sizeof(ngx_proxy_protocol_tlv_t));
+
+    tlv = (ngx_proxy_protocol_tlv_t *) start;
+    tlv->type = NGX_PROXY_PROTOCOL_TLV_SSL;
+    tlv->len[0] = (u_char) (len >> 8);
+    tlv->len[1] = (u_char)  len;
+
+    return p;
+}
+
+
+static void
+ngx_proxy_protocol_v2_set_len(u_char *buf, u_char *p)
+{
+    uint16_t                      len;
+    ngx_proxy_protocol_header_t  *header;
+
+    len = (uint16_t) (p - buf - sizeof(ngx_proxy_protocol_header_t));
+
+    header = (ngx_proxy_protocol_header_t *) buf;
+    header->len[0] = (u_char) (len >> 8);
+    header->len[1] = (u_char) len;
+}
+
+
+static u_char *
+ngx_proxy_protocol_v2_write_crc32c(ngx_connection_t *c, u_char *buf,
+    u_char *p, u_char *last)
+{
+    u_char                    *value;
+    uint32_t                   crc;
+    ngx_proxy_protocol_tlv_t  *tlv;
+
+    if ((size_t) (last - p) < sizeof(ngx_proxy_protocol_tlv_t) + 4) {
+        ngx_log_error(NGX_LOG_ALERT, c->log, 0,
+                      "too small buffer for PROXY protocol v2 CRC32c TLV");
+        return NULL;
+    }
+
+    /* append the CRC32c TLV with a zeroed value field */
+
+    tlv = (ngx_proxy_protocol_tlv_t *) p;
+    tlv->type = NGX_PROXY_PROTOCOL_TLV_CRC32C;
+    tlv->len[0] = 0;
+    tlv->len[1] = 4;
+
+    value = p + sizeof(ngx_proxy_protocol_tlv_t);
+
+    ngx_memzero(value, 4);
+
+    p = value + 4;
+
+    /* the checksum covers the entire header with the zeroed value */
+
+    crc = ngx_crc32c_long(buf, p - buf);
+
+    value[0] = (u_char) (crc >> 24);
+    value[1] = (u_char) (crc >> 16);
+    value[2] = (u_char) (crc >> 8);
+    value[3] = (u_char) crc;
+
+    return p;
+}
index d1749f57bccf42e3500f24469a7e1b530e864555..9f5fee4f25c73aaca485b0cd5db999e1db762f62 100644 (file)
@@ -30,6 +30,8 @@ u_char *ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf,
     u_char *last);
 u_char *ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf,
     u_char *last);
+u_char *ngx_proxy_protocol_v2_write(ngx_connection_t *c, u_char *buf,
+    u_char *last, ngx_array_t *tlvs);
 ngx_int_t ngx_proxy_protocol_get_tlv(ngx_connection_t *c, ngx_str_t *name,
     ngx_str_t *value);
 
index 12ac01b3c60c49cafaf8a58c1cefb8c875c3e944..010711481f1a990f07f95e2883e02cb1a4592ff4 100644 (file)
@@ -64,6 +64,7 @@
 
 #if (OPENSSL_VERSION_NUMBER < 0x10002000L)
 #define SSL_is_server(s)        (s)->server
+#define X509_get_signature_nid(x)    OBJ_obj2nid((x)->sig_alg->algorithm)
 #endif