Skip to content

Commit f96b455

Browse files
Merge pull request #12 from kosmas-valianos/updateDocs
Update documentation
2 parents 9e1c2e1 + ec916ce commit f96b455

3 files changed

Lines changed: 173 additions & 60 deletions

File tree

README.md

Lines changed: 149 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,161 @@
11
# libproxyprotocol
22
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.
55
* Socket free logic. Does not hook, manipulate, assume any networking. It merely works on buffers.
66
* Compilable with most compilers and usable at any platform as it is written in ANSI C.
77

88
## Installation
99
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.
1010

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`
1413

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+
```
2335

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+
```
2564

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+
```
27107

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+
```

examples/client_server.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,21 @@ int main()
7575
}
7676
};
7777
/* Add SSL TLVs */
78-
pp_info_add_ssl(&pp_info_in_v2, "TLSv1.2", "ECDHE-RSA-AES128-GCM-SHA256", "SHA256", "RSA2048", "example.com", 11);
78+
/* IMPORTANT: Always clear the pp_info to be passed in pp_create_hdr() because TLVs are allocated in heap. Otherwise memory will be leaked */
79+
if (!pp_info_add_ssl(&pp_info_in_v2, "TLSv1.2", "ECDHE-RSA-AES128-GCM-SHA256", "SHA256", "RSA2048", "example.com", 11))
80+
{
81+
fprintf(stderr, "pp_info_add_ssl() failed\n");
82+
pp_info_clear(&pp_info_in_v2);
83+
return EXIT_FAILURE;
84+
}
7985
/* Add Azure Link ID TLV */
80-
pp_info_add_azure_linkid(&pp_info_in_v2, 1234);
86+
if (!pp_info_add_azure_linkid(&pp_info_in_v2, 1234))
87+
{
88+
fprintf(stderr, "pp_info_add_azure_linkid() failed\n");
89+
pp_info_clear(&pp_info_in_v2);
90+
return EXIT_FAILURE;
91+
}
8192
uint8_t *pp2_hdr = pp_create_hdr(2, &pp_info_in_v2, &pp1_hdr_len, &error);
82-
/* IMPORTANT: Clear the pp_info passed in pp_create_hdr() because TLVs were created. Otherwise memory will be leaked */
8393
pp_info_clear(&pp_info_in_v2);
8494
if (error != ERR_NULL)
8595
{
@@ -115,7 +125,7 @@ int main()
115125
"\tSSL sig_alg: %s\n"
116126
"\tSSL key_alg: %s\n"
117127
"\tSSL CN: %.*s\n"
118-
"%s %s %hu %hu\n",
128+
"\t%s %s %hu %hu\n",
119129
rc, linkid,
120130
pp_info_out.pp2_info.crc32c == 1 ? "verified" : "not present",
121131
pp_info_get_ssl_version(&pp_info_out, &length),

src/proxy_protocol.h

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ typedef struct
8888
* 1: calculate and add crc32c checksum TLV
8989
* 0: no crc32c checksum
9090
* In parsing:
91-
* 1: crc32c checksum TLV is present. Optionally, pp_info_get_crc32c() can be used to get the value
91+
* 1: crc32c checksum TLV is present and verified. Optionally, pp_info_get_crc32c() can be used to get the value
9292
* 0: crc32c checksum is not present
9393
*/
9494
uint8_t crc32c;
@@ -156,9 +156,14 @@ const uint8_t *pp_info_get_netns(const pp_info_t *pp_info, uint16_t *length);
156156
const uint8_t *pp_info_get_aws_vpce_id(const pp_info_t *pp_info, uint16_t *length);
157157
const uint8_t *pp_info_get_azure_linkid(const pp_info_t *pp_info, uint16_t *length);
158158

159-
/* Clears the pp_info_t structure and frees any allocated memory associated with it. Shall always be called after a call to pp_parse_hdr()
159+
/* Clears the pp_info_t structure and frees any allocated memory associated with it
160+
* Parsing: Always call it after pp_parse_hdr()
161+
* Creating: Always call it after pp_create_hdr() or failure in pp_info_add_*() functions
160162
*
161-
* pp_info Pointer to a filled pp_info_t structure which has been used to a previous call to pp_parse_hdr()
163+
* In case it is forgotten and the parsing/creation is about a v2 PROXY protocol header with TLVs, memory leaks will appear!
164+
*
165+
* pp_info Parsing: Pointer to a filled pp_info_t structure which has been used to a previous call to pp_parse_hdr()
166+
* Creating: Pointer to an initialized pp_info_t structure in which pp_info_add_*() functions have been used
162167
*/
163168
void pp_info_clear(pp_info_t *pp_info);
164169

@@ -170,7 +175,7 @@ void pp_info_clear(pp_info_t *pp_info);
170175
* pp_hdr_len Pointer to a uint16_t where the length of the create PROXY protocol header will be set
171176
* error Pointer to a uint32_t where the error value will be set
172177
* ERR_NULL No error occurred
173-
* < 0 Error occurred. pp_strerror() with that value can be used to get a descriptive message
178+
* < 0 Error occurred. Optionally, pp_strerror() with that value can be used to get a descriptive message
174179
* return Pointer to a heap allocated buffer containing the PROXY protocol header. Must be freed with free()
175180
*/
176181
uint8_t *pp_create_hdr(uint8_t version, const pp_info_t *pp_info, uint16_t *pp_hdr_len, int32_t *error);
@@ -182,7 +187,7 @@ uint8_t *pp_create_hdr(uint8_t version, const pp_info_t *pp_info, uint16_t *pp_h
182187
* pp_info Pointer to a pp_info_t structure which will get filled with all the extracted information
183188
* return > 0 Length of the PROXY protocol header
184189
* == 0 No PROXY protocol header found
185-
* < 0 Error occurred. pp_strerror() with that value can be used to get a descriptive message
190+
* < 0 Error occurred. Optionally, pp_strerror() with that value can be used to get a descriptive message
186191
*/
187192
int32_t pp_parse_hdr(uint8_t *buffer, uint32_t buffer_length, pp_info_t *pp_info);
188193

0 commit comments

Comments
 (0)