Coverage Report

Created: 2025-12-27 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-sapni.c
Line
Count
Source
1
/* packet-sapni.c
2
 * Routines for SAP NI (Network Interface) dissection
3
 * Copyright 2022, Martin Gallo <martin.gallo [AT] gmail.com>
4
 * Code contributed by SecureAuth Corp.
5
 *
6
 * Wireshark - Network traffic analyzer
7
 * By Gerald Combs <gerald@wireshark.org>
8
 * Copyright 1998 Gerald Combs
9
 *
10
 * SPDX-License-Identifier: GPL-2.0-or-later
11
 */
12
13
/*
14
 * This is a simple dissector for the SAP NI protocol, mainly responsible for reassembly and calling the right registered dissector
15
 * based on the port number.
16
 *
17
 * Some details and example requests can be found in pysap's documentation: https://pysap.readthedocs.io/en/latest/protocols/SAPNI.html.
18
 */
19
20
#include <config.h>
21
22
#include <epan/packet.h>
23
#include <epan/prefs.h>
24
#include <epan/expert.h>
25
#include "packet-tcp.h"
26
#include <epan/next_tvb.h>
27
#include <epan/conversation.h>
28
#include <wsutil/wmem/wmem.h>
29
30
#include "packet-sapni.h"
31
32
33
/*
34
 * Define default ports. The right range should be 32NN and 4NNNN, but as port numbers are proprietary and not
35
 * IANA assigned, we leave only the ones corresponding to the instance 00.
36
 */
37
14
#define SAP_PROTOCOL_PORT_RANGE "3200,40000"
38
39
/*
40
 * Length of the frame header
41
 */
42
278
#define SAP_PROTOCOL_HEADER_LEN 4
43
44
static int proto_sap_protocol;
45
46
static int hf_sap_protocol_length;
47
static int hf_sap_protocol_payload;
48
49
static int hf_sap_protocol_ping;
50
static int hf_sap_protocol_pong;
51
52
static int ett_sap_protocol;
53
54
/* Expert info */
55
static expert_field ei_sap_invalid_length;
56
57
/* Global port preference */
58
static range_t *global_sap_protocol_port_range;
59
60
/* Global reassemble preference */
61
static bool global_sap_protocol_desegment = true;
62
63
/* Protocol handle */
64
static dissector_handle_t sap_protocol_handle;
65
static dissector_handle_t sap_router_handle;
66
67
/* Sub-dissectors table */
68
static dissector_table_t sub_dissectors_table;
69
static heur_dissector_list_t heur_subdissector_list;
70
71
/*
72
 *
73
 */
74
void proto_reg_handoff_sap_protocol(void);
75
void proto_register_sap_protocol(void);
76
77
78
/*
79
 * Get the SAPNI pdu length
80
 */
81
static unsigned
82
get_sap_protocol_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset _U_, void *dissector_data _U_)
83
4.04k
{
84
4.04k
  return ((unsigned)tvb_get_ntohl(tvb, 0) + 4);
85
4.04k
}
86
87
88
/*
89
 * Dissect the payload of a packet using a registered SAP protocol. It uses
90
 * heuristics as a first try as some protocols uses the same TCP ports
91
 * (e.g. 3200/tcp for Enqueue Server and Diag).
92
 */
93
void
94
2.71k
dissect_sap_protocol_payload(tvbuff_t *tvb, uint32_t offset, packet_info *pinfo, proto_tree *tree, uint16_t sport, uint16_t dport){
95
2.71k
  uint16_t low_port = 0, high_port = 0;
96
2.71k
  tvbuff_t *next_tvb = NULL;
97
2.71k
  heur_dtbl_entry_t *hdtbl_entry = NULL;
98
99
  /* Set the new tvb for further dissection of the payload */
100
2.71k
  next_tvb = tvb_new_subset_remaining(tvb, offset);
101
102
  /* Determine if this packet is part of a conversation and call dissector
103
   * for the conversation if available.
104
   */
105
2.71k
  if (try_conversation_dissector(&pinfo->dst, &pinfo->src, CONVERSATION_TCP,
106
2.71k
      dport, sport, next_tvb, pinfo, tree, NULL, 0)) {
107
0
    return;
108
0
  }
109
110
  /* Try with the heuristic dissectors first */
111
  /* TODO: When the protocol is guessed via heuristic dissector (Enqueue
112
   * Server), the NI Protocol tree is missed. */
113
2.71k
  if (dissector_try_heuristic(heur_subdissector_list, next_tvb, pinfo, tree, &hdtbl_entry, NULL)) {
114
0
    return;
115
0
  }
116
117
  /* Call the dissector in the subdissectors table according to the port number */
118
2.71k
  if (sport > dport) {
119
1.34k
    low_port = dport; high_port = sport;
120
1.37k
  } else {
121
1.37k
    low_port = sport; high_port = dport;
122
1.37k
  }
123
2.71k
  if ((low_port != 0 && dissector_try_uint(sub_dissectors_table, low_port, next_tvb, pinfo, tree)) ||
124
2.39k
    (high_port != 0 && dissector_try_uint(sub_dissectors_table, high_port, next_tvb, pinfo, tree))){
125
2.39k
    return;
126
2.39k
  }
127
2.71k
}
128
129
130
/*
131
 * Dissect a SAPNI packet, adding the length field to the protocol tree and
132
 * calling the sub-dissector according to the port number. It also identifies
133
 * PING/PONG packets at the SAPNI layer.
134
 */
