|
1 | 1 | # libproxyprotocol |
2 | 2 | An ANSI C library to parse and create [PROXY protocol](https://www.haproxy.org/download/2.6/doc/proxy-protocol.txt) v1 and v2 headers |
3 | | -* Full coverage of the latest 2.6 specification in parsing all the v2 TLVs, including the custom ones from AWS and Azure. |
4 | | -* Easy access of the values of the extracted v2 TLVs though the API. In case the v2 TLV values are US-ASCII string name, they are given as proper NULL terminated strings for easy usage. |
| 3 | +* Full coverage of the latest 2.6 specification in parsing and creating all the v2 TLVs, including the custom ones from AWS and Azure. |
| 4 | +* Easy access of the values of the extracted v2 TLVs though the API. In case the v2 TLV values are US-ASCII string names, they are given as proper NULL terminated strings for easy usage. |
5 | 5 | * Socket free logic. Does not hook, manipulate, assume any networking. It merely works on buffers. |
6 | 6 | * Compilable with most compilers and usable at any platform as it is written in ANSI C. |
7 | 7 |
|
8 | 8 | ## Installation |
9 | 9 | 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. 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. |
10 | 10 |
|
11 | | -## API |
12 | | -### Parsing |
13 | | -**`int32_t pp_parse_hdr(uint8_t *buffer, uint32_t buffer_length, pp_info_t *pp_info)`**: |
| 11 | +## API/Usage |
| 12 | +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` |
14 | 13 |
|
15 | | -Inpects the buffer for a PROXY protocol header and extracts all the information if any. |
16 | | -* `buffer`: Pointer to a buffer with the data to parse. Normally it will be the buffer used to peek data from a socket. |
17 | | -* `buffer_length`: Buffer's length. Typically the bytes read from the `recv(MSG_PEEK)` operation. |
18 | | -* `pp_info`: Pointer to a `pp_info_t` structure which will get filled with all the extracted information. |
19 | | -* `return`: |
20 | | - * \> 0 Length of the PROXY protocol header |
21 | | - * == 0 No PROXY protocol header found |
22 | | - * < 0 Error occurred. `pp_strerror()` with that value can be used to get a descriptive message |
| 14 | +### Create a v1 PROXY protocol header |
| 15 | +``` |
| 16 | +pp_info_t pp_info_in_v1 = { |
| 17 | + .address_family = ADDR_FAMILY_INET, |
| 18 | + .transport_protocol = TRANSPORT_PROTOCOL_STREAM, |
| 19 | + .src_addr = "172.22.32.1", |
| 20 | + .dst_addr = "172.22.33.1", |
| 21 | + .src_port = 4040, |
| 22 | + .dst_port = 443 |
| 23 | + }; |
| 24 | + uint16_t pp1_hdr_len; |
| 25 | + uint32_t error; |
| 26 | + uint8_t *pp1_hdr = pp_create_hdr(1, &pp_info_in_v1, &pp1_hdr_len, &error); |
| 27 | + /* Clear the pp_info passed in pp_create_hdr(). Not really needed for v1 but good to do out of principle */ |
| 28 | + pp_info_clear(&pp_info_in_v1); |
| 29 | + if (error != ERR_NULL) |
| 30 | + { |
| 31 | + fprintf(stderr, "pp_create_hdr() failed: %s", pp_strerror(error)); |
| 32 | + return EXIT_FAILURE; |
| 33 | + } |
| 34 | +``` |
23 | 35 |
|
24 | | -You shall not pass your `pp_info_t` variable to `pp_parse()` again without first clearing it with `pp_info_clear()` |
| 36 | +### Parse a v1 PROXY protocol header |
| 37 | +``` |
| 38 | +pp_info_t pp_info_out; |
| 39 | + int32_t rc = pp_parse_hdr(pp1_hdr, pp1_hdr_len, &pp_info_out); |
| 40 | + free(pp1_hdr); |
| 41 | + if (!rc) |
| 42 | + { |
| 43 | + printf("Not a PROXY protocol header\n"); |
| 44 | + } |
| 45 | + else if (rc < 0) |
| 46 | + { |
| 47 | + fprintf(stderr, "pp_parse_hdr() failed: %s", pp_strerror(rc)); |
| 48 | + pp_info_clear(&pp_info_out); |
| 49 | + return EXIT_FAILURE; |
| 50 | + } |
| 51 | + else |
| 52 | + { |
| 53 | + printf("%d bytes PROXY protocol header: %s %s %hu %hu\n", |
| 54 | + rc, |
| 55 | + pp_info_out.src_addr, pp_info_out.dst_addr, |
| 56 | + pp_info_out.src_port, pp_info_out.dst_port); |
| 57 | + } |
| 58 | + /* ALWAYS clear the pp_info after a call to pp_parse_hdr() */ |
| 59 | + pp_info_clear(&pp_info_out); |
| 60 | +``` |
| 61 | +``` |
| 62 | +45 bytes PROXY protocol header: 172.22.32.1 172.22.33.1 4040 443 |
| 63 | +``` |
25 | 64 |
|
26 | | -**`void pp_info_clear(pp_info_t *pp_info)`** |
| 65 | +### Create a v2 PROXY protocol header |
| 66 | +``` |
| 67 | +pp_info_t pp_info_in_v2 = { |
| 68 | + .address_family = ADDR_FAMILY_INET, |
| 69 | + .transport_protocol = TRANSPORT_PROTOCOL_STREAM, |
| 70 | + .src_addr = "192.168.10.100", |
| 71 | + .dst_addr = "192.168.11.90", |
| 72 | + .src_port = 42332, |
| 73 | + .dst_port = 8080, |
| 74 | + .pp2_info = { |
| 75 | + .crc32c = 1, /* Add crc32c checksum */ |
| 76 | + .pp2_ssl_info = { /* Add SSL information */ |
| 77 | + .ssl = 1, |
| 78 | + .cert_in_connection = 1, |
| 79 | + .cert_in_session = 1, |
| 80 | + .cert_verified = 1, |
| 81 | + } |
| 82 | + } |
| 83 | + }; |
| 84 | + /* Add SSL TLVs */ |
| 85 | + /* IMPORTANT: Always clear the pp_info to be passed in pp_create_hdr() because TLVs are allocated in heap. Otherwise memory will be leaked */ |
| 86 | + if (!pp_info_add_ssl(&pp_info_in_v2, "TLSv1.2", "ECDHE-RSA-AES128-GCM-SHA256", "SHA256", "RSA2048", "example.com", 11)) |
| 87 | + { |
| 88 | + fprintf(stderr, "pp_info_add_ssl() failed\n"); |
| 89 | + pp_info_clear(&pp_info_in_v2); |
| 90 | + return EXIT_FAILURE; |
| 91 | + } |
| 92 | + /* Add Azure Link ID TLV */ |
| 93 | + if (!pp_info_add_azure_linkid(&pp_info_in_v2, 1234)) |
| 94 | + { |
| 95 | + fprintf(stderr, "pp_info_add_azure_linkid() failed\n"); |
| 96 | + pp_info_clear(&pp_info_in_v2); |
| 97 | + return EXIT_FAILURE; |
| 98 | + } |
| 99 | + uint8_t *pp2_hdr = pp_create_hdr(2, &pp_info_in_v2, &pp1_hdr_len, &error); |
| 100 | + pp_info_clear(&pp_info_in_v2); |
| 101 | + if (error != ERR_NULL) |
| 102 | + { |
| 103 | + fprintf(stderr, "pp_create_hdr() failed: %s", pp_strerror(error)); |
| 104 | + return EXIT_FAILURE; |
| 105 | + } |
| 106 | +``` |
27 | 107 |
|
28 | | -Clears the `pp_info_t` structure and frees any allocated memory associated with it. Shall always be called after a call to `pp_parse()` |
29 | | -* `pp_info`: Pointer to a filled `pp_info_t` structure which has been used to a previous call to `pp_parse()` |
30 | | - |
31 | | -**`const uint8_t *pp_info_get_*(const pp_info_t *pp_info, uint16_t *length);`** |
32 | | - |
33 | | -Searches for the specified TLV and returns its value |
34 | | -* `pp_info` Pointer to a `pp_info_t` structure used in `pp_parse()` |
35 | | -* `length` Pointer to a `uint16_t` where the TLV's value length will be set |
36 | | -* `return` Pointer to a buffer holding the TLV's value if found else `NULL`. In case of US-ASCII value the buffer is `NULL` terminated |
37 | | - |
38 | | -### Creating |
39 | | -**`uint8_t *pp_create_hdr(uint8_t version, const pp_info_t *pp_info, uint16_t *pp_hdr_len, int32_t *error)`**: |
40 | | - |
41 | | -Creates a PROXY protocol header considering the information inside the pp_info. |
42 | | -* `version`: |
43 | | - * 0 Create a v1 PROXY protocol header. |
44 | | - * 1 Create a v2 PROXY protocol header. |
45 | | -* `pp_info`: Pointer to a filled `pp_info_t` structure whose information will be used for the creation of the PROXY protocol header. |
46 | | -* `pp_hdr_len`: Pointer to a `uint16_t` where the length of the create PROXY protocol header will be set |
47 | | -* `error`: Pointer to a `int32_t` where the error value will be set |
48 | | - * `ERR_NULL` No error occurred |
49 | | - * < 0 Error |
50 | | -* `return`: Pointer to a heap allocated buffer containing the PROXY protocol header. Must be freed with free() |
51 | | - |
52 | | -### Error reporting |
53 | | -**`const char *pp_strerror(int32_t error)`** |
54 | | - |
55 | | -Returns a descriptive error message |
56 | | -* `error`: `int32_t` value from other API functions |
57 | | -* `return`: Pointer to the descriptive message if the error value is recognized else `NULL` |
58 | | - |
59 | | -## Example |
60 | | -See `examples/client_server.c` |
61 | | - |
62 | | -## In progress |
63 | | -* Creating v2 PROXY protocol headers with TLVs is not yet supported. Will be added in the next major release |
| 108 | +### Parse a v2 PROXY protocol header |
| 109 | +``` |
| 110 | +rc = pp_parse_hdr(pp2_hdr, pp1_hdr_len, &pp_info_out); |
| 111 | + free(pp2_hdr); |
| 112 | + if (!rc) |
| 113 | + { |
| 114 | + printf("Not a PROXY protocol header\n"); |
| 115 | + } |
| 116 | + else if (rc < 0) |
| 117 | + { |
| 118 | + fprintf(stderr, "pp_parse_hdr() failed: %s", pp_strerror(rc)); |
| 119 | + pp_info_clear(&pp_info_out); |
| 120 | + return EXIT_FAILURE; |
| 121 | + } |
| 122 | + else |
| 123 | + { |
| 124 | + uint16_t length, cn_length; |
| 125 | + const uint8_t *azure_linkid = pp_info_get_azure_linkid(&pp_info_out, &length); |
| 126 | + uint32_t linkid; |
| 127 | + memcpy(&linkid, azure_linkid, length); |
| 128 | + const uint8_t *cn = pp_info_get_ssl_cn(&pp_info_out, &cn_length); |
| 129 | + printf("%d bytes PROXY protocol header:\n" |
| 130 | + "\tAzure Link ID: %u\n" |
| 131 | + "\tCRC32C checksum: %s\n" |
| 132 | + "\tSSL version: %s\n" |
| 133 | + "\tSSL cipher: %s\n" |
| 134 | + "\tSSL sig_alg: %s\n" |
| 135 | + "\tSSL key_alg: %s\n" |
| 136 | + "\tSSL CN: %.*s\n" |
| 137 | + "%s %s %hu %hu\n", |
| 138 | + rc, linkid, |
| 139 | + pp_info_out.pp2_info.crc32c == 1 ? "verified" : "not present", |
| 140 | + pp_info_get_ssl_version(&pp_info_out, &length), |
| 141 | + pp_info_get_ssl_cipher(&pp_info_out, &length), |
| 142 | + pp_info_get_ssl_sig_alg(&pp_info_out, &length), |
| 143 | + pp_info_get_ssl_key_alg(&pp_info_out, &length), |
| 144 | + cn_length, cn, |
| 145 | + pp_info_out.src_addr, pp_info_out.dst_addr, |
| 146 | + pp_info_out.src_port, pp_info_out.dst_port); |
| 147 | + } |
| 148 | + /* ALWAYS clear the pp_info after a call to pp_parse_hdr() */ |
| 149 | + pp_info_clear(&pp_info_out); |
| 150 | +``` |
| 151 | +``` |
| 152 | +124 bytes PROXY protocol header: |
| 153 | + Azure Link ID: 1234 |
| 154 | + CRC32C checksum: verified |
| 155 | + SSL version: TLSv1.2 |
| 156 | + SSL cipher: ECDHE-RSA-AES128-GCM-SHA256 |
| 157 | + SSL sig_alg: SHA256 |
| 158 | + SSL key_alg: RSA2048 |
| 159 | + SSL CN: example.com |
| 160 | + 192.168.10.100 192.168.11.90 42332 8080 |
| 161 | +``` |
0 commit comments