blob: ddcf89381616f2bdf68c4f2517cec07582706835 [file] [log] [blame] [raw]
/*
* Copyright 2015-2017 Rivoreo
*
* 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 2 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.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
extern int forward(int, int);
static void signal_handler(int sig) {
if(sig != SIGCHLD) return;
while(waitpid(-1, NULL, WNOHANG) < 0) {
if(errno == EINTR) continue;
perror("waitpid");
return;
}
}
static uint16_t inet_cksum(const void *buffer, size_t len) {
const uint16_t *p = buffer;
unsigned int sum = 0;
//signed int sum = 0;
while(len > 1) {
sum += *p++;
len -= sizeof(uint16_t);
}
if(len) sum += *(uint8_t *)p;
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
return (uint16_t)~sum;
}
// Port numbers in network byte order
static void send_reset(const struct in_addr *dest_in_addr, uint16_t dest_port, const struct in_addr *src_in_addr, uint16_t src_port, uint32_t ack_seq) {
//char buffer[sizeof(struct tcphdr) + sizeof(struct ip)];
char buffer[sizeof(struct tcphdr) + sizeof(struct ip) + (1 << 2)];
struct iphdr *iph = (struct iphdr *)buffer;
struct tcphdr *tcph = (struct tcphdr *)(iph + 1);
struct sockaddr_in dest_addr = {
.sin_family = AF_INET,
.sin_addr = *dest_in_addr,
.sin_port = dest_port
};
int fd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);
if(fd == -1) {
perror("socket: AF_INET,SOCK_RAW,IPPROTO_TCP");
return;
}
int hdrincl = 1;
if(setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &hdrincl, sizeof hdrincl) < 0) {
perror("setsockopt: IPPROTO_IP,IP_HDRINCL");
return;
}
iph->ihl = sizeof(struct iphdr) >> 2;
iph->version = 4;
iph->tos = 0;
iph->tot_len = htons(sizeof buffer);
iph->id = htons(2149);
iph->frag_off = 0;
iph->ttl = 64;
iph->protocol = 6; // TCP
iph->check = 0;
iph->saddr = src_in_addr->s_addr;
iph->daddr = dest_in_addr->s_addr;
fprintf(stderr, "ntohl(ack_seq) = %u\n", (unsigned int)ntohl(ack_seq));
tcph->th_seq = htonl(1);
tcph->th_ack = ack_seq;
tcph->th_x2 = 0;
//tcph->th_off = (sizeof(struct tcphdr) >> 2);
tcph->th_off = (sizeof(struct tcphdr) >> 2) + 1;
tcph->th_flags = TH_RST | TH_ACK;
//tcph->th_win = htons(4000) + random() % 1000;
tcph->th_win = 0;
tcph->th_urp = 0;
tcph->th_sport = src_port;
tcph->th_dport = dest_port;
tcph->th_sum = 0;
uint8_t *option = (uint8_t *)(tcph + 1);
option[0] = 1;
option[1] = 1;
option[2] = 1;
option[3] = 0;
#if 1
#if 1
struct {
uint32_t src;
uint32_t dest;
uint8_t zero;
uint8_t protocol;
uint16_t tcplen;
} tcpph = {
.src = src_in_addr->s_addr,
.dest = dest_in_addr->s_addr,
.zero = 0,
.protocol = iph->protocol,
.tcplen = htons(sizeof(struct tcphdr) + (1 << 2))
//.tcplen = (sizeof(struct tcphdr) >> 2) + 1
};
char tcp_cksum_buffer[sizeof tcpph + sizeof(struct tcphdr) + (1 << 2)];
memcpy(tcp_cksum_buffer, &tcpph, sizeof tcpph);
memcpy(tcp_cksum_buffer + sizeof tcpph, tcph, sizeof(struct tcphdr) + (1 << 2));
tcph->th_sum = inet_cksum(tcp_cksum_buffer, sizeof tcp_cksum_buffer);
#else
struct {
uint32_t src;
uint32_t dest;
uint8_t zero;
uint8_t protocol;
uint16_t tcplen;
struct tcphdr tcph;
uint32_t options[0];
} tcp_cksum = {
.src = src_in_addr->s_addr,
.dest = dest_in_addr->s_addr,
.zero = 0,
.protocol = iph->protocol,
//.tcplen = htons(sizeof(struct tcphdr) + (1 << 2))
.tcplen = (sizeof(struct tcphdr) >> 2) + 1
.tcph = *tcph
};
tcph->th_sum = inet_cksum(&tcp_cksum, sizeof tcp_cksum);
#endif
#endif
iph->check = inet_cksum(iph, sizeof(struct iphdr));
if(sendto(fd, buffer, sizeof buffer, 0, (struct sockaddr *)&dest_addr, sizeof dest_addr) < 0) {
perror("sendto");
}
close(fd);
}
static void start_mirror_port(const struct iphdr *iph, const struct tcphdr *tcph) {
static struct in_addr last_source_in_addr;
static uint16_t last_dest_port;
static unsigned int repeat_count;
struct in_addr source_in_addr = { iph->saddr };
struct in_addr dest_in_addr = { iph->daddr };
char source_address[16], dest_address[16];
fprintf(stderr, "TCP SYN & AckSeq=0 received from %s:%hu to %s:%hu\n",
inet_ntop(AF_INET, &source_in_addr, source_address, sizeof source_address), ntohs(tcph->source),
inet_ntop(AF_INET, &dest_in_addr, dest_address, sizeof dest_address), ntohs(tcph->dest));
if(memcmp(&last_source_in_addr, &source_in_addr, sizeof source_in_addr) == 0 && last_dest_port == tcph->dest) {
if(repeat_count > 8) {
fprintf(stderr, "Too many request from host %s to port %d\n",
inet_ntop(AF_INET, &source_in_addr, source_address,
sizeof source_address), ntohs(tcph->dest));
return;
}
repeat_count++;
} else {
last_source_in_addr = source_in_addr;
last_dest_port = tcph->dest;
repeat_count = 0;
}
pid_t pid = fork();
if(pid < 0) {
perror("fork");
return;
}
if(pid) return;
// child
int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(fd == -1) {
perror("socket: AF_INET,SOCK_STREAM,IPPROTO_TCP");
_exit(1);
}
struct sockaddr_in connect_addr = {
.sin_family = AF_INET,
.sin_addr = source_in_addr,
.sin_port = tcph->dest
};
while(connect(fd, (struct sockaddr *)&connect_addr, sizeof connect_addr) < 0) {
int e = errno;
if(e == EINTR) continue;
perror("connect");
close(fd);
if(e == ECONNREFUSED) send_reset(&source_in_addr, tcph->source, &dest_in_addr, tcph->dest, htonl(ntohl(tcph->seq) + 1));
//if(e == ECONNREFUSED) send_reset(&dest_in_addr, tcph->dest, &source_in_addr, tcph->source, htonl(ntohl(tcph->seq) + 1));
_exit(1);
}
int listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(listen_fd == -1) {
perror("socket: AF_INET,SOCK_STREAM,IPPROTO_TCP");
close(fd);
_exit(1);
}
int reuseaddr = 1;
if(setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof reuseaddr) < 0) perror("setsockopt");
struct sockaddr_in listen_addr = {
.sin_family = AF_INET,
//.sin_addr.s_addr = htonl(INADDR_ANY),
.sin_addr = dest_in_addr,
.sin_port = tcph->dest
};
while(bind(listen_fd, (struct sockaddr *)&listen_addr, sizeof listen_addr) < 0) {
if(errno == EINTR) continue;
perror("bind");
goto fail;
}
if(listen(listen_fd, 1) < 0) {
perror("listen");
goto fail;
}
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(listen_fd, &fdset);
struct timeval timeout = { .tv_sec = 10 };
switch(select(listen_fd + 1, &fdset, NULL, NULL, &timeout)) {
case -1:
perror("select");
goto fail;
case 0:
close(fd);
close(listen_fd);
_exit(1);
}
int afd;
struct sockaddr_in client_addr;
socklen_t client_addr_len = sizeof client_addr;
do {
afd = accept(listen_fd, (struct sockaddr *)&client_addr, &client_addr_len);
} while(afd == -1 && errno == EINTR);
if(afd == -1) {
perror("accept");
goto fail;
}
fprintf(stderr, "Accepted connection from %s port %hu to port %hu\n",
inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), ntohs(listen_addr.sin_port));
_exit(forward(fd, afd) < 0 ? 1 : 0);
fail:
close(fd);
close(listen_fd);
_exit(1);
}
int main(int argc, char **argv) {
int begin_port = -1, end_port = -1; // host endian
uint16_t *exclude_ports = NULL; // network endian
size_t exclude_ports_count = 0, exclude_ports_allocated_size = 0;
if(argc < 2) {
fprintf(stderr, "Usage: %s <port-range> [<exclude-port>] [...]\n", argv[0]);
return -1;
}
char *dash = strchr(argv[1], '-');
if(dash) {
*dash = 0;
begin_port = atoi(argv[1]);
end_port = atoi(dash + 1);
} else {
begin_port = end_port = atoi(argv[1]);
}
if(begin_port < 1 || end_port < 1) {
fprintf(stderr, "%s: Port number must be greater than 0\n", argv[0]);
return -1;
}
char **v = argv + 2;
while(*v) {
if(exclude_ports_count * sizeof(uint16_t) >= exclude_ports_allocated_size) {
exclude_ports = realloc(exclude_ports, exclude_ports_allocated_size += 2 * sizeof(uint16_t));
if(!exclude_ports) {
perror("realloc");
return 1;
}
}
int port = atoi(*v++);
if(port < 1) {
fprintf(stderr, "%s: Port number must be greater than 0\n", argv[0]);
return -1;
}
//exclude_ports[exclude_ports_count++] = atoi(*v++);
exclude_ports[exclude_ports_count++] = htons(port);
}
//unsigned char *buffer = (unsigned char *)malloc(65536); //Its Big!
unsigned char buffer[16384];
//struct sigaction act = { .sa_handler = singal_hander };
struct sigaction act = { .sa_handler = SIG_IGN };
if(sigaction(SIGPIPE, &act, NULL) < 0) {
perror("sigaction");
return 1;
}
act.sa_handler = signal_handler;
sigaction(SIGCHLD, &act, NULL);
setvbuf(stdout, NULL, _IOLBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
int fd = socket(AF_INET, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_TCP);
if(fd == -1) {
perror("socket: AF_INET,SOCK_RAW,IPPROTO_TCP");
return 1;
}
struct sockaddr source_addr;
socklen_t source_addr_len;
int s, i;
while(1) {
first_loop:
source_addr_len = sizeof source_addr;
s = recvfrom(fd, buffer, sizeof buffer, 0 , &source_addr, &source_addr_len);
if(s < 0) {
if(errno == EINTR) continue;
perror("recvfrom");
return 1;
}
struct iphdr *iph = (struct iphdr*)buffer;
if(iph->protocol != 6) continue;
unsigned short int iphdr_len = iph->ihl * 4;
struct tcphdr *tcph = (struct tcphdr*)(buffer + iphdr_len);
if((iph->saddr & 0xffffff) == 0x7f || (iph->daddr & 0xffffff) == 0x7f) continue;
if(ntohs(tcph->dest) < begin_port || ntohs(tcph->dest) > end_port) continue;
for(i=0; i<exclude_ports_count; i++) if(tcph->dest == exclude_ports[i]) goto first_loop;
if(!tcph->syn || tcph->ack_seq) continue;
start_mirror_port(iph, tcph);
usleep(100000);
}
}