| /* |
| * 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. |
| */ |
| |
| #define _BSD_SOURCE |
| #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 <stdint.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <errno.h> |
| |
| #define NEED_TCP_OPTION |
| |
| #ifndef __GLIBC__ |
| struct iphdr { |
| #if __BYTE_ORDER == __LITTLE_ENDIAN |
| unsigned int ihl:4; |
| unsigned int version:4; |
| #elif __BYTE_ORDER == __BIG_ENDIAN |
| unsigned int version:4; |
| unsigned int ihl:4; |
| #else |
| # error "No endian information" |
| #endif |
| uint8_t tos; |
| uint16_t tot_len; |
| uint16_t id; |
| uint16_t frag_off; |
| uint8_t ttl; |
| uint8_t protocol; |
| uint16_t check; |
| uint32_t saddr; |
| uint32_t daddr; |
| /*The options start here. */ |
| }; |
| #endif |
| |
| 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) { |
| #ifdef NEED_TCP_OPTION |
| char buffer[sizeof(struct tcphdr) + sizeof(struct ip) + (1 << 2)]; |
| #else |
| char buffer[sizeof(struct tcphdr) + sizeof(struct ip)]; |
| #endif |
| 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; |
| #ifdef NEED_TCP_OPTION |
| tcph->th_off = (sizeof(struct tcphdr) >> 2) + 1; |
| #else |
| tcph->th_off = (sizeof(struct tcphdr) >> 2); |
| #endif |
| 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; |
| |
| 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, |
| #ifdef NEED_TCP_OPTION |
| .tcplen = htons(sizeof(struct tcphdr) + (1 << 2)) |
| #else |
| .tcplen = htons(sizeof(struct tcphdr)) |
| #endif |
| }; |
| #ifdef NEED_TCP_OPTION |
| char tcp_cksum_buffer[sizeof tcpph + sizeof(struct tcphdr) + (1 << 2)]; |
| #else |
| char tcp_cksum_buffer[sizeof tcpph + sizeof(struct tcphdr)]; |
| #endif |
| memcpy(tcp_cksum_buffer, &tcpph, sizeof tcpph); |
| #ifdef NEED_TCP_OPTION |
| memcpy(tcp_cksum_buffer + sizeof tcpph, tcph, sizeof(struct tcphdr) + (1 << 2)); |
| #else |
| memcpy(tcp_cksum_buffer + sizeof tcpph, tcph, sizeof(struct tcphdr)); |
| #endif |
| tcph->th_sum = inet_cksum(tcp_cksum_buffer, sizeof tcp_cksum_buffer); |
| 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 = { .s_addr = iph->saddr }; |
| struct in_addr dest_in_addr = { .s_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->th_sport), |
| inet_ntop(AF_INET, &dest_in_addr, dest_address, sizeof dest_address), ntohs(tcph->th_dport)); |
| if(memcmp(&last_source_in_addr, &source_in_addr, sizeof source_in_addr) == 0 && last_dest_port == tcph->th_dport) { |
| 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->th_dport)); |
| return; |
| } |
| repeat_count++; |
| } else { |
| last_source_in_addr = source_in_addr; |
| last_dest_port = tcph->th_dport; |
| 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->th_dport |
| }; |
| 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->th_sport, &dest_in_addr, tcph->th_dport, htonl(ntohl(tcph->th_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->th_dport |
| }; |
| 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); |
| #ifdef SOCK_CLOEXEC |
| int fd = socket(AF_INET, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_TCP); |
| #else |
| int fd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP); |
| #endif |
| 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; |
| if(iphdr_len > (unsigned int)s) continue; |
| struct tcphdr *tcph = (struct tcphdr*)(buffer + iphdr_len); |
| if((iph->saddr & 0xffffff) == 0x7f || (iph->daddr & 0xffffff) == 0x7f) continue; |
| if(ntohs(tcph->th_dport) < begin_port || ntohs(tcph->th_dport) > end_port) continue; |
| for(i=0; i<exclude_ports_count; i++) if(tcph->th_dport == exclude_ports[i]) goto first_loop; |
| #if 0 |
| if(!tcph->syn || tcph->ack_seq) continue; |
| #else |
| if(!(tcph->th_flags & TH_SYN) || (tcph->th_flags & TH_ACK)) continue; |
| #endif |
| start_mirror_port(iph, tcph); |
| usleep(100000); |
| } |
| } |