Coverage Report

Created: 2025-08-29 06:29

/src/ndpi/src/lib/protocols/http.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * http.c
3
 *
4
 * Copyright (C) 2011-25 - 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 <assert.h>
25
26
#include "ndpi_protocol_ids.h"
27
28
#define NDPI_CURRENT_PROTO NDPI_PROTOCOL_HTTP
29
30
#include "ndpi_api.h"
31
#include "ndpi_private.h"
32
33
static const char* binary_exec_file_mimes_e[] = { "exe", NULL };
34
static const char* binary_exec_file_mimes_j[] = { "java-vm", NULL };
35
static const char* binary_exec_file_mimes_v[] = { "vnd.ms-cab-compressed", "vnd.microsoft.portable-executable", NULL };
36
static const char* binary_exec_file_mimes_x[] = { "x-msdownload", "x-dosexec", NULL };
37
38
static const char* download_file_mimes_b[] = { "bz", "bz2", NULL };
39
static const char* download_file_mimes_o[] = { "octet-stream", NULL };
40
static const char* download_file_mimes_x[] = { "x-tar", "x-zip", "x-bzip", NULL };
41
42
9.48k
#define ATTACHMENT_LEN    3
43
static const char* binary_exec_file_ext[] = {
44
          "exe",
45
          "msi",
46
          "cab",
47
          NULL
48
};
49
50
static void ndpi_search_http_tcp(struct ndpi_detection_module_struct *ndpi_struct,
51
         struct ndpi_flow_struct *flow);
52
static void ndpi_check_http_header(struct ndpi_detection_module_struct *ndpi_struct,
53
           struct ndpi_flow_struct *flow);
54
55
/* *********************************************** */
56
57
4.85k
static char* forge_attempt_msg(struct ndpi_flow_struct *flow, char *msg, char *buf, u_int buf_len) {
58
4.85k
  if((flow->http.response_status_code >= 200) && (flow->http.response_status_code < 300))
59
3.83k
    return(msg);
60
1.01k
  else {
61
1.01k
    snprintf(buf, buf_len, "%s (attempt)", msg);
62
1.01k
    return(buf);
63
1.01k
  }
64
4.85k
}
65
66
/* *********************************************** */
67
68
static void ndpi_set_binary_data_transfer(struct ndpi_detection_module_struct *ndpi_struct, struct ndpi_flow_struct *flow,
69
3.96k
            char *msg) {
70
3.96k
  char buf[256];
71
72
3.96k
  ndpi_set_risk(ndpi_struct, flow, NDPI_BINARY_DATA_TRANSFER,
73
3.96k
    forge_attempt_msg(flow, msg, buf, sizeof(buf)));
74
3.96k
}
75
76
/* *********************************************** */
77
78
static void ndpi_set_binary_application_transfer(struct ndpi_detection_module_struct *ndpi_struct,
79
             struct ndpi_flow_struct *flow,
80
962
             char *msg) {
81
  /*
82
    Check known exceptions
83
    https://learn.microsoft.com/en-us/windows/privacy/windows-endpoints-1909-non-enterprise-editions
84
  */
85
962
  if(ends_with(ndpi_struct, (char*)flow->host_server_name, ".windowsupdate.com")
86
962
     || ends_with(ndpi_struct, (char*)flow->host_server_name, ".microsoft.com")
87
962
     || ends_with(ndpi_struct, (char*)flow->host_server_name, ".office365.com")
88
962
     || ends_with(ndpi_struct, (char*)flow->host_server_name, ".windows.com")
89
962
     )
90
71
    ;
91
891
  else {
92
891
    char buf[256];
93
94
891
    ndpi_set_risk(ndpi_struct, flow, NDPI_BINARY_APPLICATION_TRANSFER, forge_attempt_msg(flow, msg, buf, sizeof(buf)));
95
891
  }
96
962
 }
97
98
/* *********************************************** */
99
100
static void ndpi_analyze_content_signature(struct ndpi_detection_module_struct *ndpi_struct,
101
8.02k
             struct ndpi_flow_struct *flow) {
102
8.02k
  u_int8_t set_risk = 0;
103
8.02k
  const char *msg = NULL;
104
105
  /*
106
    NOTE: see also (ndpi_main.c)
107
    - ndpi_search_elf
108
    - ndpi_search_portable_executable
109
    - ndpi_search_shellscript
110
  */
111
112
8.02k
  if((flow->initial_binary_bytes_len >= 2) && (flow->initial_binary_bytes[0] == 0x4D) && (flow->initial_binary_bytes[1] == 0x5A))
113
122
    set_risk = 1, msg = "Found DOS/Windows Exe"; /* Win executable */
114
7.89k
  else if((flow->initial_binary_bytes_len >= 4) && (flow->initial_binary_bytes[0] == 0x7F) && (flow->initial_binary_bytes[1] == 'E')
115
7.89k
    && (flow->initial_binary_bytes[2] == 'L') && (flow->initial_binary_bytes[3] == 'F'))
116
13
    set_risk = 1, msg = "Found Linux Exe"; /* Linux ELF executable */
117
7.88k
  else if((flow->initial_binary_bytes_len >= 4) && (flow->initial_binary_bytes[0] == 0xCF) && (flow->initial_binary_bytes[1] == 0xFA)
118
7.88k
    && (flow->initial_binary_bytes[2] == 0xED) && (flow->initial_binary_bytes[3] == 0xFE))
119
6
    set_risk = 1, msg = "Found Linux Exe"; /* Linux executable */
120
7.87k
  else if((flow->initial_binary_bytes_len >= 3)
121
7.87k
    && (flow->initial_binary_bytes[0] == '#')
122
7.87k
    && (flow->initial_binary_bytes[1] == '!')
123
7.87k
    && (flow->initial_binary_bytes[2] == '/'))
124
6
    set_risk = 1, msg = "Found Unix Script"; /* Unix script (e.g. #!/bin/sh) */
125
7.87k
  else if(flow->initial_binary_bytes_len >= 8) {
126
7.37k
    u_int8_t exec_pattern[] = { 0x64, 0x65, 0x78, 0x0A, 0x30, 0x33, 0x35, 0x00 };
127
128
7.37k
    if(memcmp(flow->initial_binary_bytes, exec_pattern, 8) == 0)
129
55
      set_risk = 1, msg = "Found Android Exe"; /* Dalvik Executable (Android) */
130
7.37k
  }
131
132
8.02k
  if(set_risk)
133
202
    ndpi_set_binary_application_transfer(ndpi_struct, flow, (char*)msg);
134
8.02k
}
135
136
/* *********************************************** */
137
138
static int ndpi_search_http_tcp_again(struct ndpi_detection_module_struct *ndpi_struct,
139
455k
              struct ndpi_flow_struct *flow) {
140
455k
  struct ndpi_packet_struct *packet = &ndpi_struct->packet;
141
455k
  if(packet->payload_packet_len == 0 || packet->tcp_retransmission)
142
205k
    return 1;
143
144
249k
  ndpi_search_http_tcp(ndpi_struct, flow);
145
146
#ifdef HTTP_DEBUG
147
  printf("=> %s()\n", __FUNCTION__);
148
#endif
149
150
249k
  if(flow->extra_packets_func == NULL) {
151
    /* HTTP stuff completed */
152
153
    /* Loook for TLS over websocket */
154
15.2k
    if((ndpi_struct->cfg.tls_heuristics & NDPI_HEURISTICS_TLS_OBFUSCATED_HTTP) && /* Feature enabled */
155
15.2k
       (flow->host_server_name[0] != '\0' &&
156
13.0k
        flow->http.response_status_code != 0) && /* Bidirectional HTTP traffic */
157
15.2k
       flow->http.websocket) {
158
159
208
      switch_extra_dissection_to_tls_obfuscated_heur(ndpi_struct, flow);
160
208
      return(1);
161
208
    }
162
163
15.0k
    return(0); /* We are good now */
164
15.2k
  }
165
166
  /* Possibly more processing */
167
233k
  return(1);
168
249k
}
169
170
/* *********************************************** */
171
172
36.1k
static int ndpi_http_is_print(char c) {
173
36.1k
  if(ndpi_isprint(c) || (c == '\t') || (c == '\r') || (c == '\n'))
174
31.0k
    return(1);
175
5.05k
  else
176
5.05k
    return(0);
177
36.1k
}
178
179
/* *********************************************** */
180
181
static void ndpi_http_check_human_redeable_content(struct ndpi_detection_module_struct *ndpi_struct,
182
               struct ndpi_flow_struct *flow,
183
11.9k
               const u_int8_t *content, u_int16_t content_len) {
184
11.9k
  if(content_len >= 4) {
185
11.9k
    NDPI_LOG_DBG(ndpi_struct, " [len: %u] [%02X %02X %02X %02X][%c%c%c%c]", content_len,
186
11.9k
     content[0], content[1], content[2], content[3],
187
11.9k
     content[0], content[1], content[2], content[3]
188
11.9k
     );
189
190
11.9k
    if(ndpi_http_is_print(content[0]) && ndpi_http_is_print(content[1])
191
11.9k
       && ndpi_http_is_print(content[2]) && ndpi_http_is_print(content[3])) {
192
      /* OK */
193
6.92k
    } else {
194
      /* Looks bad: last resort check if it's gzipped [1F 8B 08 00] */
195
196
5.05k
      if((content[0] == 0x1F)
197
5.05k
   && (content[1] == 0x8B)
198
5.05k
   && (content[2] == 0x08)
199
5.05k
   && (content[3] == 0x00)) {
200
  /* Looks like compressed data */
201
2.60k
      } else {
202
2.44k
        if(is_flowrisk_info_enabled(ndpi_struct, NDPI_HTTP_SUSPICIOUS_CONTENT)) {
203
2.30k
    char str[32];
204
205
2.30k
    snprintf(str, sizeof(str), "Susp content %02X%02X%02X%02X",
206
2.30k
       content[0], content[1], content[2], content[3]);
207
2.30k
    ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_CONTENT, str);
208
2.30k
        } else {
209
147
          ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_CONTENT, NULL);
210
147
        }
211
2.44k
      }
212
5.05k
    }
213
11.9k
  }
214
11.9k
}
215
216
/* *********************************************** */
217
218
static void ndpi_validate_http_content(struct ndpi_detection_module_struct *ndpi_struct,
219
55.3k
               struct ndpi_flow_struct *flow) {
220
55.3k
  struct ndpi_packet_struct *packet = &ndpi_struct->packet;
221
55.3k
  const u_int8_t *double_ret = (const u_int8_t *)ndpi_strnstr((const char *)packet->payload, "\r\n\r\n", packet->payload_packet_len);
222
223
55.3k
  NDPI_LOG_DBG(ndpi_struct, "==>>> [len: %u] ", packet->payload_packet_len);
224
55.3k
  NDPI_LOG_DBG(ndpi_struct, "->> %.*s\n", packet->content_line.len, (const char *)packet->content_line.ptr);
225
226
55.3k
  if(double_ret) {
227
32.2k
    u_int len;
228
229
32.2k
    len = packet->payload_packet_len - (double_ret - packet->payload);
230
231
32.2k
    if(flow->http.is_form
232
32.2k
       || ndpi_strnstr((const char *)packet->content_line.ptr, "text/", packet->content_line.len)
233
32.2k
       || ndpi_strnstr((const char *)packet->content_line.ptr, "/json", packet->content_line.len)
234
32.2k
       ) {
235
      /* This is supposed to be a human-readeable text file */
236
13.3k
      packet->http_check_content = 1;
237
238
13.3k
      if(len >= 8 /* 4 chars for \r\n\r\n and at least 4 charts for content guess */) {
239
11.9k
  double_ret += 4;
240
11.9k
  len -= 4;
241
242
11.9k
  ndpi_http_check_human_redeable_content(ndpi_struct, flow, double_ret, len);
243
11.9k
  if (flow->skip_entropy_check == 0) {
244
11.9k
    flow->entropy = ndpi_entropy(double_ret, len);
245
11.9k
  }
246
11.9k
      }
247
13.3k
    }
248
249
    /* Final checks */
250
251
32.2k
    if(ndpi_isset_risk(flow, NDPI_BINARY_APPLICATION_TRANSFER)
252
32.2k
       && flow->http.user_agent && flow->http.content_type) {
253
186
      if(((strncmp((const char *)flow->http.user_agent, "Java/", 5) == 0))
254
186
   &&
255
186
   ((strcmp((const char *)flow->http.content_type, "application/java-vm") == 0))
256
186
   ) {
257
  /*
258
    Java downloads Java: Log4J:
259
    https://corelight.com/blog/detecting-log4j-exploits-via-zeek-when-java-downloads-java
260
  */
261
262
42
  ndpi_set_risk(ndpi_struct, flow, NDPI_POSSIBLE_EXPLOIT, "Suspicious Log4J");
263
42
      }
264
186
    }
265
266
32.2k
    NDPI_LOG_DBG(ndpi_struct, "\n");
267
32.2k
  }
268
269
55.3k
  if((flow->http.user_agent == NULL) || (flow->http.user_agent[0] == '\0'))
270
45.9k
    ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_USER_AGENT, "Empty or missing User-Agent");
271
55.3k
}
272
273
/* *********************************************** */
274
275
/* https://www.freeformatter.com/mime-types-list.html */
276
static ndpi_protocol_category_t ndpi_http_check_content(struct ndpi_detection_module_struct *ndpi_struct,
277
35.7k
              struct ndpi_flow_struct *flow) {
278
35.7k
  struct ndpi_packet_struct *packet = &ndpi_struct->packet;
279
280
35.7k
  if(packet->content_line.len > 0) {
281
35.7k
    u_int app_len = sizeof("application");
282
283
35.7k
    if(packet->content_line.len > app_len) {
284
14.4k
      const char *app     = (const char *)&packet->content_line.ptr[app_len];
285
14.4k
      u_int app_len_avail = packet->content_line.len-app_len;
286
287
14.4k
      if(strncasecmp(app, "mpeg", app_len_avail) == 0) {
288
83
  flow->category = NDPI_PROTOCOL_CATEGORY_STREAMING;
289
83
  return(flow->category);
290
14.3k
      } else {
291
14.3k
  if(app_len_avail > 3) {
292
12.1k
    const char** cmp_mimes = NULL;
293
12.1k
    bool found = false;
294
295
12.1k
    switch(app[0]) {
296
95
    case 'b': cmp_mimes = download_file_mimes_b; break;
297
4.25k
    case 'o': cmp_mimes = download_file_mimes_o; break;
298
1.69k
    case 'x': cmp_mimes = download_file_mimes_x; break;
299
12.1k
    }
300
301
12.1k
    if(cmp_mimes != NULL) {
302
6.03k
      u_int8_t i;
303
304
12.9k
      for(i = 0; cmp_mimes[i] != NULL; i++) {
305
9.50k
        if(strncasecmp(app, cmp_mimes[i], app_len_avail) == 0) {
306
2.63k
    char str[64];
307
308
2.63k
    flow->category = NDPI_PROTOCOL_CATEGORY_DOWNLOAD_FT;
309
2.63k
    NDPI_LOG_INFO(ndpi_struct, "found HTTP file transfer");
310
311
2.63k
    snprintf(str, sizeof(str), "Found binary mime %s", cmp_mimes[i]);
312
2.63k
    ndpi_set_binary_data_transfer(ndpi_struct, flow, str);
313
2.63k
    found = true;
314
2.63k
    break;
315
2.63k
        }
316
9.50k
      }
317
6.03k
    }
318
319
    /* ***************************************** */
320
321
12.1k
    if(!found) {
322
9.51k
      switch(app[0]) {
323
187
      case 'e': cmp_mimes = binary_exec_file_mimes_e; break;
324
2.41k
      case 'j': cmp_mimes = binary_exec_file_mimes_j; break;
325
546
      case 'v': cmp_mimes = binary_exec_file_mimes_v; break;
326
1.69k
      case 'x': cmp_mimes = binary_exec_file_mimes_x; break;
327
9.51k
      }
328
329
9.51k
      if(cmp_mimes != NULL) {
330
6.54k
        u_int8_t i;
331
332
15.4k
        for(i = 0; cmp_mimes[i] != NULL; i++) {
333
8.86k
    if(strncasecmp(app, cmp_mimes[i], app_len_avail) == 0) {
334
455
      char str[64];
335
336
455
      snprintf(str, sizeof(str), "Found mime exe %s", cmp_mimes[i]);
337
455
      flow->category = NDPI_PROTOCOL_CATEGORY_DOWNLOAD_FT;
338
455
      ndpi_set_binary_application_transfer(ndpi_struct, flow, str);
339
455
      NDPI_LOG_INFO(ndpi_struct, "Found executable HTTP transfer");
340
455
    }
341
8.86k
        }
342
6.54k
      }
343
9.51k
    }
344
12.1k
  }
345
14.3k
      }
346
14.4k
    }
347
348
    /* check for attachment */
349
35.6k
    if(packet->content_disposition_line.len > 0) {
350
2.73k
      u_int8_t attachment_len = sizeof("attachment; filename");
351
352
2.73k
      if(packet->content_disposition_line.len > attachment_len &&
353
2.73k
         strncmp((char *)packet->content_disposition_line.ptr, "attachment; filename", 20) == 0) {
354
1.80k
  u_int8_t filename_len = packet->content_disposition_line.len - attachment_len;
355
1.80k
  int i;
356
357
1.80k
  if(packet->content_disposition_line.ptr[attachment_len] == '\"') {
358
698
    if(packet->content_disposition_line.ptr[packet->content_disposition_line.len-1] != '\"') {
359
      //case: filename="file_name
360
199
      if(filename_len >= 2) {
361
129
        flow->http.filename = ndpi_malloc(filename_len);
362
129
        if(flow->http.filename != NULL) {
363
129
          strncpy(flow->http.filename, (char*)packet->content_disposition_line.ptr+attachment_len+1, filename_len-1);
364
129
          flow->http.filename[filename_len-1] = '\0';
365
129
        }
366
129
      }
367
199
    }
368
499
    else if(filename_len >= 2) {
369
      //case: filename="file_name"
370
443
      flow->http.filename = ndpi_malloc(filename_len-1);
371
372
443
      if(flow->http.filename != NULL) {
373
443
        strncpy(flow->http.filename, (char*)packet->content_disposition_line.ptr+attachment_len+1,
374
443
          filename_len-2);
375
443
        flow->http.filename[filename_len-2] = '\0';
376
443
      }
377
443
    }
378
1.10k
  } else {
379
    //case: filename=file_name
380
1.10k
    flow->http.filename = ndpi_malloc(filename_len+1);
381
382
1.10k
    if(flow->http.filename != NULL) {
383
1.10k
      strncpy(flow->http.filename, (char*)packet->content_disposition_line.ptr+attachment_len, filename_len);
384
1.10k
      flow->http.filename[filename_len] = '\0';
385
1.10k
    }
386
1.10k
  }
387
388
1.80k
  if(filename_len > ATTACHMENT_LEN) {
389
1.62k
    attachment_len += filename_len-ATTACHMENT_LEN-1;
390
391
1.62k
    if((attachment_len+ATTACHMENT_LEN) <= packet->content_disposition_line.len) {
392
1.62k
      char str[64];
393
394
5.75k
      for(i = 0; binary_exec_file_ext[i] != NULL; i++) {
395
        /* Use memcmp in case content-disposition contains binary data */
396
4.43k
        if(memcmp(&packet->content_disposition_line.ptr[attachment_len],
397
4.43k
      binary_exec_file_ext[i], ATTACHMENT_LEN) == 0) {
398
399
305
    snprintf(str, sizeof(str), "Found file extn %s", binary_exec_file_ext[i]);
400
305
    flow->category = NDPI_PROTOCOL_CATEGORY_DOWNLOAD_FT;
401
305
    ndpi_set_binary_application_transfer(ndpi_struct, flow, str);
402
305
    NDPI_LOG_INFO(ndpi_struct, "found executable HTTP transfer");
403
305
    return(flow->category);
404
305
        }
405
4.43k
      }
406
407
      /* No executable but just data transfer */
408
1.32k
      snprintf(str, sizeof(str), "File download %s",
409
1.32k
         flow->http.filename ? flow->http.filename : "");
410
1.32k
      ndpi_set_binary_data_transfer(ndpi_struct, flow, str);
411
1.32k
    }
412
1.62k
  }
413
1.80k
      }
414
2.73k
    }
415
416
35.3k
    switch(packet->content_line.ptr[0]) {
417
10.5k
    case 'a':
418
10.5k
      if(strncasecmp((const char *)packet->content_line.ptr, "audio",
419
10.5k
         ndpi_min(packet->content_line.len, 5)) == 0)
420
196
  flow->category = NDPI_PROTOCOL_CATEGORY_MEDIA;
421
10.5k
      break;
422
423
805
    case 'v':
424
805
      if(strncasecmp((const char *)packet->content_line.ptr, "video",
425
805
         ndpi_min(packet->content_line.len, 5)) == 0)
426
434
  flow->category = NDPI_PROTOCOL_CATEGORY_MEDIA;
427
805
      break;
428
35.3k
    }
429
35.3k
  }
430
431
35.3k
  return(flow->category);
432
35.7k
}
433
434
/* *********************************************** */
435
436
static void ndpi_int_http_add_connection(struct ndpi_detection_module_struct *ndpi_struct,
437
           struct ndpi_flow_struct *flow,
438
775k
           u_int16_t master_protocol) {
439
#ifdef HTTP_DEBUG
440
  printf("=> %s()\n", __FUNCTION__);
441
#endif
442
443
  /* Update the classification only if we don't already have master + app;
444
     for example don't change the protocols if we have already detected a
445
     sub-protocol via the (content-matched) subprotocols logic (i.e.
446
     MPEGDASH, SOAP, ....) */
447
775k
  if(flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN) {
448
767k
    NDPI_LOG_DBG2(ndpi_struct, "Master: %d\n", master_protocol);
449
767k
    if(flow->detected_protocol_stack[0] != master_protocol) {
450
576k
      NDPI_LOG_DBG2(ndpi_struct, "Previous master was different\n");
451
576k
      proto_stack_reset(&flow->protocol_stack);
452
576k
    }
453
767k
    ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_UNKNOWN,
454
767k
             master_protocol, NDPI_CONFIDENCE_DPI);
455
767k
  }
456
457
775k
  flow->max_extra_packets_to_check = 8;
458
775k
  flow->extra_packets_func = ndpi_search_http_tcp_again;
459
775k
}
460
461
/* ************************************************************* */
462
463
393k
static void setHttpUserAgent(struct ndpi_flow_struct *flow, char *ua) {
464
393k
  if(    !strcmp(ua, "Windows NT 5.0"))  ua = "Windows 2000";
465
393k
  else if(!strcmp(ua, "Windows NT 5.1"))  ua = "Windows XP";
466
392k
  else if(!strcmp(ua, "Windows NT 5.2"))  ua = "Windows Server 2003";
467
392k
  else if(!strcmp(ua, "Windows NT 6.0"))  ua = "Windows Vista";
468
392k
  else if(!strcmp(ua, "Windows NT 6.1"))  ua = "Windows 7";
469
385k
  else if(!strcmp(ua, "Windows NT 6.2"))  ua = "Windows 8";
470
385k
  else if(!strcmp(ua, "Windows NT 6.3"))  ua = "Windows 8.1";
471
384k
  else if(!strcmp(ua, "Windows NT 10.0")) ua = "Windows 10";
472
383k
  else if(!strcmp(ua, "Windows NT 11.0")) ua = "Windows 11";
473
474
  /* Good reference for future implementations:
475
   * https://github.com/ua-parser/uap-core/blob/master/regexes.yaml */
476
477
393k
  if(flow->http.detected_os == NULL)
478
390k
    flow->http.detected_os = ndpi_strdup(ua);
479
393k
}
480
481
/* ************************************************************* */
482
483
static void ndpi_http_parse_subprotocol(struct ndpi_detection_module_struct *ndpi_struct,
484
          struct ndpi_flow_struct *flow,
485
817k
          int hostname_just_set) {
486
817k
  u_int16_t master_protocol;
487
817k
  struct ndpi_packet_struct *packet = &ndpi_struct->packet;
488
489
817k
  if(!ndpi_struct->cfg.http_subclassification_enabled) {
490
95
    NDPI_LOG_DBG2(ndpi_struct, "Skip sub-protocol check because subclassification is disabled\n");
491
95
    return;
492
95
  }
493
494
817k
  master_protocol = NDPI_PROTOCOL_HTTP;
495
817k
  if(flow->detected_protocol_stack[1] != NDPI_PROTOCOL_UNKNOWN)
496
15.6k
    master_protocol = flow->detected_protocol_stack[1];
497
801k
  else if(flow->detected_protocol_stack[0] == NDPI_PROTOCOL_HTTP_CONNECT ||
498
801k
          flow->detected_protocol_stack[0] == NDPI_PROTOCOL_HTTP_PROXY)
499
2.97k
    master_protocol = flow->detected_protocol_stack[0];
500
501
817k
  if(packet->server_line.len > 7 &&
502
817k
     strncmp((const char *)packet->server_line.ptr, "ntopng ", 7) == 0) {
503
965
    ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_NTOP, NDPI_PROTOCOL_HTTP, NDPI_CONFIDENCE_DPI);
504
965
    ndpi_unset_risk(ndpi_struct, flow, NDPI_KNOWN_PROTOCOL_ON_NON_STANDARD_PORT);
505
965
  }
506
507
  /* Matching on Content-Type.
508
      OCSP:  application/ocsp-request, application/ocsp-response
509
  */
510
  /* We overwrite any previous sub-classification (example: via hostname) */
511
817k
  if(packet->content_line.len > 17 &&
512
817k
     strncmp((const char *)packet->content_line.ptr, "application/ocsp-", 17) == 0) {
513
938
    NDPI_LOG_DBG2(ndpi_struct, "Found OCSP\n");
514
938
    ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_OCSP, master_protocol, NDPI_CONFIDENCE_DPI);
515
938
  }
516
517
  /* HTTP Live Streaming */
518
817k
  if (packet->content_line.len > 28 &&
519
817k
      (strncmp((const char *)packet->content_line.ptr, "application/vnd.apple.mpegurl", 29) == 0 ||
520
25.4k
      strncmp((const char *)packet->content_line.ptr, "application/x-mpegURL", 21) == 0 ||
521
25.4k
      strncmp((const char *)packet->content_line.ptr, "application/x-mpegurl", 21) == 0)) {
522
539
    NDPI_LOG_DBG2(ndpi_struct, "Found HLS\n");
523
539
    ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_HLS, master_protocol, NDPI_CONFIDENCE_DPI);
524
539
  }
525
526
817k
  if((flow->http.method == NDPI_HTTP_METHOD_RPC_CONNECT) ||
527
817k
     (flow->http.method == NDPI_HTTP_METHOD_RPC_IN_DATA) ||
528
817k
     (flow->http.method == NDPI_HTTP_METHOD_RPC_OUT_DATA)) {
529
445
    ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_MS_RPCH, master_protocol, NDPI_CONFIDENCE_DPI);
530
445
  }
531
532
817k
  switch (flow->http.method) {
533
433
    case NDPI_HTTP_METHOD_MKCOL:
534
736
    case NDPI_HTTP_METHOD_MOVE:
535
991
    case NDPI_HTTP_METHOD_COPY:
536
1.21k
    case NDPI_HTTP_METHOD_LOCK:
537
1.49k
    case NDPI_HTTP_METHOD_UNLOCK:
538
1.93k
    case NDPI_HTTP_METHOD_PROPFIND:
539
2.20k
    case NDPI_HTTP_METHOD_PROPPATCH:
540
2.20k
      ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_WEBDAV, master_protocol, NDPI_CONFIDENCE_DPI);
541
2.20k
      break;
542
814k
    default:
543
814k
      break;
544
817k
  }
545
546
817k
  if(flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN &&
547
817k
     hostname_just_set && flow->host_server_name[0] != '\0') {
548
430k
    ndpi_match_hostname_protocol(ndpi_struct, flow,
549
430k
         master_protocol,
550
430k
         flow->host_server_name,
551
430k
         strlen(flow->host_server_name));
552
430k
  }
553
554
817k
  if(flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN &&
555
817k
     packet->http_origin.len > 0) {
556
5.87k
    ndpi_protocol_match_result ret_match;
557
5.87k
    char *ptr, *origin_hostname;
558
5.87k
    size_t origin_hostname_len;
559
560
    /* Origin syntax:
561
        Origin: null
562
        Origin: <scheme>://<hostname>
563
        Origin: <scheme>://<hostname>:<port>
564
    Try extracting hostname */
565
566
5.87k
    ptr = ndpi_strnstr((const char *)packet->http_origin.ptr, "://", packet->http_origin.len);
567
5.87k
    if(ptr) {
568
3.72k
      origin_hostname = ptr + 3;
569
3.72k
      origin_hostname_len = packet->http_origin.len - (ptr - (char *)packet->http_origin.ptr) - 3;
570
3.72k
      ptr = ndpi_strnstr(origin_hostname, ":", origin_hostname_len);
571
3.72k
      if(ptr) {
572
1.29k
        origin_hostname_len = ptr - origin_hostname;
573
1.29k
      }
574
3.72k
      NDPI_LOG_DBG2(ndpi_struct, "Origin: [%.*s] -> [%.*s]\n", packet->http_origin.len, packet->http_origin.ptr,
575
3.72k
        (int)origin_hostname_len, origin_hostname);
576
      /* We already checked hostname...*/
577
3.72k
      if(strncmp(origin_hostname, flow->host_server_name, origin_hostname_len) != 0) {
578
3.13k
        ndpi_match_host_subprotocol(ndpi_struct, flow,
579
3.13k
            origin_hostname,
580
3.13k
            origin_hostname_len,
581
3.13k
            &ret_match,
582
3.13k
            master_protocol, 1);
583
3.13k
      }
584
3.72k
    }
585
5.87k
  }
586
587
817k
  if(flow->http.url
588
817k
     && (
589
493k
       ends_with(ndpi_struct, (char*)flow->http.url, "/generate_204")
590
493k
       || ends_with(ndpi_struct, (char*)flow->http.url, "/generate204")
591
493k
       )
592
817k
    ) {
593
1.14k
    flow->category = NDPI_PROTOCOL_CATEGORY_CONNECTIVITY_CHECK;
594
1.14k
    return;
595
1.14k
  }
596
597
816k
  if(flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN &&
598
816k
     flow->http.url &&
599
816k
     ((strstr(flow->http.url, ":8080/downloading?n=0.") != NULL) ||
600
467k
      (strstr(flow->http.url, ":8080/upload?n=0.") != NULL))) {
601
    /* This looks like Ookla speedtest */
602
1.38k
    ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_OOKLA, master_protocol, NDPI_CONFIDENCE_DPI);
603
1.38k
    ookla_add_to_cache(ndpi_struct, flow);
604
1.38k
  }
605
606
816k
  if(flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN &&
607
816k
     flow->http.url != NULL &&
608
816k
     strstr(flow->http.url, "micloud.xiaomi.net") != NULL) {
609
376
    ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_XIAOMI, master_protocol, NDPI_CONFIDENCE_DPI);
610
376
  }
611
612
816k
  if(flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN &&
613
816k
     packet->referer_line.len > 0 &&
614
816k
     ndpi_strnstr((const char *)packet->referer_line.ptr, "www.speedtest.net", packet->referer_line.len)) {
615
239
    ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_OOKLA, master_protocol, NDPI_CONFIDENCE_DPI);
616
239
    ookla_add_to_cache(ndpi_struct, flow);
617
239
  }
618
619
  /* WindowsUpdate over some kind of CDN */
620
816k
  if(flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN &&
621
816k
     flow->http.user_agent && flow->http.url &&
622
816k
     (strstr(flow->http.url, "delivery.mp.microsoft.com/") ||
623
366k
      strstr(flow->http.url, "download.windowsupdate.com/")) &&
624
816k
     strstr(flow->http.user_agent, "Microsoft-Delivery-Optimization/") &&
625
816k
     ndpi_isset_risk(flow, NDPI_NUMERIC_IP_HOST)) {
626
48
    ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_WINDOWS_UPDATE, master_protocol, NDPI_CONFIDENCE_DPI);
627
48
  }
628
629
816k
  if(flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN &&
630
816k
     packet->payload_packet_len >= 23 &&
631
816k
     memcmp(packet->payload, "<policy-file-request/>", 23) == 0) {
632
    /*
633
      <policy-file-request/>
634
      <cross-domain-policy>
635
      <allow-access-from domain="*.ookla.com" to-ports="8080"/>
636
      <allow-access-from domain="*.speedtest.net" to-ports="8080"/>
637
      </cross-domain-policy>
638
     */
639
34
    ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_OOKLA, master_protocol, NDPI_CONFIDENCE_DPI);
640
34
    ookla_add_to_cache(ndpi_struct, flow);
641
34
  }
642
643
816k
  if ((flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN) &&
644
816k
      flow->http.user_agent && strstr(flow->http.user_agent, "MSRPC")) {
645
304
    ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_MS_RPCH, master_protocol, NDPI_CONFIDENCE_DPI);
646
304
  }
647
648
816k
  if ((flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN) &&
649
816k
      flow->http.user_agent && strstr(flow->http.user_agent, "Valve/Steam HTTP Client")) {
650
162
    ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_STEAM, master_protocol, NDPI_CONFIDENCE_DPI);
651
162
  }
652
653
816k
  if ((flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN) &&
654
816k
      flow->http.user_agent && strstr(flow->http.user_agent, "AirControl Agent v1.0")) {
655
122
    ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_UBNTAC2, master_protocol, NDPI_CONFIDENCE_DPI);
656
122
  }
657
658
816k
  if ((flow->detected_protocol_stack[1] == NDPI_PROTOCOL_UNKNOWN) &&
659
816k
      flow->http.user_agent && strstr(flow->http.user_agent, "gtk-gnutella")) {
660
263
    ndpi_set_detected_protocol(ndpi_struct, flow, NDPI_PROTOCOL_GNUTELLA, master_protocol, NDPI_CONFIDENCE_DPI);
661
263
  }
662
663
816k
  if(flow->http.request_header_observed) {
664
799k
    if(flow->http.first_payload_after_header_observed == 0) {
665
      /* Skip the last part of the HTTP request */
666
564k
      flow->http.first_payload_after_header_observed = 1;
667
564k
    } else if(flow->http.is_form && (packet->payload_packet_len > 0) &&
668
235k
              (ndpi_struct->cfg.http_username_enabled || ndpi_struct->cfg.http_password_enabled)) {
669
      /* Response payload */
670
5.14k
      char *dup = ndpi_strndup((const char *)packet->payload, packet->payload_packet_len);
671
672
5.14k
      if(dup) {
673
5.14k
  char *key, *value, *tmp;
674
675
5.14k
  key = strtok_r(dup, "=", &tmp);
676
677
17.1k
  while((key != NULL)
678
17.1k
        && ((flow->http.username == NULL) || (flow->http.password == NULL))) {
679
13.2k
    value = strtok_r(NULL, "&", &tmp);
680
681
13.2k
    if(!value)
682
1.28k
      break;
683
684
12.0k
    if((strcmp(key, "user") == 0) || (strcmp(key, "username") == 0)) {
685
192
      if(!flow->http.username && ndpi_struct->cfg.http_username_enabled) flow->http.username = ndpi_strdup(value);
686
11.8k
    } else if((strcmp(key, "pwd") == 0) || (strcmp(key, "password") == 0)) {
687
642
      if(!flow->http.password && ndpi_struct->cfg.http_password_enabled) flow->http.password = ndpi_strdup(value);
688
642
      ndpi_set_risk(ndpi_struct, flow, NDPI_CLEAR_TEXT_CREDENTIALS, "Found password");
689
642
    }
690
691
12.0k
    key = strtok_r(NULL, "=", &tmp);
692
12.0k
  }
693
694
5.14k
  ndpi_free(dup);
695
5.14k
      }
696
5.14k
    }
697
799k
  }
698
816k
}
699
700
/* ************************************************************* */
701
702
static void ndpi_check_user_agent(struct ndpi_detection_module_struct *ndpi_struct,
703
                                  struct ndpi_flow_struct *flow,
704
514k
          char const *ua, size_t ua_len) {
705
514k
  char *double_slash;
706
707
514k
  if((!ua) || (ua[0] == '\0'))
708
1.65k
    return;
709
710
512k
  if (ua_len > 12)
711
503k
  {
712
503k
    size_t i, upper_case_count = 0;
713
714
3.67M
    for (i = 0; i < ua_len; ++i)
715
3.67M
    {
716
      /*
717
       * We assume at least one non alpha char.
718
       * e.g. ' ', '-' or ';' ...
719
       */
720
3.67M
      if (ndpi_isalpha(ua[i]) == 0)
721
503k
      {
722
503k
        break;
723
503k
      }
724
3.16M
      if (isupper((unsigned char)ua[i]) != 0)
725
483k
      {
726
483k
        upper_case_count++;
727
483k
      }
728
3.16M
    }
729
730
503k
    if (i == ua_len) {
731
455
      float upper_case_ratio = (float)upper_case_count / (float)ua_len;
732
733
455
      if (upper_case_ratio >= 0.2f) {
734
239
        if(is_flowrisk_info_enabled(ndpi_struct, NDPI_HTTP_SUSPICIOUS_USER_AGENT)) {
735
209
          char str[64];
736
737
209
    snprintf(str, sizeof(str), "UA %s", ua);
738
209
          ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_USER_AGENT, str);
739
209
        } else {
740
30
          ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_USER_AGENT, NULL);
741
30
        }
742
239
      }
743
455
    }
744
503k
  }
745
746
512k
  if((!strncmp(ua, "<?", 2))
747
512k
     || strchr(ua, '$')
748
512k
     ) {
749
4.89k
    if(is_flowrisk_info_enabled(ndpi_struct, NDPI_HTTP_SUSPICIOUS_USER_AGENT)) {
750
2.99k
      char str[64];
751
752
2.99k
      snprintf(str, sizeof(str), "UA %s", ua);
753
2.99k
      ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_USER_AGENT, str);
754
2.99k
    } else {
755
1.90k
      ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_USER_AGENT, NULL);
756
1.90k
    }
757
4.89k
  }
758
759
512k
  if((double_slash = strstr(ua, "://")) != NULL) {
760
3.76k
    if(double_slash != ua) /* We're not at the beginning of the user agent */{
761
3.55k
      if((double_slash[-1] != 'p') /* http:// */
762
3.55k
   && (double_slash[-1] != 's') /* https:// */) {
763
841
        if(is_flowrisk_info_enabled(ndpi_struct, NDPI_HTTP_SUSPICIOUS_USER_AGENT)) {
764
573
          char str[64];
765
766
573
    snprintf(str, sizeof(str), "UA %s", ua);
767
573
    ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_USER_AGENT, str);
768
573
        } else {
769
268
          ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_USER_AGENT, NULL);
770
268
        }
771
841
      }
772
3.55k
    }
773
3.76k
  }
774
775
  /* no else */
776
512k
  if(!strncmp(ua, "jndi:ldap://", 12)) /* Log4J */ {
777
962
    ndpi_set_risk(ndpi_struct, flow, NDPI_POSSIBLE_EXPLOIT, "Suspicious Log4J");
778
511k
  } else if(
779
511k
    (ua_len < 4)      /* Too short */
780
511k
    || (ua_len > 256) /* Too long  */
781
511k
    || (!strncmp(ua, "test", 4))
782
511k
    || strchr(ua, '{')
783
511k
    || strchr(ua, '}')
784
511k
    ) {
785
56.6k
    ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_USER_AGENT, "Suspicious Log4J");
786
56.6k
  }
787
788
  /*
789
    Mozilla/5.0 (compatible; YandexBot/3.0; +http://yandex.com/bots)
790
    Amazon-Route53-Health-Check-Service (ref 68784dad-be98-49e4-a63c-9fbbe2816d7c; report http://amzn.to/1vsZADi)
791
    Anonymous Crawler/1.0 (Webcrawler developed with StormCrawler; http://example.com/; webcrawler@example.com)
792
   */
793
512k
  if((strstr(ua, "+http:") != NULL)
794
512k
     || (strstr(ua, " http:") != NULL)
795
512k
     || ndpi_strncasestr(ua, "Crawler", ua_len)
796
512k
     || ndpi_strncasestr(ua, "Bot", ua_len) /* bot/robot */
797
512k
     ) {
798
2.17k
    if(is_flowrisk_info_enabled(ndpi_struct, NDPI_HTTP_CRAWLER_BOT)) {
799
1.73k
      char str[64];
800
801
1.73k
      snprintf(str, sizeof(str), "UA %s", ua);
802
803
1.73k
      ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_CRAWLER_BOT, str);
804
1.73k
    } else {
805
441
      ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_CRAWLER_BOT, NULL);
806
441
    }
807
2.17k
  }
808
512k
}
809
810
/* ************************************************************* */
811
812
static void http_process_user_agent(struct ndpi_detection_module_struct *ndpi_struct,
813
                                    struct ndpi_flow_struct *flow,
814
518k
                                    const u_int8_t *ua_ptr, u_int16_t ua_ptr_len) {
815
  /**
816
      Format examples:
817
      Mozilla/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit/531.21.10 (KHTML, like Gecko) ....
818
      Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:54.0) Gecko/20100101 Firefox/54.0
819
   */
820
518k
  if(ua_ptr_len > 7) {
821
514k
    char ua[256];
822
514k
    u_int mlen = ndpi_min(ua_ptr_len, sizeof(ua)-1);
823
824
514k
    strncpy(ua, (const char *)ua_ptr, mlen);
825
514k
    ua[mlen] = '\0';
826
827
514k
    if(strncmp(ua, "Mozilla", 7) == 0) {
828
408k
      char *parent = strchr(ua, '(');
829
830
408k
      if(parent) {
831
394k
  char *token, *end;
832
833
394k
  parent++;
834
394k
  end = strchr(parent, ')');
835
394k
  if(end) end[0] = '\0';
836
837
394k
  token = strsep(&parent, ";");
838
394k
  if(token) {
839
394k
    if((strcmp(token, "X11") == 0)
840
394k
       || (strcmp(token, "compatible") == 0)
841
394k
       || (strcmp(token, "Linux") == 0)
842
394k
       || (strcmp(token, "Macintosh") == 0)
843
394k
       ) {
844
29.5k
      token = strsep(&parent, ";");
845
29.5k
      if(token && (token[0] == ' ')) token++; /* Skip space */
846
847
29.5k
      if(token
848
29.5k
         && ((strcmp(token, "U") == 0)
849
29.3k
       || (strncmp(token, "MSIE", 4) == 0))) {
850
4.12k
        token = strsep(&parent, ";");
851
4.12k
        if(token && (token[0] == ' ')) token++; /* Skip space */
852
853
4.12k
              if(token && (strncmp(token, "Update", 6)  == 0)) {
854
394
                token = strsep(&parent, ";");
855
856
394
                if(token && (token[0] == ' ')) token++; /* Skip space */
857
858
394
                if(token && (strncmp(token, "AOL", 3)  == 0)) {
859
860
34
                  token = strsep(&parent, ";");
861
34
                  if(token && (token[0] == ' ')) token++; /* Skip space */
862
34
                }
863
394
              }
864
4.12k
            }
865
29.5k
          }
866
867
394k
          if(token)
868
393k
            setHttpUserAgent(flow, token);
869
394k
  }
870
394k
      }
871
408k
    }
872
514k
  }
873
874
518k
  if(ndpi_user_agent_set(flow, ua_ptr, ua_ptr_len) != NULL) {
875
514k
    ndpi_unset_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_USER_AGENT);
876
514k
    ndpi_check_user_agent(ndpi_struct, flow, flow->http.user_agent, ua_ptr_len);
877
514k
  } else {
878
4.12k
    NDPI_LOG_DBG2(ndpi_struct, "Could not set HTTP user agent (already set?)\n");
879
4.12k
  }
880
881
518k
  NDPI_LOG_DBG2(ndpi_struct, "User Agent Type line found %.*s\n",
882
518k
    ua_ptr_len, ua_ptr);
883
518k
}
884
885
/* ************************************************************* */
886
887
static void ndpi_check_numeric_ip(struct ndpi_detection_module_struct *ndpi_struct,
888
                                  struct ndpi_flow_struct *flow,
889
378k
          char *ip, u_int ip_len) {
890
378k
  char buf[22], *double_dot;
891
378k
  struct in_addr ip_addr;
892
893
378k
  strncpy(buf, ip, ip_len);
894
378k
  buf[ip_len] = '\0';
895
896
378k
  if((double_dot = strchr(buf, ':')) != NULL)
897
3.08k
    double_dot[0] = '\0';
898
899
378k
  ip_addr.s_addr = inet_addr(buf);
900
378k
  if(strcmp(inet_ntoa(ip_addr), buf) == 0) {
901
344k
    if(is_flowrisk_info_enabled(ndpi_struct, NDPI_NUMERIC_IP_HOST)) {
902
244k
      char str[64];
903
904
244k
      snprintf(str, sizeof(str), "Found host %s", buf);
905
244k
      ndpi_set_risk(ndpi_struct, flow, NDPI_NUMERIC_IP_HOST, str);
906
244k
    } else {
907
100k
      ndpi_set_risk(ndpi_struct, flow, NDPI_NUMERIC_IP_HOST, NULL);
908
100k
    }
909
344k
  }
910
378k
}
911
912
/* ************************************************************* */
913
914
static void ndpi_check_http_url(struct ndpi_detection_module_struct *ndpi_struct,
915
                                struct ndpi_flow_struct *flow,
916
477k
        char *url) {
917
477k
  char msg[512];
918
477k
  ndpi_risk_enum r;
919
  
920
477k
  if(strstr(url, "<php>") != NULL /* PHP code in the URL */) {
921
168
    r = NDPI_URL_POSSIBLE_RCE_INJECTION;
922
168
    snprintf(msg, sizeof(msg), "PHP code in URL [%s]", url);
923
477k
  } else if(strncmp(url, "/shell?", 7) == 0) {
924
730
    r = NDPI_URL_POSSIBLE_RCE_INJECTION;
925
730
    snprintf(msg, sizeof(msg), "Possible WebShell detected [%s]", url);
926
476k
  } else if(strncmp(url, "/.", 2) == 0) {
927
1.80k
    r = NDPI_POSSIBLE_EXPLOIT;
928
1.80k
    snprintf(msg, sizeof(msg), "URL starting with dot [%s]", url);
929
474k
  } else {
930
474k
    r = ndpi_validate_url(ndpi_struct, flow, url);
931
474k
    return;
932
474k
  }
933
  
934
2.70k
  ndpi_set_risk(ndpi_struct, flow, r, msg);
935
2.70k
}
936
937
/* ************************************************************* */
938
939
4.57k
#define MIN_APACHE_VERSION 2004000 /* 2.4.X  [https://endoflife.date/apache] */
940
2.48k
#define MIN_NGINX_VERSION  1022000 /* 1.22.0 [https://endoflife.date/nginx]  */
941
942
static void ndpi_check_http_server(struct ndpi_detection_module_struct *ndpi_struct,
943
                                   struct ndpi_flow_struct *flow,
944
41.7k
           const char *server, u_int server_len) {
945
41.7k
  if(server[0] != '\0') {
946
41.5k
    if(server_len > 7) {
947
30.8k
      u_int off, i;
948
949
30.8k
      if((strncasecmp(server, "Apache/", off = 7) == 0) /* X.X.X */
950
30.8k
   || (strncasecmp(server, "nginx/", off = 6) == 0) /* X.X.X */) {
951
8.50k
  u_int j, a, b, c;
952
8.50k
  char buf[16] = { '\0' };
953
954
56.5k
  for(i=off, j=0; (i<server_len) && (j<sizeof(buf)-1)
955
56.5k
        && (ndpi_isdigit(server[i]) || (server[i] == '.')); i++)
956
48.0k
    buf[j++] = server[i];
957
958
8.50k
  if(sscanf(buf, "%d.%d.%d", &a, &b, &c) == 3) {
959
7.06k
    u_int32_t version = (a * 1000000) + (b * 1000) + c;
960
7.06k
    char msg[64];
961
962
7.06k
    if((off == 7) && (version < MIN_APACHE_VERSION)) {
963
1.45k
      if(is_flowrisk_info_enabled(ndpi_struct, NDPI_HTTP_OBSOLETE_SERVER)) {
964
1.39k
        snprintf(msg, sizeof(msg), "Obsolete Apache server %s", buf);
965
1.39k
        ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_OBSOLETE_SERVER, msg);
966
1.39k
      } else {
967
60
        ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_OBSOLETE_SERVER, NULL);
968
60
      }
969
5.61k
    } else if((off == 6) && (version < MIN_NGINX_VERSION)) {
970
2.20k
      if(is_flowrisk_info_enabled(ndpi_struct, NDPI_HTTP_OBSOLETE_SERVER)) {
971
1.98k
        snprintf(msg, sizeof(msg), "Obsolete nginx server %s", buf);
972
1.98k
        ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_OBSOLETE_SERVER, msg);
973
1.98k
      } else {
974
223
        ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_OBSOLETE_SERVER, NULL);
975
223
      }
976
2.20k
    }
977
7.06k
  }
978
8.50k
      }
979
980
      /* Check server content */
981
588k
      for(i=0; i<server_len; i++) {
982
563k
  if(!ndpi_isprint(server[i])) {
983
5.88k
    char msg[64];
984
985
5.88k
    snprintf(msg, sizeof(msg), "Suspicious Agent [%.*s]", server_len, server);
986
987
5.88k
    ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_HEADER, msg);
988
5.88k
    break;
989
5.88k
  }
990
563k
      }
991
30.8k
    }
992
41.5k
  }
993
41.7k
}
994
995
/* ************************************************************* */
996
997
/**
998
   NOTE
999
   ndpi_parse_packet_line_info is in ndpi_main.c
1000
*/
1001
static void check_content_type_and_change_protocol(struct ndpi_detection_module_struct *ndpi_struct,
1002
817k
               struct ndpi_flow_struct *flow) {
1003
817k
  struct ndpi_packet_struct *packet = &ndpi_struct->packet;
1004
817k
  u_int len;
1005
817k
  int hostname_just_set = 0;
1006
1007
817k
  if((flow->http.url == NULL)
1008
817k
     && (packet->http_url_name.len > 0)
1009
817k
     && (packet->host_line.len > 0)) {
1010
477k
    int len = packet->http_url_name.len + packet->host_line.len + 1;
1011
1012
477k
    if(ndpi_isdigit(packet->host_line.ptr[0])
1013
477k
       && (packet->host_line.len < 21))
1014
378k
      ndpi_check_numeric_ip(ndpi_struct, flow, (char*)packet->host_line.ptr, packet->host_line.len);
1015
1016
477k
    flow->http.url = ndpi_malloc(len);
1017
477k
    if(flow->http.url) {
1018
477k
      u_int offset = 0, host_end = 0;
1019
1020
477k
      if(flow->detected_protocol_stack[0] == NDPI_PROTOCOL_HTTP_CONNECT) {
1021
236
  strncpy(flow->http.url, (char*)packet->http_url_name.ptr,
1022
236
    packet->http_url_name.len);
1023
1024
236
  flow->http.url[packet->http_url_name.len] = '\0';
1025
477k
      } else {
1026
  /* Check if we pass through a proxy (usually there is also the Via: ... header) */
1027
477k
  if(strncmp((char*)packet->http_url_name.ptr, "http://", 7) != 0) {
1028
475k
    strncpy(flow->http.url, (char*)packet->host_line.ptr, offset = packet->host_line.len);
1029
475k
    host_end = packet->host_line.len;
1030
475k
  }
1031
1032
477k
  if((packet->host_line.len == packet->http_url_name.len)
1033
477k
     && (strncmp((char*)packet->host_line.ptr,
1034
33.8k
           (char*)packet->http_url_name.ptr, packet->http_url_name.len) == 0))
1035
49
    ;
1036
477k
  else {
1037
477k
    strncpy(&flow->http.url[offset], (char*)packet->http_url_name.ptr,
1038
477k
      packet->http_url_name.len);
1039
477k
    offset += packet->http_url_name.len;
1040
477k
  }
1041
1042
477k
  flow->http.url[offset] = '\0';
1043
477k
      }
1044
1045
477k
      ndpi_check_http_url(ndpi_struct, flow, &flow->http.url[host_end]);
1046
477k
    }
1047
477k
  }
1048
1049
817k
  if(packet->http_method.ptr != NULL)
1050
553k
    flow->http.method = ndpi_http_str2method((const char*)packet->http_method.ptr,
1051
553k
               (u_int16_t)packet->http_method.len);
1052
1053
817k
  if(packet->server_line.ptr != NULL)
1054
41.7k
    ndpi_check_http_server(ndpi_struct, flow, (const char *)packet->server_line.ptr, packet->server_line.len);
1055
1056
817k
  if(packet->user_agent_line.ptr != NULL) {
1057
518k
    http_process_user_agent(ndpi_struct, flow, packet->user_agent_line.ptr, packet->user_agent_line.len);
1058
518k
  }
1059
1060
817k
  if(packet->forwarded_line.ptr != NULL) {
1061
701
    if(flow->http.nat_ip == NULL) {
1062
592
      len = packet->forwarded_line.len;
1063
592
      flow->http.nat_ip = ndpi_malloc(len + 1);
1064
592
      if(flow->http.nat_ip != NULL) {
1065
592
        strncpy(flow->http.nat_ip, (char*)packet->forwarded_line.ptr, len);
1066
592
        flow->http.nat_ip[len] = '\0';
1067
592
      }
1068
592
    }
1069
701
  }
1070
1071
817k
  if(packet->upgrade_line.ptr != NULL) {
1072
3.50k
    if((flow->http.response_status_code == 101)
1073
3.50k
       && (packet->upgrade_line.len >= 9)
1074
3.50k
       && memcmp((char *)packet->upgrade_line.ptr, "websocket", 9) == 0)
1075
695
      flow->http.websocket = 1;
1076
3.50k
  }
1077
1078
817k
  if(packet->server_line.ptr != NULL) {
1079
41.7k
    if(flow->http.server == NULL) {
1080
40.7k
      len = packet->server_line.len + 1;
1081
40.7k
      flow->http.server = ndpi_malloc(len);
1082
40.7k
      if(flow->http.server) {
1083
40.7k
        strncpy(flow->http.server, (char*)packet->server_line.ptr,
1084
40.7k
                packet->server_line.len);
1085
40.7k
  flow->http.server[packet->server_line.len] = '\0';
1086
40.7k
      }
1087
40.7k
    }
1088
41.7k
  }
1089
1090
817k
  if(packet->authorization_line.ptr != NULL &&
1091
817k
     (ndpi_struct->cfg.http_username_enabled || ndpi_struct->cfg.http_password_enabled)) {
1092
3.25k
    const char *a = NULL, *b = NULL;
1093
1094
3.25k
    NDPI_LOG_DBG2(ndpi_struct, "Authorization line found %.*s\n",
1095
3.25k
      packet->authorization_line.len, packet->authorization_line.ptr);
1096
1097
3.25k
    if(flow->http.username == NULL && flow->http.password == NULL) {
1098
3.10k
      if((a = ndpi_strncasestr((const char*)packet->authorization_line.ptr,
1099
3.10k
                               "Basic", packet->authorization_line.len))
1100
3.10k
         || (b = ndpi_strncasestr((const char*)packet->authorization_line.ptr,
1101
1.89k
                                  "Digest", packet->authorization_line.len))) {
1102
1.89k
        size_t content_len;
1103
1.89k
        u_int len = b ? 7 : 6;
1104
1105
1.89k
  if(packet->authorization_line.len > len) {
1106
1.79k
    u_char *content = ndpi_base64_decode((const u_char*)&packet->authorization_line.ptr[len],
1107
1.79k
                 packet->authorization_line.len - len, &content_len);
1108
1109
1.79k
    if(content != NULL) {
1110
1.36k
      char *double_dot = strchr((char*)content, ':');
1111
1112
1.36k
      if(double_dot) {
1113
1.16k
        double_dot[0] = '\0';
1114
1.16k
        if(ndpi_struct->cfg.http_username_enabled)
1115
1.16k
          flow->http.username = ndpi_strdup((char*)content);
1116
1.16k
        if(ndpi_struct->cfg.http_password_enabled)
1117
1.16k
          flow->http.password = ndpi_strdup(&double_dot[1]);
1118
1.16k
      }
1119
1120
1.36k
      ndpi_free(content);
1121
1.36k
    }
1122
1123
1.79k
    ndpi_set_risk(ndpi_struct, flow, NDPI_CLEAR_TEXT_CREDENTIALS,
1124
1.79k
      "Found credentials in HTTP Auth Line");
1125
1.79k
  }
1126
1.89k
      }
1127
3.10k
    }
1128
3.25k
  }
1129
1130
817k
  if((packet->referer_line.ptr != NULL) && (flow->http.referer == NULL))
1131
64.1k
    if(ndpi_struct->cfg.http_referer_enabled)
1132
64.1k
      flow->http.referer = ndpi_strndup((const char *)packet->referer_line.ptr, packet->referer_line.len);
1133
1134
817k
  if((packet->host_line.ptr != NULL) && (flow->http.host == NULL)) {
1135
576k
    if(ndpi_struct->cfg.http_host_enabled) {
1136
575k
      flow->http.host = ndpi_strndup((const char *)packet->host_line.ptr, packet->host_line.len);
1137
1138
575k
      if(flow->http.host != NULL) {
1139
575k
  char *double_column = strchr(flow->http.host, ':');
1140
1141
575k
  if(double_column != NULL)
1142
30.5k
    double_column[0] = '\0';
1143
    
1144
575k
  if(ndpi_struct->cfg.hostname_dns_check_enabled
1145
575k
     && (ndpi_check_is_numeric_ip(flow->http.host) == false)) {
1146
158k
    ndpi_ip_addr_t ip_addr;
1147
1148
158k
    memset(&ip_addr, 0, sizeof(ip_addr));
1149
          
1150
158k
    if(packet->iph)
1151
157k
      ip_addr.ipv4 = packet->iph->daddr;
1152
290
    else
1153
290
      memcpy(&ip_addr.ipv6, &packet->iphv6->ip6_dst, sizeof(struct ndpi_in6_addr));
1154
          
1155
158k
    if(!ndpi_cache_find_hostname_ip(ndpi_struct, &ip_addr, flow->http.host)) {
1156
#ifdef DEBUG_HTTP
1157
      printf("[HTTP] Not found host %s\n", flow->http.host);
1158
#endif
1159
155k
      ndpi_set_risk(ndpi_struct, flow, NDPI_UNRESOLVED_HOSTNAME, flow->http.host);
1160
1161
155k
    } else {
1162
#ifdef DEBUG_HTTP
1163
      printf("[HTTP] Found host %s\n", flow->http.host);
1164
#endif
1165
3.08k
    }
1166
1167
158k
  }
1168
575k
      }
1169
575k
    }
1170
576k
  }
1171
  
1172
817k
  if(packet->content_line.ptr != NULL) {
1173
69.0k
    NDPI_LOG_DBG2(ndpi_struct, "Content Type line found %.*s\n",
1174
69.0k
      packet->content_line.len, packet->content_line.ptr);
1175
1176
69.0k
    if(flow->http.response_status_code == 0) {
1177
      /* Request */
1178
32.6k
      if((flow->http.request_content_type == NULL) && (packet->content_line.len > 0)) {
1179
31.4k
  if(ndpi_struct->cfg.http_request_content_type_enabled) {
1180
31.4k
    int len = packet->content_line.len + 1;
1181
1182
31.4k
          flow->http.request_content_type = ndpi_malloc(len);
1183
31.4k
          if(flow->http.request_content_type) {
1184
31.4k
            strncpy(flow->http.request_content_type, (char*)packet->content_line.ptr,
1185
31.4k
                    packet->content_line.len);
1186
31.4k
            flow->http.request_content_type[packet->content_line.len] = '\0';
1187
31.4k
          }
1188
31.4k
  }
1189
1190
31.4k
  if(ndpi_strnstr((char*)packet->content_line.ptr, "x-www-form-urlencoded", packet->content_line.len))
1191
11.7k
     flow->http.is_form = 1;
1192
31.4k
      }
1193
36.4k
    } else {
1194
      /* Response */
1195
36.4k
      if((flow->http.content_type == NULL) && (packet->content_line.len > 0)) {
1196
35.7k
  int len = packet->content_line.len + 1;
1197
1198
35.7k
  flow->http.content_type = ndpi_malloc(len);
1199
35.7k
  if(flow->http.content_type) {
1200
35.7k
    strncpy(flow->http.content_type, (char*)packet->content_line.ptr,
1201
35.7k
      packet->content_line.len);
1202
35.7k
    flow->http.content_type[packet->content_line.len] = '\0';
1203
1204
35.7k
    flow->category = ndpi_http_check_content(ndpi_struct, flow);
1205
35.7k
  }
1206
35.7k
      }
1207
36.4k
    }
1208
69.0k
  }
1209
1210
  /* check for host line (only if we don't already have an hostname) */
1211
817k
  if(packet->host_line.ptr != NULL && flow->host_server_name[0] == '\0') {
1212
1213
439k
    NDPI_LOG_DBG2(ndpi_struct, "HOST line found %.*s\n",
1214
439k
      packet->host_line.len, packet->host_line.ptr);
1215
1216
    /* Copy result for nDPI apps */
1217
439k
    ndpi_hostname_sni_set(flow, packet->host_line.ptr, packet->host_line.len, NDPI_HOSTNAME_NORM_ALL);
1218
1219
439k
    if(strlen(flow->host_server_name) > 0) {
1220
433k
      char *double_col;
1221
433k
      int a, b, c, d;
1222
1223
433k
      hostname_just_set = 1;
1224
1225
433k
      if(ndpi_is_valid_hostname((char *)packet->host_line.ptr,
1226
433k
        packet->host_line.len) == 0) {
1227
50.9k
  char str[128];
1228
  
1229
50.9k
        if(is_flowrisk_info_enabled(ndpi_struct, NDPI_INVALID_CHARACTERS)) {
1230
35.6k
    snprintf(str, sizeof(str), "Invalid host %s", flow->host_server_name);
1231
35.6k
    ndpi_set_risk(ndpi_struct, flow, NDPI_INVALID_CHARACTERS, str);
1232
35.6k
        } else {
1233
15.2k
          ndpi_set_risk(ndpi_struct, flow, NDPI_INVALID_CHARACTERS, NULL);
1234
15.2k
        }
1235
1236
  /* This looks like an attack */
1237
1238
50.9k
  snprintf(str, sizeof(str), "Suspicious hostname [%.*s]: attack ?", packet->host_line.len, (char *)packet->host_line.ptr);
1239
50.9k
  ndpi_set_risk(ndpi_struct, flow, NDPI_POSSIBLE_EXPLOIT, str);
1240
50.9k
      }
1241
1242
433k
      double_col = strchr((char*)flow->host_server_name, ':');
1243
433k
      if(double_col) double_col[0] = '\0';
1244
433k
      if(ndpi_struct->packet.iph
1245
433k
         && (sscanf(flow->host_server_name, "%d.%d.%d.%d", &a, &b, &c, &d) == 4)) {
1246
        /* IPv4 */
1247
1248
321k
        if(ndpi_struct->packet.iph->daddr != inet_addr(flow->host_server_name)) {
1249
37.4k
          if(is_flowrisk_info_enabled(ndpi_struct, NDPI_HTTP_SUSPICIOUS_HEADER)) {
1250
27.7k
            char buf[64], msg[128];
1251
1252
27.7k
      snprintf(msg, sizeof(msg), "Expected %s, found %s",
1253
27.7k
         ndpi_intoav4(ntohl(ndpi_struct->packet.iph->daddr), buf, sizeof(buf)), flow->host_server_name);
1254
27.7k
      ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_HEADER, msg);
1255
27.7k
          } else {
1256
9.64k
            ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_HEADER, NULL);
1257
9.64k
          }
1258
37.4k
        }
1259
321k
      }
1260
433k
    }
1261
1262
439k
  }
1263
1264
817k
  ndpi_http_parse_subprotocol(ndpi_struct, flow, hostname_just_set);
1265
1266
817k
  if(hostname_just_set && strlen(flow->host_server_name) > 0) {
1267
433k
    ndpi_check_dga_name(ndpi_struct, flow, flow->host_server_name, 1, 0, 0);
1268
433k
  }
1269
1270
817k
  ndpi_check_http_header(ndpi_struct, flow);
1271
817k
}
1272
1273
/* ************************************************************* */
1274
1275
#ifdef NDPI_ENABLE_DEBUG_MESSAGES
1276
static uint8_t non_ctrl(uint8_t c) {
1277
  return c < 32 ? '.':c;
1278
}
1279
#endif
1280
1281
/* ************************************************************* */
1282
1283
/**
1284
 * Functions to check whether the packet begins with a valid http request
1285
 * @param ndpi_struct
1286
 * @returnvalue 0 if no valid request has been found
1287
 * @returnvalue >0 indicates start of filename but not necessarily in packet limit
1288
 */
1289
1290
#define STATIC_STRING_L(a) {.str=a, .len=sizeof(a)-1 }
1291
1292
static struct l_string {
1293
  const char *str;
1294
  size_t     len;
1295
} http_methods[] = {
1296
        STATIC_STRING_L("GET "),
1297
        STATIC_STRING_L("POST "),
1298
        STATIC_STRING_L("OPTIONS "),
1299
        STATIC_STRING_L("HEAD "),
1300
        STATIC_STRING_L("PUT "),
1301
        STATIC_STRING_L("PATCH "),
1302
        STATIC_STRING_L("DELETE "),
1303
        STATIC_STRING_L("CONNECT "),
1304
        STATIC_STRING_L("PROPFIND "),
1305
        STATIC_STRING_L("PROPPATCH "),
1306
        STATIC_STRING_L("MKCOL "),
1307
        STATIC_STRING_L("MOVE "),
1308
        STATIC_STRING_L("COPY "),
1309
        STATIC_STRING_L("LOCK "),
1310
        STATIC_STRING_L("UNLOCK "),
1311
        STATIC_STRING_L("REPORT "),
1312
        STATIC_STRING_L("RPC_CONNECT "),
1313
        STATIC_STRING_L("RPC_IN_DATA "),
1314
        STATIC_STRING_L("RPC_OUT_DATA ")
1315
};
1316
static const char *http_fs = "CDGHLMOPRU";
1317
1318
static u_int16_t http_request_url_offset(struct ndpi_detection_module_struct *ndpi_struct)
1319
3.05M
{
1320
3.05M
  struct ndpi_packet_struct *packet = &ndpi_struct->packet;
1321
3.05M
  unsigned int i;
1322
1323
3.05M
  NDPI_LOG_DBG2(ndpi_struct, "====>>>> HTTP: %c%c%c%c [len: %u]\n",
1324
3.05M
    packet->payload_packet_len > 0 ? non_ctrl(packet->payload[0]) : '.',
1325
3.05M
    packet->payload_packet_len > 1 ? non_ctrl(packet->payload[1]) : '.',
1326
3.05M
    packet->payload_packet_len > 2 ? non_ctrl(packet->payload[2]) : '.',
1327
3.05M
    packet->payload_packet_len > 3 ? non_ctrl(packet->payload[3]) : '.',
1328
3.05M
    packet->payload_packet_len);
1329
1330
  /* Check first char */
1331
3.05M
  if(!packet->payload_packet_len || !strchr(http_fs,packet->payload[0]))
1332
1.82M
    return 0;
1333
1334
  /**
1335
     FIRST PAYLOAD PACKET FROM CLIENT
1336
  **/
1337
10.4M
  for(i=0; i < sizeof(http_methods)/sizeof(http_methods[0]); i++) {
1338
9.98M
    if(packet->payload_packet_len >= http_methods[i].len &&
1339
9.98M
       strncasecmp((const char*)packet->payload,http_methods[i].str,http_methods[i].len) == 0) {
1340
743k
      size_t url_start = http_methods[i].len;
1341
753k
      while (url_start < packet->payload_packet_len &&
1342
753k
             url_start < http_methods[i].len + 2048 && /* We assume 2048 chars as maximum for URLs. */
1343
753k
             packet->payload[url_start] == ' ') { url_start++; }
1344
743k
      NDPI_LOG_DBG2(ndpi_struct, "HTTP: %sFOUND\n",http_methods[i].str);
1345
743k
      return url_start;
1346
743k
    }
1347
9.98M
  }
1348
482k
  return 0;
1349
1.22M
}
1350
1351
/* *********************************************************************************************** */
1352
1353
/* Trick to speed-up detection */
1354
static const char* suspicious_http_header_keys_A[] = { "Arch", NULL};
1355
static const char* suspicious_http_header_keys_C[] = { "Cores", NULL};
1356
static const char* suspicious_http_header_keys_M[] = { "Mem", NULL};
1357
static const char* suspicious_http_header_keys_O[] = { "Os", "Osname", "Osversion", NULL};
1358
static const char* suspicious_http_header_keys_R[] = { "Root", NULL};
1359
static const char* suspicious_http_header_keys_S[] = { "S", NULL};
1360
static const char* suspicious_http_header_keys_T[] = { "TLS_version", NULL};
1361
static const char* suspicious_http_header_keys_U[] = { "Uuid", NULL};
1362
static const char* suspicious_http_header_keys_X[] = { "X-Hire-Me", NULL};
1363
1364
2.05M
static int is_a_suspicious_header(const char* suspicious_headers[], struct ndpi_int_one_line_struct packet_line) {
1365
2.05M
  int i;
1366
2.05M
  unsigned int header_len;
1367
2.05M
  const u_int8_t* header_limit;
1368
1369
2.05M
  if((header_limit = memchr(packet_line.ptr, ':', packet_line.len))) {
1370
1.94M
    header_len = header_limit - packet_line.ptr;
1371
3.91M
    for(i=0; suspicious_headers[i] != NULL; i++) {
1372
1.96M
      if(!strncasecmp((const char*) packet_line.ptr,
1373
1.96M
          suspicious_headers[i], header_len))
1374
4.25k
  return 1;
1375
1.96M
    }
1376
1.94M
  }
1377
1378
2.04M
  return 0;
1379
2.05M
}
1380
1381
/* *********************************************************************************************** */
1382
1383
static void ndpi_check_http_header(struct ndpi_detection_module_struct *ndpi_struct,
1384
817k
           struct ndpi_flow_struct *flow) {
1385
817k
  u_int32_t i;
1386
817k
  struct ndpi_packet_struct *packet = &ndpi_struct->packet;
1387
1388
4.67M
  for(i=0; (i < packet->parsed_lines)
1389
4.67M
  && (packet->line[i].ptr != NULL)
1390
4.67M
  && (packet->line[i].len > 0); i++) {
1391
3.86M
    switch(packet->line[i].ptr[0]) {
1392
228k
    case 'A':
1393
228k
      if(is_a_suspicious_header(suspicious_http_header_keys_A, packet->line[i])) {
1394
380
        if(is_flowrisk_info_enabled(ndpi_struct, NDPI_HTTP_SUSPICIOUS_HEADER)) {
1395
232
          char str[64];
1396
1397
232
    snprintf(str, sizeof(str), "Found %.*s", packet->line[i].len, packet->line[i].ptr);
1398
232
    ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_HEADER, str);
1399
232
        } else {
1400
148
          ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_HEADER, NULL);
1401
148
        }
1402
380
  return;
1403
380
      }
1404
228k
      break;
1405
921k
    case 'C':
1406
921k
      if(is_a_suspicious_header(suspicious_http_header_keys_C, packet->line[i])) {
1407
722
        if(is_flowrisk_info_enabled(ndpi_struct, NDPI_HTTP_SUSPICIOUS_HEADER)) {
1408
525
          char str[64];
1409
1410
525
    snprintf(str, sizeof(str), "Found %.*s", packet->line[i].len, packet->line[i].ptr);
1411
525
    ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_HEADER, str);
1412
525
        } else {
1413
197
          ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_HEADER, NULL);
1414
197
        }
1415
722
  return;
1416
722
      }
1417
920k
      break;
1418
920k
    case 'M':
1419
8.86k
      if(is_a_suspicious_header(suspicious_http_header_keys_M, packet->line[i])) {
1420
130
        if(is_flowrisk_info_enabled(ndpi_struct, NDPI_HTTP_SUSPICIOUS_HEADER)) {
1421
102
          char str[64];
1422
1423
102
    snprintf(str, sizeof(str), "Found %.*s", packet->line[i].len, packet->line[i].ptr);
1424
102
    ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_HEADER, str);
1425
102
        } else {
1426
28
          ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_HEADER, NULL);
1427
28
        }
1428
130
  return;
1429
130
      }
1430
8.73k
      break;
1431
12.5k
    case 'O':
1432
12.5k
      if(is_a_suspicious_header(suspicious_http_header_keys_O, packet->line[i])) {
1433
144
        if(is_flowrisk_info_enabled(ndpi_struct, NDPI_HTTP_SUSPICIOUS_HEADER)) {
1434
129
          char str[64];
1435
1436
129
    snprintf(str, sizeof(str), "Found %.*s", packet->line[i].len, packet->line[i].ptr);
1437
129
    ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_HEADER, str);
1438
129
        } else {
1439
15
          ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_HEADER, NULL);
1440
15
        }
1441
144
  return;
1442
144
      }
1443
12.4k
      break;
1444
87.4k
    case 'R':
1445
87.4k
      if(is_a_suspicious_header(suspicious_http_header_keys_R, packet->line[i])) {
1446
427
        if(is_flowrisk_info_enabled(ndpi_struct, NDPI_HTTP_SUSPICIOUS_HEADER)) {
1447
316
          char str[64];
1448
1449
316
    snprintf(str, sizeof(str), "Found %.*s", packet->line[i].len, packet->line[i].ptr);
1450
316
    ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_HEADER, str);
1451
316
        } else {
1452
111
          ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_HEADER, NULL);
1453
111
        }
1454
427
  return;
1455
427
      }
1456
87.0k
      break;
1457
87.0k
    case 'S':
1458
67.9k
      if(is_a_suspicious_header(suspicious_http_header_keys_S, packet->line[i])) {
1459
692
        if(is_flowrisk_info_enabled(ndpi_struct, NDPI_HTTP_SUSPICIOUS_HEADER)) {
1460
641
          char str[64];
1461
1462
641
    snprintf(str, sizeof(str), "Found %.*s", packet->line[i].len, packet->line[i].ptr);
1463
641
    ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_HEADER, str);
1464
641
        } else {
1465
51
          ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_HEADER, NULL);
1466
51
        }
1467
692
  return;
1468
692
      }
1469
67.2k
      break;
1470
67.2k
    case 'T':
1471
9.07k
      if(is_a_suspicious_header(suspicious_http_header_keys_T, packet->line[i])) {
1472
317
        if(is_flowrisk_info_enabled(ndpi_struct, NDPI_HTTP_SUSPICIOUS_HEADER)) {
1473
239
          char str[64];
1474
1475
239
    snprintf(str, sizeof(str), "Found %.*s", packet->line[i].len, packet->line[i].ptr);
1476
239
    ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_HEADER, str);
1477
239
        } else {
1478
78
          ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_HEADER, NULL);
1479
78
        }
1480
317
  return;
1481
317
      }
1482
8.76k
      break;
1483
682k
    case 'U':
1484
682k
      if(is_a_suspicious_header(suspicious_http_header_keys_U, packet->line[i])) {
1485
538
        if(is_flowrisk_info_enabled(ndpi_struct, NDPI_HTTP_SUSPICIOUS_HEADER)) {
1486
388
          char str[64];
1487
1488
388
    snprintf(str, sizeof(str), "Found %.*s", packet->line[i].len, packet->line[i].ptr);
1489
388
    ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_HEADER, str);
1490
388
        } else {
1491
150
          ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_HEADER, NULL);
1492
150
        }
1493
538
  return;
1494
538
      }
1495
682k
      break;
1496
682k
    case 'X':
1497
33.6k
      if(is_a_suspicious_header(suspicious_http_header_keys_X, packet->line[i])) {
1498
900
        if(is_flowrisk_info_enabled(ndpi_struct, NDPI_HTTP_SUSPICIOUS_HEADER)) {
1499
856
          char str[64];
1500
1501
856
    snprintf(str, sizeof(str), "Found %.*s", packet->line[i].len, packet->line[i].ptr);
1502
856
    ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_HEADER, str);
1503
856
        } else {
1504
44
          ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_HEADER, NULL);
1505
44
        }
1506
900
  return;
1507
900
      }
1508
1509
32.7k
      break;
1510
3.86M
    }
1511
3.86M
  }
1512
817k
}
1513
1514
static void parse_response_code(struct ndpi_detection_module_struct *ndpi_struct,
1515
        struct ndpi_flow_struct *flow)
1516
55.3k
{
1517
55.3k
  struct ndpi_packet_struct *packet = &ndpi_struct->packet;
1518
55.3k
  char buf[4];
1519
55.3k
  char ec[48];
1520
1521
55.3k
  if(packet->payload_packet_len >= 12) {
1522
    /* Set server HTTP response code */
1523
54.8k
    strncpy(buf, (char*)&packet->payload[9], 3);
1524
54.8k
    buf[3] = '\0';
1525
1526
54.8k
    flow->http.response_status_code = atoi(buf);
1527
54.8k
    NDPI_LOG_DBG2(ndpi_struct, "Response code %d\n", flow->http.response_status_code);
1528
1529
    /* https://en.wikipedia.org/wiki/List_of_HTTP_status_codes */
1530
54.8k
    if((flow->http.response_status_code < 100) || (flow->http.response_status_code > 509))
1531
6.75k
      flow->http.response_status_code = 0; /* Out of range */
1532
1533
54.8k
    if(flow->http.response_status_code >= 400) {
1534
3.55k
      snprintf(ec, sizeof(ec), "HTTP Error Code %u", flow->http.response_status_code);
1535
3.55k
      ndpi_set_risk(ndpi_struct, flow, NDPI_ERROR_CODE_DETECTED, ec);
1536
1537
3.55k
      if(flow->http.url != NULL) {
1538
        /* Let's check for Wordpress */
1539
794
        char *slash = strchr(flow->http.url, '/');
1540
1541
794
  if(slash != NULL &&
1542
794
           (((flow->http.method == NDPI_HTTP_METHOD_POST) && (strncmp(slash, "/wp-admin/", 10) == 0))
1543
736
      || ((flow->http.method == NDPI_HTTP_METHOD_GET) && (strncmp(slash, "/wp-content/uploads/", 20) == 0))
1544
736
     )) {
1545
          /* Example of popular exploits https://www.wordfence.com/blog/2022/05/millions-of-attacks-target-tatsu-builder-plugin/ */
1546
5
    char str[128];
1547
1548
5
    snprintf(str, sizeof(str), "Possible Wordpress Exploit [%s]", slash);
1549
5
          ndpi_set_risk(ndpi_struct, flow, NDPI_POSSIBLE_EXPLOIT, str);
1550
5
  }
1551
794
      }
1552
3.55k
    }
1553
54.8k
  }
1554
55.3k
}
1555
1556
3.05M
static int is_request(struct ndpi_detection_module_struct *ndpi_struct) {
1557
3.05M
  struct ndpi_packet_struct *packet = &ndpi_struct->packet;
1558
3.05M
  u_int16_t filename_start;
1559
1560
3.05M
  filename_start = http_request_url_offset(ndpi_struct);
1561
  /* This check is required as RTSP is pretty similiar to HTTP */
1562
3.05M
  if(filename_start > 0 &&
1563
3.05M
     strncasecmp((const char *)packet->payload + filename_start,
1564
743k
                 "rtsp://", ndpi_min(7, packet->payload_packet_len - filename_start)) == 0)
1565
600
    return 0;
1566
3.05M
  return filename_start;
1567
3.05M
}
1568
1569
2.32M
static int is_response(struct ndpi_detection_module_struct *ndpi_struct) {
1570
2.32M
  struct ndpi_packet_struct *packet = &ndpi_struct->packet;
1571
2.32M
  if(packet->payload_packet_len >= 7 &&
1572
2.32M
     strncasecmp((const char *)packet->payload, "HTTP/1.", 7) == 0)
1573
55.3k
    return 1;
1574
2.27M
  return 0;
1575
2.32M
}
1576
1577
static void process_request(struct ndpi_detection_module_struct *ndpi_struct,
1578
          struct ndpi_flow_struct *flow,
1579
742k
          u_int16_t filename_start) {
1580
742k
  struct ndpi_packet_struct *packet = &ndpi_struct->packet;
1581
742k
  u_int16_t master_protocol;
1582
1583
742k
  ndpi_parse_packet_line_info(ndpi_struct, flow);
1584
1585
742k
  master_protocol = NDPI_PROTOCOL_HTTP;
1586
1587
742k
  if(packet->parsed_lines == 0 ||
1588
742k
     !(packet->line[0].len >= (9 + filename_start) &&
1589
727k
       strncasecmp((const char *)&packet->line[0].ptr[packet->line[0].len - 9], " HTTP/1.", 8) == 0)) {
1590
188k
    NDPI_LOG_DBG2(ndpi_struct, "Request with an incomplete or invalid first line\n");
1591
    /* Since we don't save data across different packets, we will never have
1592
       the complete url: we can't check for HTTP_PROXY */
1593
188k
    if(filename_start == 8 &&
1594
188k
       strncasecmp((const char *)packet->payload, "CONNECT ", 8) == 0) {
1595
400
      master_protocol = NDPI_PROTOCOL_HTTP_CONNECT;
1596
400
    }
1597
553k
  } else {
1598
    /* First line is complete (example: "GET / HTTP/1.1"): extract url */
1599
1600
553k
    packet->http_url_name.ptr = &packet->payload[filename_start];
1601
553k
    packet->http_url_name.len = packet->line[0].len - (filename_start + 9);
1602
1603
553k
    packet->http_method.ptr = packet->line[0].ptr;
1604
553k
    packet->http_method.len = filename_start - 1;
1605
1606
    /* Set the HTTP requested version: 0=HTTP/1.0 and 1=HTTP/1.1 */
1607
553k
    if(memcmp(&packet->line[0].ptr[packet->line[0].len - 1], "1", 1) == 0)
1608
523k
      flow->http.request_version = 1;
1609
30.1k
    else
1610
30.1k
      flow->http.request_version = 0;
1611
1612
553k
    if(packet->http_url_name.len > 7 &&
1613
553k
       !strncasecmp((const char*) packet->http_url_name.ptr, "http://", 7)) {
1614
2.21k
      master_protocol = NDPI_PROTOCOL_HTTP_PROXY;
1615
2.21k
    }
1616
553k
    if(filename_start == 8 &&
1617
553k
       strncasecmp((const char *)packet->payload, "CONNECT ", 8) == 0) {
1618
303
      master_protocol = NDPI_PROTOCOL_HTTP_CONNECT;
1619
303
    }
1620
553k
  }
1621
742k
  ndpi_int_http_add_connection(ndpi_struct, flow, master_protocol);
1622
742k
  check_content_type_and_change_protocol(ndpi_struct, flow);
1623
1624
742k
  if(flow->http.user_agent == NULL ||
1625
742k
     flow->http.user_agent[0] == '\0') {
1626
232k
    ndpi_set_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_USER_AGENT, "Empty or missing User-Agent");
1627
232k
  }
1628
742k
}
1629
1630
static void process_response(struct ndpi_detection_module_struct *ndpi_struct,
1631
55.3k
           struct ndpi_flow_struct *flow) {
1632
1633
55.3k
  ndpi_parse_packet_line_info(ndpi_struct, flow);
1634
55.3k
  parse_response_code(ndpi_struct, flow);
1635
55.3k
  check_content_type_and_change_protocol(ndpi_struct, flow);
1636
1637
55.3k
  ndpi_validate_http_content(ndpi_struct, flow);
1638
55.3k
}
1639
1640
static void reset(struct ndpi_detection_module_struct *ndpi_struct,
1641
213k
                  struct ndpi_flow_struct *flow) {
1642
1643
213k
  NDPI_LOG_DBG2(ndpi_struct, "Reset status and risks\n");
1644
1645
  /* Reset everything in flow->http.
1646
     TODO: Could we be smarter? Probably some info don't change across
1647
     different req-res transactions... */
1648
1649
213k
  flow->http.method = 0;
1650
213k
  flow->http.request_version = 0;
1651
213k
  flow->http.response_status_code = 0;
1652
213k
  if(flow->http.url) {
1653
119k
    ndpi_free(flow->http.url);
1654
119k
    flow->http.url = NULL;
1655
119k
  }
1656
213k
  if(flow->http.content_type) {
1657
10.1k
    ndpi_free(flow->http.content_type);
1658
10.1k
    flow->http.content_type = NULL;
1659
10.1k
  }
1660
213k
  if(flow->http.request_content_type) {
1661
8.64k
    ndpi_free(flow->http.request_content_type);
1662
8.64k
    flow->http.request_content_type = NULL;
1663
8.64k
  }
1664
213k
  if(flow->http.user_agent) {
1665
131k
    ndpi_free(flow->http.user_agent);
1666
131k
    flow->http.user_agent = NULL;
1667
131k
  }
1668
213k
  if(flow->http.server) {
1669
11.6k
    ndpi_free(flow->http.server);
1670
11.6k
    flow->http.server = NULL;
1671
11.6k
  }
1672
213k
  if(flow->http.referer) {
1673
17.2k
    ndpi_free(flow->http.referer);
1674
17.2k
    flow->http.referer = NULL;
1675
17.2k
  }
1676
213k
  if(flow->http.host) {
1677
148k
    ndpi_free(flow->http.host);
1678
148k
    flow->http.host = NULL;
1679
148k
  }
1680
213k
  if(flow->http.detected_os) {
1681
97.6k
    ndpi_free(flow->http.detected_os);
1682
97.6k
    flow->http.detected_os = NULL;
1683
97.6k
  }
1684
213k
  if(flow->http.nat_ip) {
1685
225
    ndpi_free(flow->http.nat_ip);
1686
225
    flow->http.nat_ip = NULL;
1687
225
  }
1688
213k
  if(flow->http.filename) {
1689
505
    ndpi_free(flow->http.filename);
1690
505
    flow->http.filename = NULL;
1691
505
  }
1692
213k
  if(flow->http.username) {
1693
359
    ndpi_free(flow->http.username);
1694
359
    flow->http.username = NULL;
1695
359
  }
1696
213k
  if(flow->http.password) {
1697
459
    ndpi_free(flow->http.password);
1698
459
    flow->http.password = NULL;
1699
459
  }
1700
1701
  /* Reset flow risks. We should reset only those risks triggered by
1702
     the previous HTTP response... */
1703
  /* TODO */
1704
213k
  ndpi_unset_risk(ndpi_struct, flow, NDPI_BINARY_APPLICATION_TRANSFER);
1705
213k
  ndpi_unset_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_CONTENT);
1706
213k
  ndpi_unset_risk(ndpi_struct, flow, NDPI_POSSIBLE_EXPLOIT);
1707
213k
  ndpi_unset_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_USER_AGENT);
1708
213k
  ndpi_unset_risk(ndpi_struct, flow, NDPI_HTTP_CRAWLER_BOT);
1709
213k
  ndpi_unset_risk(ndpi_struct, flow, NDPI_NUMERIC_IP_HOST);
1710
213k
  ndpi_unset_risk(ndpi_struct, flow, NDPI_URL_POSSIBLE_RCE_INJECTION);
1711
213k
  ndpi_unset_risk(ndpi_struct, flow, NDPI_HTTP_OBSOLETE_SERVER);
1712
213k
  ndpi_unset_risk(ndpi_struct, flow, NDPI_CLEAR_TEXT_CREDENTIALS);
1713
213k
  ndpi_unset_risk(ndpi_struct, flow, NDPI_INVALID_CHARACTERS);
1714
213k
  ndpi_unset_risk(ndpi_struct, flow, NDPI_HTTP_SUSPICIOUS_HEADER);
1715
213k
  ndpi_unset_risk(ndpi_struct, flow, NDPI_ERROR_CODE_DETECTED);
1716
213k
  ndpi_unset_risk(ndpi_struct, flow, NDPI_MALFORMED_PACKET);
1717
213k
}
1718
1719
static void ndpi_check_http_tcp(struct ndpi_detection_module_struct *ndpi_struct,
1720
3.08M
        struct ndpi_flow_struct *flow) {
1721
3.08M
  struct ndpi_packet_struct *packet = &ndpi_struct->packet;
1722
3.08M
  u_int16_t filename_start;
1723
1724
3.08M
  NDPI_LOG_DBG(ndpi_struct, "http_stage %d dir %d req/res %d/%d\n",
1725
3.08M
         flow->l4.tcp.http_stage, packet->packet_direction,
1726
3.08M
         is_request(ndpi_struct), is_response(ndpi_struct));
1727
1728
3.08M
  if(flow->l4.tcp.http_stage == 0) { /* Start: waiting for (the beginning of) a request */
1729
2.84M
    filename_start = is_request(ndpi_struct);
1730
2.84M
    if(filename_start == 0) {
1731
      /* Flow starting with a response? */
1732
2.29M
      if(is_response(ndpi_struct)) {
1733
32.4k
        NDPI_LOG_DBG2(ndpi_struct, "Response where a request were expected\n");
1734
  /* This is tricky. Two opposing goals:
1735
     1) We want to correctly match request with response!! -> Skip this response
1736
        and keep looking for a request.
1737
     2) We want to support asymmetric detection
1738
     Trade-off:
1739
     a) set HTTP as master (it is a guess; we can't know it from the reply only)
1740
     b) process the response(s) and save the metadata
1741
     c) look for a request. If we found it, reset everything (master,
1742
        classification and metadata!) */
1743
32.4k
        ndpi_int_http_add_connection(ndpi_struct, flow, NDPI_PROTOCOL_HTTP);
1744
32.4k
        process_response(ndpi_struct, flow);
1745
1746
32.4k
  flow->l4.tcp.http_stage = packet->packet_direction + 3; // packet_direction 0: stage 3, packet_direction 1: stage 4
1747
32.4k
        return;
1748
32.4k
      }
1749
      /* The first pkt is neither a request nor a response -> no http */
1750
2.26M
      NDPI_LOG_DBG2(ndpi_struct, "Neither req nor response -> exclude\n");
1751
2.26M
      NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
1752
2.26M
      return;
1753
2.29M
    }
1754
544k
    NDPI_LOG_DBG2(ndpi_struct, "Request where expected\n");
1755
1756
544k
    process_request(ndpi_struct, flow, filename_start);
1757
1758
    /* Wait for the response */
1759
544k
    flow->l4.tcp.http_stage = packet->packet_direction + 1; // packet_direction 0: stage 1, packet_direction 1: stage 2
1760
1761
544k
    return;
1762
2.84M
  } else if(flow->l4.tcp.http_stage == 1 || flow->l4.tcp.http_stage == 2) {
1763
    /* Found a request, looking for the response */
1764
1765
228k
    if(flow->l4.tcp.http_stage - packet->packet_direction == 1) {
1766
      /* Another pkt from the same direction (probably another fragment of the request)
1767
         Keep lookng for the response */
1768
212k
      NDPI_LOG_DBG2(ndpi_struct, "Another piece of request\n");
1769
212k
      filename_start = is_request(ndpi_struct);
1770
212k
      if(filename_start > 0) {
1771
        /* Probably a new, separated request (asymmetric flow or missing pkts?).
1772
     What should we do? We definitely don't want to mix data from different
1773
     requests. The easiest (but costly) idea is to reset the state and
1774
     process it (i.e. we keep the metadata of the last request that we
1775
     have processed) */
1776
197k
        if(flow->l4.tcp.http_asymmetric_stage < 2)
1777
197k
          flow->l4.tcp.http_asymmetric_stage++;
1778
197k
        reset(ndpi_struct, flow);
1779
197k
        process_request(ndpi_struct, flow, filename_start);
1780
197k
  return;
1781
197k
      }
1782
14.8k
      ndpi_parse_packet_line_info(ndpi_struct, flow);
1783
14.8k
      check_content_type_and_change_protocol(ndpi_struct, flow);
1784
14.8k
      return;
1785
212k
    } else if(is_response(ndpi_struct)) {
1786
9.04k
      NDPI_LOG_DBG2(ndpi_struct, "Response where expected\n");
1787
1788
9.04k
      process_response(ndpi_struct, flow);
1789
1790
9.04k
      flow->l4.tcp.http_stage = 0;
1791
9.04k
    } else {
1792
6.63k
      NDPI_LOG_DBG2(ndpi_struct, "The msg from the server doesn't look like a response...\n");
1793
      /* TODO */
1794
6.63k
    }
1795
228k
  } else if(flow->l4.tcp.http_stage == 3 || flow->l4.tcp.http_stage == 4) {
1796
    /* Found a response but we want a request */
1797
1798
20.2k
    if(flow->l4.tcp.http_stage - packet->packet_direction == 3) {
1799
      /* Another pkt from the same direction (probably another fragment of the response)
1800
         Keep lookng for the request */
1801
18.3k
      NDPI_LOG_DBG2(ndpi_struct, "Another piece of response\n");
1802
18.3k
      if(is_response(ndpi_struct)) {
1803
        /* See the comment above about how we handle consecutive requests/responses */
1804
13.8k
        if(flow->l4.tcp.http_asymmetric_stage < 2)
1805
13.5k
          flow->l4.tcp.http_asymmetric_stage++;
1806
13.8k
        reset(ndpi_struct, flow);
1807
13.8k
        process_response(ndpi_struct, flow);
1808
13.8k
  return;
1809
13.8k
      }
1810
4.51k
      ndpi_parse_packet_line_info(ndpi_struct, flow);
1811
4.51k
      check_content_type_and_change_protocol(ndpi_struct, flow);
1812
4.51k
      return;
1813
18.3k
    }
1814
1815
1.87k
    NDPI_LOG_DBG2(ndpi_struct, "Found a request. We need to reset the state!\n");
1816
1817
1.87k
    reset(ndpi_struct, flow);
1818
1.87k
    flow->l4.tcp.http_stage = 0;
1819
1.87k
    return ndpi_check_http_tcp(ndpi_struct, flow);
1820
20.2k
  }
1821
3.08M
}
1822
1823
/* ********************************* */
1824
1825
static void ndpi_search_http_tcp(struct ndpi_detection_module_struct *ndpi_struct,
1826
3.08M
         struct ndpi_flow_struct *flow) {
1827
  /* Break after 20 packets. */
1828
3.08M
  if(flow->packet_counter > 20) {
1829
9
    NDPI_EXCLUDE_DISSECTOR(ndpi_struct, flow);
1830
9
    return;
1831
9
  }
1832
1833
3.08M
  NDPI_LOG_DBG(ndpi_struct, "search HTTP\n");
1834
3.08M
  ndpi_check_http_tcp(ndpi_struct, flow);
1835
1836
3.08M
  if((ndpi_struct->cfg.http_parse_response_enabled &&
1837
3.08M
      flow->host_server_name[0] != '\0' &&
1838
3.08M
      flow->http.response_status_code != 0) ||
1839
3.08M
     (!ndpi_struct->cfg.http_parse_response_enabled &&
1840
3.07M
      (flow->host_server_name[0] != '\0' ||
1841
314
       flow->http.response_status_code != 0)) ||
1842
     /* We have found 3 consecutive requests (without the reply) or 3
1843
        consecutive replies (without the request). If the traffic is really
1844
        asymmetric, stop here, because we will never find the metadata from
1845
        both the request and the reply. We wait for 3 events (instead of 2)
1846
        to avoid false positives triggered by missing/dropped packets */
1847
3.08M
     (flow->l4.tcp.http_asymmetric_stage == 2 &&
1848
3.07M
      (flow->packet_direction_complete_counter[0] == 0 ||
1849
16.0k
       flow->packet_direction_complete_counter[1] == 0))) {
1850
16.0k
    flow->extra_packets_func = NULL; /* We're good now */
1851
1852
16.0k
    if(flow->initial_binary_bytes_len) ndpi_analyze_content_signature(ndpi_struct, flow);
1853
16.0k
  }
1854
3.08M
}
1855
1856
15.4k
void init_http_dissector(struct ndpi_detection_module_struct *ndpi_struct) {
1857
15.4k
  register_dissector("HTTP", ndpi_struct,
1858
15.4k
                     ndpi_search_http_tcp,
1859
15.4k
                     NDPI_SELECTION_BITMASK_PROTOCOL_V4_V6_TCP_WITH_PAYLOAD_WITHOUT_RETRANSMISSION,
1860
15.4k
                     1, NDPI_PROTOCOL_HTTP);
1861
15.4k
}