Skip to content

An ANSI C library to parse and create PROXY protocol v1 and v2 headers

License

LGPL-3.0, GPL-3.0 licenses found

Licenses found

LGPL-3.0
COPYING.LESSER
GPL-3.0
COPYING
Notifications You must be signed in to change notification settings

kosmas-valianos/libproxyprotocol

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

78 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

libproxyprotocol

An ANSI C library to parse and create PROXY protocol v1 and v2 headers with support for all TLVs including the custom ones from AWS and Azure

  • Full coverage of the latest specification.
  • Easy addition of any TLV through API functions when creating v2 PROXY protocol headers.
  • Easy access of the values of the extracted v2 TLVs through API functions. Moreover, in case the v2 TLV values are US-ASCII string names, they are given as proper NULL terminated strings for easy usage.
  • Easy way through the API to request a specific alignment, CRC32C checksum when creating v2 PROXY protocol headers.
  • Easy way through an API function to create health check v2 PROXY protocol headers.
  • Socket free logic. Does not hook, manipulate, assume any networking. It merely works on buffers.
  • Compilable with most compilers and usable at any platform as it is written in ANSI C.

Installation

The library should be compilable to any platform as it is written in ANSI C. It comes with a Makefile which can create the shared library libproxyprotocol.so which can then be linked to your application. Dynamic linking is the suggested way as it applies at all cases! You can link statically using the .o directly but keep in mind that in case of a commercial product you must use the shared library .sodue to the LGPL restrictions. Special care has been taken to make it work with Windows as well. In that case you have to compile it to a .dll/.lib yourself. In case of Windows remember that you have to link with the ws2_32.lib. An example of this is shown in tests.

API/Usage

All the API details are in the proxy_protocol.h. The complete example for creating/parsing v1 and v2 PROXY protocol headers can be found at examples/client_server.c

Create a v1 PROXY protocol header

pp_info_t pp_info_in_v1 = {
        .address_family = ADDR_FAMILY_INET,
        .transport_protocol = TRANSPORT_PROTOCOL_STREAM,
        .src_addr = "172.22.32.1",
        .dst_addr = "172.22.33.1",
        .src_port = 4040,
        .dst_port = 443
    };
uint16_t pp1_hdr_len;
int32_t error;
uint8_t *pp1_hdr = pp_create_hdr(1, &pp_info_in_v1, &pp1_hdr_len, &error);
/* Clear the pp_info passed in pp_create_hdr(). Not really needed for v1 but good to do out of principle */
pp_info_clear(&pp_info_in_v1);
if (error != ERR_NULL)
{
    fprintf(stderr, "pp_create_hdr() failed: %s", pp_strerror(error));
    return EXIT_FAILURE;
}

Parse a v1 PROXY protocol header

pp_info_t pp_info_out;
int32_t rc = pp_parse_hdr(pp1_hdr, pp1_hdr_len, &pp_info_out);
free(pp1_hdr);
if (!rc)
{
    printf("Not a PROXY protocol header\n");
}
else if (rc < 0)
{
    fprintf(stderr, "pp_parse_hdr() failed: %s", pp_strerror(rc));
    pp_info_clear(&pp_info_out);
    return EXIT_FAILURE;
}
else
{
    printf("%d bytes PROXY protocol header: %s %s %hu %hu\n",
        rc,
        pp_info_out.src_addr, pp_info_out.dst_addr,
        pp_info_out.src_port, pp_info_out.dst_port);
}
/* ALWAYS clear the pp_info after a call to pp_parse_hdr() */
pp_info_clear(&pp_info_out);
45 bytes PROXY protocol header: 172.22.32.1 172.22.33.1 4040 443

Create a v2 PROXY protocol header

pp_info_t pp_info_in_v2 = {
    .address_family = ADDR_FAMILY_INET,
    .transport_protocol = TRANSPORT_PROTOCOL_STREAM,
    .src_addr = "192.168.10.100",
    .dst_addr = "192.168.11.90",
    .src_port = 42332,
    .dst_port = 8080,
    .pp2_info = {
        .crc32c = 1,        /* Add crc32c checksum */
        .pp2_ssl_info = {   /* Add SSL information */
            .ssl = 1,
            .cert_in_connection = 1,
            .cert_in_session = 1,
            .cert_verified = 1,
        }
    }
};
/* Add SSL TLVs */
/* IMPORTANT: Always clear the pp_info to be passed in pp_create_hdr() because TLVs are allocated in heap. Otherwise memory will be leaked */
if (!pp_info_add_ssl(&pp_info_in_v2, "TLSv1.2", "ECDHE-RSA-AES128-GCM-SHA256", "SHA256", "RSA2048", "example.com", 11))
{
    fprintf(stderr, "pp_info_add_ssl() failed\n");
    pp_info_clear(&pp_info_in_v2);
    return EXIT_FAILURE;
}
/* Add Azure Link ID TLV */
if (!pp_info_add_azure_linkid(&pp_info_in_v2, 1234))
{
    fprintf(stderr, "pp_info_add_azure_linkid() failed\n");
    pp_info_clear(&pp_info_in_v2);
    return EXIT_FAILURE;
}
uint8_t *pp2_hdr = pp_create_hdr(2, &pp_info_in_v2, &pp2_hdr_len, &error);
pp_info_clear(&pp_info_in_v2);
if (error != ERR_NULL)
{
    fprintf(stderr, "pp_create_hdr() failed: %s", pp_strerror(error));
    return EXIT_FAILURE;
}

Parse a v2 PROXY protocol header

rc = pp_parse_hdr(pp2_hdr, pp2_hdr_len, &pp_info_out);
free(pp2_hdr);
if (!rc)
{
    printf("Not a PROXY protocol header\n");
}
else if (rc < 0)
{
    fprintf(stderr, "pp_parse_hdr() failed: %s", pp_strerror(rc));
    pp_info_clear(&pp_info_out);
    return EXIT_FAILURE;
}
else
{
    uint16_t length, cn_length;
    const uint8_t *azure_linkid = pp_info_get_azure_linkid(&pp_info_out, &length);
    uint32_t linkid;
    memcpy(&linkid, azure_linkid, length);
    const uint8_t *cn = pp_info_get_ssl_cn(&pp_info_out, &cn_length);
    printf("%d bytes PROXY protocol header:\n"
            "\tAzure Link ID: %u\n"
            "\tCRC32C checksum: %s\n"
            "\tSSL version: %s\n"
            "\tSSL cipher: %s\n"
            "\tSSL sig_alg: %s\n"
            "\tSSL key_alg: %s\n"
            "\tSSL CN: %.*s\n"
            "%s %s %hu %hu\n",
        rc, linkid,
        /* In case CRC32c is wrong then rc < 0 => pp_strerror(rc) at previous block will print the error */
        pp_info_out.pp2_info.crc32c == 1 ? "verified" : "not present",
        pp_info_get_ssl_version(&pp_info_out, &length),
        pp_info_get_ssl_cipher(&pp_info_out, &length),
        pp_info_get_ssl_sig_alg(&pp_info_out, &length),
        pp_info_get_ssl_key_alg(&pp_info_out, &length),
        cn_length, cn,
        pp_info_out.src_addr, pp_info_out.dst_addr,
        pp_info_out.src_port, pp_info_out.dst_port);
}
/* ALWAYS clear the pp_info after a call to pp_parse_hdr() */
pp_info_clear(&pp_info_out);
124 bytes PROXY protocol header:
   Azure Link ID: 1234
   CRC32C checksum: verified
   SSL version: TLSv1.2
   SSL cipher: ECDHE-RSA-AES128-GCM-SHA256
   SSL sig_alg: SHA256
   SSL key_alg: RSA2048
   SSL CN: example.com
   192.168.10.100 192.168.11.90 42332 8080