Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-proxy.c
blob85f31c27e94f31baa911424815e69c53a2dbc8cf
1 /* packet-proxy.c
2 * Routines for HAPROXY PROXY (v1/v2) dissection
3 * Copyright 2015, Alexis La Goutte (See AUTHORS)
4 * Copyright 2019 Peter Wu <peter@lekensteyn.nl>
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later
14 * The PROXY protocol is a single, unfragmented header before the initial client
15 * packet. Following this header, the proxied protocol will take over and
16 * proceed normally.
18 * https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt
20 * Requires "Try heuristics sub-dissectors first" in TCP protocol preferences.
23 #include <config.h>
25 #include <epan/packet.h>
26 #include <epan/expert.h>
27 #include <epan/to_str.h>
28 #include <wsutil/inet_addr.h>
29 #include <wsutil/strtoi.h>
30 #include <wsutil/utf8_entities.h>
32 #include "packet-tcp.h"
33 #include "packet-udp.h"
35 void proto_reg_handoff_proxy(void);
36 void proto_register_proxy(void);
38 static int proto_proxy;
40 static int hf_proxy_version;
42 static int hf_proxy_src_ipv4;
43 static int hf_proxy_dst_ipv4;
44 static int hf_proxy_src_ipv6;
45 static int hf_proxy_dst_ipv6;
46 static int hf_proxy_srcport;
47 static int hf_proxy_dstport;
49 /* V1 */
50 static int hf_proxy1_magic;
51 static int hf_proxy1_proto;
52 static int hf_proxy1_unknown;
54 /* V2 */
55 static int hf_proxy2_magic;
56 static int hf_proxy2_ver;
57 static int hf_proxy2_cmd;
58 static int hf_proxy2_addr_family;
59 static int hf_proxy2_protocol;
60 static int hf_proxy2_addr_family_protocol;
61 static int hf_proxy2_len;
62 static int hf_proxy2_src_unix;
63 static int hf_proxy2_dst_unix;
65 static int hf_proxy2_unknown;
67 static int hf_proxy2_tlv;
68 static int hf_proxy2_tlv_type;
69 static int hf_proxy2_tlv_length;
70 static int hf_proxy2_tlv_value;
71 static int hf_proxy2_tlv_ssl_client;
72 static int hf_proxy2_tlv_ssl_verify;
73 static int hf_proxy2_tlv_ssl_version;
74 static int hf_proxy2_tlv_ssl_cn;
75 static int hf_proxy2_tlv_ssl_cipher;
76 static int hf_proxy2_tlv_ssl_sig_alg;
77 static int hf_proxy2_tlv_ssl_key_alg;
79 static expert_field ei_proxy_header_length_too_small;
80 static expert_field ei_proxy_bad_format;
83 static int ett_proxy1;
84 static int ett_proxy2;
85 static int ett_proxy2_fampro;
86 static int ett_proxy2_tlv;
88 static dissector_handle_t proxy_v1_handle;
89 static dissector_handle_t proxy_v2_handle;
91 static const uint8_t proxy_v2_magic[] = { 0x0d, 0x0a, 0x0d, 0x0a, 0x00, 0x0d, 0x0a, 0x51, 0x55, 0x49, 0x54, 0x0a };
93 static const value_string proxy2_cmd_vals[] = {
94 { 0x0, "LOCAL" },
95 { 0x1, "PROXY" },
96 { 0 , NULL }
99 static const value_string proxy2_family_protocol_vals[] = {
100 { 0x00, "UNSPEC" },
101 { 0x11, "TCP over IPv4" },
102 { 0x12, "UDP over IPv4" },
103 { 0x21, "TCP over IPv6" },
104 { 0x22, "UDP over IPv6" },
105 { 0x31, "UNIX stream" },
106 { 0x32, "UNIX datagram" },
107 { 0, NULL }
110 static const value_string proxy2_family_vals[] = {
111 { 0x1, "IPv4" },
112 { 0x2, "IPv6" },
113 { 0x3, "UNIX" },
114 { 0, NULL }
117 #define PP2_TYPE_ALPN 0x01
118 #define PP2_TYPE_AUTHORITY 0x02
119 #define PP2_TYPE_CRC32C 0x03
120 #define PP2_TYPE_NOOP 0x04
121 #define PP2_TYPE_UNIQUE_ID 0x05
122 #define PP2_TYPE_SSL 0x20
123 #define PP2_SUBTYPE_SSL_VERSION 0x21
124 #define PP2_SUBTYPE_SSL_CN 0x22
125 #define PP2_SUBTYPE_SSL_CIPHER 0x23
126 #define PP2_SUBTYPE_SSL_SIG_ALG 0x24
127 #define PP2_SUBTYPE_SSL_KEY_ALG 0x25
128 #define PP2_TYPE_NETNS 0x30
129 #define PP2_TYPE_AWS 0xEA
131 static const value_string proxy2_tlv_vals[] = {
132 { 0x00, "UNSPEC" },
133 { PP2_TYPE_ALPN, "ALPN" },
134 { PP2_TYPE_AUTHORITY, "AUTHORITY" },
135 { PP2_TYPE_CRC32C, "CRC32C" },
136 { PP2_TYPE_NOOP, "NOOP" },
137 { PP2_TYPE_UNIQUE_ID, "UNIQUE_ID" },
138 { PP2_TYPE_SSL, "SSL" },
139 { PP2_SUBTYPE_SSL_VERSION, "SSL VERSION" },
140 { PP2_SUBTYPE_SSL_CN, "SSL CN" },
141 { PP2_SUBTYPE_SSL_CIPHER, "SSL CIPHER" },
142 { PP2_SUBTYPE_SSL_SIG_ALG, "SSL SIG ALG" },
143 { PP2_SUBTYPE_SSL_KEY_ALG, "SSL KEY ALG" },
144 { PP2_TYPE_NETNS, "NETNS" },
145 { PP2_TYPE_AWS, "AWS" },
146 { 0, NULL }
149 /* XXX: The protocol specification says that the PROXY header is present
150 * only once, at the beginning of a TCP connection. If we ever do find
151 * the header more than once, we should use a wmem_tree. */
152 typedef struct _proxy_conv_info_t {
153 address src;
154 address dst;
155 port_type ptype;
156 uint16_t srcport;
157 uint16_t dstport;
158 uint32_t setup_frame;
159 } proxy_conv_info_t;
161 static int
162 dissect_proxy_proxied(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree,
163 int offset, void* data, proxy_conv_info_t *proxy_info)
165 conversation_t* conv = find_or_create_conversation(pinfo);
166 /* A PROXY header was parsed here or in a previous frame, and
167 * there's remaining data, so call the subdissector.
169 if (offset > 0) {
170 /* If this is the frame with the header, set a fence. */
171 col_append_str(pinfo->cinfo, COL_INFO, " | ");
172 col_set_fence(pinfo->cinfo, COL_INFO);
174 uint32_t srcport, dstport;
175 port_type ptype;
176 if (proxy_info == NULL) {
177 /* If we're not passed proxied information, e.g., a LOCAL command or
178 * transported over UDP (connectionless), use the outer addressing.
179 * Note that if the other dissector calls conversation_set_dissector()
180 * or similar then on the second pass we won't call the PROXY heuristic
181 * dissector for coalesced frames.
183 srcport = pinfo->srcport;
184 dstport = pinfo->destport;
185 ptype = pinfo->ptype;
187 else {
188 /* If we're passed proxied connection info, set the endpoint used for
189 * conversations to the proxied connection, so that if the subdissector
190 * calls conversation_set_dissector() it will not prevent calling the
191 * PROXY dissector on the header frame on the second pass.
192 * Determine our direction.
194 * XXX: Perhaps we should actually change the values in pinfo, but
195 * currently that doesn't work well with Follow Stream (whether we
196 * change them back before returning to the TCP dissector or not.)
198 if (addresses_equal(&pinfo->src, conversation_key_addr1(conv->key_ptr)) &&
199 (pinfo->srcport == conversation_key_port1(conv->key_ptr))) {
200 conversation_set_conv_addr_port_endpoints(pinfo, &proxy_info->src, &proxy_info->dst,
201 CONVERSATION_PROXY, proxy_info->srcport,
202 proxy_info->dstport);
203 srcport = proxy_info->srcport;
204 dstport = proxy_info->dstport;
205 ptype = proxy_info->ptype;
207 else {
208 conversation_set_conv_addr_port_endpoints(pinfo, &proxy_info->dst, &proxy_info->src,
209 CONVERSATION_PROXY, proxy_info->dstport,
210 proxy_info->srcport);
211 srcport = proxy_info->dstport;
212 dstport = proxy_info->srcport;
213 ptype = proxy_info->ptype;
217 tvbuff_t* next_tvb = tvb_new_subset_remaining(tvb, offset);
218 /* Allow subdissector to perform reassembly. */
219 if (pinfo->can_desegment > 0)
220 pinfo->can_desegment++;
221 switch (ptype) {
222 case (PT_TCP):
223 /* Get the TCP conversation data associated with the transporting
224 * connection. Note that this means that decode_tcp_ports() can
225 * try tcp->server_port from the outer connection as well as
226 * the ports from the proxied connection.
228 decode_tcp_ports(next_tvb, 0, pinfo, tree, srcport, dstport,
229 get_tcp_conversation_data(conv, pinfo), (struct tcpinfo*)data);
230 break;
231 case (PT_UDP):
232 decode_udp_ports(next_tvb, 0, pinfo, tree, srcport, dstport, -1);
233 break;
234 default:
235 /* Dissect UNIX and UNSPEC protocols as data */
236 call_data_dissector(next_tvb, pinfo, tree);
238 if (pinfo->desegment_len > 0) {
239 /* If the subdissector requests desegmentation, adjust the
240 * desegment offset past the PROXY header.
242 pinfo->desegment_offset += offset;
244 return tvb_reported_length_remaining(tvb, offset);
247 static int
248 // NOLINTNEXTLINE(misc-no-recursion)
249 dissect_proxy_v2_tlv(tvbuff_t *tvb, packet_info *pinfo, proto_tree *proxy_tree, int offset, int next_offset)
251 increment_dissection_depth(pinfo);
252 while (offset < next_offset) {
253 uint32_t type, length;
254 proto_item *ti_tlv;
255 proto_tree *tlv_tree;
257 ti_tlv = proto_tree_add_item(proxy_tree, hf_proxy2_tlv, tvb, offset, 3, ENC_NA);
258 tlv_tree = proto_item_add_subtree(ti_tlv, ett_proxy2_tlv);
259 proto_tree_add_item_ret_uint(tlv_tree, hf_proxy2_tlv_type, tvb, offset, 1, ENC_NA, &type);
260 offset += 1;
261 proto_tree_add_item_ret_uint(tlv_tree, hf_proxy2_tlv_length, tvb, offset, 2, ENC_BIG_ENDIAN, &length);
262 offset += 2;
264 proto_item_append_text(ti_tlv, ": (t=%u,l=%d) %s", type, length, val_to_str_const(type, proxy2_tlv_vals ,"Unknown type") );
265 proto_item_set_len(ti_tlv, 1 + 2 + length);
267 proto_tree_add_item(tlv_tree, hf_proxy2_tlv_value, tvb, offset, length, ENC_NA);
268 switch (type) {
269 case PP2_TYPE_SSL: /* SSL */
270 proto_tree_add_item(tlv_tree, hf_proxy2_tlv_ssl_client, tvb, offset, 1, ENC_NA);
271 offset += 1;
272 proto_tree_add_item(tlv_tree, hf_proxy2_tlv_ssl_verify, tvb, offset, 4, ENC_NA);
273 offset += 4;
274 offset = dissect_proxy_v2_tlv(tvb, pinfo, tlv_tree, offset, next_offset);
275 break;
276 case PP2_SUBTYPE_SSL_VERSION: /* SSL Version */
277 proto_tree_add_item(tlv_tree, hf_proxy2_tlv_ssl_version, tvb, offset, length, ENC_ASCII);
278 proto_item_append_text(ti_tlv, ": %s", tvb_get_string_enc(pinfo->pool, tvb, offset, length, ENC_ASCII));
279 offset += length;
280 break;
281 case PP2_SUBTYPE_SSL_CN: /* SSL CommonName */
282 proto_tree_add_item(tlv_tree, hf_proxy2_tlv_ssl_cn, tvb, offset, length, ENC_ASCII);
283 proto_item_append_text(ti_tlv, ": %s", tvb_get_string_enc(pinfo->pool, tvb, offset, length, ENC_ASCII));
284 offset += length;
285 break;
286 case PP2_SUBTYPE_SSL_CIPHER: /* SSL Cipher */
287 proto_tree_add_item(tlv_tree, hf_proxy2_tlv_ssl_cipher, tvb, offset, length, ENC_ASCII);
288 offset += length;
289 break;
290 case PP2_SUBTYPE_SSL_SIG_ALG: /* SSL Signature Algorithm */
291 proto_tree_add_item(tlv_tree, hf_proxy2_tlv_ssl_sig_alg, tvb, offset, length, ENC_ASCII);
292 offset += length;
293 break;
294 case PP2_SUBTYPE_SSL_KEY_ALG: /* SSL Key Algorithm */
295 proto_tree_add_item(tlv_tree, hf_proxy2_tlv_ssl_key_alg, tvb, offset, length, ENC_ASCII);
296 offset += length;
297 break;
298 default:
299 offset += length;
300 break;
303 decrement_dissection_depth(pinfo);
305 return offset;
308 static bool
309 is_proxy_v2(tvbuff_t* tvb)
311 int offset = 0;
312 int length = tvb_reported_length(tvb);
314 if (length < 16) {
315 return false;
318 if (tvb_memeql(tvb, offset, (const uint8_t*)proxy_v2_magic, sizeof(proxy_v2_magic)) != 0) {
319 return false;
321 // TODO maybe check for "(hdr.v2.ver_cmd & 0xF0) == 0x20" as done in "9. Sample code" from
322 // https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt?
324 return true;
327 /* "a 108-byte buffer is always enough to store all the line and a trailing zero" */
328 #define PROXY_V1_MAX_LINE_LENGTH 107
330 static bool
331 is_proxy_v1(tvbuff_t *tvb, int *header_length)
333 const int min_header_size = sizeof("PROXY \r\n") - 1;
334 int length = tvb_reported_length(tvb);
335 int next_offset;
337 if (length < min_header_size) {
338 return false;
341 if (tvb_memeql(tvb, 0, (const uint8_t*)"PROXY ", 6) != 0) {
342 return false;
345 length = MIN(length, PROXY_V1_MAX_LINE_LENGTH);
346 if (tvb_find_line_end(tvb, 6, length, &next_offset, false) == -1) {
347 return false;
350 /* The line must end with a CRLF and not just a single CR or LF. */
351 if (tvb_memeql(tvb, next_offset - 2, (const uint8_t*)"\r\n", 2) != 0) {
352 return false;
355 if (header_length) {
356 *header_length = next_offset;
358 return true;
362 * Scan for the next non-empty token (terminated by a space). If invalid, add
363 * expert info for the remaining part and return false. Otherwise return true
364 * and the token length.
366 static bool
367 proxy_v1_get_token_length(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, int header_length, char *token, int *token_length)
369 int space_pos = tvb_find_uint8(tvb, offset, header_length - offset, ' ');
370 if (space_pos == -1) {
371 proto_tree_add_expert(tree, pinfo, &ei_proxy_bad_format, tvb, offset, header_length - offset);
372 return false;
374 int length = space_pos - offset;
375 if (token && length) {
376 DISSECTOR_ASSERT(length + 1 < PROXY_V1_MAX_LINE_LENGTH);
377 tvb_memcpy(tvb, token, offset, length);
378 token[length] = '\0';
380 *token_length = length;
381 return length != 0;
384 static int
385 dissect_proxy_v1_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
387 proto_item *ti;
388 proto_tree *proxy_tree;
389 unsigned offset = 0;
390 int header_length = 0;
391 int token_length = 0;
392 int tcp_ip_version = 0;
393 uint16_t srcport, dstport;
394 char buffer[PROXY_V1_MAX_LINE_LENGTH];
395 uint32_t src_ipv4, dst_ipv4;
396 ws_in6_addr src_ipv6, dst_ipv6;
397 address src_addr, dst_addr;
399 if (!is_proxy_v1(tvb, &header_length)) {
400 return 0;
403 col_set_str(pinfo->cinfo, COL_PROTOCOL, "PROXYv1");
405 ti = proto_tree_add_item(tree, proto_proxy, tvb, 0, header_length, ENC_NA);
406 proxy_tree = proto_item_add_subtree(ti, ett_proxy1);
408 /* Skip "PROXY" plus a space. */
409 proto_tree_add_item(proxy_tree, hf_proxy1_magic, tvb, offset, 5, ENC_NA);
410 offset += 5 + 1;
412 /* Protocol and family */
413 if (!proxy_v1_get_token_length(tvb, pinfo, proxy_tree, offset, header_length, buffer, &token_length)) {
414 return tvb_captured_length(tvb);
416 proto_tree_add_item(proxy_tree, hf_proxy1_proto, tvb, offset, token_length, ENC_NA|ENC_ASCII);
417 if (token_length == 4) {
418 if (memcmp(buffer, "TCP4", 4) == 0) {
419 tcp_ip_version = 4;
420 } else if (memcmp(buffer, "TCP6", 4) == 0) {
421 tcp_ip_version = 6;
424 offset += token_length + 1;
426 switch (tcp_ip_version) {
427 case 4:
428 /* IPv4 source address */
429 if (!proxy_v1_get_token_length(tvb, pinfo, proxy_tree, offset, header_length, buffer, &token_length)) {
430 return tvb_captured_length(tvb);
432 if (!ws_inet_pton4(buffer, &src_ipv4)) {
433 proto_tree_add_expert_format(proxy_tree, pinfo, &ei_proxy_bad_format, tvb, offset, token_length,
434 "Unrecognized IPv4 address");
435 return tvb_captured_length(tvb);
437 proto_tree_add_ipv4(proxy_tree, hf_proxy_src_ipv4, tvb, offset, token_length, src_ipv4);
438 set_address(&src_addr, AT_IPv4, 4, &src_ipv4);
439 offset += token_length + 1;
441 /* IPv4 destination address */
442 if (!proxy_v1_get_token_length(tvb, pinfo, proxy_tree, offset, header_length, buffer, &token_length)) {
443 return tvb_captured_length(tvb);
445 if (!ws_inet_pton4(buffer, &dst_ipv4)) {
446 proto_tree_add_expert_format(proxy_tree, pinfo, &ei_proxy_bad_format, tvb, offset, token_length,
447 "Unrecognized IPv4 address");
448 return tvb_captured_length(tvb);
450 proto_tree_add_ipv4(proxy_tree, hf_proxy_dst_ipv4, tvb, offset, token_length, dst_ipv4);
451 set_address(&dst_addr, AT_IPv4, 4, &dst_ipv4);
452 offset += token_length + 1;
453 break;
455 case 6:
456 /* IPv6 source address */
457 if (!proxy_v1_get_token_length(tvb, pinfo, proxy_tree, offset, header_length, buffer, &token_length)) {
458 return tvb_captured_length(tvb);
460 if (!ws_inet_pton6(buffer, &src_ipv6)) {
461 proto_tree_add_expert_format(proxy_tree, pinfo, &ei_proxy_bad_format, tvb, offset, token_length,
462 "Unrecognized IPv6 address");
463 return tvb_captured_length(tvb);
465 proto_tree_add_ipv6(proxy_tree, hf_proxy_src_ipv6, tvb, offset, token_length, &src_ipv6);
466 set_address(&src_addr, AT_IPv6, sizeof(ws_in6_addr), &src_ipv6);
467 offset += token_length + 1;
469 /* IPv6 destination address */
470 if (!proxy_v1_get_token_length(tvb, pinfo, proxy_tree, offset, header_length, buffer, &token_length)) {
471 return tvb_captured_length(tvb);
473 if (!ws_inet_pton6(buffer, &dst_ipv6)) {
474 proto_tree_add_expert_format(proxy_tree, pinfo, &ei_proxy_bad_format, tvb, offset, token_length,
475 "Unrecognized IPv6 address");
476 return tvb_captured_length(tvb);
478 proto_tree_add_ipv6(proxy_tree, hf_proxy_dst_ipv6, tvb, offset, token_length, &dst_ipv6);
479 set_address(&dst_addr, AT_IPv6, sizeof(ws_in6_addr), &dst_ipv6);
480 offset += token_length + 1;
481 break;
483 default:
484 proto_tree_add_item(proxy_tree, hf_proxy1_unknown, tvb, offset, header_length - 2 - offset, ENC_NA|ENC_ASCII);
485 return tvb_captured_length(tvb);
488 /* Source port */
489 if (!proxy_v1_get_token_length(tvb, pinfo, proxy_tree, offset, header_length, buffer, &token_length)) {
490 return tvb_captured_length(tvb);
492 if (!ws_strtou16(buffer, NULL, &srcport)) {
493 proto_tree_add_expert_format(proxy_tree, pinfo, &ei_proxy_bad_format, tvb, offset, token_length,
494 "Unrecognized port");
495 return tvb_captured_length(tvb);
497 proto_tree_add_uint(proxy_tree, hf_proxy_srcport, tvb, offset, token_length, srcport);
498 offset += token_length + 1;
500 /* Destination port */
501 token_length = header_length - 2 - offset;
502 if (token_length <= 0) {
503 proto_tree_add_expert(proxy_tree, pinfo, &ei_proxy_bad_format, tvb, offset, token_length);
504 return tvb_captured_length(tvb);
506 tvb_memcpy(tvb, buffer, offset, token_length);
507 buffer[token_length] = '\0';
508 if (!ws_strtou16(buffer, NULL, &dstport)) {
509 proto_tree_add_expert_format(proxy_tree, pinfo, &ei_proxy_bad_format, tvb, offset, token_length,
510 "Unrecognized port");
511 return tvb_captured_length(tvb);
513 proto_tree_add_uint(proxy_tree, hf_proxy_dstport, tvb, offset, token_length, dstport);
515 col_add_lstr(pinfo->cinfo, COL_INFO, "PROXY ", address_to_str(pinfo->pool, &src_addr),
516 " "UTF8_RIGHTWARDS_ARROW" ", address_to_str(pinfo->pool, &dst_addr), ", ",
517 COL_ADD_LSTR_TERMINATOR);
518 col_append_ports(pinfo->cinfo, COL_INFO, PT_TCP, srcport, dstport);
519 conversation_t* conv = find_or_create_conversation(pinfo);
520 proxy_conv_info_t* proxy_info = conversation_get_proto_data(conv, proto_proxy);
521 if (proxy_info == NULL) {
522 proxy_info = wmem_new(wmem_file_scope(), proxy_conv_info_t);
523 copy_address_wmem(wmem_file_scope(), &proxy_info->src, &src_addr);
524 copy_address_wmem(wmem_file_scope(), &proxy_info->dst, &dst_addr);
525 proxy_info->ptype = PT_TCP;
526 proxy_info->srcport = srcport;
527 proxy_info->dstport = dstport;
528 proxy_info->setup_frame = pinfo->num;
529 conversation_add_proto_data(conv, proto_proxy, proxy_info);
531 return header_length;
534 static int
535 dissect_proxy_v1(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
537 conversation_t* conv = find_or_create_conversation(pinfo);
538 proxy_conv_info_t* proxy_info;
539 int offset = dissect_proxy_v1_header(tvb, pinfo, tree);
540 proxy_info = conversation_get_proto_data(conv, proto_proxy);
541 if (proxy_info && pinfo->num >= proxy_info->setup_frame &&
542 tvb_reported_length_remaining(tvb, offset)) {
543 /* XXX: If this is a later frame, should we add some
544 * generated fields with the proxy header information,
545 * and a link back to the proxy setup frame? */
546 offset += dissect_proxy_proxied(tvb, pinfo, tree, offset, data, proxy_info);
548 return offset;
551 static int
552 dissect_proxy_v2_header(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
554 proto_item *ti , *ti_ver;
555 proto_tree *proxy_tree, *fampro_tree;
556 unsigned offset = 0, next_offset;
557 uint32_t header_len, fam_pro, cmd;
558 address src_addr = ADDRESS_INIT_NONE, dst_addr = ADDRESS_INIT_NONE;
559 uint32_t srcport, dstport;
560 port_type ptype = PT_NONE;
562 if (!is_proxy_v2(tvb)) {
563 return 0;
566 col_set_str(pinfo->cinfo, COL_PROTOCOL, "PROXYv2");
568 ti = proto_tree_add_item(tree, proto_proxy, tvb, 0, -1, ENC_NA);
570 proxy_tree = proto_item_add_subtree(ti, ett_proxy2);
572 proto_tree_add_item(proxy_tree, hf_proxy2_magic, tvb, offset, 12, ENC_NA);
573 offset += 12;
575 proto_tree_add_item(proxy_tree, hf_proxy2_ver, tvb, offset, 1, ENC_NA);
576 proto_tree_add_item_ret_uint(proxy_tree, hf_proxy2_cmd, tvb, offset, 1, ENC_NA, &cmd);
577 ti_ver = proto_tree_add_uint(proxy_tree, hf_proxy_version, tvb, offset, 1, 2);
578 proto_item_set_generated(ti_ver);
579 offset += 1;
581 ti = proto_tree_add_item_ret_uint(proxy_tree, hf_proxy2_addr_family_protocol, tvb, offset, 1, ENC_NA, &fam_pro);
582 fampro_tree = proto_item_add_subtree(ti, ett_proxy2_fampro);
583 proto_tree_add_item(fampro_tree, hf_proxy2_addr_family, tvb, offset, 1, ENC_NA);
584 proto_tree_add_item(fampro_tree, hf_proxy2_protocol, tvb, offset, 1, ENC_NA);
585 offset += 1;
587 proto_tree_add_item_ret_uint(proxy_tree, hf_proxy2_len, tvb, offset, 2, ENC_BIG_ENDIAN, &header_len);
588 offset += 2;
590 next_offset = offset + header_len;
592 switch (fam_pro){
593 case 0x11: /* TCP over IPv4 */
594 case 0x12: /* UDP over IPv4 */
595 proto_tree_add_item(proxy_tree, hf_proxy_src_ipv4, tvb, offset, 4, ENC_NA);
596 set_address_tvb(&src_addr, AT_IPv4, 4, tvb, offset);
597 offset += 4;
598 proto_tree_add_item(proxy_tree, hf_proxy_dst_ipv4, tvb, offset, 4, ENC_NA);
599 set_address_tvb(&dst_addr, AT_IPv4, 4, tvb, offset);
600 offset += 4;
601 proto_tree_add_item_ret_uint(proxy_tree, hf_proxy_srcport, tvb, offset, 2, ENC_BIG_ENDIAN, &srcport);
602 offset += 2;
603 proto_tree_add_item_ret_uint(proxy_tree, hf_proxy_dstport, tvb, offset, 2, ENC_BIG_ENDIAN, &dstport);
604 offset += 2;
605 ptype = (fam_pro & 1) ? PT_TCP : PT_UDP;
606 break;
607 case 0x21: /* TCP over IPv6 */
608 case 0x22: /* UDP over IPv6 */
609 proto_tree_add_item(proxy_tree, hf_proxy_src_ipv6, tvb, offset, 16, ENC_NA);
610 set_address_tvb(&src_addr, AT_IPv6, sizeof(ws_in6_addr), tvb, offset);
611 offset += 16;
612 proto_tree_add_item(proxy_tree, hf_proxy_dst_ipv6, tvb, offset, 16, ENC_NA);
613 set_address_tvb(&dst_addr, AT_IPv6, sizeof(ws_in6_addr), tvb, offset);
614 offset += 16;
615 proto_tree_add_item_ret_uint(proxy_tree, hf_proxy_srcport, tvb, offset, 2, ENC_BIG_ENDIAN, &srcport);
616 offset += 2;
617 proto_tree_add_item_ret_uint(proxy_tree, hf_proxy_dstport, tvb, offset, 2, ENC_BIG_ENDIAN, &dstport);
618 offset += 2;
619 ptype = (fam_pro & 1) ? PT_TCP : PT_UDP;
620 break;
621 case 0x31: /* UNIX stream */
622 case 0x32: /* UNIX datagram */
623 proto_tree_add_item(proxy_tree, hf_proxy2_src_unix, tvb, offset, 108, ENC_NA);
624 offset += 108;
625 proto_tree_add_item(proxy_tree, hf_proxy2_dst_unix, tvb, offset, 108, ENC_NA);
626 offset += 108;
627 break;
628 default:
629 if (header_len) {
630 proto_tree_add_item(proxy_tree, hf_proxy2_unknown, tvb, offset, header_len, ENC_NA);
631 offset += header_len;
633 break;
636 if (offset > next_offset) {
637 proto_tree_add_expert(proxy_tree, pinfo, &ei_proxy_header_length_too_small,
638 tvb, offset, -1);
639 return offset;
642 /* Do we have additional TLV to parse? */
643 if (offset < next_offset) {
644 /* TLV */
645 offset = dissect_proxy_v2_tlv(tvb, pinfo, proxy_tree, offset, next_offset);
648 /* If the cmd is LOCAL, then ignore the proxy protocol block,
649 * even if address and protocol information exists. */
650 if (src_addr.type != AT_NONE) {
651 col_add_lstr(pinfo->cinfo, COL_INFO, "PROXY ", address_to_str(pinfo->pool, &src_addr),
652 " "UTF8_RIGHTWARDS_ARROW" ", address_to_str(pinfo->pool, &dst_addr), ", ",
653 COL_ADD_LSTR_TERMINATOR);
654 col_append_ports(pinfo->cinfo, COL_INFO, ptype, srcport, dstport);
655 conversation_t* conv = find_or_create_conversation(pinfo);
656 proxy_conv_info_t* proxy_info = conversation_get_proto_data(conv, proto_proxy);
657 if (proxy_info == NULL && pinfo->ptype != PT_UDP && cmd != 0) {
658 /* Don't add conversation info on connectionless transport (UDP) */
659 /* If the command is LOCAL, then the receiver "MUST use the real
660 * connection endpoints". */
661 proxy_info = wmem_new(wmem_file_scope(), proxy_conv_info_t);
662 copy_address_wmem(wmem_file_scope(), &proxy_info->src, &src_addr);
663 copy_address_wmem(wmem_file_scope(), &proxy_info->dst, &dst_addr);
664 proxy_info->ptype = PT_TCP;
665 proxy_info->srcport = srcport;
666 proxy_info->dstport = dstport;
667 proxy_info->setup_frame = pinfo->num;
668 conversation_add_proto_data(conv, proto_proxy, proxy_info);
672 return offset;
675 static int
676 dissect_proxy_v2(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data)
678 conversation_t* conv = find_or_create_conversation(pinfo);
679 proxy_conv_info_t *proxy_info;
680 int offset = dissect_proxy_v2_header(tvb, pinfo, tree);
681 proxy_info = conversation_get_proto_data(conv, proto_proxy);
682 if (proxy_info && pinfo->num >= proxy_info->setup_frame &&
683 tvb_reported_length_remaining(tvb, offset)) {
684 /* XXX: If this is a later frame, should we add some
685 * generated fields with the proxy header information,
686 * and a link back to the proxy setup frame? */
687 offset += dissect_proxy_proxied(tvb, pinfo, tree, offset, data, proxy_info);
689 return offset;
692 static bool
693 dissect_proxy_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
695 conversation_t* conv = find_or_create_conversation(pinfo);
696 if (is_proxy_v2(tvb)) {
697 conversation_set_dissector(conv, proxy_v2_handle);
698 dissect_proxy_v2(tvb, pinfo, tree, data);
699 return true;
700 } else if (is_proxy_v1(tvb, NULL)) {
701 conversation_set_dissector(conv, proxy_v1_handle);
702 dissect_proxy_v1(tvb, pinfo, tree, data);
703 return true;
705 return false;
708 static bool
709 dissect_proxy_heur_udp(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data)
711 int offset;
712 if (is_proxy_v2(tvb)) {
713 offset = dissect_proxy_v2(tvb, pinfo, tree, data);
714 if (offset && tvb_reported_length_remaining(tvb, offset)) {
715 /* When the transport is UDP, treat this as connectionless
716 * and just skip past the PROXY header after putting it
717 * in the tree. If this is DNS, for example, every request
718 * will have a PROXY header; the responses don't, and
719 * there's no good way to associate the PROXY information
720 * with the header that won't have problems if the UDP
721 * responses are out of order. Note if the proxied dissector
722 * calls conversation_set_dissector() then this dissector
723 * won't get called on the second pass. */
724 dissect_proxy_proxied(tvb, pinfo, tree, offset, data, NULL);
726 return true;
727 #if 0
728 /* Proxy v1 is only for TCP */
729 } else if (is_proxy_v1(tvb, NULL)) {
730 dissect_proxy_v1(tvb, pinfo, tree, data);
731 #endif
733 return false;
736 void
737 proto_register_proxy(void)
740 expert_module_t *expert_proxy;
742 static hf_register_info hf[] = {
743 { &hf_proxy_version,
744 { "Version", "proxy.version",
745 FT_UINT8, BASE_DEC, NULL, 0x0,
746 NULL, HFILL }
749 { &hf_proxy_src_ipv4,
750 { "Source Address", "proxy.src.ipv4",
751 FT_IPv4, BASE_NONE, NULL, 0x0,
752 NULL, HFILL }
754 { &hf_proxy_dst_ipv4,
755 { "Destination Address", "proxy.dst.ipv4",
756 FT_IPv4, BASE_NONE, NULL, 0x0,
757 NULL, HFILL }
759 { &hf_proxy_src_ipv6,
760 { "Source Address", "proxy.src.ipv6",
761 FT_IPv6, BASE_NONE, NULL, 0x0,
762 NULL, HFILL }
764 { &hf_proxy_dst_ipv6,
765 { "Destination Address", "proxy.dst.ipv6",
766 FT_IPv6, BASE_NONE, NULL, 0x0,
767 NULL, HFILL }
769 { &hf_proxy_srcport,
770 { "Source Port", "proxy.srcport",
771 FT_UINT16, BASE_DEC, NULL, 0x0,
772 NULL, HFILL }
774 { &hf_proxy_dstport,
775 { "Destination Port", "proxy.dstport",
776 FT_UINT16, BASE_DEC, NULL, 0x0,
777 NULL, HFILL }
780 { &hf_proxy1_magic,
781 { "PROXY v1 magic", "proxy.v1.magic",
782 FT_NONE, BASE_NONE, NULL, 0x0,
783 NULL, HFILL }
785 { &hf_proxy1_proto,
786 { "Protocol", "proxy.v1.proto",
787 FT_STRING, BASE_NONE, NULL, 0x0,
788 "Proxied protocol and family", HFILL }
790 { &hf_proxy1_unknown,
791 { "Unknown data", "proxy.v1.unknown",
792 FT_STRING, BASE_NONE, NULL, 0x0,
793 NULL, HFILL }
796 { &hf_proxy2_magic,
797 { "Magic", "proxy.v2.magic",
798 FT_BYTES, BASE_NONE, NULL, 0x0,
799 NULL, HFILL }
801 { &hf_proxy2_ver,
802 { "Version", "proxy.v2.version",
803 FT_UINT8, BASE_DEC, NULL, 0xF0,
804 NULL, HFILL }
806 { &hf_proxy2_cmd,
807 { "Command", "proxy.v2.cmd",
808 FT_UINT8, BASE_DEC, VALS(proxy2_cmd_vals), 0x0F,
809 NULL, HFILL }
811 { &hf_proxy2_addr_family_protocol,
812 { "Address Family Protocol", "proxy.v2.addr_family_protocol",
813 FT_UINT8, BASE_HEX, VALS(proxy2_family_protocol_vals), 0x00,
814 NULL, HFILL }
816 { &hf_proxy2_addr_family,
817 { "Address Family", "proxy.v2.addr_family",
818 FT_UINT8, BASE_HEX, VALS(proxy2_family_vals), 0xF0,
819 NULL, HFILL }
821 { &hf_proxy2_protocol,
822 { "Protocol", "proxy.v2.protocol",
823 FT_UINT8, BASE_HEX, NULL, 0x0F,
824 NULL, HFILL }
826 { &hf_proxy2_len,
827 { "Length", "proxy.v2.length",
828 FT_UINT16, BASE_DEC, NULL, 0x00,
829 "Size of addresses and additional properties", HFILL }
833 { &hf_proxy2_src_unix,
834 { "Source Address", "proxy.v2.src.unix",
835 FT_BYTES, BASE_NONE, NULL, 0x0,
836 NULL, HFILL }
838 { &hf_proxy2_dst_unix,
839 { "Destination Address", "proxy.v2.dst.unix",
840 FT_BYTES, BASE_NONE, NULL, 0x0,
841 NULL, HFILL }
844 { &hf_proxy2_unknown,
845 { "Unknown data", "proxy.v2.unknown",
846 FT_BYTES, BASE_NONE, NULL, 0x0,
847 NULL, HFILL }
850 { &hf_proxy2_tlv,
851 { "TLV", "proxy.v2.tlv",
852 FT_NONE, BASE_NONE, NULL, 0x0,
853 NULL, HFILL }
855 { &hf_proxy2_tlv_type,
856 { "Type", "proxy.v2.tlv.type",
857 FT_UINT8, BASE_HEX, VALS(proxy2_tlv_vals), 0x0,
858 NULL, HFILL }
860 { &hf_proxy2_tlv_length,
861 { "Length", "proxy.v2.tlv.length",
862 FT_UINT16, BASE_DEC, NULL, 0x0,
863 NULL, HFILL }
865 { &hf_proxy2_tlv_value,
866 { "Value", "proxy.v2.tlv.value",
867 FT_BYTES, BASE_NONE, NULL, 0x0,
868 NULL, HFILL }
871 { &hf_proxy2_tlv_ssl_client,
872 { "Client", "proxy.v2.tlv.ssl.client",
873 FT_UINT8, BASE_HEX, NULL, 0x0,
874 NULL, HFILL }
876 { &hf_proxy2_tlv_ssl_verify,
877 { "Verify", "proxy.v2.tlv.ssl.verify",
878 FT_UINT32, BASE_HEX, NULL, 0x0,
879 NULL, HFILL }
881 { &hf_proxy2_tlv_ssl_version,
882 { "Version", "proxy.v2.tlv.ssl.version",
883 FT_STRING, BASE_NONE, NULL, 0x0,
884 NULL, HFILL }
886 { &hf_proxy2_tlv_ssl_cn,
887 { "CN", "proxy.v2.tlv.ssl.cn",
888 FT_STRING, BASE_NONE, NULL, 0x0,
889 "CommonName", HFILL }
891 { &hf_proxy2_tlv_ssl_cipher,
892 { "Cipher", "proxy.v2.tlv.ssl.cipher",
893 FT_STRING, BASE_NONE, NULL, 0x0,
894 NULL, HFILL }
896 { &hf_proxy2_tlv_ssl_sig_alg,
897 { "SIG ALG", "proxy.v2.tlv.ssl.sig_alg",
898 FT_STRING, BASE_NONE, NULL, 0x0,
899 "Signature Algorithm", HFILL }
901 { &hf_proxy2_tlv_ssl_key_alg,
902 { "Key ALG", "proxy.v2.tlv.ssl.keu_alg",
903 FT_STRING, BASE_NONE, NULL, 0x0,
904 "Key Algorithm", HFILL }
908 static int *ett[] = {
909 &ett_proxy1,
910 &ett_proxy2,
911 &ett_proxy2_fampro,
912 &ett_proxy2_tlv,
915 static ei_register_info ei[] = {
916 { &ei_proxy_header_length_too_small,
917 { "proxy.header.length_too_small", PI_MALFORMED, PI_WARN,
918 "Header length is too small", EXPFILL }
920 { &ei_proxy_bad_format,
921 { "proxy.bad_format", PI_MALFORMED, PI_WARN,
922 "Badly formatted PROXY header line", EXPFILL }
926 proto_proxy = proto_register_protocol("PROXY Protocol", "PROXY", "proxy");
928 proto_register_field_array(proto_proxy, hf, array_length(hf));
929 proto_register_subtree_array(ett, array_length(ett));
931 expert_proxy = expert_register_protocol(proto_proxy);
932 expert_register_field_array(expert_proxy, ei, array_length(ei));
934 proxy_v1_handle = register_dissector("proxy_v1", dissect_proxy_v1, proto_proxy);
935 proxy_v2_handle = register_dissector("proxy_v2", dissect_proxy_v2, proto_proxy);
938 void
939 proto_reg_handoff_proxy(void)
941 heur_dissector_add("tcp", dissect_proxy_heur, "PROXY over TCP", "proxy_tcp", proto_proxy, HEURISTIC_ENABLE);
942 /* XXX: PROXY v1 is defined to be transported over TCP only. PROXY v2 is
943 * strongly implied to also only be transported over TCP (though the
944 * proxied connection can be UDP), but dnsdist (and others?) use PROXY
945 * over UDP, adding the header to every request (but not response.)
946 * Presumably the proxied payload can only be DGRAM, since there's no
947 * way to deal with desegmentation or out of order without TCP sequence
948 * numbers. */
949 heur_dissector_add("udp", dissect_proxy_heur_udp, "PROXY over UDP", "proxy_udp", proto_proxy, HEURISTIC_ENABLE);
953 * Editor modelines - https://www.wireshark.org/tools/modelines.html
955 * Local variables:
956 * c-basic-offset: 4
957 * tab-width: 8
958 * indent-tabs-mode: nil
959 * End:
961 * vi: set shiftwidth=4 tabstop=8 expandtab:
962 * :indentSize=4:tabSize=8:noTabs=true: