Coverage Report

Created: 2026-05-14 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-whois.c
Line
Count
Source
1
/* packet-whois.c
2
 * Routines for whois dissection (see https://tools.ietf.org/html/rfc3912)
3
 * Copyright 2013, Christopher Maynard <Christopher.Maynard@gtech.com>
4
 *
5
 * Wireshark - Network traffic analyzer
6
 * By Gerald Combs <gerald@wireshark.org>
7
 * Copyright 1998 Gerald Combs
8
 *
9
 * SPDX-License-Identifier: GPL-2.0-or-later
10
 */
11
12
#include "config.h"
13
#include <epan/packet.h>
14
#include <epan/conversation.h>
15
#include <epan/expert.h>
16
17
#include "packet-tcp.h"
18
19
33
#define WHOIS_PORT      43  /* This is the registered IANA port (nicname) */
20
21
void proto_register_whois(void);
22
void proto_reg_handoff_whois(void);
23
24
static dissector_handle_t whois_handle;
25
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;
32
33
static expert_field ei_whois_nocrlf;
34
static expert_field ei_whois_encoding;
35
36
static int ett_whois;
37
38
typedef struct _whois_transaction_t {
39
    uint32_t req_frame;
40
    uint32_t rep_frame;
41
    nstime_t req_time;
42
    char*  query;
43
} whois_transaction_t;
44
45
static int
46
dissect_whois(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
47
    void *data)
48
18
{
49
18
    proto_item          *ti, *expert_ti;
50
18
    proto_tree          *whois_tree;
51
18
    conversation_t      *conversation;
52
18
    whois_transaction_t *whois_trans;
53
18
    bool                 is_query;
54
18
    unsigned             len;
55
18
    struct tcpinfo      *tcpinfo = (struct tcpinfo*)data;
56
57
18
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "WHOIS");
58
59
18
    if (pinfo->destport == WHOIS_PORT) {
60
2
        is_query = true;
61
2
        col_set_str(pinfo->cinfo, COL_INFO, "Query");
62
16
    } else {
63
16
        is_query = false;
64
16
        col_set_str(pinfo->cinfo, COL_INFO, "Answer");
65
16
    }
66
67
18
    conversation = find_or_create_conversation(pinfo);
68
18
    whois_trans = (whois_transaction_t *)conversation_get_proto_data(conversation, proto_whois);
69
18
    if (whois_trans == NULL) {
70
11
        unsigned linelen;
71
11
        whois_trans = wmem_new0(wmem_file_scope(), whois_transaction_t);
72
73
        /*
74
         * Find the end of the first line.
75
         */
76
11
        tvb_find_line_end_remaining(tvb, 0, &linelen, NULL);
77
11
        whois_trans->query = (char*)tvb_get_string_enc(wmem_file_scope(), tvb, 0, linelen, ENC_ASCII|ENC_NA);
78
11
        conversation_add_proto_data(conversation, proto_whois, whois_trans);
79
11
    }
80
81
18
    if (whois_trans->query) {
82
18
        col_append_str(pinfo->cinfo, COL_INFO, ": ");
83
18
        col_append_str(pinfo->cinfo, COL_INFO, whois_trans->query);
84
18
    }
85
86
18
    len = tvb_reported_length(tvb);
87
18
    if (!PINFO_FD_VISITED(pinfo)) {
88
18
        if (pinfo->can_desegment) {
89
0
            if (is_query) {
90
0
                if ((len < 2) || (tvb_memeql(tvb, len - 2, (const uint8_t*)"\r\n", 2))) {
91
0
                    pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT;
92
0
                    pinfo->desegment_offset = 0;
93
0
                    return -1;
94
0
                } else {
95
0
                    whois_trans->req_frame = pinfo->num;
96
0
                    whois_trans->req_time = pinfo->abs_ts;
97
0
                }
98
0
            } else if (!(tcpinfo && (IS_TH_FIN(tcpinfo->flags) || tcpinfo->is_reassembled))) {
99
                /* If this is the FIN (or already desegmented, as with an out
100
                 * of order segment received after FIN) go ahead and dissect
101
                 * on the first pass.
102
                 */
103
0
                pinfo->desegment_len = DESEGMENT_UNTIL_FIN;
104
0
                pinfo->desegment_offset = 0;
105
0
                return -1;
106
0
            }
107
0
        }
108
18
    } else if (is_query && (whois_trans->req_frame == 0)) {
109
0
        whois_trans->req_frame = pinfo->num;
110
0
        whois_trans->req_time = pinfo->abs_ts;
111
0
    }
112
113
18
    if (!is_query && (whois_trans->rep_frame == 0)) {
114
        /* By comparing whois_trans->rep_frame to 0, if reassembly is turned
115
         * on, whois_trans->rep_frame will be assigned to the reassembled frame
116
         * number, and if reassembly is turned off, whois_trans->rep_frame will
117
         * be assigned to the first frame number of the response.  This seems
118
         * to match other protocols' behavior.  The alternative is:
119
         *      if (pinfo->num > whois_trans->rep_frame)
120
         * which will give us the same frame number either way.
121
         */
122
9
        whois_trans->rep_frame = pinfo->num;
123
9
    }
124
125
18
    ti = proto_tree_add_protocol_format(tree, proto_whois, tvb, 0, -1,
126
18
        "WHOIS: %s", is_query ? "Query" : "Answer");
127
18
    whois_tree = proto_item_add_subtree(ti, ett_whois);
128
129
    /*
130
     * XXX - WHOIS, as RFC 3912 says, "has no mechanism for indicating
131
     * the character set in use."  We assume UTF-8, which is backwards
132
     * compatible with ASCII; if somebody wants to support WHOIS requests
133
     * or responses in other encodings, they should add a preference.
134
     * (Show Packet Bytes works well enough for many use cases.)
135
     * Some servers do use other character encodings;
136
     * e.g., in 2022 RIPE still uses ISO-8859-1.
137
     */
