Coverage Report

Created: 2025-10-28 07:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ndpi/src/lib/protocols/slp.c
Line
Count
Source
1
/*
2
 * slp.c
3
 *
4
 * Copyright (C) 2023 - ntop.org
5
 *
6
 * This file is part of nDPI, an open source deep packet inspection
7
 * library based on the OpenDPI and PACE technology by ipoque GmbH
8
 *
9
 * nDPI is free software: you can redistribute it and/or modify
10
 * it under the terms of the GNU Lesser General Public License as published by
11
 * the Free Software Foundation, either version 3 of the License, or
12
 * (at your option) any later version.
13
 *
14
 * nDPI is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU Lesser General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU Lesser General Public License
20
 * along with nDPI.  If not, see <http://www.gnu.org/licenses/>.
21
 * 
22
 */
23
24
#include "ndpi_protocol_ids.h"
25
26
#define NDPI_CURRENT_PROTO NDPI_PROTOCOL_SERVICE_LOCATION
27
28
#include "ndpi_api.h"
29
#include "ndpi_private.h"
30
31
PACK_ON
32
struct slp_hdr_v1 {
33
  uint8_t version;
34
  uint8_t function;
35
  uint16_t length;
36
  uint8_t flags;
37
  uint8_t dialect;
38
  uint16_t lang_code;
39
  uint16_t encoding;
40
  uint16_t xid;
41
} PACK_OFF;
42
43
PACK_ON
44
struct slp_hdr_v2 {
45
  uint8_t version;
46
  uint8_t function_id;
47
  PACK_ON struct {
48
    uint16_t high;
49
    uint8_t low;
50
  } PACK_OFF length;
51
  uint16_t flags;
52
  PACK_ON struct {
53
    uint16_t high;
54
    uint8_t low;
55
  } PACK_OFF next_ext_offset;
56
  uint16_t xid;
57
  uint16_t lang_tag_length;
58
  uint16_t lang_tag;
59
} PACK_OFF;
60
61
PACK_ON
62
struct slp_url_entry {
63
  uint8_t reserved;
64
  uint16_t lifetime;
65
  uint16_t length;
66
  // char URL[length]
67
  // uint8_t num_auths;
68
} PACK_OFF;
69
70
enum function_id {
71
  FID_UNKNOWN     = 0,
72
  FID_SrvRqst     = 1,
73
  FID_SrvRply     = 2,
74
  FID_SrvReg      = 3,
75
  FID_SrvDeReg    = 4,
76
  FID_SrvAck      = 5,
77
  FID_AttrRqst    = 6,
78
  FID_AttrRply    = 7,
79
  FID_DAAdvert    = 8,
80
  FID_SrvTypeRqst = 9,
81
  FID_SrvTypeRply = 10,
82
  FID_MAX_v1      = 11,
83
  FID_SAAdvert    = 11, // Not available in version 1
84
  FID_MAX
85
};
86
87
static void ndpi_int_slp_add_connection(struct ndpi_detection_module_struct *ndpi_struct,
88
                                        struct ndpi_flow_struct *flow)
89
0
{
90
0
  NDPI_LOG_INFO(ndpi_struct, "found Service Location Protocol\n");
91
92
0
  ndpi_set_detected_protocol(ndpi_struct, flow,
93
0
                             NDPI_PROTOCOL_SERVICE_LOCATION, NDPI_PROTOCOL_UNKNOWN,
94
0
                             NDPI_CONFIDENCE_DPI);
95
0
}
96
97
static int slp_check_packet_length(struct ndpi_detection_module_struct *ndpi_struct,
98
                                   struct ndpi_flow_struct *flow,
99
                                   unsigned int packet_length)
100
94
{
101
94
  struct ndpi_packet_struct const * const packet = &ndpi_struct->packet;
102
103
94
  if (packet->payload_packet_len != packet_length) {
104
92
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
105
92
    return 1;
106
92
  }
107
108
2
  return 0;
109
94
}
110
111
static int slp_check_fid(struct ndpi_detection_module_struct *ndpi_struct,
112
                         struct ndpi_flow_struct *flow,
113
                         enum function_id fid, uint8_t slp_version)
114
2
{
115
2
  if (fid <= FID_UNKNOWN) {
116
1
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
117
1
    return 1;
118
1
  }
119
120
1
  switch (slp_version) {
121
1
    case 0x01:
122
1
      if (fid >= FID_MAX_v1) {
123
1
        NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
124
1
        return 1;
125
1
      }
126
0
      break;
127
0
    case 0x02:
128
0
      if (fid >= FID_MAX) {
129
0
        NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
130
0
        return 1;
131
0
      }
132
0
      break;
133
0
    default:
134
0
      NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
135
0
      return 1;
136
1
  }
137
138
0
  return 0;
139
1
}
140
141
static int slp_dissect_url_entries(struct ndpi_detection_module_struct *ndpi_struct,
142
                                   struct ndpi_flow_struct *flow,
143
                                   uint16_t url_entries_offset)