135
static int
136
dissect_sap_protocol_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
137
4.04k
{
138
4.04k
  uint32_t length = 0;
139
4.04k
  proto_item *ti = NULL, *sap_protocol_length = NULL;
140
4.04k
  proto_tree *sap_protocol_tree = NULL;
141
4.04k
  conversation_t *conversation = NULL;
142
4.04k
  tvbuff_t *next_tvb = NULL;
143
144
  /* Add the protocol to the column */
145
4.04k
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "SAPNI");
146
  /* Clear out stuff in the info column */
147
4.04k
  col_clear(pinfo->cinfo,COL_INFO);
148
149
  /* Get the length field */
150
4.04k
  length = tvb_get_ntohl(tvb, 0);
151
152
  /* Add the payload length to the info column */
153
4.04k
  col_add_fstr(pinfo->cinfo, COL_INFO, "Length=%d ", length);
154
155
  /* Add the main SAP Protocol subtree */
156
4.04k
  ti = proto_tree_add_item(tree, proto_sap_protocol, tvb, 0, -1, ENC_NA);
157
4.04k
  sap_protocol_tree = proto_item_add_subtree(ti, ett_sap_protocol);
158
159
  /* Add the length item */
160
4.04k
  proto_item_append_text(ti, ", Len: %u", length);
161
4.04k
  sap_protocol_length = proto_tree_add_item(sap_protocol_tree, hf_sap_protocol_length, tvb, 0, 4, ENC_BIG_ENDIAN);
162
163
  /* Add expert info in case of no match between the given length and the actual one */
164
4.04k
  if (tvb_reported_length(tvb) != length + 4) {
165
3.51k
    expert_add_info(pinfo, sap_protocol_length, &ei_sap_invalid_length);
166
3.51k
  }
167
168
  /* Add the payload subtree */
169
4.04k
  if (length > 0){
170
3.63k
    proto_tree_add_item(sap_protocol_tree, hf_sap_protocol_payload, tvb, 4, -1, ENC_NA);
171
3.63k
  }
172
173
  /* Check for NI_PING */
174
4.04k
  if ((length == 8)&&(tvb_strneql(tvb, 4, "NI_PING\00", 8) == 0)){
175
0
    col_set_str(pinfo->cinfo, COL_INFO, "Ping message");
176
177
0
    proto_item_append_text(ti, ", Ping message (keep-alive request)");
178
0
    proto_tree_add_item(sap_protocol_tree, hf_sap_protocol_ping, tvb, 4, -1, ENC_NA);
179
180
  /* Chek for NI_PONG */
181
4.04k
  } else if ((length == 8)&&(tvb_strneql(tvb, 4, "NI_PONG\00", 8) == 0)){
182
0
    col_set_str(pinfo->cinfo, COL_INFO, "Pong message");
183
0
    proto_item_append_text(ti, ", Pong message");
184
185
    /* We need to check if this is a keep-alive response, or it's part of
186
     * a SAP Router conversation and thus a route accepted message.
187
     */
188
0
    conversation = find_conversation_pinfo(pinfo, 0);
189
0
    if (conversation == NULL){
190
0
      col_append_str(pinfo->cinfo, COL_INFO, " (keep-alive response)");
191
0
      proto_item_append_text(ti, " (keep-alive response)");
192
0
      proto_tree_add_item(sap_protocol_tree, hf_sap_protocol_pong, tvb, 4, -1, ENC_NA);
193
194
0
    } else {
195
0
      col_append_str(pinfo->cinfo, COL_INFO, " (route accepted)");
196
0
      proto_item_append_text(ti, " (route accepted)");
197
198
      /* Call the SAP Router dissector */
199
0
      if (sap_router_handle){
200
        /* Create a new tvb buffer and call the dissector */
201
0
        next_tvb = tvb_new_subset_remaining(tvb, 4);
202
0
        call_dissector_only(sap_router_handle, next_tvb, pinfo, tree, NULL);
203
0
      }
204
0
    }
205
206
  /* Dissect the payload */
207
4.04k
  } else if (length > 0){
208
2.71k
    dissect_sap_protocol_payload(tvb, 4, pinfo, tree, pinfo->srcport, pinfo->destport);
209
2.71k
  }
210
211
  /* TODO: We need to return the *actual* length processed */
212
4.04k
  return (length);
213
4.04k
}
214
215
/*
216
 * Performs the TCP reassembling and dissects the packet.
217
 */