138
18
    if (is_query) {
139
2
        expert_ti = proto_tree_add_item(whois_tree, hf_whois_query, tvb, 0, -1, ENC_ASCII);
140
2
        if ((len < 2) || (tvb_memeql(tvb, len - 2, (const uint8_t*)"\r\n", 2))) {
141
            /*
142
             * From RFC3912, section 2:
143
             * All requests are terminated with ASCII CR and then ASCII LF.
144
             */
145
2
            expert_add_info(pinfo, expert_ti, &ei_whois_nocrlf);
146
2
        }
147
2
        if (tree && whois_trans->rep_frame) {
148
0
            ti = proto_tree_add_uint(whois_tree, hf_whois_answer_in,
149
0
                tvb, 0, 0, whois_trans->rep_frame);
150
0
            proto_item_set_generated(ti);
151
0
        }
152
16
    } else if (tree && whois_trans->rep_frame) {
153
        /*
154
         * If we know the request frame, show it and the time delta between
155
         * the request and the response.
156
         */
157
16
        if (whois_trans->req_frame) {
158
0
            nstime_t ns;
159
160
0
            ti = proto_tree_add_uint(whois_tree, hf_whois_answer_to,
161
0
                tvb, 0, 0, whois_trans->req_frame);
162
0
            proto_item_set_generated(ti);
163
164
0
            if (pinfo->num == whois_trans->rep_frame) {
165
0
                nstime_delta(&ns, &pinfo->abs_ts, &whois_trans->req_time);
166
0
                ti = proto_tree_add_time(whois_tree, hf_whois_response_time, tvb, 0, 0, &ns);
167
0
                proto_item_set_generated(ti);
168
0
            }
169
0
        }
170
171
        /*
172
         * Show the response as text, a line at a time.
173
         */
174
16
        unsigned offset = 0, next_offset;
175
289
        while (tvb_offset_exists(tvb, offset)) {
176
            /*
177
             * Find the end of the line.
178
             */
179
273
            tvb_find_line_end_remaining(tvb, offset, NULL, &next_offset);
180
181
            /*
182
             * Put this line.
183
             */
184
273
            proto_tree_add_item(whois_tree, hf_whois_answer, tvb, offset,
185
273
                next_offset - offset, ENC_UTF_8);
186
273
            offset = next_offset;
187
273
        }
188
16
        proto_tree_add_expert_remaining(whois_tree, pinfo, &ei_whois_encoding, tvb, 0);
189
16
    }
190
191
18
    return tvb_captured_length(tvb);
192
18
}
193
194
void
195
proto_register_whois(void)
196
15
{
197
15
    expert_module_t *expert_whois;
198
199
15
    static hf_register_info hf[] = {
200
15
        { &hf_whois_query,
201
15
            { "Query", "whois.query", FT_STRING, BASE_NONE, NULL, 0x0,
202
15
              NULL, HFILL }
203
15
        },
204
15
        { &hf_whois_answer,
205
15
            { "Answer", "whois.answer", FT_STRING, BASE_NONE, NULL, 0x0,
206
15
              NULL, HFILL }
207
15
        },
208
15
        { &hf_whois_answer_in,
209
15
            { "Answer In", "whois.answer_in", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE),
210
15
              0x0, "The answer to this WHOIS query is in this frame",
211
15
              HFILL }
212
15
        },
213
15
        { &hf_whois_answer_to,
214
15
            { "Query In", "whois.answer_to", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST),
215
15
              0x0, "This is the answer to the WHOIS query in this frame",
216
15
              HFILL }
217
15
        },
218
15
        { &hf_whois_response_time,
219
15
            { "Response Time", "whois.response_time", FT_RELATIVE_TIME,
220
15
              BASE_NONE, NULL, 0x0,
221
15
              "The time between the Query and the Answer", HFILL }
222
15
        }
223
15
    };
224
225
15
    static int *ett[] = {
226
15
        &ett_whois
227
15
    };
228
229
15
    static ei_register_info ei[] = {
230
15
        { &ei_whois_nocrlf,
231
15
            { "whois.nocrlf", PI_MALFORMED, PI_WARN, "Missing <CR><LF>", EXPFILL}
232
15
        },
233
15
        { &ei_whois_encoding,
234
15
            { "whois.encoding", PI_ASSUMPTION, PI_CHAT, "WHOIS has no mechanism to indicate encoding (RFC 3912), assuming UTF-8", EXPFILL}
235
15
        }
236
15
    };
237
238
15
    proto_whois = proto_register_protocol("whois", "WHOIS", "whois");
239
15
    proto_register_field_array(proto_whois, hf, array_length(hf));
240
15
    proto_register_subtree_array(ett, array_length(ett));
241
15
    expert_whois = expert_register_protocol(proto_whois);
242
15
    expert_register_field_array(expert_whois, ei, array_length(ei));
243
15
    whois_handle = register_dissector("whois", dissect_whois, proto_whois);
244
15
}
245
246
void
247
proto_reg_handoff_whois(void)
248
15
{
249
15
    dissector_add_uint_with_preference("tcp.port", WHOIS_PORT, whois_handle);
250
15
}
251
252
/*
253
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
254
 *
255
 * Local variables:
256
 * c-basic-offset: 4
257
 * tab-width: 8
258
 * indent-tabs-mode: nil
259
 * End:
260
 *
261
 * vi: set shiftwidth=4 tabstop=8 expandtab:
262
 * :indentSize=4:tabSize=8:noTabs=true:
263
 */
264