You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
228 lines
4.3 KiB
228 lines
4.3 KiB
/* |
|
* Copyright (c) 2013 Patrick McHardy <kaber@trash.net> |
|
* |
|
* This program is free software; you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License version 2 as |
|
* published by the Free Software Foundation. |
|
*/ |
|
|
|
#include <stdlib.h> |
|
#include <stdbool.h> |
|
#include <unistd.h> |
|
#include <string.h> |
|
#include <errno.h> |
|
#include <getopt.h> |
|
#include <sys/types.h> |
|
#include <sys/socket.h> |
|
#include <netinet/in.h> |
|
#include <arpa/inet.h> |
|
#include <pcap/pcap.h> |
|
#include <netinet/ip.h> |
|
#include <netinet/tcp.h> |
|
|
|
static const char *iface = "lo"; |
|
static uint16_t port; |
|
static const char *chain = "SYNPROXY"; |
|
|
|
static int parse_packet(const char *host, const uint8_t *data) |
|
{ |
|
const struct iphdr *iph = (void *)data + 14; |
|
const struct tcphdr *th = (void *)iph + iph->ihl * 4; |
|
int length; |
|
uint8_t *ptr; |
|
|
|
if (!th->syn || !th->ack) |
|
return 0; |
|
|
|
printf("-A %s -d %s -p tcp --dport %u " |
|
"-m state --state UNTRACKED,INVALID " |
|
"-j SYNPROXY ", chain, host, port); |
|
|
|
/* ECE && !CWR */ |
|
if (th->res2 == 0x1) |
|
printf("--ecn "); |
|
|
|
length = th->doff * 4 - sizeof(*th); |
|
ptr = (uint8_t *)(th + 1); |
|
while (length > 0) { |
|
int opcode = *ptr++; |
|
int opsize; |
|
|
|
switch (opcode) { |
|
case TCPOPT_EOL: |
|
return 1; |
|
case TCPOPT_NOP: |
|
length--; |
|
continue; |
|
default: |
|
opsize = *ptr++; |
|
if (opsize < 2) |
|
return 1; |
|
if (opsize > length) |
|
return 1; |
|
|
|
switch (opcode) { |
|
case TCPOPT_MAXSEG: |
|
if (opsize == TCPOLEN_MAXSEG) |
|
printf("--mss %u ", ntohs(*(uint16_t *)ptr)); |
|
break; |
|
case TCPOPT_WINDOW: |
|
if (opsize == TCPOLEN_WINDOW) |
|
printf("--wscale %u ", *ptr); |
|
break; |
|
case TCPOPT_TIMESTAMP: |
|
if (opsize == TCPOLEN_TIMESTAMP) |
|
printf("--timestamp "); |
|
break; |
|
case TCPOPT_SACK_PERMITTED: |
|
if (opsize == TCPOLEN_SACK_PERMITTED) |
|
printf("--sack-perm "); |
|
break; |
|
} |
|
|
|
ptr += opsize - 2; |
|
length -= opsize; |
|
} |
|
} |
|
printf("\n"); |
|
return 1; |
|
} |
|
|
|
static void probe_host(const char *host) |
|
{ |
|
struct sockaddr_in sin; |
|
char pcap_errbuf[PCAP_ERRBUF_SIZE]; |
|
struct pcap_pkthdr pkthdr; |
|
const uint8_t *data; |
|
struct bpf_program fp; |
|
pcap_t *ph; |
|
int fd; |
|
|
|
ph = pcap_create(iface, pcap_errbuf); |
|
if (ph == NULL) { |
|
perror("pcap_create"); |
|
goto err1; |
|
} |
|
|
|
if (pcap_setnonblock(ph, 1, pcap_errbuf) == -1) { |
|
perror("pcap_setnonblock"); |
|
goto err2; |
|
} |
|
|
|
if (pcap_setfilter(ph, &fp) == -1) { |
|
pcap_perror(ph, "pcap_setfilter"); |
|
goto err2; |
|
} |
|
|
|
if (pcap_activate(ph) != 0) { |
|
pcap_perror(ph, "pcap_activate"); |
|
goto err2; |
|
} |
|
|
|
if (pcap_compile(ph, &fp, "src host 127.0.0.1 and tcp and src port 80", |
|
1, PCAP_NETMASK_UNKNOWN) == -1) { |
|
pcap_perror(ph, "pcap_compile"); |
|
goto err2; |
|
} |
|
|
|
fd = socket(AF_INET, SOCK_STREAM, 0); |
|
if (fd < 0) { |
|
perror("socket"); |
|
goto err3; |
|
} |
|
|
|
memset(&sin, 0, sizeof(sin)); |
|
sin.sin_family = AF_INET; |
|
sin.sin_port = htons(port); |
|
sin.sin_addr.s_addr = inet_addr(host); |
|
|
|
if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { |
|
perror("connect"); |
|
goto err4; |
|
} |
|
|
|
for (;;) { |
|
data = pcap_next(ph, &pkthdr); |
|
if (data == NULL) |
|
break; |
|
if (parse_packet(host, data)) |
|
break; |
|
} |
|
|
|
close(fd); |
|
|
|
err4: |
|
close(fd); |
|
err3: |
|
pcap_freecode(&fp); |
|
err2: |
|
pcap_close(ph); |
|
err1: |
|
return; |
|
} |
|
|
|
enum { |
|
OPT_HELP = 'h', |
|
OPT_IFACE = 'i', |
|
OPT_PORT = 'p', |
|
OPT_CHAIN = 'c', |
|
}; |
|
|
|
static const struct option options[] = { |
|
{ .name = "help", .has_arg = false, .val = OPT_HELP }, |
|
{ .name = "iface", .has_arg = true, .val = OPT_IFACE }, |
|
{ .name = "port" , .has_arg = true, .val = OPT_PORT }, |
|
{ .name = "chain", .has_arg = true, .val = OPT_CHAIN }, |
|
{ } |
|
}; |
|
|
|
static void print_help(const char *name) |
|
{ |
|
printf("%s [ options ] address...\n" |
|
"\n" |
|
"Options:\n" |
|
" -i/--iface Outbound interface\n" |
|
" -p/--port Port number to probe\n" |
|
" -c/--chain Chain name to use for rules\n" |
|
" -h/--help Show this help\n", |
|
name); |
|
} |
|
|
|
int main(int argc, char **argv) |
|
{ |
|
int optidx = 0, c; |
|
|
|
for (;;) { |
|
c = getopt_long(argc, argv, "hi:p:c:", options, &optidx); |
|
if (c == -1) |
|
break; |
|
|
|
switch (c) { |
|
case OPT_IFACE: |
|
iface = optarg; |
|
break; |
|
case OPT_PORT: |
|
port = atoi(optarg); |
|
break; |
|
case OPT_CHAIN: |
|
chain = optarg; |
|
break; |
|
case OPT_HELP: |
|
print_help(argv[0]); |
|
exit(0); |
|
case '?': |
|
print_help(argv[0]); |
|
exit(1); |
|
} |
|
} |
|
|
|
argc -= optind; |
|
argv += optind; |
|
|
|
while (argc > 0) { |
|
probe_host(*argv); |
|
argc--; |
|
argv++; |
|
} |
|
return 0; |
|
}
|
|
|