218
static int
219
dissect_sap_protocol(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
220
278
{
221
278
  tcp_dissect_pdus(tvb, pinfo, tree, global_sap_protocol_desegment, SAP_PROTOCOL_HEADER_LEN,
222
278
    get_sap_protocol_pdu_len, dissect_sap_protocol_message, data);
223
278
  return tvb_reported_length(tvb);
224
278
}
225
226
void
227
proto_register_sap_protocol(void)
228
14
{
229
14
  static hf_register_info hf[] = {
230
14
    { &hf_sap_protocol_length,
231
14
      { "Length", "sapni.length", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }},
232
14
    { &hf_sap_protocol_payload,
233
14
      { "Payload", "sapni.payload", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
234
14
    { &hf_sap_protocol_ping,
235
14
      { "Ping", "sapni.ping", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
236
14
    { &hf_sap_protocol_pong,
237
14
      { "Pong", "sapni.pong", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
238
14
  };
239
240
  /* Setup protocol subtree array */
241
14
  static int *ett[] = {
242
14
    &ett_sap_protocol
243
14
  };
244
245
  /* Register the expert info */
246
14
  static ei_register_info ei[] = {
247
14
    { &ei_sap_invalid_length, { "sapni.length.invalid", PI_MALFORMED, PI_WARN, "The reported length is incorrect", EXPFILL }},
248
14
  };
249
250
14
  module_t *sap_protocol_module;
251
14
  expert_module_t* sap_protocol_expert;
252
253
  /* Register the protocol */
254
14
  proto_sap_protocol = proto_register_protocol("SAP NI Protocol", "SAPNI", "sapni");
255
256
14
  proto_register_field_array(proto_sap_protocol, hf, array_length(hf));
257
14
  proto_register_subtree_array(ett, array_length(ett));
258
259
14
  sap_protocol_expert = expert_register_protocol(proto_sap_protocol);
260
14
  expert_register_field_array(sap_protocol_expert, ei, array_length(ei));
261
262
14
  register_dissector("sapni", dissect_sap_protocol, proto_sap_protocol);
263
264
  /* Sub dissector code */
265
14
  sub_dissectors_table = register_dissector_table("sapni.port", "SAP Protocol Port", proto_sap_protocol, FT_UINT16, BASE_DEC);
266
14
  heur_subdissector_list = register_heur_dissector_list_with_description("sapni", "SAP NI payload", proto_sap_protocol);
267
268
  /* Register the preferences */
269
14
  sap_protocol_module = prefs_register_protocol(proto_sap_protocol, proto_reg_handoff_sap_protocol);
270
271
14
  range_convert_str(wmem_epan_scope(), &global_sap_protocol_port_range, SAP_PROTOCOL_PORT_RANGE, MAX_TCP_PORT);
272
14
  prefs_register_range_preference(sap_protocol_module, "tcp_ports", "SAP NI Protocol TCP port numbers", "Port numbers used for SAP NI Protocol (default " SAP_PROTOCOL_PORT_RANGE ")", &global_sap_protocol_port_range, MAX_TCP_PORT);
273
274
14
  prefs_register_bool_preference(sap_protocol_module, "desegment", "Reassemble SAP NI Protocol messages spanning multiple TCP segments", "Whether the SAP NI Protocol dissector should reassemble messages spanning multiple TCP segments.", &global_sap_protocol_desegment);
275
14
}
276
277
/**
278
 * Helpers for dealing with the port range
279
 */
280
static void range_delete_callback (uint32_t port, void *ptr _U_)
281
0
{
282
0
  dissector_delete_uint("tcp.port", port, sap_protocol_handle);
283
0
}
284
285
static void range_add_callback (uint32_t port, void *ptr _U_)
286
28
{
287
28
  dissector_add_uint("tcp.port", port, sap_protocol_handle);
288
28
}
289
290
/**
291
 * Register Hand off for the SAP NI Protocol
292
 */
293
void
294
proto_reg_handoff_sap_protocol(void)
295
14
{
296
14
  static range_t *sap_protocol_port_range;
297
14
  static bool initialized = false;
298
299
14
  if (!initialized) {
300
14
    sap_protocol_handle = find_dissector("sapni");
301
14
    initialized = true;
302
14
  } else {
303
0
    range_foreach(sap_protocol_port_range, range_delete_callback, NULL);
304
0
    wmem_free(wmem_epan_scope(), sap_protocol_port_range);
305
0
  }
306
307
14
  sap_protocol_port_range = range_copy(wmem_epan_scope(), global_sap_protocol_port_range);
308
14
  range_foreach(sap_protocol_port_range, range_add_callback, NULL);
309
310
14
  sap_router_handle = find_dissector("saprouter");
311
312
14
}
313
314
/*
315
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
316
 *
317
 * Local variables:
318
 * c-basic-offset: 8
319
 * tab-width: 8
320
 * indent-tabs-mode: t
321
 * End:
322
 *
323
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
324
 * :indentSize=8:tabSize=8:noTabs=false:
325
 */