144
0
{
145
0
  struct ndpi_packet_struct const * const packet = &ndpi_struct->packet;
146
0
  struct slp_url_entry const *url_entry;
147
0
  uint16_t url_entries_count;
148
0
  size_t i;
149
150
0
  if (packet->payload_packet_len <= url_entries_offset + sizeof(uint16_t)) {
151
0
    return 1;
152
0
  }
153
0
  url_entries_count = ntohs(*(uint16_t *)&packet->payload[url_entries_offset]);
154
0
  url_entries_offset += sizeof(uint16_t);
155
156
0
  for (i = 0; i < ndpi_min(url_entries_count, NDPI_ARRAY_LENGTH(flow->protos.slp.url)); ++i) {
157
0
    if (packet->payload_packet_len < url_entries_offset + sizeof(*url_entry)) {
158
0
      return 1;
159
0
    }
160
0
    url_entry = (struct slp_url_entry *)&packet->payload[url_entries_offset];
161
0
    url_entries_offset += sizeof(*url_entry);
162
0
    uint16_t url_length = ntohs(url_entry->length);
163
164
0
    if (packet->payload_packet_len < url_entries_offset + url_length + 1 /* num_auths */) {
165
0
      return 1;
166
0
    }
167
0
    url_entries_offset += url_length;
168
169
0
    flow->protos.slp.url_count++;
170
0
    char const * const url = (char *)&url_entry->length + sizeof(url_entry->length);
171
0
    strncpy(flow->protos.slp.url[i], url, ndpi_min(url_length, NDPI_ARRAY_LENGTH(flow->protos.slp.url[i]) - 1));
172
0
    flow->protos.slp.url[i][NDPI_ARRAY_LENGTH(flow->protos.slp.url[i]) - 1] = '\0';
173
174
    // handle Authentication Blocks
175
0
    uint8_t num_auths = packet->payload[url_entries_offset++];
176
0
    size_t j;
177
0
    for (j = 0; j < num_auths; ++j) {      
178
0
      size_t auth_block_offset = url_entries_offset + 2;
179
0
      if (packet->payload_packet_len <= auth_block_offset + 2) {
180
0
        return 1;
181
0
      }
182
0
      uint16_t auth_block_length = ntohs(*(uint16_t *)&packet->payload[auth_block_offset]);
183
0
      if (packet->payload_packet_len < auth_block_offset + auth_block_length) {
184
0
        return 1;
185
0
      }
186
0
      url_entries_offset += auth_block_length;
187
0
    }
188
0
  }
189
190
0
  return flow->protos.slp.url_count == 0;
191
0
}
192
193
static void ndpi_search_slp_v1(struct ndpi_detection_module_struct *ndpi_struct,
194
                               struct ndpi_flow_struct *flow)
195
72
{
196
72
  struct ndpi_packet_struct const * const packet = &ndpi_struct->packet;
197
72
  struct slp_hdr_v1 const * const hdr = (struct slp_hdr_v1 *)&packet->payload[0];
198
199
72
  NDPI_LOG_DBG(ndpi_struct, "search Service Location Protocol v1\n");
200
201
72
  if (packet->payload_packet_len < sizeof(*hdr)) {
202
2
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
203
2
    return;
204
2
  }
205
206
70
  const unsigned int packet_length = ntohs(hdr->length);
207
70
  if (slp_check_packet_length(ndpi_struct, flow, packet_length) != 0)
208
68
    return;
209
210
2
  if (slp_check_fid(ndpi_struct, flow, hdr->function, hdr->version) != 0)
211
2
    return;
212
213
0
  ndpi_int_slp_add_connection(ndpi_struct, flow);
214
0
}
215
216
static int ndpi_search_slp_v2(struct ndpi_detection_module_struct *ndpi_struct,
217
                              struct ndpi_flow_struct *flow)
218
24
{
219
24
  struct ndpi_packet_struct const * const packet = &ndpi_struct->packet;
220
24
  struct slp_hdr_v2 const * const hdr = (struct slp_hdr_v2 *)&packet->payload[0];
221
222
24
  NDPI_LOG_DBG(ndpi_struct, "search Service Location Protocol v2\n");
223
224
24
  if (packet->payload_packet_len < sizeof(*hdr)) {
225
0
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
226
0
    return 1;
227
0
  }
228
229
24
  const unsigned int packet_length = (ntohs(hdr->length.high) << 8) | hdr->length.low;
230
24
  if (slp_check_packet_length(ndpi_struct, flow, packet_length) != 0)
231
24
    return 1;
232
233
0
  if (slp_check_fid(ndpi_struct, flow, hdr->function_id, hdr->version) != 0)
234
0
    return 1;
235
236
0
  ndpi_int_slp_add_connection(ndpi_struct, flow);
237
0
  return 0;
238
0
}
239
240
static void ndpi_dissect_slp_v2(struct ndpi_detection_module_struct *ndpi_struct,
241
                                struct ndpi_flow_struct *flow)
242
0
{
243
0
  struct ndpi_packet_struct const * const packet = &ndpi_struct->packet;
244
0
  struct slp_hdr_v2 const * const hdr = (struct slp_hdr_v2 *)&packet->payload[0];
245
0
  int url_offset = -1; // Can be either an offset to <URL String> or <URL Entry>
246
0
  int url_length_offset = -1; // length of <URL String>
247
0
  int url_entry_count_offset = -1; // amount of <URL Entry>'s
248
249
0
  switch (hdr->function_id) {
250
0
    case FID_SrvRply:
251
0
      url_entry_count_offset = 2;
252
0
      url_offset = 4;
253
0
      break;
254
0
    case FID_SrvReg:
255
0
      url_offset = 3; // contains always 1 <URL Entry>
256
0
      break;
257
0
    case FID_DAAdvert:
258
0
      url_length_offset = 6;
259
0
      url_offset = 8;
260
0
      break;
261
0
    case FID_SAAdvert:
262
0
      url_length_offset = 0;
263
0
      url_offset = 2;
264
0
      break;
265
0
    case FID_AttrRqst:
266
0
      url_length_offset = 4;
267
0
      url_offset = 6;
268
0
      break;
269
0
    case FID_SrvDeReg:
270
0
      url_offset = 7; // contains always 1 <URL Entry>
271
0
      break;
272
0
  }
273
274
0
  if (url_offset >= 0) {
275
0
    uint16_t url_length_or_count = 0;
276
277
0
    if (url_length_offset > 0 && packet->payload_packet_len > sizeof(*hdr) + url_length_offset + 2) {
278
      // <URL String>
279
0
      url_length_or_count = ntohs(*(uint16_t *)&packet->payload[sizeof(*hdr) + url_length_offset]);
280
0
      if (packet->payload_packet_len > sizeof(*hdr) + url_offset + 2 + url_length_or_count) {
281
0
        size_t len = ndpi_min(sizeof(flow->protos.slp.url[0]) - 1, url_length_or_count);
282
0
        flow->protos.slp.url_count = 1;
283
0
        strncpy(flow->protos.slp.url[0], (char *)&packet->payload[sizeof(*hdr) + url_offset + 2], len);
284
0
        flow->protos.slp.url[0][len] = 0;
285
0
      }
286
0
    } else if (url_entry_count_offset > 0 && packet->payload_packet_len > sizeof(*hdr) + url_entry_count_offset + 2) {
287
0
      if (slp_dissect_url_entries(ndpi_struct, flow, sizeof(*hdr) + url_entry_count_offset) != 0) {
288
0
        ndpi_set_risk(ndpi_struct, flow, NDPI_MALFORMED_PACKET, "Invalid URL entries");
289
0
      }
290
0
    } else if (packet->payload_packet_len > sizeof(*hdr) + url_offset + 2) {
291
0
      url_length_or_count = ntohs(*(uint16_t *)&packet->payload[sizeof(*hdr) + url_offset]); // FID_SrvReg or FID_SrvDeReg
292
0
      if (packet->payload_packet_len > sizeof(*hdr) + url_offset + 2 + url_length_or_count) {
293
0
        size_t len = ndpi_min(sizeof(flow->protos.slp.url[0]) - 1, url_length_or_count);
294
0
        flow->protos.slp.url_count = 1;
295
0
        strncpy(flow->protos.slp.url[0], (char *)&packet->payload[sizeof(*hdr) + url_offset + 2], len);
296
0
        flow->protos.slp.url[0][len] = 0;
297
0
      }
298
0
    }
299
0
  }
300
0
}
301
302
static void ndpi_search_slp(struct ndpi_detection_module_struct *ndpi_struct,
303
                            struct ndpi_flow_struct *flow)
304
1.38k
{
305
1.38k
  struct ndpi_packet_struct const * const packet = &ndpi_struct->packet;
306
307
1.38k
  NDPI_LOG_DBG(ndpi_struct, "search Service Location Protocol\n");
308
309
1.38k
  switch (packet->payload[0]) {
310
72
    case 0x01:
311
72
      ndpi_search_slp_v1(ndpi_struct, flow);
312
72
      break;
313
24
    case 0x02:
314
24
      if (ndpi_search_slp_v2(ndpi_struct, flow) == 0) {
315
0
        ndpi_dissect_slp_v2(ndpi_struct, flow);
316
0
      }
317
24
      break;
318
1.28k
    default:
319
1.28k
      NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
320
1.28k
      break;
321
1.38k
  }
322
1.38k
}
323
324
void init_slp_dissector(struct ndpi_detection_module_struct *ndpi_struct)
325
4.29k
{
326
4.29k
  register_dissector("Service_Location_Protocol", ndpi_struct,
327
4.29k
                     ndpi_search_slp,
328
4.29k
                     NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_OR_UDP_WITH_PAYLOAD_WITHOUT_RETRANSMISSION,
329
4.29k
                     1, NDPI_PROTOCOL_SERVICE_LOCATION);
330
4.29k
}