2 * Routines for AJP13 dissection
3 * Copyright 2002, Christopher K. St. John <cks@distributopia.com>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
16 #include <epan/packet.h>
17 #include <epan/proto_data.h>
18 #include <epan/expert.h>
19 #include "packet-tcp.h"
21 #include <wsutil/strtoi.h>
23 void proto_register_ajp13(void);
24 void proto_reg_handoff_ajp13(void);
26 static dissector_handle_t ajp13_handle
;
28 #define AJP13_TCP_PORT 8009 /* Not IANA registered */
30 /* IMPORTANT IMPLEMENTATION NOTES
32 * You need to be looking at:
34 * http://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html
36 * If you're a wireshark dissector guru, then you can skip the rest of
37 * this. I'm writing it all down because I've written 3 dissectors so
38 * far and every time I've forgotten it all and had to re-learn it
39 * from scratch. Not this time, damnit.
41 * Dissector routines get called in two phases:
43 * The first phase is an in-order traversal of every incoming
44 * frame. Since we know it's in-order, we can set up a "conversational
45 * state" that records context-sensitive stuff like "was there a
46 * content-length in the previous request". During this first pass
47 * through the data, the "tree" parameter might be null, or not. For
48 * the regular gui-based Wireshark, it's null, which means we don't
49 * actually display the dissected data in the gui quite yet. For the
50 * text based interface, we might do the parsing and display both in
53 * The second phase happens when the data is actually displayed. In
54 * this pase the "tree" param is non-null, so you've got a hook to
55 * hang the parsed-out display data on. Since there might be gigabytes
56 * worth of capture data, the display code only calls the dissector
57 * for the stuff the user actually clicks on. So you have to assume
58 * the dissector is getting called on random frames, you can't depend
59 * on ordering anymore.
61 * But some parts of the AJP13 capture stream are context sensitive.
62 * That's no big deal during the first in-order pass, but the second
63 * phase requires us to display any random frame correctly. So during
64 * the first in-order phase we create a per-frame user data structure
65 * and attach it to the frame using p_add_proto_data.
67 * Since AJP13 is a TCP/IP based protocol, writing a dissector for it
68 * requires addressing several other issues:
70 * 1) TCP/IP segments can get retransmitted or be sent out of
71 * order. Users don't normally care, because the low-level kernel
72 * networking code takes care of reassembling them properly. But we're
73 * looking at raw network packets, aren't we? The stuff on the
74 * wire. Wireshark has been getting better and better at helping
75 * dissectors with this. I'm a little fuzzy on the details, but my
76 * understanding is that wireshark now contains a fairly substantial
77 * user-space TCP/IP stack so it can re-assemble the data. But I might
78 * be wrong. Since AJP13 is going to be used either on the loopback
79 * interface or on a LAN, it isn't likely to be a big issues anyway.
81 * 2) AJP13 packets (PDU's or protocol data unit's in
82 * networking-speak) don't necessarily line up with TCP segments. That
83 * is, one TCP segment can have more than one AJP13 PDU, or one AJP13
84 * PDU can stretch across multiple TCP segments. Assembling them is
85 * obviously possible, but a royal pain. During the "phase one"
86 * in-order pass you have to keep track of a bunch of offsets and
87 * store which PDU goes with which TCP segment. Luckily, recent
88 * (0.9.4+) versions of wireshark provide the "tcp_dissect_pdus()"
89 * function that takes care of much of the work. See the comments in
90 * packet-tcp.c, the example code in packet-dns.c, or check the
91 * wireshark-dev archives for details.
93 * 3) Wireshark isn't guaranteed to see all the data. I'm a little
94 * unclear on all the possible failure modes, but it comes down to: a)
95 * Not your fault: it's an imperfect world, we're eavesdroppers, and
96 * stuff happens. We might totally miss packets or get garbled
97 * data. Or b) Totally your fault: you turn on the capture during the
98 * middle of an AJP13 conversation and the capture starts out with
99 * half an AJP13 PDU. This code doesn't currently handle either case
100 * very well, but you can get arbitrarily clever. Like: put in tests
101 * to see if this packet has reasonable field values, and if it
102 * doesn't, walk the offset ahead until we see a matching magic number
103 * field, then re-test. But we don't do that now, and since we're
104 * using tcp_dissect_pdu's, I'm not sure how to do it.
110 #define MTYPE_FORWARD_REQUEST 2
111 #define MTYPE_SEND_BODY_CHUNK 3
112 #define MTYPE_SEND_HEADERS 4
113 #define MTYPE_END_RESPONSE 5
114 #define MTYPE_GET_BODY_CHUNK 6
115 #define MTYPE_SHUTDOWN 7
116 #define MTYPE_CPONG 9
117 #define MTYPE_CPING 10
119 static const value_string mtype_codes
[] = {
120 { MTYPE_FORWARD_REQUEST
, "FORWARD REQUEST" },
121 { MTYPE_SEND_BODY_CHUNK
, "SEND BODY CHUNK" },
122 { MTYPE_SEND_HEADERS
, "SEND HEADERS" },
123 { MTYPE_END_RESPONSE
, "END RESPONSE" },
124 { MTYPE_GET_BODY_CHUNK
, "GET BODY CHUNK" },
125 { MTYPE_SHUTDOWN
, "SHUTDOWN" },
126 { MTYPE_CPONG
, "CPONG" },
127 { MTYPE_CPING
, "CPING" },
132 static const value_string http_method_codes
[] = {
149 { 17, "VERSION-CONTROL" },
152 { 20, "UNCHECKOUT" },
159 static int proto_ajp13
;
160 static int hf_ajp13_magic
;
161 static int hf_ajp13_len
;
162 static int hf_ajp13_code
;
163 static int hf_ajp13_method
;
164 static int hf_ajp13_ver
;
165 static int hf_ajp13_uri
;
166 static int hf_ajp13_raddr
;
167 static int hf_ajp13_rhost
;
168 static int hf_ajp13_srv
;
169 static int hf_ajp13_port
;
170 static int hf_ajp13_sslp
;
171 static int hf_ajp13_nhdr
;
173 /* response headers */
174 static int hf_ajp13_unknown_header
;
175 static int hf_ajp13_content_type
;
176 static int hf_ajp13_content_language
;
177 static int hf_ajp13_content_length
;
178 static int hf_ajp13_date
;
179 static int hf_ajp13_last_modified
;
180 static int hf_ajp13_location
;
181 static int hf_ajp13_set_cookie
;
182 static int hf_ajp13_set_cookie2
;
183 static int hf_ajp13_servlet_engine
;
184 static int hf_ajp13_status
;
185 static int hf_ajp13_www_authenticate
;
187 /* request headers */
188 static int hf_ajp13_accept
;
189 static int hf_ajp13_accept_charset
;
190 static int hf_ajp13_accept_encoding
;
191 static int hf_ajp13_accept_language
;
192 static int hf_ajp13_authorization
;
193 static int hf_ajp13_connection
;
196 static int hf_ajp13_cookie
;
197 static int hf_ajp13_cookie2
;
198 static int hf_ajp13_host
;
199 static int hf_ajp13_pragma
;
200 static int hf_ajp13_referer
;
201 static int hf_ajp13_user_agent
;
203 /* request attributes */
204 static int hf_ajp13_unknown_attribute
;
205 static int hf_ajp13_req_attribute
;
206 static int hf_ajp13_context
;
207 static int hf_ajp13_servlet_path
;
208 static int hf_ajp13_remote_user
;
209 static int hf_ajp13_auth_type
;
210 static int hf_ajp13_query_string
;
211 static int hf_ajp13_route
;
212 static int hf_ajp13_ssl_cert
;
213 static int hf_ajp13_ssl_cipher
;
214 static int hf_ajp13_ssl_session
;
215 static int hf_ajp13_ssl_key_size
;
216 static int hf_ajp13_secret
;
217 static int hf_ajp13_stored_method
;
219 static int hf_ajp13_rlen
;
220 static int hf_ajp13_reusep
;
221 static int hf_ajp13_rstatus
;
222 static int hf_ajp13_rsmsg
;
223 static int hf_ajp13_data
;
224 static int ett_ajp13
;
226 static expert_field ei_ajp13_content_length_invalid
;
229 * Request/response header codes. Common headers are stored as ints in
230 * an effort to improve performance. Why can't we just have one big
233 static int * const rsp_headers
[] = {
234 &hf_ajp13_unknown_header
,
235 &hf_ajp13_content_type
,
236 &hf_ajp13_content_language
,
237 &hf_ajp13_content_length
,
239 &hf_ajp13_last_modified
,
241 &hf_ajp13_set_cookie
,
242 &hf_ajp13_set_cookie2
,
243 &hf_ajp13_servlet_engine
,
245 &hf_ajp13_www_authenticate
248 static int * const req_headers
[] = {
249 &hf_ajp13_unknown_header
,
251 &hf_ajp13_accept_charset
,
252 &hf_ajp13_accept_encoding
,
253 &hf_ajp13_accept_language
,
254 &hf_ajp13_authorization
,
255 &hf_ajp13_connection
,
256 &hf_ajp13_content_type
,
257 &hf_ajp13_content_length
,
266 static int * const req_attributes
[] = {
267 &hf_ajp13_unknown_attribute
,
269 &hf_ajp13_servlet_path
,
270 &hf_ajp13_remote_user
,
272 &hf_ajp13_query_string
,
275 &hf_ajp13_ssl_cipher
,
276 &hf_ajp13_ssl_session
,
277 &hf_ajp13_req_attribute
, /* 0x0A - name and value follows */
278 &hf_ajp13_ssl_key_size
,
280 &hf_ajp13_stored_method
283 typedef struct ajp13_conv_data
{
285 bool was_get_body_chunk
; /* XXX - not used */
288 typedef struct ajp13_frame_data
{
289 bool is_request_body
;
292 /* ajp13, in sort of a belt-and-suspenders move, encodes strings with
293 * both a leading length field, and a trailing null. Mostly, see
294 * ajpv13a.html. The returned length _includes_ the trailing null, if
297 * XXX - is there a tvbuff routine to handle this?
300 ajp13_get_nstring(wmem_allocator_t
*scope
, tvbuff_t
*tvb
, int offset
, uint16_t* ret_len
)
304 len
= tvb_get_ntohs(tvb
, offset
);
309 /* a size of 0xFFFF indicates a null string - no data follows */
313 return tvb_format_text(scope
, tvb
, offset
+2, len
);
318 /* dissect a response. more work to do here.
321 display_rsp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*ajp13_tree
, ajp13_conv_data
* cd
)
330 proto_tree_add_item(ajp13_tree
, hf_ajp13_magic
, tvb
, pos
, 2, ENC_NA
);
336 proto_tree_add_item(ajp13_tree
, hf_ajp13_len
, tvb
, pos
, 2, ENC_BIG_ENDIAN
);
341 mcode
= tvb_get_uint8(tvb
, pos
);
342 col_append_str(pinfo
->cinfo
, COL_INFO
, val_to_str(mcode
, mtype_codes
, "Unknown message code %u"));
344 proto_tree_add_item(ajp13_tree
, hf_ajp13_code
, tvb
, pos
, 1, ENC_BIG_ENDIAN
);
349 case MTYPE_END_RESPONSE
:
351 proto_tree_add_item(ajp13_tree
, hf_ajp13_reusep
, tvb
, pos
, 1, ENC_BIG_ENDIAN
);
355 case MTYPE_SEND_HEADERS
:
362 /* HTTP RESPONSE STATUS CODE
364 rcode_num
= tvb_get_ntohs(tvb
, pos
);
365 col_append_fstr(pinfo
->cinfo
, COL_INFO
, ":%d", rcode_num
);
367 proto_tree_add_item(ajp13_tree
, hf_ajp13_rstatus
, tvb
, pos
, 2, ENC_BIG_ENDIAN
);
370 /* HTTP RESPONSE STATUS MESSAGE
372 rsmsg
= ajp13_get_nstring(pinfo
->pool
, tvb
, pos
, &rsmsg_len
);
373 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " %s", rsmsg
);
375 proto_tree_add_string(ajp13_tree
, hf_ajp13_rsmsg
, tvb
, pos
, rsmsg_len
+2, rsmsg
);
380 nhdr
= tvb_get_ntohs(tvb
, pos
);
382 proto_tree_add_item(ajp13_tree
, hf_ajp13_nhdr
, tvb
, pos
, 2, ENC_BIG_ENDIAN
);
387 for(i
=0; i
<nhdr
; i
++) {
392 uint16_t hval_len
, hname_len
;
393 const char* hname
= NULL
;
395 /* int cl = 0; TODO: Content-Length header (encoded by 0x08) is special */
399 hcd
= tvb_get_uint8(tvb
, pos
);
403 hid
= tvb_get_uint8(tvb
, pos
);
406 if (hid
>= array_length(rsp_headers
))
409 hval
= ajp13_get_nstring(pinfo
->pool
, tvb
, pos
, &hval_len
);
411 proto_tree_add_string_format_value(ajp13_tree
, *rsp_headers
[hid
],
412 tvb
, hpos
, 2+hval_len
+2, hval
,
416 /* TODO: Content-Length header (encoded by 0x08) is special */
421 hname
= ajp13_get_nstring(pinfo
->pool
, tvb
, pos
, &hname_len
);
424 hval
= ajp13_get_nstring(pinfo
->pool
, tvb
, pos
, &hval_len
);
426 if (hcd
>= array_length(rsp_headers
)) {
430 proto_tree_add_string_format(ajp13_tree
, *rsp_headers
[hcd
],
431 tvb
, hpos
, hname_len
+2+hval_len
+2,
432 wmem_strdup_printf(pinfo
->pool
, "%s: %s", hname
, hval
),
433 "%s: %s", hname
, hval
);
440 case MTYPE_GET_BODY_CHUNK
:
443 rlen
= tvb_get_ntohs(tvb
, pos
);
444 cd
->content_length
= rlen
;
446 proto_tree_add_item(ajp13_tree
, hf_ajp13_rlen
, tvb
, pos
, 2, ENC_BIG_ENDIAN
);
455 /* MESSAGE DATA (COPOUT)
458 proto_tree_add_item(ajp13_tree
, hf_ajp13_data
, tvb
, pos
+2, -1, ENC_UTF_8
);
465 /* dissect a request body. see AJPv13.html, but the idea is that these
466 * packets, unlike all other packets, have no type field. you just
467 * sort of have to know that they're coming based on the previous
471 display_req_body(tvbuff_t
*tvb
, proto_tree
*ajp13_tree
, ajp13_conv_data
* cd
)
473 /*printf("ajp13:display_req_body()\n");*/
475 * In a resued connection this is never reset.
477 uint16_t content_length
;
478 uint16_t packet_length
;
484 proto_tree_add_item(ajp13_tree
, hf_ajp13_magic
, tvb
, pos
, 2, ENC_NA
);
489 packet_length
= tvb_get_ntohs(tvb
, pos
);
490 proto_tree_add_item(ajp13_tree
, hf_ajp13_len
, tvb
, pos
, 2, ENC_BIG_ENDIAN
);
493 if (packet_length
== 0)
496 * We've got an empty packet:
497 * 0x12 0x34 0x00 0x00
498 * It signals that there is no more data in the body
500 cd
->content_length
= 0;
506 content_length
= tvb_get_ntohs( tvb
, pos
);
507 if (content_length
== 0) {
508 /* zero length content also signals no more body data */
509 cd
->content_length
= 0;
512 cd
->content_length
-= content_length
;
513 proto_tree_add_item(ajp13_tree
, hf_ajp13_data
, tvb
, pos
+2, content_length
, ENC_UTF_8
);
518 /* note that even if ajp13_tree is null on the first pass, we still
519 * need to dissect the packet in order to determine if there is a
520 * content-length, and thus if there is a subsequent automatic
521 * request-body transmitted in the next request packet. if there is a
522 * content-length, we record the fact in the conversation context.
523 * ref the top of this file for comments explaining the multi-pass
527 display_req_forward(tvbuff_t
*tvb
, packet_info
*pinfo
,
528 proto_tree
*ajp13_tree
,
548 proto_tree_add_item(ajp13_tree
, hf_ajp13_magic
, tvb
, pos
, 2, ENC_NA
);
552 proto_tree_add_item(ajp13_tree
, hf_ajp13_len
, tvb
, pos
, 2, ENC_BIG_ENDIAN
);
557 cod
= tvb_get_uint8(tvb
, 4);
559 proto_tree_add_item(ajp13_tree
, hf_ajp13_code
, tvb
, pos
, 1, ENC_BIG_ENDIAN
);
561 if ( cod
== MTYPE_CPING
) {
562 col_append_str(pinfo
->cinfo
, COL_INFO
, "CPING" );
566 /* HTTP METHOD (ENCODED AS INTEGER)
568 meth
= tvb_get_uint8(tvb
, pos
);
569 col_append_str(pinfo
->cinfo
, COL_INFO
, val_to_str(meth
, http_method_codes
, "Unknown method %u"));
571 proto_tree_add_item(ajp13_tree
, hf_ajp13_method
, tvb
, pos
, 1, ENC_BIG_ENDIAN
);
574 /* HTTP VERSION STRING
576 ver
= ajp13_get_nstring(pinfo
->pool
, tvb
, pos
, &ver_len
);
578 proto_tree_add_string(ajp13_tree
, hf_ajp13_ver
, tvb
, pos
, ver_len
+2, ver
);
579 pos
=pos
+ver_len
+2; /* skip over size + chars + trailing null */
583 uri
= ajp13_get_nstring(pinfo
->pool
, tvb
, pos
, &uri_len
);
585 proto_tree_add_string(ajp13_tree
, hf_ajp13_uri
, tvb
, pos
, uri_len
+2, uri
);
586 pos
=pos
+uri_len
+2; /* skip over size + chars + trailing null */
589 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " %s %s", uri
, ver
);
594 raddr
= ajp13_get_nstring(pinfo
->pool
, tvb
, pos
, &raddr_len
);
596 proto_tree_add_string(ajp13_tree
, hf_ajp13_raddr
, tvb
, pos
, raddr_len
+2, raddr
);
597 pos
=pos
+raddr_len
+2; /* skip over size + chars + trailing null */
601 rhost
= ajp13_get_nstring(pinfo
->pool
, tvb
, pos
, &rhost_len
);
603 proto_tree_add_string(ajp13_tree
, hf_ajp13_rhost
, tvb
, pos
, rhost_len
+2, rhost
);
604 pos
=pos
+rhost_len
+2; /* skip over size + chars + trailing null */
608 srv
= ajp13_get_nstring(pinfo
->pool
, tvb
, pos
, &srv_len
);
610 proto_tree_add_string(ajp13_tree
, hf_ajp13_srv
, tvb
, pos
, srv_len
+2, srv
);
611 pos
=pos
+srv_len
+2; /* skip over size + chars + trailing null */
616 proto_tree_add_item(ajp13_tree
, hf_ajp13_port
, tvb
, pos
, 2, ENC_BIG_ENDIAN
);
622 proto_tree_add_item(ajp13_tree
, hf_ajp13_sslp
, tvb
, pos
, 1, ENC_NA
);
627 nhdr
= tvb_get_ntohs(tvb
, pos
);
630 proto_tree_add_item(ajp13_tree
, hf_ajp13_nhdr
, tvb
, pos
, 2, ENC_BIG_ENDIAN
);
632 cd
->content_length
= 0;
636 for(i
=0; i
<nhdr
; i
++) {
640 const char* hname
= NULL
;
643 uint16_t hval_len
, hname_len
;
647 hcd
= tvb_get_uint8(tvb
, pos
);
653 hid
= tvb_get_uint8(tvb
, pos
);
656 if (hid
>= array_length(req_headers
))
659 hval
= ajp13_get_nstring(pinfo
->pool
, tvb
, pos
, &hval_len
);
662 pi
= proto_tree_add_string_format_value(ajp13_tree
, *req_headers
[hid
],
663 tvb
, hpos
, 2+hval_len
+2, hval
,
666 if (hid
== 0x08 && !ws_strtou32(hval
, NULL
, &cd
->content_length
)) {
667 expert_add_info(pinfo
, pi
, &ei_ajp13_content_length_invalid
);
672 hname
= ajp13_get_nstring(pinfo
->pool
, tvb
, pos
, &hname_len
);
675 if (hcd
>= array_length(req_headers
)) {
679 hval
= ajp13_get_nstring(pinfo
->pool
, tvb
, pos
, &hval_len
);
681 proto_tree_add_string_format(ajp13_tree
, *req_headers
[hcd
],
682 tvb
, hpos
, hname_len
+2+hval_len
+2,
683 wmem_strdup_printf(pinfo
->pool
, "%s: %s", hname
, hval
),
684 "%s: %s", hname
, hval
);
691 while(tvb_reported_length_remaining(tvb
, pos
) > 0) {
693 const char* aname
= NULL
;
695 uint16_t aval_len
, aname_len
, key_len
;
699 /* ATTRIBUTE CODE/NAME
701 aid
= tvb_get_uint8(tvb
, pos
);
705 /* request terminator */
709 /* req_attribute - name and value follow */
711 aname
= ajp13_get_nstring(pinfo
->pool
, tvb
, pos
, &aname_len
);
714 aval
= ajp13_get_nstring(pinfo
->pool
, tvb
, pos
, &aval_len
);
717 proto_tree_add_string_format(ajp13_tree
, hf_ajp13_req_attribute
,
718 tvb
, apos
, 1+aname_len
+2+aval_len
+2,
719 wmem_strdup_printf(pinfo
->pool
, "%s: %s", aname
, aval
),
720 "%s: %s", aname
, aval
);
721 } else if (aid
== 0x0B ) {
723 key_len
= tvb_get_ntohs(tvb
, pos
);
724 proto_tree_add_uint(ajp13_tree
, hf_ajp13_ssl_key_size
,
725 tvb
, apos
, 1+2, key_len
);
729 if (aid
>= array_length(req_attributes
))
732 aval
= ajp13_get_nstring(pinfo
->pool
, tvb
, pos
, &aval_len
);
735 proto_tree_add_string_format_value(ajp13_tree
, *req_attributes
[aid
],
736 tvb
, apos
, 1+aval_len
+2, aval
,
744 /* main dissector function. wireshark calls it for segments in both
748 dissect_ajp13_tcp_pdu(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
752 conversation_t
*conv
= NULL
;
753 ajp13_conv_data
*cd
= NULL
;
754 proto_tree
*ajp13_tree
= NULL
;
755 ajp13_frame_data
* fd
= NULL
;
757 /* conversational state really only does us good during the first
760 conv
= find_or_create_conversation(pinfo
);
762 cd
= (ajp13_conv_data
*)conversation_get_proto_data(conv
, proto_ajp13
);
764 cd
= wmem_new(wmem_file_scope(), ajp13_conv_data
);
765 cd
->content_length
= 0;
766 cd
->was_get_body_chunk
= false;
767 conversation_add_proto_data(conv
, proto_ajp13
, cd
);
770 /* we use the per segment user data to record the conversational
771 * state for use later on when we're called out of order (see
772 * comments at top of this file)
774 fd
= (ajp13_frame_data
*)p_get_proto_data(wmem_file_scope(), pinfo
, proto_ajp13
, 0);
776 /*printf("ajp13:dissect_ajp13_common():no frame data, adding");*/
777 /* since there's no per-packet user data, this must be the first
778 * time we've see the packet, and it must be the first "in order"
779 * pass through the data.
781 fd
= wmem_new(wmem_file_scope(), ajp13_frame_data
);
782 p_add_proto_data(wmem_file_scope(), pinfo
, proto_ajp13
, 0, fd
);
783 fd
->is_request_body
= false;
784 if (cd
->content_length
) {
785 /* this is screwy, see AJPv13.html. the idea is that if the
786 * request has a body (as determined by the content-length
787 * header), then there's always an immediate follow-up PDU with
788 * no GET_BODY_CHUNK from the container.
790 fd
->is_request_body
= true;
794 col_clear(pinfo
->cinfo
, COL_INFO
);
796 mag
= tvb_get_ntohs(tvb
, 0);
797 /* len = tvb_get_ntohs(tvb, 2); */
799 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "AJP13");
801 if (mag
== 0x1234 && !fd
->is_request_body
)
802 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "%d:REQ:", conv
->conv_index
);
803 else if (mag
== 0x1234 && fd
->is_request_body
)
804 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "%d:REQ:Body", conv
->conv_index
);
805 else if (mag
== 0x4142)
806 col_append_fstr(pinfo
->cinfo
, COL_INFO
, "%d:RSP:", conv
->conv_index
);
808 col_set_str(pinfo
->cinfo
, COL_INFO
, "AJP13 Error?");
812 ti
= proto_tree_add_item(tree
, proto_ajp13
, tvb
, 0, -1, ENC_NA
);
813 ajp13_tree
= proto_item_add_subtree(ti
, ett_ajp13
);
818 if (fd
->is_request_body
)
819 display_req_body(tvb
, ajp13_tree
, cd
);
821 display_req_forward(tvb
, pinfo
, ajp13_tree
, cd
);
823 } else if (mag
== 0x4142) {
825 display_rsp(tvb
, pinfo
, ajp13_tree
, cd
);
829 return tvb_reported_length(tvb
);
834 /* given the first chunk of the AJP13 pdu, extract out and return the
835 * packet length. see comments in packet-tcp.c:tcp_dissect_pdus().
838 get_ajp13_pdu_len(packet_info
*pinfo _U_
, tvbuff_t
*tvb
, int offset
, void *data _U_
)
842 /*magic = tvb_get_ntohs(tvb, offset); */
843 plen
= tvb_get_ntohs(tvb
, offset
+2);
850 /* Code to actually dissect the packets.
853 dissect_ajp13(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data
)
855 /* Set up structures needed to add the protocol subtree and manage it
857 tcp_dissect_pdus(tvb
, pinfo
, tree
,
858 true, /* desegment or not */
859 4, /* magic + length */
860 get_ajp13_pdu_len
, /* use first 4, calc data len */
861 dissect_ajp13_tcp_pdu
, data
); /* the naive dissector */
863 return tvb_reported_length(tvb
);
869 proto_register_ajp13(void)
871 expert_module_t
* expert_ajp13
;
873 static hf_register_info hf
[] = {
875 { "Magic", "ajp13.magic", FT_BYTES
, BASE_NONE
, NULL
, 0x0, "Magic Number",
879 { "Length", "ajp13.len", FT_UINT16
, BASE_DEC
, NULL
, 0x0, "Data Length",
883 { "Code", "ajp13.code", FT_UINT32
, BASE_DEC
, VALS(mtype_codes
), 0x0, "Type Code",
887 { "Method", "ajp13.method", FT_UINT8
, BASE_DEC
, VALS(http_method_codes
), 0x0, "HTTP Method",
891 { "Version", "ajp13.ver", FT_STRING
, BASE_NONE
, NULL
, 0x0, "HTTP Version",
895 { "URI", "ajp13.uri", FT_STRING
, BASE_NONE
, NULL
, 0x0, "HTTP URI",
899 { "RADDR", "ajp13.raddr", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Remote Address",
903 { "RHOST", "ajp13.rhost", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Remote Host",
907 { "SRV", "ajp13.srv", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Server",
911 { "PORT", "ajp13.port", FT_UINT16
, BASE_DEC
, NULL
, 0x0, NULL
,
915 { "SSLP", "ajp13.sslp", FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0, "Is SSL?",
919 { "NHDR", "ajp13.nhdr", FT_UINT16
, BASE_DEC
, NULL
, 0x0, "Num Headers",
922 /* response headers */
923 { &hf_ajp13_unknown_header
,
924 { "unknown_header", "ajp13.unknown_header", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Unknown Header Type",
927 { &hf_ajp13_content_type
,
928 { "Content-Type", "ajp13.content_type", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Content-Type Header",
931 { &hf_ajp13_content_language
,
932 { "Content-Language", "ajp13.content_language", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Content-Language Header",
935 { &hf_ajp13_content_length
,
936 { "Content-Length", "ajp13.content_length", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Content-Length header",
940 { "Date", "ajp13.date", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Date Header",
943 { &hf_ajp13_last_modified
,
944 { "Last-Modified", "ajp13.last_modified", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Last Modified Header",
947 { &hf_ajp13_location
,
948 { "Location", "ajp13.location", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Location Header",
951 { &hf_ajp13_set_cookie
,
952 { "Set-Cookie", "ajp13.set_cookie", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Set-Cookie Header",
955 { &hf_ajp13_set_cookie2
,
956 { "Set-Cookie2", "ajp13.set_cookie2", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Set-Cookie2 Header",
959 { &hf_ajp13_servlet_engine
,
960 { "Servlet-Engine", "ajp13.servlet_engine", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Servlet-Engine Header",
964 { "Status", "ajp13.status", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Status Header",
967 { &hf_ajp13_www_authenticate
,
968 { "WWW-Authenticate", "ajp13.www_authenticate", FT_STRING
, BASE_NONE
, NULL
, 0x0, "WWW-Authenticate Header",
971 /* request headers */
973 { "Accept", "ajp13.accept", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Accept Header",
976 { &hf_ajp13_accept_charset
,
977 { "Accept-Charset", "ajp13.accept_charset", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Accept-Charset Header",
980 { &hf_ajp13_accept_encoding
,
981 { "Accept-Encoding", "ajp13.accept_encoding", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Accept-Encoding Header",
984 { &hf_ajp13_accept_language
,
985 { "Accept-Language", "ajp13.accept_language", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Accept-Language Header",
988 { &hf_ajp13_authorization
,
989 { "Authorization", "ajp13.authorization", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Authorization Header",
992 { &hf_ajp13_connection
,
993 { "Connection", "ajp13.connection", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Connection Header",
997 { "Cookie", "ajp13.cookie", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Cookie Header",
1000 { &hf_ajp13_cookie2
,
1001 { "Cookie2", "ajp13.cookie2", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Cookie2 Header",
1005 { "Host", "ajp13.host", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Host Header",
1009 { "Pragma", "ajp13.pragma", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Pragma Header",
1012 { &hf_ajp13_referer
,
1013 { "Referer", "ajp13.referer", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Referer Header",
1016 { &hf_ajp13_user_agent
,
1017 { "User-Agent", "ajp13.user_agent", FT_STRING
, BASE_NONE
, NULL
, 0x0, "User-Agent Header",
1020 /* request attributes */
1021 { &hf_ajp13_unknown_attribute
,
1022 { "unknown_attribute", "ajp13.unknown_attribute", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Unknown Attribute Type",
1025 { &hf_ajp13_req_attribute
,
1026 { "req_attribute", "ajp13.req_attribute", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Additional Attribute Type",
1029 { &hf_ajp13_context
,
1030 { "Context", "ajp13.context", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Context Attribute",
1033 { &hf_ajp13_servlet_path
,
1034 { "Servlet-Path", "ajp13.servlet_path", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Servlet-Path Attribute",
1037 { &hf_ajp13_remote_user
,
1038 { "Remote-User", "ajp13.remote_user", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Remote-User Attribute",
1041 { &hf_ajp13_auth_type
,
1042 { "Auth-Type", "ajp13.auth_type", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Auth-Type Attribute",
1045 { &hf_ajp13_query_string
,
1046 { "Query-String", "ajp13.query_string", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Query-String Attribute",
1050 { "Route", "ajp13.route", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Route Attribute",
1053 { &hf_ajp13_ssl_cert
,
1054 { "SSL-Cert", "ajp13.ssl_cert", FT_STRING
, BASE_NONE
, NULL
, 0x0, "SSL-Cert Attribute",
1057 { &hf_ajp13_ssl_cipher
,
1058 { "SSL-Cipher", "ajp13.ssl_cipher", FT_STRING
, BASE_NONE
, NULL
, 0x0, "SSL-Cipher Attribute",
1061 { &hf_ajp13_ssl_session
,
1062 { "SSL-Session", "ajp13.ssl_session", FT_STRING
, BASE_NONE
, NULL
, 0x0, "SSL-Session Attribute",
1065 { &hf_ajp13_ssl_key_size
,
1066 { "SSL-Key-Size", "ajp13.ssl_key_size", FT_UINT16
, BASE_DEC
, NULL
, 0x0, "SSL-Key-Size Attribute",
1070 { "Secret", "ajp13.secret", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Secret Attribute",
1073 { &hf_ajp13_stored_method
,
1074 { "Stored-Method", "ajp13.stored_method", FT_STRING
, BASE_NONE
, NULL
, 0x0, "Stored-Method Attribute",
1079 { "RLEN", "ajp13.rlen", FT_UINT16
, BASE_DEC
, NULL
, 0x0, "Requested Length",
1083 { "REUSEP", "ajp13.reusep", FT_UINT8
, BASE_DEC
, NULL
, 0x0, "Reuse Connection?",
1086 { &hf_ajp13_rstatus
,
1087 { "RSTATUS", "ajp13.rstatus", FT_UINT16
, BASE_DEC
, NULL
, 0x0, "HTTP Status Code",
1091 { "RSMSG", "ajp13.rsmsg", FT_STRING
, BASE_NONE
, NULL
, 0x0, "HTTP Status Message",
1095 { "Data", "ajp13.data", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
,
1100 static ei_register_info ei
[] = {
1101 { &ei_ajp13_content_length_invalid
, { "ajp13.content_length.invalid", PI_MALFORMED
, PI_ERROR
,
1102 "Content-Length must be a string containing an integer", EXPFILL
}}
1105 static int *ett
[] = {
1109 /* Register the protocol name and description
1111 proto_ajp13
= proto_register_protocol("Apache JServ Protocol v1.3", "AJP13", "ajp13");
1113 proto_register_field_array(proto_ajp13
, hf
, array_length(hf
));
1114 proto_register_subtree_array(ett
, array_length(ett
));
1116 expert_ajp13
= expert_register_protocol(proto_ajp13
);
1117 expert_register_field_array(expert_ajp13
, ei
, array_length(ei
));
1119 ajp13_handle
= register_dissector("ajp13", dissect_ajp13
, proto_ajp13
);
1125 proto_reg_handoff_ajp13(void)
1127 dissector_add_uint_with_preference("tcp.port", AJP13_TCP_PORT
, ajp13_handle
);
1131 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1136 * indent-tabs-mode: nil
1139 * ex: set shiftwidth=2 tabstop=8 expandtab:
1140 * :indentSize=2:tabSize=8:noTabs=true: