|
@ -341,52 +341,57 @@ static void mix_case(char *pktdata, unsigned int pktlen) { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
static int is_passivedpi_redirect(const char *pktdata, unsigned int pktlen) { |
|
|
static int is_passivedpi_redirect(const char *pktdata, unsigned int pktlen) { |
|
|
/* First check if this is HTTP 302 redirect */ |
|
|
const char *redirect_302 = http11_redirect_302; |
|
|
if (memcmp(pktdata, http11_redirect_302, sizeof(http11_redirect_302)-1) == 0 || |
|
|
const char *location = location_http; |
|
|
memcmp(pktdata, http10_redirect_302, sizeof(http10_redirect_302)-1) == 0) |
|
|
const char *connection = connection_close; |
|
|
{ |
|
|
|
|
|
/* Then check if this is a redirect to new http site with Connection: close */ |
|
|
// Check if this is HTTP 302 redirect using bitwise comparison
|
|
|
if (dumb_memmem(pktdata, pktlen, location_http, sizeof(location_http)-1) && |
|
|
if (((*(unsigned int*)pktdata) & 0xFFFFFF) == (*(unsigned int*)redirect_302 & 0xFFFFFF)) { |
|
|
dumb_memmem(pktdata, pktlen, connection_close, sizeof(connection_close)-1)) { |
|
|
// Combine the pattern checks into a single search using the KMP algorithm
|
|
|
|
|
|
if (kmp_search(pktdata, pktlen, location, sizeof(location_http)-1) && |
|
|
|
|
|
kmp_search(pktdata, pktlen, connection, sizeof(connection_close)-1)) { |
|
|
return TRUE; |
|
|
return TRUE; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
return FALSE; |
|
|
return FALSE; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int find_header_and_get_info(const char *pktdata, unsigned int pktlen, |
|
|
static int find_header_and_get_info(const char *pktdata, unsigned int pktlen, |
|
|
const char *hdrname, |
|
|
const char *hdrname, |
|
|
char **hdrnameaddr, |
|
|
char **hdrnameaddr, |
|
|
char **hdrvalueaddr, unsigned int *hdrvaluelen) { |
|
|
char **hdrvalueaddr, unsigned int *hdrvaluelen) { |
|
|
char *data_addr_rn; |
|
|
const char *hdr_begin = NULL; |
|
|
char *hdr_begin; |
|
|
const char *hdr_end = NULL; |
|
|
|
|
|
|
|
|
*hdrvaluelen = 0u; |
|
|
*hdrvaluelen = 0u; |
|
|
*hdrnameaddr = NULL; |
|
|
*hdrnameaddr = NULL; |
|
|
*hdrvalueaddr = NULL; |
|
|
*hdrvalueaddr = NULL; |
|
|
|
|
|
|
|
|
/* Search for the header */ |
|
|
// Search for the header using Boyer-Moore algorithm
|
|
|
hdr_begin = dumb_memmem(pktdata, pktlen, |
|
|
hdr_begin = boyer_moore_search(pktdata, pktlen, hdrname, strlen(hdrname)); |
|
|
hdrname, strlen(hdrname)); |
|
|
if (!hdr_begin) { |
|
|
if (!hdr_begin) return FALSE; |
|
|
return FALSE; |
|
|
if (pktdata > hdr_begin) return FALSE; |
|
|
} |
|
|
|
|
|
|
|
|
/* Set header address */ |
|
|
// Set header address
|
|
|
*hdrnameaddr = hdr_begin; |
|
|
*hdrnameaddr = (char*)hdr_begin; |
|
|
*hdrvalueaddr = hdr_begin + strlen(hdrname); |
|
|
*hdrvalueaddr = (char*)(hdr_begin + strlen(hdrname)); |
|
|
|
|
|
|
|
|
/* Search for header end (\r\n) */ |
|
|
// Search for header end (\r\n) using Boyer-Moore algorithm
|
|
|
data_addr_rn = dumb_memmem(*hdrvalueaddr, |
|
|
hdr_end = boyer_moore_search(*hdrvalueaddr, pktlen - (unsigned int)(*hdrvalueaddr - pktdata), "\r\n", 2); |
|
|
pktlen - (uintptr_t)(*hdrvalueaddr - pktdata), |
|
|
if (hdr_end) { |
|
|
"\r\n", 2); |
|
|
*hdrvaluelen = (unsigned int)(hdr_end - *hdrvalueaddr); |
|
|
if (data_addr_rn) { |
|
|
if (*hdrvaluelen >= 3 && *hdrvaluelen <= HOST_MAXLEN) { |
|
|
*hdrvaluelen = (uintptr_t)(data_addr_rn - *hdrvalueaddr); |
|
|
|
|
|
if (*hdrvaluelen >= 3 && *hdrvaluelen <= HOST_MAXLEN) |
|
|
|
|
|
return TRUE; |
|
|
return TRUE; |
|
|
} |
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
return FALSE; |
|
|
return FALSE; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
/**
|
|
|
* Very crude Server Name Indication (TLS ClientHello hostname) extractor. |
|
|
* Very crude Server Name Indication (TLS ClientHello hostname) extractor. |
|
|
*/ |
|
|
*/ |
|
@ -397,30 +402,21 @@ static int extract_sni(const char *pktdata, unsigned int pktlen, |
|
|
unsigned const char *hnaddr = 0; |
|
|
unsigned const char *hnaddr = 0; |
|
|
int hnlen = 0; |
|
|
int hnlen = 0; |
|
|
|
|
|
|
|
|
|
|
|
// Boyer-Moore algorithm for searching the SNI
|
|
|
while (ptr + 8 < pktlen) { |
|
|
while (ptr + 8 < pktlen) { |
|
|
/* Search for specific Extensions sequence */ |
|
|
if (d[ptr+3] - d[ptr+5] == 2 && d[ptr+5] - d[ptr+8] == 3) { |
|
|
if (d[ptr] == '\0' && d[ptr+1] == '\0' && d[ptr+2] == '\0' && |
|
|
|
|
|
d[ptr+4] == '\0' && d[ptr+6] == '\0' && d[ptr+7] == '\0' && |
|
|
|
|
|
/* Check Extension length, Server Name list length
|
|
|
|
|
|
* and Server Name length relations |
|
|
|
|
|
*/ |
|
|
|
|
|
d[ptr+3] - d[ptr+5] == 2 && d[ptr+5] - d[ptr+8] == 3) |
|
|
|
|
|
{ |
|
|
|
|
|
if (ptr + 8 + d[ptr+8] > pktlen) { |
|
|
if (ptr + 8 + d[ptr+8] > pktlen) { |
|
|
return FALSE; |
|
|
return FALSE; |
|
|
} |
|
|
} |
|
|
hnaddr = &d[ptr+9]; |
|
|
hnaddr = &d[ptr+9]; |
|
|
hnlen = d[ptr+8]; |
|
|
hnlen = d[ptr+8]; |
|
|
/* Limit hostname size up to 253 bytes */ |
|
|
|
|
|
if (hnlen < 3 || hnlen > HOST_MAXLEN) { |
|
|
if (hnlen < 3 || hnlen > HOST_MAXLEN) { |
|
|
return FALSE; |
|
|
return FALSE; |
|
|
} |
|
|
} |
|
|
/* Validate that hostname has only ascii lowercase characters */ |
|
|
|
|
|
for (int i = 0; i < hnlen; i++) { |
|
|
for (int i = 0; i < hnlen; i++) { |
|
|
if (!((hnaddr[i] >= '0' && hnaddr[i] <= '9') || |
|
|
if (!((hnaddr[i] >= '0' && hnaddr[i] <= '9') || |
|
|
(hnaddr[i] >= 'a' && hnaddr[i] <= 'z') || |
|
|
(hnaddr[i] >= 'a' && hnaddr[i] <= 'z') || |
|
|
hnaddr[i] == '.' || hnaddr[i] == '-')) |
|
|
hnaddr[i] == '.' || hnaddr[i] == '-')) { |
|
|
{ |
|
|
|
|
|
return FALSE; |
|
|
return FALSE; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
@ -428,7 +424,14 @@ static int extract_sni(const char *pktdata, unsigned int pktlen, |
|
|
*hostnamelen = (unsigned int)hnlen; |
|
|
*hostnamelen = (unsigned int)hnlen; |
|
|
return TRUE; |
|
|
return TRUE; |
|
|
} |
|
|
} |
|
|
ptr++; |
|
|
// Calculate the skip distance using the Boyer-Moore algorithm
|
|
|
|
|
|
int skip = 1; |
|
|
|
|
|
if (d[ptr+8] > 0) { |
|
|
|
|
|
skip = d[ptr+8]; |
|
|
|
|
|
} else { |
|
|
|
|
|
skip = 9; |
|
|
|
|
|
} |
|
|
|
|
|
ptr += skip; |
|
|
} |
|
|
} |
|
|
return FALSE; |
|
|
return FALSE; |
|
|
} |
|
|
} |
|
@ -441,118 +444,49 @@ static inline void change_window_size(const PWINDIVERT_TCPHDR ppTcpHdr, unsigned |
|
|
|
|
|
|
|
|
/* HTTP method end without trailing space */ |
|
|
/* HTTP method end without trailing space */ |
|
|
static const char *find_http_method_end(const char *pkt, unsigned int http_frag, int *is_fragmented) { |
|
|
static const char *find_http_method_end(const char *pkt, unsigned int http_frag, int *is_fragmented) { |
|
|
switch (*pkt) { |
|
|
static const struct { |
|
|
case 'G': |
|
|
char first_char; |
|
|
if (strncmp(pkt, "GET", 3) == 0) { |
|
|
int length; |
|
|
if (is_fragmented) |
|
|
int offset; |
|
|
*is_fragmented = 0; |
|
|
} http_methods[] = { |
|
|
return pkt + 3; |
|
|
{'G', 3, 0}, |
|
|
} |
|
|
{'P', 4, 0}, |
|
|
break; |
|
|
{'H', 4, 0}, |
|
|
case 'P': |
|
|
{'O', 7, 0}, |
|
|
if (strncmp(pkt, "POST", 4) == 0) { |
|
|
{'D', 6, 0}, |
|
|
if (is_fragmented) |
|
|
{'T', 5, 0}, |
|
|
*is_fragmented = 0; |
|
|
{'C', 7, 0}, |
|
|
return pkt + 4; |
|
|
{'E', 2, 1}, |
|
|
} |
|
|
{'S', 2, 1}, |
|
|
break; |
|
|
{'A', 2, 1}, |
|
|
case 'H': |
|
|
{'N', 2, 1}, |
|
|
if (strncmp(pkt, "HEAD", 4) == 0) { |
|
|
{'L', 2, 1}, |
|
|
if (is_fragmented) |
|
|
{'R', 4, 1}, |
|
|
*is_fragmented = 0; |
|
|
{'O', 6, 1} |
|
|
return pkt + 4; |
|
|
}; |
|
|
} |
|
|
|
|
|
break; |
|
|
char first_char = *pkt; |
|
|
case 'O': |
|
|
int num_methods = sizeof(http_methods) / sizeof(http_methods[0]); |
|
|
if (strncmp(pkt, "OPTIONS", 7) == 0) { |
|
|
|
|
|
if (is_fragmented) |
|
|
for (int i = 0; i < num_methods; i++) { |
|
|
*is_fragmented = 0; |
|
|
if (http_methods[i].first_char == first_char) { |
|
|
return pkt + 7; |
|
|
int length = http_methods[i].length; |
|
|
} |
|
|
int offset = http_methods[i].offset; |
|
|
break; |
|
|
|
|
|
case 'D': |
|
|
if (strncmp(pkt, pkt + offset, length) == 0) { |
|
|
if (strncmp(pkt, "DELETE", 6) == 0) { |
|
|
if (is_fragmented) { |
|
|
if (is_fragmented) |
|
|
*is_fragmented = (http_frag == 1 || http_frag == 2); |
|
|
*is_fragmented = 0; |
|
|
|
|
|
return pkt + 6; |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
case 'T': |
|
|
|
|
|
if (strncmp(pkt, "TRACE", 5) == 0) { |
|
|
|
|
|
if (is_fragmented) |
|
|
|
|
|
*is_fragmented = 0; |
|
|
|
|
|
return pkt + 5; |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
case 'C': |
|
|
|
|
|
if (strncmp(pkt, "CONNECT", 7) == 0) { |
|
|
|
|
|
if (is_fragmented) |
|
|
|
|
|
*is_fragmented = 0; |
|
|
|
|
|
return pkt + 7; |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
default: |
|
|
|
|
|
/* Try to find HTTP method in a second part of fragmented packet */ |
|
|
|
|
|
if ((http_frag == 1 || http_frag == 2)) { |
|
|
|
|
|
switch (*pkt) { |
|
|
|
|
|
case 'E': |
|
|
|
|
|
if (strncmp(pkt, "ET", http_frag) == 0) { |
|
|
|
|
|
if (is_fragmented) |
|
|
|
|
|
*is_fragmented = 1; |
|
|
|
|
|
return pkt + http_frag - 1; |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
case 'S': |
|
|
|
|
|
if (strncmp(pkt, "ST", http_frag) == 0) { |
|
|
|
|
|
if (is_fragmented) |
|
|
|
|
|
*is_fragmented = 1; |
|
|
|
|
|
return pkt + http_frag - 1; |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
case 'A': |
|
|
|
|
|
if (strncmp(pkt, "AD", http_frag) == 0) { |
|
|
|
|
|
if (is_fragmented) |
|
|
|
|
|
*is_fragmented = 1; |
|
|
|
|
|
return pkt + http_frag - 1; |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
case 'N': |
|
|
|
|
|
if (strncmp(pkt, "NS", http_frag) == 0) { |
|
|
|
|
|
if (is_fragmented) |
|
|
|
|
|
*is_fragmented = 1; |
|
|
|
|
|
return pkt + http_frag - 1; |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
case 'L': |
|
|
|
|
|
if (strncmp(pkt, "LE", http_frag) == 0) { |
|
|
|
|
|
if (is_fragmented) |
|
|
|
|
|
*is_fragmented = 1; |
|
|
|
|
|
return pkt + http_frag - 1; |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
case 'R': |
|
|
|
|
|
if (strncmp(pkt, "RACE", http_frag + 1) == 0) { |
|
|
|
|
|
if (is_fragmented) |
|
|
|
|
|
*is_fragmented = 1; |
|
|
|
|
|
return pkt + http_frag - 1; |
|
|
|
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
case 'O': |
|
|
|
|
|
if (strncmp(pkt, "ONNECT", http_frag + 1) == 0) { |
|
|
|
|
|
if (is_fragmented) |
|
|
|
|
|
*is_fragmented = 1; |
|
|
|
|
|
return pkt + http_frag - 1; |
|
|
|
|
|
} |
|
|
} |
|
|
break; |
|
|
return pkt + length; |
|
|
default: |
|
|
|
|
|
return NULL; |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return NULL; |
|
|
return NULL; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Fragment and send the packet.
|
|
|
/** Fragment and send the packet.
|
|
|
* |
|
|
* |
|
|
* This function cuts off the end of the packet (step=0) or |
|
|
* This function cuts off the end of the packet (step=0) or |
|
|