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 #define AJP13_TCP_PORT 8009 /* Not IANA registered */
28 /* IMPORTANT IMPLEMENTATION NOTES
30 * You need to be looking at:
32 * http://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html
34 * If you're a wireshark dissector guru, then you can skip the rest of
35 * this. I'm writing it all down because I've written 3 dissectors so
36 * far and every time I've forgotten it all and had to re-learn it
37 * from scratch. Not this time, damnit.
39 * Dissector routines get called in two phases:
41 * The first phase is an in-order traversal of every incoming
42 * frame. Since we know it's in-order, we can set up a "conversational
43 * state" that records context-sensitive stuff like "was there a
44 * content-length in the previous request". During this first pass
45 * through the data, the "tree" parameter might be null, or not. For
46 * the regular gui-based Wireshark, it's null, which means we don't
47 * actually display the dissected data in the gui quite yet. For the
48 * text based interface, we might do the parsing and display both in
51 * The second phase happens when the data is actually displayed. In
52 * this pase the "tree" param is non-null, so you've got a hook to
53 * hang the parsed-out display data on. Since there might be gigabytes
54 * worth of capture data, the display code only calls the dissector
55 * for the stuff the user actually clicks on. So you have to assume
56 * the dissector is getting called on random frames, you can't depend
57 * on ordering anymore.
59 * But some parts of the AJP13 capture stream are context sensitive.
60 * That's no big deal during the first in-order pass, but the second
61 * phase requires us to display any random frame correctly. So during
62 * the first in-order phase we create a per-frame user data structure
63 * and attach it to the frame using p_add_proto_data.
65 * Since AJP13 is a TCP/IP based protocol, writing a dissector for it
66 * requires addressing several other issues:
68 * 1) TCP/IP segments can get retransmitted or be sent out of
69 * order. Users don't normally care, because the low-level kernel
70 * networking code takes care of reassembling them properly. But we're
71 * looking at raw network packets, aren't we? The stuff on the
72 * wire. Wireshark has been getting better and better at helping
73 * dissectors with this. I'm a little fuzzy on the details, but my
74 * understanding is that wireshark now contains a fairly substantial
75 * user-space TCP/IP stack so it can re-assemble the data. But I might
76 * be wrong. Since AJP13 is going to be used either on the loopback
77 * interface or on a LAN, it isn't likely to be a big issues anyway.
79 * 2) AJP13 packets (PDU's or protocol data unit's in
80 * networking-speak) don't necessarily line up with TCP segments. That
81 * is, one TCP segment can have more than one AJP13 PDU, or one AJP13
82 * PDU can stretch across multiple TCP segments. Assembling them is
83 * obviously possible, but a royal pain. During the "phase one"
84 * in-order pass you have to keep track of a bunch of offsets and
85 * store which PDU goes with which TCP segment. Luckily, recent
86 * (0.9.4+) versions of wireshark provide the "tcp_dissect_pdus()"
87 * function that takes care of much of the work. See the comments in
88 * packet-tcp.c, the example code in packet-dns.c, or check the
89 * wireshark-dev archives for details.
91 * 3) Wireshark isn't guaranteed to see all the data. I'm a little
92 * unclear on all the possible failure modes, but it comes down to: a)
93 * Not your fault: it's an imperfect world, we're eavesdroppers, and
94 * stuff happens. We might totally miss packets or get garbled
95 * data. Or b) Totally your fault: you turn on the capture during the
96 * middle of an AJP13 conversation and the capture starts out with
97 * half an AJP13 PDU. This code doesn't currently handle either case
98 * very well, but you can get arbitrarily clever. Like: put in tests
99 * to see if this packet has reasonable field values, and if it
100 * doesn't, walk the offset ahead until we see a matching magic number
101 * field, then re-test. But we don't do that now, and since we're
102 * using tcp_dissect_pdu's, I'm not sure how to do it.
108 #define MTYPE_FORWARD_REQUEST 2
109 #define MTYPE_SEND_BODY_CHUNK 3
110 #define MTYPE_SEND_HEADERS 4
111 #define MTYPE_END_RESPONSE 5
112 #define MTYPE_GET_BODY_CHUNK 6
113 #define MTYPE_SHUTDOWN 7
114 #define MTYPE_CPONG 9
115 #define MTYPE_CPING 10
117 static const value_string mtype_codes[] = {
118 { MTYPE_FORWARD_REQUEST, "FORWARD REQUEST" },
119 { MTYPE_SEND_BODY_CHUNK, "SEND BODY CHUNK" },
120 { MTYPE_SEND_HEADERS, "SEND HEADERS" },
121 { MTYPE_END_RESPONSE, "END RESPONSE" },
122 { MTYPE_GET_BODY_CHUNK, "GET BODY CHUNK" },
123 { MTYPE_SHUTDOWN, "SHUTDOWN" },
124 { MTYPE_CPONG, "CPONG" },
125 { MTYPE_CPING, "CPING" },
130 static const value_string http_method_codes[] = {
147 { 17, "VERSION-CONTROL" },
150 { 20, "UNCHECKOUT" },
157 static int proto_ajp13 = -1;
158 static int hf_ajp13_magic = -1;
159 static int hf_ajp13_len = -1;
160 static int hf_ajp13_code = -1;
161 static int hf_ajp13_method = -1;
162 static int hf_ajp13_ver = -1;
163 static int hf_ajp13_uri = -1;
164 static int hf_ajp13_raddr = -1;
165 static int hf_ajp13_rhost = -1;
166 static int hf_ajp13_srv = -1;
167 static int hf_ajp13_port = -1;
168 static int hf_ajp13_sslp = -1;
169 static int hf_ajp13_nhdr = -1;
171 /* response headers */
172 static int hf_ajp13_unknown_header = -1;
173 static int hf_ajp13_additional_header = -1;
174 static int hf_ajp13_content_type = -1;
175 static int hf_ajp13_content_language = -1;
176 static int hf_ajp13_content_length = -1;
177 static int hf_ajp13_date = -1;
178 static int hf_ajp13_last_modified = -1;
179 static int hf_ajp13_location = -1;
180 static int hf_ajp13_set_cookie = -1;
181 static int hf_ajp13_set_cookie2 = -1;
182 static int hf_ajp13_servlet_engine = -1;
183 static int hf_ajp13_status = -1;
184 static int hf_ajp13_www_authenticate = -1;
186 /* request headers */
187 static int hf_ajp13_accept = -1;
188 static int hf_ajp13_accept_charset = -1;
189 static int hf_ajp13_accept_encoding = -1;
190 static int hf_ajp13_accept_language = -1;
191 static int hf_ajp13_authorization = -1;
192 static int hf_ajp13_connection = -1;
195 static int hf_ajp13_cookie = -1;
196 static int hf_ajp13_cookie2 = -1;
197 static int hf_ajp13_host = -1;
198 static int hf_ajp13_pragma = -1;
199 static int hf_ajp13_referer = -1;
200 static int hf_ajp13_user_agent = -1;
202 /* request attributes */
203 static int hf_ajp13_unknown_attribute = -1;
204 static int hf_ajp13_req_attribute = -1;
205 static int hf_ajp13_context = -1;
206 static int hf_ajp13_servlet_path = -1;
207 static int hf_ajp13_remote_user = -1;
208 static int hf_ajp13_auth_type = -1;
209 static int hf_ajp13_query_string = -1;
210 static int hf_ajp13_route = -1;
211 static int hf_ajp13_ssl_cert = -1;
212 static int hf_ajp13_ssl_cipher = -1;
213 static int hf_ajp13_ssl_session = -1;
214 static int hf_ajp13_ssl_key_size = -1;
215 static int hf_ajp13_secret = -1;
216 static int hf_ajp13_stored_method = -1;
218 static int hf_ajp13_rlen = -1;
219 static int hf_ajp13_reusep = -1;
220 static int hf_ajp13_rstatus= -1;
221 static int hf_ajp13_rsmsg = -1;
222 static int hf_ajp13_data = -1;
223 static gint ett_ajp13 = -1;
225 static expert_field ei_ajp13_content_length_invalid = EI_INIT;
228 * Request/response header codes. Common headers are stored as ints in
229 * an effort to improve performance. Why can't we just have one big
232 static const int* rsp_headers[] = {
233 &hf_ajp13_unknown_header,
234 &hf_ajp13_content_type,
235 &hf_ajp13_content_language,
236 &hf_ajp13_content_length,
238 &hf_ajp13_last_modified,
240 &hf_ajp13_set_cookie,
241 &hf_ajp13_set_cookie2,
242 &hf_ajp13_servlet_engine,
244 &hf_ajp13_www_authenticate
247 static const int* req_headers[] = {
248 &hf_ajp13_unknown_header,
250 &hf_ajp13_accept_charset,
251 &hf_ajp13_accept_encoding,
252 &hf_ajp13_accept_language,
253 &hf_ajp13_authorization,
254 &hf_ajp13_connection,
255 &hf_ajp13_content_type,
256 &hf_ajp13_content_length,
265 static const int* req_attributes[] = {
266 &hf_ajp13_unknown_attribute,
268 &hf_ajp13_servlet_path,
269 &hf_ajp13_remote_user,
271 &hf_ajp13_query_string,
274 &hf_ajp13_ssl_cipher,
275 &hf_ajp13_ssl_session,
276 &hf_ajp13_req_attribute, /* 0x0A - name and value follows */
277 &hf_ajp13_ssl_key_size,
279 &hf_ajp13_stored_method
282 typedef struct ajp13_conv_data {
284 gboolean was_get_body_chunk; /* XXX - not used */
287 typedef struct ajp13_frame_data {
288 gboolean is_request_body;
291 /* ajp13, in sort of a belt-and-suspenders move, encodes strings with
292 * both a leading length field, and a trailing null. Mostly, see
293 * ajpv13a.html. The returned length _includes_ the trailing null, if
296 * XXX - is there a tvbuff routine to handle this?
299 ajp13_get_nstring(tvbuff_t *tvb, gint offset, guint16* ret_len)
303 len = tvb_get_ntohs(tvb, offset);
308 /* a size of 0xFFFF indicates a null string - no data follows */
312 return tvb_format_text(tvb, offset+2, MIN(len, ITEM_LABEL_LENGTH));
317 /* dissect a response. more work to do here.
320 display_rsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *ajp13_tree, ajp13_conv_data* cd)
329 proto_tree_add_item(ajp13_tree, hf_ajp13_magic, tvb, pos, 2, ENC_NA);
335 proto_tree_add_item(ajp13_tree, hf_ajp13_len, tvb, pos, 2, ENC_BIG_ENDIAN);
340 mcode = tvb_get_guint8(tvb, pos);
341 col_append_str(pinfo->cinfo, COL_INFO, val_to_str(mcode, mtype_codes, "Unknown message code %u"));
343 proto_tree_add_item(ajp13_tree, hf_ajp13_code, tvb, pos, 1, ENC_BIG_ENDIAN);
348 case MTYPE_END_RESPONSE:
350 proto_tree_add_item(ajp13_tree, hf_ajp13_reusep, tvb, pos, 1, ENC_BIG_ENDIAN);
354 case MTYPE_SEND_HEADERS:
361 /* HTTP RESPONSE STATUS CODE
363 rcode_num = tvb_get_ntohs(tvb, pos);
364 col_append_fstr(pinfo->cinfo, COL_INFO, ":%d", rcode_num);
366 proto_tree_add_item(ajp13_tree, hf_ajp13_rstatus, tvb, pos, 2, ENC_BIG_ENDIAN);
369 /* HTTP RESPONSE STATUS MESSAGE
371 rsmsg = ajp13_get_nstring(tvb, pos, &rsmsg_len);
372 col_append_fstr(pinfo->cinfo, COL_INFO, " %s", rsmsg);
374 proto_tree_add_string(ajp13_tree, hf_ajp13_rsmsg, tvb, pos, rsmsg_len+2, rsmsg);
379 nhdr = tvb_get_ntohs(tvb, pos);
381 proto_tree_add_item(ajp13_tree, hf_ajp13_nhdr, tvb, pos, 2, ENC_BIG_ENDIAN);
386 for(i=0; i<nhdr; i++) {
391 guint16 hval_len, hname_len;
392 const gchar* hname = NULL;
394 /* int cl = 0; TODO: Content-Length header (encoded by 0x08) is special */
398 hcd = tvb_get_guint8(tvb, pos);
402 hid = tvb_get_guint8(tvb, pos);
405 if (hid >= array_length(rsp_headers))
408 hval = ajp13_get_nstring(tvb, pos, &hval_len);
410 proto_tree_add_string_format_value(ajp13_tree, *rsp_headers[hid],
411 tvb, hpos, 2+hval_len+2, hval,
415 /* TODO: Content-Length header (encoded by 0x08) is special */
420 hname = ajp13_get_nstring(tvb, pos, &hname_len);
423 hval = ajp13_get_nstring(tvb, pos, &hval_len);
425 proto_tree_add_string_format(ajp13_tree, hf_ajp13_additional_header,
426 tvb, hpos, hname_len+2+hval_len+2,
427 wmem_strdup_printf(wmem_packet_scope(), "%s: %s", hname, hval),
428 "%s: %s", hname, hval);
435 case MTYPE_GET_BODY_CHUNK:
438 rlen = tvb_get_ntohs(tvb, pos);
439 cd->content_length = rlen;
441 proto_tree_add_item(ajp13_tree, hf_ajp13_rlen, tvb, pos, 2, ENC_BIG_ENDIAN);
450 /* MESSAGE DATA (COPOUT)
453 proto_tree_add_item(ajp13_tree, hf_ajp13_data, tvb, pos+2, -1, ENC_UTF_8|ENC_NA);
460 /* dissect a request body. see AJPv13.html, but the idea is that these
461 * packets, unlike all other packets, have no type field. you just
462 * sort of have to know that they're coming based on the previous
466 display_req_body(tvbuff_t *tvb, proto_tree *ajp13_tree, ajp13_conv_data* cd)
468 /*printf("ajp13:display_req_body()\n");*/
470 * In a resued connection this is never reset.
472 guint16 content_length;
473 guint16 packet_length;
479 proto_tree_add_item(ajp13_tree, hf_ajp13_magic, tvb, pos, 2, ENC_NA);
484 packet_length = tvb_get_ntohs(tvb, pos);
485 proto_tree_add_item(ajp13_tree, hf_ajp13_len, tvb, pos, 2, ENC_BIG_ENDIAN);
488 if (packet_length == 0)
491 * We've got an empty packet:
492 * 0x12 0x34 0x00 0x00
493 * It signals that there is no more data in the body
495 cd->content_length = 0;
501 content_length = tvb_get_ntohs( tvb, pos);
502 if (content_length == 0) {
503 /* zero length content also signals no more body data */
504 cd->content_length = 0;
507 cd->content_length -= content_length;
508 proto_tree_add_item(ajp13_tree, hf_ajp13_data, tvb, pos+2, content_length, ENC_UTF_8|ENC_NA);
513 /* note that even if ajp13_tree is null on the first pass, we still
514 * need to dissect the packet in order to determine if there is a
515 * content-length, and thus if there is a subsequent automatic
516 * request-body transmitted in the next request packet. if there is a
517 * content-length, we record the fact in the conversation context.
518 * ref the top of this file for comments explaining the multi-pass
522 display_req_forward(tvbuff_t *tvb, packet_info *pinfo,
523 proto_tree *ajp13_tree,
543 proto_tree_add_item(ajp13_tree, hf_ajp13_magic, tvb, pos, 2, ENC_NA);
547 proto_tree_add_item(ajp13_tree, hf_ajp13_len, tvb, pos, 2, ENC_BIG_ENDIAN);
552 cod = tvb_get_guint8(tvb, 4);
554 proto_tree_add_item(ajp13_tree, hf_ajp13_code, tvb, pos, 1, ENC_BIG_ENDIAN);
556 if ( cod == MTYPE_CPING ) {
557 col_append_str(pinfo->cinfo, COL_INFO, "CPING" );
561 /* HTTP METHOD (ENCODED AS INTEGER)
563 meth = tvb_get_guint8(tvb, pos);
564 col_append_str(pinfo->cinfo, COL_INFO, val_to_str(meth, http_method_codes, "Unknown method %u"));
566 proto_tree_add_item(ajp13_tree, hf_ajp13_method, tvb, pos, 1, ENC_BIG_ENDIAN);
569 /* HTTP VERSION STRING
571 ver = ajp13_get_nstring(tvb, pos, &ver_len);
573 proto_tree_add_string(ajp13_tree, hf_ajp13_ver, tvb, pos, ver_len+2, ver);
574 pos=pos+ver_len+2; /* skip over size + chars + trailing null */
578 uri = ajp13_get_nstring(tvb, pos, &uri_len);
580 proto_tree_add_string(ajp13_tree, hf_ajp13_uri, tvb, pos, uri_len+2, uri);
581 pos=pos+uri_len+2; /* skip over size + chars + trailing null */
584 col_append_fstr(pinfo->cinfo, COL_INFO, " %s %s", uri, ver);
589 raddr = ajp13_get_nstring(tvb, pos, &raddr_len);
591 proto_tree_add_string(ajp13_tree, hf_ajp13_raddr, tvb, pos, raddr_len+2, raddr);
592 pos=pos+raddr_len+2; /* skip over size + chars + trailing null */
596 rhost = ajp13_get_nstring(tvb, pos, &rhost_len);
598 proto_tree_add_string(ajp13_tree, hf_ajp13_rhost, tvb, pos, rhost_len+2, rhost);
599 pos=pos+rhost_len+2; /* skip over size + chars + trailing null */
603 srv = ajp13_get_nstring(tvb, pos, &srv_len);
605 proto_tree_add_string(ajp13_tree, hf_ajp13_srv, tvb, pos, srv_len+2, srv);
606 pos=pos+srv_len+2; /* skip over size + chars + trailing null */
611 proto_tree_add_item(ajp13_tree, hf_ajp13_port, tvb, pos, 2, ENC_BIG_ENDIAN);
617 proto_tree_add_item(ajp13_tree, hf_ajp13_sslp, tvb, pos, 1, ENC_NA);
622 nhdr = tvb_get_ntohs(tvb, pos);
625 proto_tree_add_item(ajp13_tree, hf_ajp13_nhdr, tvb, pos, 2, ENC_BIG_ENDIAN);
627 cd->content_length = 0;
631 for(i=0; i<nhdr; i++) {
635 const gchar* hname = NULL;
638 guint16 hval_len, hname_len;
642 hcd = tvb_get_guint8(tvb, pos);
648 hid = tvb_get_guint8(tvb, pos);
651 if (hid >= array_length(req_headers))
654 hval = ajp13_get_nstring(tvb, pos, &hval_len);
656 pi = proto_tree_add_string_format(ajp13_tree, *req_headers[hid],
657 tvb, hpos, 2+hval_len+2, hval,
660 if (hid == 0x08 && !ws_strtou32(hval, NULL, &cd->content_length)) {
661 expert_add_info(pinfo, pi, &ei_ajp13_content_length_invalid);
666 hname = ajp13_get_nstring(tvb, pos, &hname_len);
669 hval = ajp13_get_nstring(tvb, pos, &hval_len);
671 proto_tree_add_string_format(ajp13_tree, hf_ajp13_additional_header,
672 tvb, hpos, hname_len+2+hval_len+2,
673 wmem_strdup_printf(wmem_packet_scope(), "%s: %s", hname, hval),
674 "%s: %s", hname, hval);
681 while(tvb_reported_length_remaining(tvb, pos) > 0) {
683 const gchar* aname = NULL;
685 guint16 aval_len, aname_len, key_len;
689 /* ATTRIBUTE CODE/NAME
691 aid = tvb_get_guint8(tvb, pos);
695 /* request terminator */
699 /* req_attribute - name and value follow */
701 aname = ajp13_get_nstring(tvb, pos, &aname_len);
704 aval = ajp13_get_nstring(tvb, pos, &aval_len);
707 proto_tree_add_string_format(ajp13_tree, hf_ajp13_req_attribute,
708 tvb, apos, 1+aname_len+2+aval_len+2,
709 wmem_strdup_printf(wmem_packet_scope(), "%s: %s", aname, aval),
710 "%s: %s", aname, aval);
711 } else if (aid == 0x0B ) {
713 key_len = tvb_get_ntohs(tvb, pos);
714 proto_tree_add_uint(ajp13_tree, hf_ajp13_ssl_key_size,
715 tvb, apos, 1+2, key_len);
719 if (aid >= array_length(req_attributes))
722 aval = ajp13_get_nstring(tvb, pos, &aval_len);
725 proto_tree_add_string_format(ajp13_tree, *req_attributes[aid],
726 tvb, apos, 1+aval_len+2, aval,
734 /* main dissector function. wireshark calls it for segments in both
738 dissect_ajp13_tcp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
742 conversation_t *conv = NULL;
743 ajp13_conv_data *cd = NULL;
744 proto_tree *ajp13_tree = NULL;
745 ajp13_frame_data* fd = NULL;
747 /* conversational state really only does us good during the first
750 conv = find_or_create_conversation(pinfo);
752 cd = (ajp13_conv_data*)conversation_get_proto_data(conv, proto_ajp13);
754 cd = wmem_new(wmem_file_scope(), ajp13_conv_data);
755 cd->content_length = 0;
756 cd->was_get_body_chunk = FALSE;
757 conversation_add_proto_data(conv, proto_ajp13, cd);
760 /* we use the per segment user data to record the conversational
761 * state for use later on when we're called out of order (see
762 * comments at top of this file)
764 fd = (ajp13_frame_data*)p_get_proto_data(wmem_file_scope(), pinfo, proto_ajp13, 0);
766 /*printf("ajp13:dissect_ajp13_common():no frame data, adding");*/
767 /* since there's no per-packet user data, this must be the first
768 * time we've see the packet, and it must be the first "in order"
769 * pass through the data.
771 fd = wmem_new(wmem_file_scope(), ajp13_frame_data);
772 p_add_proto_data(wmem_file_scope(), pinfo, proto_ajp13, 0, fd);
773 fd->is_request_body = FALSE;
774 if (cd->content_length) {
775 /* this is screwy, see AJPv13.html. the idea is that if the
776 * request has a body (as determined by the content-length
777 * header), then there's always an immediate follow-up PDU with
778 * no GET_BODY_CHUNK from the container.
780 fd->is_request_body = TRUE;
784 col_clear(pinfo->cinfo, COL_INFO);
786 mag = tvb_get_ntohs(tvb, 0);
787 /* len = tvb_get_ntohs(tvb, 2); */
789 col_set_str(pinfo->cinfo, COL_PROTOCOL, "AJP13");
791 if (mag == 0x1234 && !fd->is_request_body)
792 col_append_fstr(pinfo->cinfo, COL_INFO, "%d:REQ:", conv->conv_index);
793 else if (mag == 0x1234 && fd->is_request_body)
794 col_append_fstr(pinfo->cinfo, COL_INFO, "%d:REQ:Body", conv->conv_index);
795 else if (mag == 0x4142)
796 col_append_fstr(pinfo->cinfo, COL_INFO, "%d:RSP:", conv->conv_index);
798 col_set_str(pinfo->cinfo, COL_INFO, "AJP13 Error?");
802 ti = proto_tree_add_item(tree, proto_ajp13, tvb, 0, -1, ENC_NA);
803 ajp13_tree = proto_item_add_subtree(ti, ett_ajp13);
808 if (fd->is_request_body)
809 display_req_body(tvb, ajp13_tree, cd);
811 display_req_forward(tvb, pinfo, ajp13_tree, cd);
813 } else if (mag == 0x4142) {
815 display_rsp(tvb, pinfo, ajp13_tree, cd);
819 return tvb_reported_length(tvb);
824 /* given the first chunk of the AJP13 pdu, extract out and return the
825 * packet length. see comments in packet-tcp.c:tcp_dissect_pdus().
828 get_ajp13_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
832 /*magic = tvb_get_ntohs(tvb, offset); */
833 plen = tvb_get_ntohs(tvb, offset+2);
840 /* Code to actually dissect the packets.
843 dissect_ajp13(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
845 /* Set up structures needed to add the protocol subtree and manage it
847 tcp_dissect_pdus(tvb, pinfo, tree,
848 TRUE, /* desegment or not */
849 4, /* magic + length */
850 get_ajp13_pdu_len, /* use first 4, calc data len */
851 dissect_ajp13_tcp_pdu, data); /* the naive dissector */
853 return tvb_reported_length(tvb);
859 proto_register_ajp13(void)
861 expert_module_t* expert_ajp13;
863 static hf_register_info hf[] = {
865 { "Magic", "ajp13.magic", FT_BYTES, BASE_NONE, NULL, 0x0, "Magic Number",
869 { "Length", "ajp13.len", FT_UINT16, BASE_DEC, NULL, 0x0, "Data Length",
873 { "Code", "ajp13.code", FT_UINT32, BASE_DEC, VALS(mtype_codes), 0x0, "Type Code",
877 { "Method", "ajp13.method", FT_UINT8, BASE_DEC, VALS(http_method_codes), 0x0, "HTTP Method",
881 { "Version", "ajp13.ver", FT_STRING, BASE_NONE, NULL, 0x0, "HTTP Version",
885 { "URI", "ajp13.uri", FT_STRING, BASE_NONE, NULL, 0x0, "HTTP URI",
889 { "RADDR", "ajp13.raddr", FT_STRING, BASE_NONE, NULL, 0x0, "Remote Address",
893 { "RHOST", "ajp13.rhost", FT_STRING, BASE_NONE, NULL, 0x0, "Remote Host",
897 { "SRV", "ajp13.srv", FT_STRING, BASE_NONE, NULL, 0x0, "Server",
901 { "PORT", "ajp13.port", FT_UINT16, BASE_DEC, NULL, 0x0, NULL,
905 { "SSLP", "ajp13.sslp", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "Is SSL?",
909 { "NHDR", "ajp13.nhdr", FT_UINT16, BASE_DEC, NULL, 0x0, "Num Headers",
912 /* response headers */
913 { &hf_ajp13_unknown_header,
914 { "unknown_header", "ajp13.unknown_header", FT_STRING, BASE_NONE, NULL, 0x0, "Unknown Header Type",
917 { &hf_ajp13_additional_header,
918 { "additional_header", "ajp13.additional_header", FT_STRING, BASE_NONE, NULL, 0x0, "Additional Header Type",
921 { &hf_ajp13_content_type,
922 { "Content-Type", "ajp13.content_type", FT_STRING, BASE_NONE, NULL, 0x0, "Content-Type Header",
925 { &hf_ajp13_content_language,
926 { "Content-Language", "ajp13.content_language", FT_STRING, BASE_NONE, NULL, 0x0, "Content-Language Header",
929 { &hf_ajp13_content_length,
930 { "Content-Length", "ajp13.content_length", FT_STRING, BASE_NONE, NULL, 0x0, "Content-Length header",
934 { "Date", "ajp13.date", FT_STRING, BASE_NONE, NULL, 0x0, "Date Header",
937 { &hf_ajp13_last_modified,
938 { "Last-Modified", "ajp13.last_modified", FT_STRING, BASE_NONE, NULL, 0x0, "Last Modified Header",
941 { &hf_ajp13_location,
942 { "Location", "ajp13.location", FT_STRING, BASE_NONE, NULL, 0x0, "Location Header",
945 { &hf_ajp13_set_cookie,
946 { "Set-Cookie", "ajp13.set_cookie", FT_STRING, BASE_NONE, NULL, 0x0, "Set-Cookie Header",
949 { &hf_ajp13_set_cookie2,
950 { "Set-Cookie2", "ajp13.set_cookie2", FT_STRING, BASE_NONE, NULL, 0x0, "Set-Cookie2 Header",
953 { &hf_ajp13_servlet_engine,
954 { "Servlet-Engine", "ajp13.servlet_engine", FT_STRING, BASE_NONE, NULL, 0x0, "Servlet-Engine Header",
958 { "Status", "ajp13.status", FT_STRING, BASE_NONE, NULL, 0x0, "Status Header",
961 { &hf_ajp13_www_authenticate,
962 { "WWW-Authenticate", "ajp13.www_authenticate", FT_STRING, BASE_NONE, NULL, 0x0, "WWW-Authenticate Header",
965 /* request headers */
967 { "Accept", "ajp13.accept", FT_STRING, BASE_NONE, NULL, 0x0, "Accept Header",
970 { &hf_ajp13_accept_charset,
971 { "Accept-Charset", "ajp13.accept_charset", FT_STRING, BASE_NONE, NULL, 0x0, "Accept-Charset Header",
974 { &hf_ajp13_accept_encoding,
975 { "Accept-Encoding", "ajp13.accept_encoding", FT_STRING, BASE_NONE, NULL, 0x0, "Accept-Encoding Header",
978 { &hf_ajp13_accept_language,
979 { "Accept-Language", "ajp13.accept_language", FT_STRING, BASE_NONE, NULL, 0x0, "Accept-Language Header",
982 { &hf_ajp13_authorization,
983 { "Authorization", "ajp13.authorization", FT_STRING, BASE_NONE, NULL, 0x0, "Authorization Header",
986 { &hf_ajp13_connection,
987 { "Connection", "ajp13.connection", FT_STRING, BASE_NONE, NULL, 0x0, "Connection Header",
991 { "Cookie", "ajp13.cookie", FT_STRING, BASE_NONE, NULL, 0x0, "Cookie Header",
995 { "Cookie2", "ajp13.cookie2", FT_STRING, BASE_NONE, NULL, 0x0, "Cookie2 Header",
999 { "Host", "ajp13.host", FT_STRING, BASE_NONE, NULL, 0x0, "Host Header",
1003 { "Pragma", "ajp13.pragma", FT_STRING, BASE_NONE, NULL, 0x0, "Pragma Header",
1006 { &hf_ajp13_referer,
1007 { "Referer", "ajp13.referer", FT_STRING, BASE_NONE, NULL, 0x0, "Referer Header",
1010 { &hf_ajp13_user_agent,
1011 { "User-Agent", "ajp13.user_agent", FT_STRING, BASE_NONE, NULL, 0x0, "User-Agent Header",
1014 /* request attributes */
1015 { &hf_ajp13_unknown_attribute,
1016 { "unknown_attribute", "ajp13.unknown_attribute", FT_STRING, BASE_NONE, NULL, 0x0, "Unknown Attribute Type",
1019 { &hf_ajp13_req_attribute,
1020 { "req_attribute", "ajp13.req_attribute", FT_STRING, BASE_NONE, NULL, 0x0, "Additional Attribute Type",
1023 { &hf_ajp13_context,
1024 { "Context", "ajp13.context", FT_STRING, BASE_NONE, NULL, 0x0, "Context Attribute",
1027 { &hf_ajp13_servlet_path,
1028 { "Servlet-Path", "ajp13.servlet_path", FT_STRING, BASE_NONE, NULL, 0x0, "Servlet-Path Attribute",
1031 { &hf_ajp13_remote_user,
1032 { "Remote-User", "ajp13.remote_user", FT_STRING, BASE_NONE, NULL, 0x0, "Remote-User Attribute",
1035 { &hf_ajp13_auth_type,
1036 { "Auth-Type", "ajp13.auth_type", FT_STRING, BASE_NONE, NULL, 0x0, "Auth-Type Attribute",
1039 { &hf_ajp13_query_string,
1040 { "Query-String", "ajp13.query_string", FT_STRING, BASE_NONE, NULL, 0x0, "Query-String Attribute",
1044 { "Route", "ajp13.route", FT_STRING, BASE_NONE, NULL, 0x0, "Route Attribute",
1047 { &hf_ajp13_ssl_cert,
1048 { "SSL-Cert", "ajp13.ssl_cert", FT_STRING, BASE_NONE, NULL, 0x0, "SSL-Cert Attribute",
1051 { &hf_ajp13_ssl_cipher,
1052 { "SSL-Cipher", "ajp13.ssl_cipher", FT_STRING, BASE_NONE, NULL, 0x0, "SSL-Cipher Attribute",
1055 { &hf_ajp13_ssl_session,
1056 { "SSL-Session", "ajp13.ssl_session", FT_STRING, BASE_NONE, NULL, 0x0, "SSL-Session Attribute",
1059 { &hf_ajp13_ssl_key_size,
1060 { "SSL-Key-Size", "ajp13.ssl_key_size", FT_UINT16, BASE_DEC, NULL, 0x0, "SSL-Key-Size Attribute",
1064 { "Secret", "ajp13.secret", FT_STRING, BASE_NONE, NULL, 0x0, "Secret Attribute",
1067 { &hf_ajp13_stored_method,
1068 { "Stored-Method", "ajp13.stored_method", FT_STRING, BASE_NONE, NULL, 0x0, "Stored-Method Attribute",
1073 { "RLEN", "ajp13.rlen", FT_UINT16, BASE_DEC, NULL, 0x0, "Requested Length",
1077 { "REUSEP", "ajp13.reusep", FT_UINT8, BASE_DEC, NULL, 0x0, "Reuse Connection?",
1080 { &hf_ajp13_rstatus,
1081 { "RSTATUS", "ajp13.rstatus", FT_UINT16, BASE_DEC, NULL, 0x0, "HTTP Status Code",
1085 { "RSMSG", "ajp13.rmsg", FT_STRING, BASE_NONE, NULL, 0x0, "HTTP Status Message",
1089 { "Data", "ajp13.data", FT_STRING, BASE_NONE, NULL, 0x0, NULL,
1094 static ei_register_info ei[] = {
1095 { &ei_ajp13_content_length_invalid, { "ajp13.content_length.invalid", PI_MALFORMED, PI_ERROR,
1096 "Content-Length must be a string containing an integer", EXPFILL }}
1099 static gint *ett[] = {
1103 /* Register the protocol name and description
1105 proto_ajp13 = proto_register_protocol("Apache JServ Protocol v1.3", "AJP13", "ajp13");
1107 proto_register_field_array(proto_ajp13, hf, array_length(hf));
1108 proto_register_subtree_array(ett, array_length(ett));
1110 expert_ajp13 = expert_register_protocol(proto_ajp13);
1111 expert_register_field_array(expert_ajp13, ei, array_length(ei));
1117 proto_reg_handoff_ajp13(void)
1119 dissector_handle_t ajp13_handle;
1120 ajp13_handle = create_dissector_handle(dissect_ajp13, proto_ajp13);
1121 dissector_add_uint_with_preference("tcp.port", AJP13_TCP_PORT, ajp13_handle);
1125 * Editor modelines - http://www.wireshark.org/tools/modelines.html
1130 * indent-tabs-mode: nil
1133 * ex: set shiftwidth=2 tabstop=8 expandtab:
1134 * :indentSize=2:tabSize=8:noTabs=true: