2 * Routines for whois dissection (see https://tools.ietf.org/html/rfc3912)
3 * Copyright 2013, Christopher Maynard <Christopher.Maynard@gtech.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
13 #include <epan/packet.h>
14 #include <epan/conversation.h>
15 #include <epan/expert.h>
17 #include "packet-tcp.h"
19 #define WHOIS_PORT 43 /* This is the registered IANA port (nicname) */
21 void proto_register_whois(void);
22 void proto_reg_handoff_whois(void);
24 static dissector_handle_t whois_handle
;
26 static int proto_whois
;
27 static int hf_whois_query
;
28 static int hf_whois_answer
;
29 static int hf_whois_answer_in
;
30 static int hf_whois_answer_to
;
31 static int hf_whois_response_time
;
33 static expert_field ei_whois_nocrlf
;
34 static expert_field ei_whois_encoding
;
38 typedef struct _whois_transaction_t
{
43 } whois_transaction_t
;
46 dissect_whois(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
,
49 proto_item
*ti
, *expert_ti
;
50 proto_tree
*whois_tree
;
51 conversation_t
*conversation
;
52 whois_transaction_t
*whois_trans
;
55 struct tcpinfo
*tcpinfo
= (struct tcpinfo
*)data
;
57 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "WHOIS");
59 if (pinfo
->destport
== WHOIS_PORT
) {
61 col_set_str(pinfo
->cinfo
, COL_INFO
, "Query");
64 col_set_str(pinfo
->cinfo
, COL_INFO
, "Answer");
67 conversation
= find_or_create_conversation(pinfo
);
68 whois_trans
= (whois_transaction_t
*)conversation_get_proto_data(conversation
, proto_whois
);
69 if (whois_trans
== NULL
) {
71 whois_trans
= wmem_new0(wmem_file_scope(), whois_transaction_t
);
74 * Find the end of the first line.
76 linelen
= tvb_find_line_end(tvb
, 0, -1, NULL
, false);
78 whois_trans
->query
= tvb_get_string_enc(wmem_file_scope(), tvb
, 0, linelen
, ENC_ASCII
|ENC_NA
);
79 conversation_add_proto_data(conversation
, proto_whois
, whois_trans
);
82 if (whois_trans
->query
) {
83 col_append_str(pinfo
->cinfo
, COL_INFO
, ": ");
84 col_append_str(pinfo
->cinfo
, COL_INFO
, whois_trans
->query
);
87 len
= tvb_reported_length(tvb
);
88 if (!PINFO_FD_VISITED(pinfo
)) {
89 if (pinfo
->can_desegment
) {
91 if ((len
< 2) || (tvb_memeql(tvb
, len
- 2, (const uint8_t*)"\r\n", 2))) {
92 pinfo
->desegment_len
= DESEGMENT_ONE_MORE_SEGMENT
;
93 pinfo
->desegment_offset
= 0;
96 whois_trans
->req_frame
= pinfo
->num
;
97 whois_trans
->req_time
= pinfo
->abs_ts
;
99 } else if (!(tcpinfo
&& (IS_TH_FIN(tcpinfo
->flags
) || tcpinfo
->is_reassembled
))) {
100 /* If this is the FIN (or already desegmented, as with an out
101 * of order segment received after FIN) go ahead and dissect
104 pinfo
->desegment_len
= DESEGMENT_UNTIL_FIN
;
105 pinfo
->desegment_offset
= 0;
109 } else if (is_query
&& (whois_trans
->req_frame
== 0)) {
110 whois_trans
->req_frame
= pinfo
->num
;
111 whois_trans
->req_time
= pinfo
->abs_ts
;
114 if (!is_query
&& (whois_trans
->rep_frame
== 0)) {
115 /* By comparing whois_trans->rep_frame to 0, if reassembly is turned
116 * on, whois_trans->rep_frame will be assigned to the reassembled frame
117 * number, and if reassembly is turned off, whois_trans->rep_frame will
118 * be assigned to the first frame number of the response. This seems
119 * to match other protocols' behavior. The alternative is:
120 * if (pinfo->num > whois_trans->rep_frame)
121 * which will give us the same frame number either way.
123 whois_trans
->rep_frame
= pinfo
->num
;
126 ti
= proto_tree_add_protocol_format(tree
, proto_whois
, tvb
, 0, -1,
127 "WHOIS: %s", is_query
? "Query" : "Answer");
128 whois_tree
= proto_item_add_subtree(ti
, ett_whois
);
131 * XXX - WHOIS, as RFC 3912 says, "has no mechanism for indicating
132 * the character set in use." We assume UTF-8, which is backwards
133 * compatible with ASCII; if somebody wants to support WHOIS requests
134 * or responses in other encodings, they should add a preference.
135 * (Show Packet Bytes works well enough for many use cases.)
136 * Some servers do use other character encodings;
137 * e.g., in 2022 RIPE still uses ISO-8859-1.
140 expert_ti
= proto_tree_add_item(whois_tree
, hf_whois_query
, tvb
, 0, -1, ENC_ASCII
);
141 if ((len
< 2) || (tvb_memeql(tvb
, len
- 2, (const uint8_t*)"\r\n", 2))) {
143 * From RFC3912, section 2:
144 * All requests are terminated with ASCII CR and then ASCII LF.
146 expert_add_info(pinfo
, expert_ti
, &ei_whois_nocrlf
);
148 if (tree
&& whois_trans
->rep_frame
) {
149 ti
= proto_tree_add_uint(whois_tree
, hf_whois_answer_in
,
150 tvb
, 0, 0, whois_trans
->rep_frame
);
151 proto_item_set_generated(ti
);
153 } else if (tree
&& whois_trans
->rep_frame
) {
155 * If we know the request frame, show it and the time delta between
156 * the request and the response.
158 if (whois_trans
->req_frame
) {
161 ti
= proto_tree_add_uint(whois_tree
, hf_whois_answer_to
,
162 tvb
, 0, 0, whois_trans
->req_frame
);
163 proto_item_set_generated(ti
);
165 if (pinfo
->num
== whois_trans
->rep_frame
) {
166 nstime_delta(&ns
, &pinfo
->abs_ts
, &whois_trans
->req_time
);
167 ti
= proto_tree_add_time(whois_tree
, hf_whois_response_time
, tvb
, 0, 0, &ns
);
168 proto_item_set_generated(ti
);
173 * Show the response as text, a line at a time.
175 int offset
= 0, next_offset
;
176 while (tvb_offset_exists(tvb
, offset
)) {
178 * Find the end of the line.
180 tvb_find_line_end(tvb
, offset
, -1, &next_offset
, false);
185 proto_tree_add_item(whois_tree
, hf_whois_answer
, tvb
, offset
,
186 next_offset
- offset
, ENC_UTF_8
);
187 offset
= next_offset
;
189 proto_tree_add_expert(whois_tree
, pinfo
, &ei_whois_encoding
, tvb
, 0, -1);
192 return tvb_captured_length(tvb
);
196 proto_register_whois(void)
198 expert_module_t
*expert_whois
;
200 static hf_register_info hf
[] = {
202 { "Query", "whois.query", FT_STRING
, BASE_NONE
, NULL
, 0x0,
206 { "Answer", "whois.answer", FT_STRING
, BASE_NONE
, NULL
, 0x0,
209 { &hf_whois_answer_in
,
210 { "Answer In", "whois.answer_in", FT_FRAMENUM
, BASE_NONE
, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE
),
211 0x0, "The answer to this WHOIS query is in this frame",
214 { &hf_whois_answer_to
,
215 { "Query In", "whois.answer_to", FT_FRAMENUM
, BASE_NONE
, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST
),
216 0x0, "This is the answer to the WHOIS query in this frame",
219 { &hf_whois_response_time
,
220 { "Response Time", "whois.response_time", FT_RELATIVE_TIME
,
221 BASE_NONE
, NULL
, 0x0,
222 "The time between the Query and the Answer", HFILL
}
226 static int *ett
[] = {
230 static ei_register_info ei
[] = {
232 { "whois.nocrlf", PI_MALFORMED
, PI_WARN
, "Missing <CR><LF>", EXPFILL
}
234 { &ei_whois_encoding
,
235 { "whois.encoding", PI_ASSUMPTION
, PI_CHAT
, "WHOIS has no mechanism to indicate encoding (RFC 3912), assuming UTF-8", EXPFILL
}
239 proto_whois
= proto_register_protocol("whois", "WHOIS", "whois");
240 proto_register_field_array(proto_whois
, hf
, array_length(hf
));
241 proto_register_subtree_array(ett
, array_length(ett
));
242 expert_whois
= expert_register_protocol(proto_whois
);
243 expert_register_field_array(expert_whois
, ei
, array_length(ei
));
244 whois_handle
= register_dissector("whois", dissect_whois
, proto_whois
);
248 proto_reg_handoff_whois(void)
250 dissector_add_uint_with_preference("tcp.port", WHOIS_PORT
, whois_handle
);
254 * Editor modelines - https://www.wireshark.org/tools/modelines.html
259 * indent-tabs-mode: nil
262 * vi: set shiftwidth=4 tabstop=8 expandtab:
263 * :indentSize=4:tabSize=8:noTabs=true: