First commit of the Windows Divert project
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
DIRS= \
|
||||
netdump \
|
||||
netfilter \
|
||||
webfilter
|
||||
@@ -0,0 +1 @@
|
||||
!INCLUDE $(NTMAKEENV)\makefile.def
|
||||
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* netdump.c
|
||||
* (C) 2011, all rights reserved,
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* DESCRIPTION:
|
||||
* This is a simple traffic monitor.
|
||||
*
|
||||
* usage: netdump.exe divert-filter
|
||||
*
|
||||
* NOTE: Using Divert for this purpose is rather inefficient, as each captured
|
||||
* packet must be reinjected. For packet sniffing, it's better to use a
|
||||
* package that copies packets, not copies-and-drops such as Divert.
|
||||
*/
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "divert.h"
|
||||
|
||||
#define MAXBUF 2048
|
||||
|
||||
/*
|
||||
* Entry.
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
HANDLE handle, console;
|
||||
size_t slen, flen;
|
||||
UINT i;
|
||||
char filter[MAXBUF];
|
||||
char packet[MAXBUF];
|
||||
PDIVERT_PACKET ppacket = (PDIVERT_PACKET)packet;
|
||||
UINT ppacket_len;
|
||||
PDIVERT_IPHDR ip_header;
|
||||
PDIVERT_IPV6HDR ipv6_header;
|
||||
PDIVERT_ICMPHDR icmp_header;
|
||||
PDIVERT_ICMPV6HDR icmpv6_header;
|
||||
PDIVERT_TCPHDR tcp_header;
|
||||
PDIVERT_UDPHDR udp_header;
|
||||
UINT8 *data;
|
||||
UINT data_len;
|
||||
|
||||
// Concat all command line args into a filter string.
|
||||
flen = 0;
|
||||
for (i = 1; (int)i < argc; i++)
|
||||
{
|
||||
slen = strlen(argv[i]);
|
||||
if (flen + slen + 1 >= MAXBUF)
|
||||
{
|
||||
fprintf(stderr, "error: filter too long\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
strcpy(filter+flen, argv[i]);
|
||||
flen += slen;
|
||||
filter[flen] = ' ';
|
||||
flen++;
|
||||
}
|
||||
filter[flen] = '\0';
|
||||
|
||||
// Get console for pretty colors.
|
||||
console = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
// Divert traffic matching the filter:
|
||||
handle = DivertOpen(filter);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
if (GetLastError() == ERROR_INVALID_PARAMETER)
|
||||
{
|
||||
fprintf(stderr, "error: filter syntax error\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
fprintf(stderr, "error: failed to open Divert device (%d)\n",
|
||||
GetLastError());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Main loop:
|
||||
while (TRUE)
|
||||
{
|
||||
// Read a matching packet.
|
||||
if (!DivertRecv(handle, ppacket, sizeof(packet), &ppacket_len))
|
||||
{
|
||||
fprintf(stderr, "warning: failed to read packet (%d)\n",
|
||||
GetLastError());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Re-inject the matching packet.
|
||||
if (!DivertSend(handle, ppacket, ppacket_len, NULL))
|
||||
{
|
||||
fprintf(stderr, "warning: failed to reinject packet (%d)\n",
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
// Print info about the matching packet.
|
||||
DivertHelperParse(ppacket, ppacket_len, &ip_header, &ipv6_header,
|
||||
&icmp_header, &icmpv6_header, &tcp_header, &udp_header, NULL,
|
||||
NULL);
|
||||
if (ip_header == NULL && ipv6_header == NULL)
|
||||
{
|
||||
fprintf(stderr, "warning: junk packet\n");
|
||||
}
|
||||
|
||||
// Dump packet info:
|
||||
putchar('\n');
|
||||
SetConsoleTextAttribute(console, FOREGROUND_RED);
|
||||
printf("Packet [Direction=%u IfIdx=%u SubIfIdx=%u]\n",
|
||||
ppacket->Direction, ppacket->IfIdx, ppacket->SubIfIdx);
|
||||
if (ip_header != NULL)
|
||||
{
|
||||
UINT8 *src_addr = (UINT8 *)&ip_header->SrcAddr;
|
||||
UINT8 *dst_addr = (UINT8 *)&ip_header->DstAddr;
|
||||
SetConsoleTextAttribute(console,
|
||||
FOREGROUND_GREEN | FOREGROUND_RED);
|
||||
printf("IPv4 [Version=%u HdrLength=%u TOS=%u Length=%u Id=0x%.4X "
|
||||
"Reserved=%u DF=%u MF=%u FragOff=%u TTL=%u Protocol=%u "
|
||||
"Checksum=0x%.4X SrcAddr=%u.%u.%u.%u DstAddr=%u.%u.%u.%u]\n",
|
||||
ip_header->Version, ip_header->HdrLength,
|
||||
ntohs(ip_header->TOS), ntohs(ip_header->Length),
|
||||
ntohs(ip_header->Id), DIVERT_IPHDR_GET_RESERVED(ip_header),
|
||||
DIVERT_IPHDR_GET_DF(ip_header), DIVERT_IPHDR_GET_MF(ip_header),
|
||||
ntohs(DIVERT_IPHDR_GET_FRAGOFF(ip_header)), ip_header->TTL,
|
||||
ip_header->Protocol, ntohs(ip_header->Checksum),
|
||||
src_addr[0], src_addr[1], src_addr[2], src_addr[3],
|
||||
dst_addr[0], dst_addr[1], dst_addr[2], dst_addr[3]);
|
||||
}
|
||||
if (ipv6_header != NULL)
|
||||
{
|
||||
UINT16 *src_addr = (UINT16 *)&ipv6_header->SrcAddr;
|
||||
UINT16 *dst_addr = (UINT16 *)&ipv6_header->DstAddr;
|
||||
SetConsoleTextAttribute(console,
|
||||
FOREGROUND_GREEN | FOREGROUND_RED);
|
||||
printf("IPv6 [Version=%u TrafficClass=%u FlowLabel=%u Length=%u "
|
||||
"NextHdr=%u HopLimit=%u SrcAddr=",
|
||||
ipv6_header->Version,
|
||||
DIVERT_IPV6HDR_GET_TRAFFICCLASS(ipv6_header),
|
||||
ntohl(DIVERT_IPV6HDR_GET_FLOWLABEL(ipv6_header)),
|
||||
ntohs(ipv6_header->Length), ipv6_header->NextHdr,
|
||||
ipv6_header->HopLimit);
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
printf("%x%c", ntohs(src_addr[i]), (i == 7? ' ': ':'));
|
||||
}
|
||||
fputs("DstAddr=", stdout);
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
printf("%x", ntohs(dst_addr[i]));
|
||||
if (i != 7)
|
||||
{
|
||||
putchar(':');
|
||||
}
|
||||
}
|
||||
fputs("]\n", stdout);
|
||||
}
|
||||
if (icmp_header != NULL)
|
||||
{
|
||||
SetConsoleTextAttribute(console, FOREGROUND_RED);
|
||||
printf("ICMP [Type=%u Code=%u Checksum=0x%.4X Body=0x%.8X]\n",
|
||||
icmp_header->Type, icmp_header->Code,
|
||||
ntohs(icmp_header->Checksum), ntohl(icmp_header->Body));
|
||||
}
|
||||
if (icmpv6_header != NULL)
|
||||
{
|
||||
SetConsoleTextAttribute(console, FOREGROUND_RED);
|
||||
printf("ICMPV6 [Type=%u Code=%u Checksum=0x%.4X Body=0x%.8X]\n",
|
||||
icmpv6_header->Type, icmpv6_header->Code,
|
||||
ntohs(icmpv6_header->Checksum), ntohl(icmpv6_header->Body));
|
||||
}
|
||||
if (tcp_header != NULL)
|
||||
{
|
||||
SetConsoleTextAttribute(console, FOREGROUND_GREEN);
|
||||
printf("TCP [SrcPort=%u DstPort=%u SeqNum=%u AckNum=%u "
|
||||
"HdrLength=%u Reserved1=%u Reserved2=%u Urg=%u Ack=%u "
|
||||
"Psh=%u Rst=%u Syn=%u Fin=%u Window=%u Checksum=0x%.4X "
|
||||
"UrgPtr=%u]\n",
|
||||
ntohs(tcp_header->SrcPort), ntohs(tcp_header->DstPort),
|
||||
ntohl(tcp_header->SeqNum), ntohl(tcp_header->AckNum),
|
||||
tcp_header->HdrLength, tcp_header->Reserved1,
|
||||
tcp_header->Reserved2, tcp_header->Urg, tcp_header->Ack,
|
||||
tcp_header->Psh, tcp_header->Rst, tcp_header->Syn,
|
||||
tcp_header->Fin, ntohs(tcp_header->Window),
|
||||
ntohs(tcp_header->Checksum), ntohs(tcp_header->UrgPtr));
|
||||
}
|
||||
if (udp_header != NULL)
|
||||
{
|
||||
SetConsoleTextAttribute(console, FOREGROUND_GREEN);
|
||||
printf("UDP [SrcPort=%u DstPort=%u Length=%u "
|
||||
"Checksum=0x%.4X]\n",
|
||||
ntohs(udp_header->SrcPort), ntohs(udp_header->DstPort),
|
||||
ntohs(udp_header->Length), ntohs(udp_header->Checksum));
|
||||
}
|
||||
SetConsoleTextAttribute(console, FOREGROUND_GREEN | FOREGROUND_BLUE);
|
||||
data = DIVERT_PACKET_DATA(ppacket);
|
||||
data_len = ppacket_len - sizeof(DIVERT_PACKET);
|
||||
for (i = 0; i < data_len; i++)
|
||||
{
|
||||
if (i % 20 == 0)
|
||||
{
|
||||
printf("\n\t");
|
||||
}
|
||||
printf("%.2X", (unsigned)data[i]);
|
||||
}
|
||||
SetConsoleTextAttribute(console, FOREGROUND_RED | FOREGROUND_BLUE);
|
||||
for (i = 0; i < data_len; i++)
|
||||
{
|
||||
if (i % 40 == 0)
|
||||
{
|
||||
printf("\n\t");
|
||||
}
|
||||
if (isprint(data[i]))
|
||||
{
|
||||
putchar(data[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
putchar('.');
|
||||
}
|
||||
}
|
||||
putchar('\n');
|
||||
SetConsoleTextAttribute(console,
|
||||
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# sources
|
||||
# (C) 2011, all rights reserved,
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
TARGETNAME=netdump
|
||||
TARGETTYPE=PROGRAM
|
||||
TARGETPATH=..\..\install
|
||||
TARGETLIBS=\
|
||||
$(SDK_LIB_PATH)\setupapi.lib \
|
||||
$(SDK_LIB_PATH)\user32.lib \
|
||||
$(SDK_LIB_PATH)\ws2_32.lib \
|
||||
$(TARGETPATH)\$(_BUILDARCH)\divert.lib
|
||||
UMTYPE=console
|
||||
UMENTRY=main
|
||||
USE_MSVCRT=1
|
||||
INCLUDES=$(DDK_INC_PATH);$(KMDF_INC_PATH)\$(KMDF_VER_PATH);..\..\include
|
||||
SOURCES=netdump.c
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
!INCLUDE $(NTMAKEENV)\makefile.def
|
||||
@@ -0,0 +1,445 @@
|
||||
/*
|
||||
* netfilter.c
|
||||
* (C) 2011, all rights reserved,
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* DESCRIPTION:
|
||||
* This is a simple traffic filter
|
||||
*
|
||||
* usage: netfilter.exe divert-filter
|
||||
*
|
||||
* Any traffic that matches the divert-filter will be blocked using one of
|
||||
* the following methods:
|
||||
* - TCP: send a TCP RST to the packet's source.
|
||||
* - UDP: send a ICMP(v6) "destination unreachable" to the packet's source.
|
||||
* - ICMP/ICMPv6: Drop the packet.
|
||||
*
|
||||
* This program is similar to Linux's iptables with the "-j REJECT" target.
|
||||
*/
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "divert.h"
|
||||
|
||||
#define MAXBUF 2048
|
||||
|
||||
/*
|
||||
* Pre-fabricated packets.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
DIVERT_PACKET divert;
|
||||
DIVERT_IPHDR ip;
|
||||
} PACKET, *PPACKET;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DIVERT_PACKET divert;
|
||||
DIVERT_IPV6HDR ipv6;
|
||||
} PACKETV6, *PPACKETV6;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PACKET header;
|
||||
DIVERT_TCPHDR tcp;
|
||||
} TCPPACKET, *PTCPPACKET;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PACKETV6 header;
|
||||
DIVERT_TCPHDR tcp;
|
||||
} TCPV6PACKET, *PTCPV6PACKET;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PACKET header;
|
||||
DIVERT_ICMPHDR icmp;
|
||||
UINT8 data[];
|
||||
} ICMPPACKET, *PICMPPACKET;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
PACKETV6 header;
|
||||
DIVERT_ICMPV6HDR icmpv6;
|
||||
UINT8 data[];
|
||||
} ICMPV6PACKET, *PICMPV6PACKET;
|
||||
|
||||
/*
|
||||
* Prototypes.
|
||||
*/
|
||||
static void PacketIpInit(PPACKET packet);
|
||||
static void PacketIpTcpInit(PTCPPACKET packet);
|
||||
static void PacketIpIcmpInit(PICMPPACKET packet);
|
||||
static void PacketIpv6Init(PPACKETV6 packet);
|
||||
static void PacketIpv6TcpInit(PTCPV6PACKET packet);
|
||||
static void PacketIpv6Icmpv6Init(PICMPV6PACKET packet);
|
||||
|
||||
/*
|
||||
* Entry.
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
HANDLE handle, console;
|
||||
size_t slen, flen;
|
||||
UINT i;
|
||||
char filter[MAXBUF];
|
||||
char packet[MAXBUF];
|
||||
PDIVERT_PACKET ppacket = (PDIVERT_PACKET)packet;
|
||||
UINT ppacket_len;
|
||||
PDIVERT_IPHDR ip_header;
|
||||
PDIVERT_IPV6HDR ipv6_header;
|
||||
PDIVERT_ICMPHDR icmp_header;
|
||||
PDIVERT_ICMPV6HDR icmpv6_header;
|
||||
PDIVERT_TCPHDR tcp_header;
|
||||
PDIVERT_UDPHDR udp_header;
|
||||
UINT payload_len;
|
||||
|
||||
TCPPACKET reset0;
|
||||
PTCPPACKET reset = &reset0;
|
||||
UINT8 dnr0[sizeof(ICMPPACKET) + 0x0F*sizeof(UINT32) + 8 + 1];
|
||||
PICMPPACKET dnr = (PICMPPACKET)dnr0;
|
||||
|
||||
TCPV6PACKET resetv6_0;
|
||||
PTCPV6PACKET resetv6 = &resetv6_0;
|
||||
UINT8 dnrv6_0[sizeof(ICMPV6PACKET) + sizeof(DIVERT_IPV6HDR) +
|
||||
sizeof(DIVERT_TCPHDR)];
|
||||
PICMPV6PACKET dnrv6 = (PICMPV6PACKET)dnrv6_0;
|
||||
|
||||
// Concat all command line args into a filter string.
|
||||
flen = 0;
|
||||
for (i = 1; (int)i < argc; i++)
|
||||
{
|
||||
slen = strlen(argv[i]);
|
||||
if (flen + slen + 1 >= MAXBUF)
|
||||
{
|
||||
fprintf(stderr, "error: filter too long\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
strcpy(filter+flen, argv[i]);
|
||||
flen += slen;
|
||||
filter[flen] = ' ';
|
||||
flen++;
|
||||
}
|
||||
filter[flen] = '\0';
|
||||
|
||||
// Initialize all packets.
|
||||
PacketIpTcpInit(reset);
|
||||
reset->tcp.Rst = 1;
|
||||
reset->tcp.Ack = 1;
|
||||
PacketIpIcmpInit(dnr);
|
||||
dnr->icmp.Type = 3; // Destination not reachable.
|
||||
dnr->icmp.Code = 3; // Port not reachable.
|
||||
PacketIpv6TcpInit(resetv6);
|
||||
resetv6->tcp.Rst = 1;
|
||||
resetv6->tcp.Ack = 1;
|
||||
PacketIpv6Icmpv6Init(dnrv6);
|
||||
dnrv6->header.ipv6.Length = htons(sizeof(DIVERT_ICMPV6HDR) + 4 +
|
||||
sizeof(DIVERT_IPV6HDR) + sizeof(DIVERT_TCPHDR));
|
||||
dnrv6->icmpv6.Type = 1; // Destination not reachable.
|
||||
dnrv6->icmpv6.Code = 4; // Port not reachable.
|
||||
|
||||
// Get console for pretty colors.
|
||||
console = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
// Divert traffic matching the filter:
|
||||
handle = DivertOpen(filter);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
if (GetLastError() == ERROR_INVALID_PARAMETER)
|
||||
{
|
||||
fprintf(stderr, "error: filter syntax error\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
fprintf(stderr, "error: failed to open Divert device (%d)\n",
|
||||
GetLastError());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Main loop:
|
||||
while (TRUE)
|
||||
{
|
||||
// Read a matching packet.
|
||||
if (!DivertRecv(handle, ppacket, sizeof(packet), &ppacket_len))
|
||||
{
|
||||
fprintf(stderr, "warning: failed to read packet\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Print info about the matching packet.
|
||||
DivertHelperParse(ppacket, ppacket_len, &ip_header, &ipv6_header,
|
||||
&icmp_header, &icmpv6_header, &tcp_header, &udp_header, NULL,
|
||||
&payload_len);
|
||||
if (ip_header == NULL && ipv6_header == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Dump packet info:
|
||||
SetConsoleTextAttribute(console, FOREGROUND_RED);
|
||||
fputs("BLOCK ", stdout);
|
||||
SetConsoleTextAttribute(console,
|
||||
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
||||
if (ip_header != NULL)
|
||||
{
|
||||
UINT8 *src_addr = (UINT8 *)&ip_header->SrcAddr;
|
||||
UINT8 *dst_addr = (UINT8 *)&ip_header->DstAddr;
|
||||
printf("ip.SrcAddr=%u.%u.%u.%u ip.DstAddr=%u.%u.%u.%u ",
|
||||
src_addr[0], src_addr[1], src_addr[2], src_addr[3],
|
||||
dst_addr[0], dst_addr[1], dst_addr[2], dst_addr[3]);
|
||||
}
|
||||
if (ipv6_header != NULL)
|
||||
{
|
||||
UINT16 *src_addr = (UINT16 *)&ipv6_header->SrcAddr;
|
||||
UINT16 *dst_addr = (UINT16 *)&ipv6_header->DstAddr;
|
||||
fputs("ipv6.SrcAddr=", stdout);
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
printf("%x%c", ntohs(src_addr[i]), (i == 7? ' ': ':'));
|
||||
}
|
||||
fputs(" ipv6.DstAddr=", stdout);
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
printf("%x%c", ntohs(dst_addr[i]), (i == 7? ' ': ':'));
|
||||
}
|
||||
putchar(' ');
|
||||
}
|
||||
if (icmp_header != NULL)
|
||||
{
|
||||
printf("icmp.Type=%u icmp.Code=%u ",
|
||||
icmp_header->Type, icmp_header->Code);
|
||||
// Simply drop ICMP
|
||||
}
|
||||
if (icmpv6_header != NULL)
|
||||
{
|
||||
printf("icmpv6.Type=%u icmpv6.Code=%u ",
|
||||
icmpv6_header->Type, icmpv6_header->Code);
|
||||
// Simply drop ICMPv6
|
||||
}
|
||||
if (tcp_header != NULL)
|
||||
{
|
||||
printf("tcp.SrcPort=%u tcp.DstPort=%u tcp.Flags=",
|
||||
ntohs(tcp_header->SrcPort), ntohs(tcp_header->DstPort));
|
||||
if (tcp_header->Fin)
|
||||
{
|
||||
fputs("[FIN]", stdout);
|
||||
}
|
||||
if (tcp_header->Rst)
|
||||
{
|
||||
fputs("[RST]", stdout);
|
||||
}
|
||||
if (tcp_header->Urg)
|
||||
{
|
||||
fputs("[URG]", stdout);
|
||||
}
|
||||
if (tcp_header->Syn)
|
||||
{
|
||||
fputs("[SYN]", stdout);
|
||||
}
|
||||
if (tcp_header->Psh)
|
||||
{
|
||||
fputs("[PSH]", stdout);
|
||||
}
|
||||
if (tcp_header->Ack)
|
||||
{
|
||||
fputs("[ACK]", stdout);
|
||||
}
|
||||
putchar(' ');
|
||||
|
||||
|
||||
if (ip_header != NULL)
|
||||
{
|
||||
reset->header.divert.IfIdx = ppacket->IfIdx;
|
||||
reset->header.divert.SubIfIdx = ppacket->SubIfIdx;
|
||||
reset->header.divert.Direction = !ppacket->Direction;
|
||||
reset->header.ip.SrcAddr = ip_header->DstAddr;
|
||||
reset->header.ip.DstAddr = ip_header->SrcAddr;
|
||||
reset->tcp.SrcPort = tcp_header->DstPort;
|
||||
reset->tcp.DstPort = tcp_header->SrcPort;
|
||||
reset->tcp.SeqNum =
|
||||
(tcp_header->Ack? tcp_header->AckNum: 0);
|
||||
reset->tcp.AckNum =
|
||||
(tcp_header->Syn?
|
||||
htonl(ntohl(tcp_header->SeqNum) + 1):
|
||||
htonl(ntohl(tcp_header->SeqNum) + payload_len));
|
||||
|
||||
DivertHelperCalcChecksums((PDIVERT_PACKET)reset,
|
||||
sizeof(TCPPACKET), 0);
|
||||
if (!DivertSend(handle, (PDIVERT_PACKET)reset,
|
||||
sizeof(TCPPACKET), NULL))
|
||||
{
|
||||
fprintf(stderr, "warning: failed to send TCP reset (%d)\n",
|
||||
GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
if (ipv6_header != NULL)
|
||||
{
|
||||
resetv6->header.divert.IfIdx = ppacket->IfIdx;
|
||||
resetv6->header.divert.SubIfIdx = ppacket->SubIfIdx;
|
||||
resetv6->header.divert.Direction = !ppacket->Direction;
|
||||
memcpy(resetv6->header.ipv6.SrcAddr, ipv6_header->DstAddr,
|
||||
sizeof(resetv6->header.ipv6.SrcAddr));
|
||||
memcpy(resetv6->header.ipv6.DstAddr, ipv6_header->SrcAddr,
|
||||
sizeof(resetv6->header.ipv6.DstAddr));
|
||||
resetv6->tcp.SrcPort = tcp_header->DstPort;
|
||||
resetv6->tcp.DstPort = tcp_header->SrcPort;
|
||||
resetv6->tcp.SeqNum =
|
||||
(tcp_header->Ack? tcp_header->AckNum: 0);
|
||||
resetv6->tcp.AckNum =
|
||||
(tcp_header->Syn?
|
||||
htonl(ntohl(tcp_header->SeqNum) + 1):
|
||||
htonl(ntohl(tcp_header->SeqNum) + payload_len));
|
||||
|
||||
DivertHelperCalcChecksums((PDIVERT_PACKET)resetv6,
|
||||
sizeof(TCPV6PACKET), 0);
|
||||
if (!DivertSend(handle, (PDIVERT_PACKET)resetv6,
|
||||
sizeof(TCPV6PACKET), NULL))
|
||||
{
|
||||
fprintf(stderr, "warning: failed to send TCP (IPV6) "
|
||||
"reset (%d)\n", GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (udp_header != NULL)
|
||||
{
|
||||
printf("udp.SrcPort=%u udp.DstPort=%u ",
|
||||
ntohs(udp_header->SrcPort), ntohs(udp_header->DstPort));
|
||||
|
||||
if (ip_header != NULL)
|
||||
{
|
||||
// NOTE: For some ICMP error messages, WFP does not seem to
|
||||
// support INBOUND injection. As a work-around, we
|
||||
// always inject OUTBOUND.
|
||||
UINT icmp_length = ip_header->HdrLength*sizeof(UINT32) + 8;
|
||||
memcpy(dnr->data, ip_header, icmp_length);
|
||||
icmp_length += sizeof(ICMPPACKET);
|
||||
dnr->header.divert.IfIdx = ppacket->IfIdx;
|
||||
dnr->header.divert.SubIfIdx = ppacket->SubIfIdx;
|
||||
dnr->header.divert.Direction =
|
||||
DIVERT_PACKET_DIRECTION_OUTBOUND;
|
||||
dnr->header.ip.Length =
|
||||
htons(icmp_length - sizeof(DIVERT_PACKET));
|
||||
dnr->header.ip.SrcAddr = ip_header->DstAddr;
|
||||
dnr->header.ip.DstAddr = ip_header->SrcAddr;
|
||||
DivertHelperCalcChecksums((PDIVERT_PACKET)dnr, icmp_length, 0);
|
||||
if (!DivertSend(handle, (PDIVERT_PACKET)dnr, icmp_length,
|
||||
NULL))
|
||||
{
|
||||
fprintf(stderr, "warning: failed to send ICMP message "
|
||||
"(%d)\n", GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
if (ipv6_header != NULL)
|
||||
{
|
||||
UINT icmpv6_length = sizeof(DIVERT_IPV6HDR) +
|
||||
sizeof(DIVERT_TCPHDR);
|
||||
memcpy(dnrv6->data, ipv6_header, icmpv6_length);
|
||||
icmpv6_length += sizeof(ICMPV6PACKET);
|
||||
dnrv6->header.divert.IfIdx = ppacket->IfIdx;
|
||||
dnrv6->header.divert.SubIfIdx = ppacket->SubIfIdx;
|
||||
dnrv6->header.divert.Direction =
|
||||
DIVERT_PACKET_DIRECTION_OUTBOUND;
|
||||
memcpy(dnrv6->header.ipv6.SrcAddr, ipv6_header->DstAddr,
|
||||
sizeof(dnrv6->header.ipv6.SrcAddr));
|
||||
memcpy(dnrv6->header.ipv6.DstAddr, ipv6_header->SrcAddr,
|
||||
sizeof(dnrv6->header.ipv6.DstAddr));
|
||||
DivertHelperCalcChecksums((PDIVERT_PACKET)dnrv6, icmpv6_length,
|
||||
0);
|
||||
if (!DivertSend(handle, (PDIVERT_PACKET)dnrv6, icmpv6_length,
|
||||
NULL))
|
||||
{
|
||||
fprintf(stderr, "warning: failed to send ICMPv6 message "
|
||||
"(%d)\n", GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize a PACKET.
|
||||
*/
|
||||
static void PacketIpInit(PPACKET packet)
|
||||
{
|
||||
memset(packet, 0, sizeof(PACKET));
|
||||
packet->ip.Version = 4;
|
||||
packet->ip.HdrLength = sizeof(DIVERT_IPHDR) / sizeof(UINT32);
|
||||
packet->ip.Id = ntohs(0xDEAD);
|
||||
packet->ip.TTL = 64;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize a TCPPACKET.
|
||||
*/
|
||||
static void PacketIpTcpInit(PTCPPACKET packet)
|
||||
{
|
||||
memset(packet, 0, sizeof(TCPPACKET));
|
||||
PacketIpInit(&packet->header);
|
||||
packet->header.ip.Length = htons(sizeof(TCPPACKET) -
|
||||
sizeof(DIVERT_PACKET));
|
||||
packet->header.ip.Protocol = IPPROTO_TCP;
|
||||
packet->tcp.HdrLength = sizeof(DIVERT_TCPHDR) / sizeof(UINT32);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize an ICMPPACKET.
|
||||
*/
|
||||
static void PacketIpIcmpInit(PICMPPACKET packet)
|
||||
{
|
||||
memset(packet, 0, sizeof(ICMPPACKET));
|
||||
PacketIpInit(&packet->header);
|
||||
packet->header.ip.Protocol = IPPROTO_ICMP;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize a PACKETV6.
|
||||
*/
|
||||
static void PacketIpv6Init(PPACKETV6 packet)
|
||||
{
|
||||
memset(packet, 0, sizeof(PACKETV6));
|
||||
packet->ipv6.Version = 6;
|
||||
packet->ipv6.HopLimit = 64;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize a TCPV6PACKET.
|
||||
*/
|
||||
static void PacketIpv6TcpInit(PTCPV6PACKET packet)
|
||||
{
|
||||
memset(packet, 0, sizeof(TCPV6PACKET));
|
||||
PacketIpv6Init(&packet->header);
|
||||
packet->header.ipv6.Length = htons(sizeof(DIVERT_TCPHDR));
|
||||
packet->header.ipv6.NextHdr = IPPROTO_TCP;
|
||||
packet->tcp.HdrLength = sizeof(DIVERT_TCPHDR) / sizeof(UINT32);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize an ICMP PACKET.
|
||||
*/
|
||||
static void PacketIpv6Icmpv6Init(PICMPV6PACKET packet)
|
||||
{
|
||||
memset(packet, 0, sizeof(ICMPV6PACKET));
|
||||
PacketIpv6Init(&packet->header);
|
||||
packet->header.ipv6.NextHdr = IPPROTO_ICMPV6;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# sources
|
||||
# (C) 2011, all rights reserved,
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
TARGETNAME=netfilter
|
||||
TARGETTYPE=PROGRAM
|
||||
TARGETPATH=..\..\install
|
||||
TARGETLIBS=\
|
||||
$(SDK_LIB_PATH)\setupapi.lib \
|
||||
$(SDK_LIB_PATH)\user32.lib \
|
||||
$(SDK_LIB_PATH)\ws2_32.lib \
|
||||
$(TARGETPATH)\$(_BUILDARCH)\divert.lib
|
||||
UMTYPE=console
|
||||
UMENTRY=main
|
||||
USE_MSVCRT=1
|
||||
INCLUDES=$(DDK_INC_PATH);$(KMDF_INC_PATH)\$(KMDF_VER_PATH);..\..\include
|
||||
SOURCES=netfilter.c
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
!INCLUDE $(NTMAKEENV)\makefile.def
|
||||
@@ -0,0 +1,30 @@
|
||||
# sources
|
||||
# (C) 2011, all rights reserved,
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
TARGETNAME=webfilter
|
||||
TARGETTYPE=PROGRAM
|
||||
TARGETPATH=..\..\install
|
||||
TARGETLIBS=\
|
||||
$(SDK_LIB_PATH)\setupapi.lib \
|
||||
$(SDK_LIB_PATH)\user32.lib \
|
||||
$(SDK_LIB_PATH)\ws2_32.lib \
|
||||
$(TARGETPATH)\$(_BUILDARCH)\divert.lib
|
||||
UMTYPE=console
|
||||
UMENTRY=main
|
||||
USE_MSVCRT=1
|
||||
INCLUDES=$(DDK_INC_PATH);$(KMDF_INC_PATH)\$(KMDF_VER_PATH);..\..\include
|
||||
SOURCES=webfilter.c
|
||||
|
||||
@@ -0,0 +1,596 @@
|
||||
/*
|
||||
* webfilter.c
|
||||
* (C) 2011, all rights reserved,
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* DESCRIPTION:
|
||||
* This is a simple web (HTTP) filter using the Divert device.
|
||||
*
|
||||
* It works by intercepting outbound HTTP GET/POST requests and matching
|
||||
* the URL against a blacklist. If the URL is matched, we hijack the TCP
|
||||
* connection, reseting the connection at the server end, and sending a
|
||||
* blockpage to the browser.
|
||||
*/
|
||||
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "divert.h"
|
||||
|
||||
#define MAXBUF 2048
|
||||
|
||||
/*
|
||||
* URL and blacklist representation.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
char *domain;
|
||||
char *uri;
|
||||
} URL, *PURL;
|
||||
typedef struct
|
||||
{
|
||||
UINT size;
|
||||
UINT length;
|
||||
PURL *urls;
|
||||
} BLACKLIST, *PBLACKLIST;
|
||||
|
||||
/*
|
||||
* Pre-fabricated packets.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
DIVERT_PACKET divert;
|
||||
DIVERT_IPHDR ip;
|
||||
DIVERT_TCPHDR tcp;
|
||||
} PACKET, *PPACKET;
|
||||
typedef struct
|
||||
{
|
||||
PACKET header;
|
||||
UINT8 data[];
|
||||
} DATAPACKET, *PDATAPACKET;
|
||||
|
||||
/*
|
||||
* THe block page contents.
|
||||
*/
|
||||
const char block_data[] =
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Content-Type: text/html\r\n"
|
||||
"\r\n"
|
||||
"<!doctype html>\n"
|
||||
"<html>\n"
|
||||
"\t<head>\n"
|
||||
"\t\t<title>BLOCKED!</title>\n"
|
||||
"\t</head>\n"
|
||||
"\t<body>\n"
|
||||
"\t\t<h1>BLOCKED!</h1>\n"
|
||||
"\t\t<hr>\n"
|
||||
"\t\t<p>This URL has been blocked!</p>\n"
|
||||
"\t</body>\n"
|
||||
"</html>\n";
|
||||
|
||||
/*
|
||||
* Prototypes
|
||||
*/
|
||||
static void PacketInit(PPACKET packet);
|
||||
static int UrlCompare(const void *a, const void *b);
|
||||
static int UrlMatch(PURL urla, PURL urlb);
|
||||
static PBLACKLIST BlackListInit(void);
|
||||
static void BlackListInsert(PBLACKLIST blacklist, PURL url);
|
||||
static void BlackListSort(PBLACKLIST blacklist);
|
||||
static BOOL BlackListMatch(PBLACKLIST blacklist, PURL url);
|
||||
static void BlackListRead(PBLACKLIST blacklist, const char *filename);
|
||||
static BOOL BlackListPayloadMatch(PBLACKLIST blacklist, char *data,
|
||||
UINT16 len);
|
||||
|
||||
/*
|
||||
* Entry.
|
||||
*/
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
HANDLE handle;
|
||||
UINT8 packet[MAXBUF];
|
||||
PDIVERT_PACKET ppacket = (PDIVERT_PACKET)packet;
|
||||
UINT ppacket_len;
|
||||
PDIVERT_IPHDR ip_header;
|
||||
PDIVERT_TCPHDR tcp_header;
|
||||
PVOID payload;
|
||||
UINT payload_len;
|
||||
PACKET reset0;
|
||||
PPACKET reset = &reset0;
|
||||
PDATAPACKET blockpage;
|
||||
UINT16 blockpage_len;
|
||||
PBLACKLIST blacklist;
|
||||
unsigned i;
|
||||
|
||||
// Read the blacklists.
|
||||
if (argc <= 1)
|
||||
{
|
||||
fprintf(stderr, "usage: %s blacklist.txt [blacklist2.txt ...]\n",
|
||||
argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
blacklist = BlackListInit();
|
||||
for (i = 1; i < (UINT)argc; i++)
|
||||
{
|
||||
BlackListRead(blacklist, argv[i]);
|
||||
}
|
||||
BlackListSort(blacklist);
|
||||
|
||||
// Initialize the pre-frabricated packets:
|
||||
blockpage_len = sizeof(DATAPACKET)+sizeof(block_data)-1;
|
||||
blockpage = (PDATAPACKET)malloc(blockpage_len);
|
||||
if (blockpage == NULL)
|
||||
{
|
||||
fprintf(stderr, "error: memory allocation failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
PacketInit(&blockpage->header);
|
||||
blockpage->header.ip.Length =
|
||||
htons(blockpage_len - sizeof(DIVERT_PACKET));
|
||||
blockpage->header.tcp.SrcPort = htons(80);
|
||||
blockpage->header.tcp.Psh = 1;
|
||||
blockpage->header.tcp.Ack = 1;
|
||||
memcpy(blockpage->data, block_data, sizeof(block_data)-1);
|
||||
PacketInit(reset);
|
||||
reset->tcp.Rst = 1;
|
||||
reset->tcp.Ack = 1;
|
||||
|
||||
// Open the Divert device:
|
||||
handle = DivertOpen(
|
||||
"outbound && " // Outbound traffic only
|
||||
"ip && " // Only IPv4 supported
|
||||
"tcp.DstPort == 80 && " // HTTP (port 80) only
|
||||
"tcp.PayloadLength > 0" // TCP data packets only
|
||||
);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
fprintf(stderr, "error: failed to open Divert device (%d)\n",
|
||||
GetLastError());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
printf("OPENED divert\n");
|
||||
|
||||
// Main loop:
|
||||
while (TRUE)
|
||||
{
|
||||
if (!DivertRecv(handle, ppacket, sizeof(packet), &ppacket_len))
|
||||
{
|
||||
fprintf(stderr, "warning: failed to read packet (%d)\n",
|
||||
GetLastError());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!DivertHelperParse(ppacket, ppacket_len, &ip_header, NULL, NULL,
|
||||
NULL, &tcp_header, NULL, &payload, &payload_len) ||
|
||||
!BlackListPayloadMatch(blacklist, payload, (UINT16)payload_len))
|
||||
{
|
||||
// Packet does not match the blacklist; simply reinject it.
|
||||
if (!DivertSend(handle, ppacket, ppacket_len, NULL))
|
||||
{
|
||||
fprintf(stderr, "warning: failed to reinject packet (%d)\n",
|
||||
GetLastError());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// The URL matched the blacklist; we block it by hijacking the TCP
|
||||
// connection.
|
||||
|
||||
// (1) Send a TCP RST to the server; immediately closing the
|
||||
// connection at the server's end.
|
||||
reset->divert.IfIdx = ppacket->IfIdx;
|
||||
reset->divert.SubIfIdx = ppacket->SubIfIdx;
|
||||
reset->divert.Direction = ppacket->Direction;
|
||||
reset->ip.SrcAddr = ip_header->SrcAddr;
|
||||
reset->ip.DstAddr = ip_header->DstAddr;
|
||||
reset->tcp.SrcPort = tcp_header->SrcPort;
|
||||
reset->tcp.DstPort = htons(80);
|
||||
reset->tcp.SeqNum = tcp_header->SeqNum;
|
||||
reset->tcp.AckNum = tcp_header->AckNum;
|
||||
DivertHelperCalcChecksums((PDIVERT_PACKET)reset, sizeof(PACKET), 0);
|
||||
if (!DivertSend(handle, (PDIVERT_PACKET)reset, sizeof(PACKET), NULL))
|
||||
{
|
||||
fprintf(stderr, "warning: failed to send reset packet (%d)\n",
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
// (2) Send the blockpage to the browser:
|
||||
blockpage->header.divert.IfIdx = ppacket->IfIdx;
|
||||
blockpage->header.divert.SubIfIdx = ppacket->SubIfIdx;
|
||||
blockpage->header.divert.Direction = !ppacket->Direction;
|
||||
blockpage->header.ip.SrcAddr = ip_header->DstAddr;
|
||||
blockpage->header.ip.DstAddr = ip_header->SrcAddr;
|
||||
blockpage->header.tcp.DstPort = tcp_header->SrcPort;
|
||||
blockpage->header.tcp.SeqNum = tcp_header->AckNum;
|
||||
blockpage->header.tcp.AckNum =
|
||||
htonl(ntohl(tcp_header->SeqNum) + payload_len);
|
||||
DivertHelperCalcChecksums((PDIVERT_PACKET)blockpage, blockpage_len, 0);
|
||||
if (!DivertSend(handle, (PDIVERT_PACKET)blockpage, blockpage_len,
|
||||
NULL))
|
||||
{
|
||||
fprintf(stderr, "warning: failed to send block page packet (%d)\n",
|
||||
GetLastError());
|
||||
}
|
||||
|
||||
// (3) Send a TCP RST to the browser; closing the connection at the
|
||||
// browser's end.
|
||||
reset->divert.IfIdx = ppacket->IfIdx;
|
||||
reset->divert.SubIfIdx = ppacket->SubIfIdx;
|
||||
reset->divert.Direction = !ppacket->Direction;
|
||||
reset->ip.SrcAddr = ip_header->DstAddr;
|
||||
reset->ip.DstAddr = ip_header->SrcAddr;
|
||||
reset->tcp.SrcPort = htons(80);
|
||||
reset->tcp.DstPort = tcp_header->SrcPort;
|
||||
reset->tcp.SeqNum =
|
||||
htonl(ntohl(tcp_header->AckNum) + sizeof(block_data) - 1);
|
||||
reset->tcp.AckNum =
|
||||
htonl(ntohl(tcp_header->SeqNum) + payload_len);
|
||||
DivertHelperCalcChecksums((PDIVERT_PACKET)reset, sizeof(PACKET), 0);
|
||||
if (!DivertSend(handle, (PDIVERT_PACKET)reset, sizeof(PACKET), NULL))
|
||||
{
|
||||
fprintf(stderr, "warning: failed to send reset packet (%d)\n",
|
||||
GetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize a PACKET.
|
||||
*/
|
||||
static void PacketInit(PPACKET packet)
|
||||
{
|
||||
memset(packet, 0, sizeof(PACKET));
|
||||
packet->ip.Version = 4;
|
||||
packet->ip.HdrLength = sizeof(DIVERT_IPHDR) / sizeof(UINT32);
|
||||
packet->ip.Length = htons(sizeof(PACKET) - sizeof(DIVERT_PACKET));
|
||||
packet->ip.TTL = 64;
|
||||
packet->ip.Protocol = IPPROTO_TCP;
|
||||
packet->tcp.HdrLength = sizeof(DIVERT_TCPHDR) / sizeof(UINT32);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize an empty blacklist.
|
||||
*/
|
||||
static PBLACKLIST BlackListInit(void)
|
||||
{
|
||||
PBLACKLIST blacklist = (PBLACKLIST)malloc(sizeof(BLACKLIST));
|
||||
if (blacklist == NULL)
|
||||
{
|
||||
goto memory_error;
|
||||
}
|
||||
blacklist->urls = (PURL *)malloc(MAXBUF*sizeof(PURL));
|
||||
if (blacklist->urls == NULL)
|
||||
{
|
||||
goto memory_error;
|
||||
}
|
||||
blacklist->size = MAXBUF;
|
||||
blacklist->length = 0;
|
||||
|
||||
return blacklist;
|
||||
|
||||
memory_error:
|
||||
fprintf(stderr, "error: failed to allocate memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert a URL into a blacklist.
|
||||
*/
|
||||
static void BlackListInsert(PBLACKLIST blacklist, PURL url)
|
||||
{
|
||||
if (blacklist->length >= blacklist->size)
|
||||
{
|
||||
blacklist->size = (blacklist->size*3) / 2;
|
||||
printf("GROW blacklist to %u\n", blacklist->size);
|
||||
blacklist->urls = (PURL *)realloc(blacklist->urls,
|
||||
blacklist->size*sizeof(PURL));
|
||||
if (blacklist->urls == NULL)
|
||||
{
|
||||
fprintf(stderr, "error: failed to reallocate memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
blacklist->urls[blacklist->length++] = url;
|
||||
}
|
||||
|
||||
/*
|
||||
* Sort the blacklist (for searching).
|
||||
*/
|
||||
static void BlackListSort(PBLACKLIST blacklist)
|
||||
{
|
||||
qsort(blacklist->urls, blacklist->length, sizeof(PURL), UrlCompare);
|
||||
}
|
||||
|
||||
/*
|
||||
* Match a URL against the blacklist.
|
||||
*/
|
||||
static BOOL BlackListMatch(PBLACKLIST blacklist, PURL url)
|
||||
{
|
||||
int lo = 0, hi = ((int)blacklist->length)-1;
|
||||
|
||||
while (lo <= hi)
|
||||
{
|
||||
INT mid = (lo + hi) / 2;
|
||||
int cmp = UrlMatch(url, blacklist->urls[mid]);
|
||||
if (cmp > 0)
|
||||
{
|
||||
hi = mid-1;
|
||||
}
|
||||
else if (cmp < 0)
|
||||
{
|
||||
lo = mid+1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Read URLs from a file.
|
||||
*/
|
||||
static void BlackListRead(PBLACKLIST blacklist, const char *filename)
|
||||
{
|
||||
char domain[MAXBUF+1];
|
||||
char uri[MAXBUF+1];
|
||||
int c;
|
||||
UINT16 i, j;
|
||||
PURL url;
|
||||
FILE *file = fopen(filename, "r");
|
||||
|
||||
if (file == NULL)
|
||||
{
|
||||
fprintf(stderr, "error: could not open blacklist file %s\n",
|
||||
filename);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Read URLs from the file and add them to the blacklist:
|
||||
while (TRUE)
|
||||
{
|
||||
while (isspace(c = getc(file)))
|
||||
;
|
||||
if (c == EOF)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (c != '-' && !isalnum(c))
|
||||
{
|
||||
while (!isspace(c = getc(file)) && c != EOF)
|
||||
;
|
||||
if (c == EOF)
|
||||
{
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
i = 0;
|
||||
domain[i++] = (char)c;
|
||||
while ((isalnum(c = getc(file)) || c == '-' || c == '.') && i < MAXBUF)
|
||||
{
|
||||
domain[i++] = (char)c;
|
||||
}
|
||||
domain[i] = '\0';
|
||||
j = 0;
|
||||
if (c == '/')
|
||||
{
|
||||
while (!isspace(c = getc(file)) && c != EOF && j < MAXBUF)
|
||||
{
|
||||
uri[j++] = (char)c;
|
||||
}
|
||||
uri[j] = '\0';
|
||||
}
|
||||
else if (isspace(c))
|
||||
{
|
||||
uri[j] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
while (!isspace(c = getc(file)) && c != EOF)
|
||||
;
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("ADD %s/%s\n", domain, uri);
|
||||
|
||||
url = (PURL)malloc(sizeof(URL));
|
||||
if (url == NULL)
|
||||
{
|
||||
goto memory_error;
|
||||
}
|
||||
url->domain = (char *)malloc((i+1)*sizeof(char));
|
||||
url->uri = (char *)malloc((j+1)*sizeof(char));
|
||||
if (url->domain == NULL || url->uri == NULL)
|
||||
{
|
||||
goto memory_error;
|
||||
}
|
||||
strcpy(url->uri, uri);
|
||||
for (j = 0; j < i; j++)
|
||||
{
|
||||
url->domain[j] = domain[i-j-1];
|
||||
}
|
||||
url->domain[j] = '\0';
|
||||
|
||||
BlackListInsert(blacklist, url);
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
return;
|
||||
|
||||
memory_error:
|
||||
fprintf(stderr, "error: memory allocation failed\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to parse a URL and match it with the blacklist.
|
||||
*/
|
||||
static BOOL BlackListPayloadMatch(PBLACKLIST blacklist, char *data, UINT16 len)
|
||||
{
|
||||
static const char get_str[] = "GET /";
|
||||
static const char post_str[] = "POST /";
|
||||
static const char http_host_str[] = " HTTP/1.1\r\nHost: ";
|
||||
char domain[MAXBUF];
|
||||
char uri[MAXBUF];
|
||||
URL url = {domain, uri};
|
||||
UINT16 i = 0, j;
|
||||
BOOL result;
|
||||
HANDLE console;
|
||||
|
||||
if (len <= sizeof(post_str) + sizeof(http_host_str))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if (strncmp(data, get_str, sizeof(get_str)-1) == 0)
|
||||
{
|
||||
i += sizeof(get_str)-1;
|
||||
}
|
||||
else if (strncmp(data, post_str, sizeof(post_str)-1) == 0)
|
||||
{
|
||||
i += sizeof(post_str)-1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (j = 0; i < len && data[i] != ' '; j++, i++)
|
||||
{
|
||||
uri[j] = data[i];
|
||||
}
|
||||
uri[j] = '\0';
|
||||
if (i + sizeof(http_host_str)-1 >= len)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (strncmp(data+i, http_host_str, sizeof(http_host_str)-1) != 0)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
i += sizeof(http_host_str)-1;
|
||||
|
||||
for (j = 0; i < len && data[i] != '\r'; j++, i++)
|
||||
{
|
||||
domain[j] = data[i];
|
||||
}
|
||||
if (i >= len)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if (j == 0)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if (domain[j-1] == '.')
|
||||
{
|
||||
// Nice try...
|
||||
j--;
|
||||
if (j == 0)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
domain[j] = '\0';
|
||||
|
||||
printf("URL %s/%s: ", domain, uri);
|
||||
|
||||
// Reverse the domain:
|
||||
for (i = 0; i < j / 2; i++)
|
||||
{
|
||||
char t = domain[i];
|
||||
domain[i] = domain[j-i-1];
|
||||
domain[j-i-1] = t;
|
||||
}
|
||||
|
||||
// Search the blacklist:
|
||||
result = BlackListMatch(blacklist, &url);
|
||||
|
||||
// Print the verdict:
|
||||
console = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (result)
|
||||
{
|
||||
SetConsoleTextAttribute(console, FOREGROUND_RED);
|
||||
puts("BLOCKED!");
|
||||
}
|
||||
else
|
||||
{
|
||||
SetConsoleTextAttribute(console, FOREGROUND_GREEN);
|
||||
puts("allowed");
|
||||
}
|
||||
SetConsoleTextAttribute(console,
|
||||
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* URL comparison.
|
||||
*/
|
||||
static int UrlCompare(const void *a, const void *b)
|
||||
{
|
||||
PURL urla = *(PURL *)a;
|
||||
PURL urlb = *(PURL *)b;
|
||||
int cmp = strcmp(urla->domain, urlb->domain);
|
||||
if (cmp != 0)
|
||||
{
|
||||
return cmp;
|
||||
}
|
||||
return strcmp(urla->uri, urlb->uri);
|
||||
}
|
||||
|
||||
/*
|
||||
* URL matching
|
||||
*/
|
||||
static int UrlMatch(PURL urla, PURL urlb)
|
||||
{
|
||||
UINT16 i;
|
||||
|
||||
for (i = 0; urla->domain[i] && urlb->domain[i]; i++)
|
||||
{
|
||||
int cmp = (int)urlb->domain[i] - (int)urla->domain[i];
|
||||
if (cmp != 0)
|
||||
{
|
||||
return cmp;
|
||||
}
|
||||
}
|
||||
if (urla->domain[i] == '\0' && urlb->domain[i] != '\0')
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; urla->uri[i] && urlb->uri[i]; i++)
|
||||
{
|
||||
int cmp = (int)urlb->uri[i] - (int)urla->uri[i];
|
||||
if (cmp != 0)
|
||||
{
|
||||
return cmp;
|
||||
}
|
||||
}
|
||||
if (urla->uri[i] == '\0' && urlb->uri[i] != '\0')
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user