diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eebe513..6bfc9ab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: sudo apt-get install -y clang-format - name: Run clang-format run: | - clang-format --dry-run --Werror src/*.c + clang-format --dry-run --Werror src/*.c include/*.h Build: name: Build @@ -130,9 +130,9 @@ jobs: VERSION="${{ inputs.version }}" if [ -n "$VERSION" ]; then - make STATIC=1 CROSS_PREFIX="$CROSS_PREFIX" VERSION="$VERSION" + make -j$(nproc) CFLAGS="-Werror" STATIC=1 CROSS_PREFIX="$CROSS_PREFIX" VERSION="$VERSION" else - make STATIC=1 CROSS_PREFIX="$CROSS_PREFIX" + make -j$(nproc) CFLAGS="-Werror" STATIC=1 CROSS_PREFIX="$CROSS_PREFIX" fi - name: Upload ${{ matrix.name }} uses: actions/upload-artifact@v4 diff --git a/Makefile b/Makefile index 5b5b8e3..76917ab 100644 --- a/Makefile +++ b/Makefile @@ -2,17 +2,21 @@ CROSS_PREFIX := CC=$(CROSS_PREFIX)gcc STRIP=$(CROSS_PREFIX)strip -override CFLAGS+=-O3 -std=c99 -pedantic -Wall -Wextra +PREFIX=/usr/local +BINDIR=$(PREFIX)/bin +SRCDIR=src +INCLUDEDIR=include +BUILDDIR=build +SRCS := $(wildcard $(SRCDIR)/*.c) +OBJS := $(patsubst $(SRCDIR)/%.c,$(BUILDDIR)/%.o,$(SRCS)) + +override CFLAGS+=-O3 -std=c99 -I$(INCLUDEDIR) -pedantic -Wall -Wextra override LDFLAGS+=-lnetfilter_queue -lnfnetlink -lmnl ifdef VERSION - override CFLAGS += -DVERSION=\"$(VERSION)\" + override CFLAGS += -DVERSION=\"$(VERSION)\" endif -PREFIX=/usr/local -BINDIR=$(PREFIX)/bin -BUILDDIR=build - FAKEHTTP=$(BUILDDIR)/fakehttp ifeq ($(STATIC), 1) @@ -24,16 +28,28 @@ all: $(FAKEHTTP) clean: $(RM) -r $(BUILDDIR) -$(FAKEHTTP): src/fakehttp.c +$(BUILDDIR): mkdir -p $(BUILDDIR) - $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) + +$(BUILDDIR)/%.d: $(SRCDIR)/%.c | $(BUILDDIR) + $(CC) $(CFLAGS) -MM -MT $(@:.d=.o) $< -MF $@ + +$(BUILDDIR)/%.o: $(SRCDIR)/%.c | $(BUILDDIR) + $(CC) $(CFLAGS) -c $< -o $@ + +$(FAKEHTTP): $(OBJS) $(MKS) + $(CC) $(OBJS) -o $@ $(LDFLAGS) $(STRIP) $@ install: all mkdir -p $(DESTDIR)$(BINDIR) - install -m 755 fakehttp $(DESTDIR)$(BINDIR)/fakehttp + install -m 755 $(FAKEHTTP) $(DESTDIR)$(BINDIR)/fakehttp uninstall: $(RM) $(DESTDIR)$(BINDIR)/fakehttp .PHONY: all clean install uninstall + +ifneq ($(MAKECMDGOALS),clean) +-include $(OBJS:.o=.d) +endif diff --git a/include/globvar.h b/include/globvar.h new file mode 100644 index 0000000..135f983 --- /dev/null +++ b/include/globvar.h @@ -0,0 +1,47 @@ +/* + * globvar.h - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#ifndef FH_GLOBVAR_H +#define FH_GLOBVAR_H + +#include +#include + +struct fh_context { + int exit; + int sockfd; + FILE *logfp; + + /* -d */ int daemon; + /* -h */ const char *hostname; + /* -i */ const char *iface; + /* -k */ int killproc; + /* -m */ uint32_t fwmark; + /* -n */ uint32_t nfqnum; + /* -r */ int repeat; + /* -s */ int silent; + /* -t */ uint8_t ttl; + /* -w */ const char *logpath; + /* -x */ uint32_t fwmask; + /* -z */ int use_iptables; +}; + +extern struct fh_context g_ctx; + +#endif /* FH_GLOBVAR_H */ diff --git a/include/ipv4ipt.h b/include/ipv4ipt.h new file mode 100644 index 0000000..49971c5 --- /dev/null +++ b/include/ipv4ipt.h @@ -0,0 +1,27 @@ +/* + * ipv4ipt.h - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#ifndef FH_IPV4IPT_H +#define FH_IPV4IPT_H + +int fh_ipt4_flush(int auto_create); + +int fh_ipt4_add(void); + +#endif /* FH_IPV4IPT_H */ diff --git a/include/ipv4nft.h b/include/ipv4nft.h new file mode 100644 index 0000000..15ace3e --- /dev/null +++ b/include/ipv4nft.h @@ -0,0 +1,27 @@ +/* + * ipv4nft.h - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#ifndef FH_IPV4NFT_H +#define FH_IPV4NFT_H + +int fh_nft4_flush(int auto_create); + +int fh_nft4_add(void); + +#endif /* FH_IPV4NFT_H */ diff --git a/include/ipv4pkt.h b/include/ipv4pkt.h new file mode 100644 index 0000000..3167c43 --- /dev/null +++ b/include/ipv4pkt.h @@ -0,0 +1,31 @@ +/* + * ipv4pkt.h - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#ifndef FH_IPV4PKT_H +#define FH_IPV4PKT_H + +#include +#include + +int fh_pkt4_make(char *buffer, size_t buffer_size, uint32_t saddr_be, + uint32_t daddr_be, uint16_t sport_be, uint16_t dport_be, + uint32_t seq_be, uint32_t ackseq_be, int psh, + char *tcp_payload, size_t tcp_payload_size); + +#endif /* FH_IPV4PKT_H */ diff --git a/include/ipv6ipt.h b/include/ipv6ipt.h new file mode 100644 index 0000000..224d08e --- /dev/null +++ b/include/ipv6ipt.h @@ -0,0 +1,29 @@ +/* + * ipv6ipt.h - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#ifndef FH_IPV6IPT_H +#define FH_IPV6IPT_H + +/* TODO: NOT IMPLEMENTED */ +int fh_ipt6_flush(int auto_create); + +/* TODO: NOT IMPLEMENTED */ +int fh_ipt6_add(void); + +#endif /* FH_IPV6IPT_H */ diff --git a/include/ipv6nft.h b/include/ipv6nft.h new file mode 100644 index 0000000..5481ae4 --- /dev/null +++ b/include/ipv6nft.h @@ -0,0 +1,29 @@ +/* + * ipv6nft.h - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#ifndef FH_IPV6NFT_H +#define FH_IPV6NFT_H + +/* TODO: NOT IMPLEMENTED */ +int fh_nft6_flush(int auto_create); + +/* TODO: NOT IMPLEMENTED */ +int fh_nft6_add(void); + +#endif /* FH_IPV6NFT_H */ diff --git a/include/ipv6pkt.h b/include/ipv6pkt.h new file mode 100644 index 0000000..05f7874 --- /dev/null +++ b/include/ipv6pkt.h @@ -0,0 +1,32 @@ +/* + * ipv6pkt.h - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#ifndef FH_IPV6PKT_H +#define FH_IPV6PKT_H + +#include +#include + +/* TODO: NOT IMPLEMENTED */ +int fh_pkt6_make(char *buffer, size_t buffer_size, uint8_t *saddr_be, + uint8_t *daddr_be, uint16_t sport_be, uint16_t dport_be, + uint32_t seq_be, uint32_t ackseq_be, int psh, + char *tcp_payload, size_t tcp_payload_size); + +#endif /* FH_IPV6PKT_H */ diff --git a/include/logging.h b/include/logging.h new file mode 100644 index 0000000..f56d5c1 --- /dev/null +++ b/include/logging.h @@ -0,0 +1,39 @@ +/* + * logging.h - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#ifndef FH_LOGGING_H +#define FH_LOGGING_H + +#define E(...) fh_logger(__func__, __FILE__, __LINE__, __VA_ARGS__) +#define E_RAW(...) fh_logger_raw(__VA_ARGS__) +#define E_INFO(...) \ + if (!g_ctx.silent) { \ + E(__VA_ARGS__); \ + } + +int fh_logger_setup(void); + +void fh_logger_cleanup(void); + +void fh_logger(const char *funcname, const char *filename, unsigned long line, + const char *fmt, ...); + +void fh_logger_raw(const char *fmt, ...); + +#endif /* FH_LOGGING_H */ diff --git a/include/mainfun.h b/include/mainfun.h new file mode 100644 index 0000000..99e565b --- /dev/null +++ b/include/mainfun.h @@ -0,0 +1,25 @@ +/* + * mainfun.h - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#ifndef FH_MAINFUN_H +#define FH_MAINFUN_H + +int main(int argc, char *argv[]); + +#endif /* FH_MAINFUN_H */ diff --git a/include/nfqueue.h b/include/nfqueue.h new file mode 100644 index 0000000..43cbfa6 --- /dev/null +++ b/include/nfqueue.h @@ -0,0 +1,29 @@ +/* + * nfqueue.h - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#ifndef FH_NFQUEUE_H +#define FH_NFQUEUE_H + +int fh_nfq_setup(void); + +void fh_nfq_cleanup(void); + +int fh_nfq_loop(void); + +#endif /* FH_NFQUEUE_H */ diff --git a/include/nfrules.h b/include/nfrules.h new file mode 100644 index 0000000..83db0df --- /dev/null +++ b/include/nfrules.h @@ -0,0 +1,27 @@ +/* + * nfrules.h - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#ifndef FH_NFRULES_H +#define FH_NFRULES_H + +int fh_nfrules_setup(void); + +void fh_nfrules_cleanup(void); + +#endif /* FH_NFRULES_H */ diff --git a/include/process.h b/include/process.h new file mode 100644 index 0000000..dd3a59a --- /dev/null +++ b/include/process.h @@ -0,0 +1,25 @@ +/* + * process.h - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#ifndef FH_PROCESS_H +#define FH_PROCESS_H + +int fh_execute_command(char **argv, int silent, char *input); + +#endif /* FH_PROCESS_H */ diff --git a/include/rawsock.h b/include/rawsock.h new file mode 100644 index 0000000..7812242 --- /dev/null +++ b/include/rawsock.h @@ -0,0 +1,27 @@ +/* + * rawsock.h - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#ifndef FH_RAWSOCK_H +#define FH_RAWSOCK_H + +int fh_rawsock_setup(void); + +void fh_rawsock_cleanup(void); + +#endif /* FH_RAWSOCK_H */ diff --git a/include/signals.h b/include/signals.h new file mode 100644 index 0000000..83b7e60 --- /dev/null +++ b/include/signals.h @@ -0,0 +1,27 @@ +/* + * signals.h - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#ifndef FH_SIGNALS_H +#define FH_SIGNALS_H + +int fh_signal_setup(void); + +int fh_kill_running(int signal); + +#endif /* FH_SIGNALS_H */ diff --git a/src/fakehttp.c b/src/fakehttp.c deleted file mode 100644 index 9da2faa..0000000 --- a/src/fakehttp.c +++ /dev/null @@ -1,1342 +0,0 @@ -/* - * fakehttp.c - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP - * - * Copyright (C) 2025 MikeWang000000 - * - * 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 . - */ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef VERSION -#define VERSION "dev" -#endif /* VERSION */ - -#define E(...) logger(__func__, __FILE__, __LINE__, __VA_ARGS__) -#define E_RAW(...) logger_raw(__VA_ARGS__) -#define E_INFO(...) \ - if (!g_silent) { \ - E(__VA_ARGS__); \ - } - -static FILE *g_logfp = NULL; -static int g_sockfd = 0; -static int g_exit = 0; -static int g_daemon = 0; -static int g_silent = 0; -static int g_killproc = 0; -static int g_use_iptables = 0; -static int g_repeat = 3; -static uint32_t g_fwmark = 0x8000; -static uint32_t g_fwmask = 0; -static uint32_t g_nfqnum = 512; -static uint8_t g_ttl = 3; -static const char *g_iface = NULL; -static const char *g_hostname = NULL; - -static void print_usage(const char *name) -{ - fprintf(stderr, - "Usage: %s [options]\n" - "\n" - "Options:\n" - " -d run as a daemon\n" - " -h hostname for obfuscation (required)\n" - " -i network interface name (required)\n" - " -k kill the running process\n" - " -m fwmark for bypassing the queue\n" - " -n netfilter queue number\n" - " -r duplicate generated packets for " - "times\n" - " -s enable silent mode\n" - " -t TTL for generated packets\n" - " -w write log to instead of stderr\n" - " -x set the mask for fwmark\n" - " -z use iptables commands instead of nft\n" - "\n" - "FakeHTTP version " VERSION "\n", - name); -} - - -static void logger(const char *funcname, const char *filename, - unsigned long line, const char *fmt, ...) -{ - FILE *fp; - va_list args; - time_t t; - char *stime; - - fp = g_logfp ? g_logfp : stderr; - t = time(NULL); - stime = ctime(&t); - if (stime) { - stime[strlen(stime) - 1] = '\0'; - fprintf(fp, "%s ", stime); - } - - fprintf(fp, "[%s() - %s:%lu] ", funcname, filename, line); - va_start(args, fmt); - vfprintf(fp, fmt, args); - va_end(args); - fputc('\n', fp); - fflush(fp); -} - - -static void logger_raw(const char *fmt, ...) -{ - FILE *fp; - va_list args; - - fp = g_logfp ? g_logfp : stderr; - va_start(args, fmt); - vfprintf(fp, fmt, args); - va_end(args); - fflush(fp); -} - - -static void signal_handler(int sig) -{ - switch (sig) { - case SIGINT: - case SIGTERM: - g_exit = 1; - break; - default: - break; - } -} - - -static int signal_setup(void) -{ - struct sigaction sa; - int res; - - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = SIG_IGN; - - res = sigaction(SIGPIPE, &sa, NULL); - if (res < 0) { - E("ERROR: sigaction(): %s", strerror(errno)); - return -1; - } - - res = sigaction(SIGHUP, &sa, NULL); - if (res < 0) { - E("ERROR: sigaction(): %s", strerror(errno)); - return -1; - } - - sa.sa_handler = signal_handler; - - res = sigaction(SIGINT, &sa, NULL); - if (res < 0) { - E("ERROR: sigaction(): %s", strerror(errno)); - return -1; - } - - res = sigaction(SIGTERM, &sa, NULL); - if (res < 0) { - E("ERROR: sigaction(): %s", strerror(errno)); - return -1; - } - - return 0; -} - - -static int kill_running(int signal) -{ - int res, matched, err; - ssize_t len; - DIR *procfs; - struct dirent *entry; - pid_t pid, self_pid; - char self_path[PATH_MAX], proc_path[PATH_MAX], exe_path[PATH_MAX]; - - self_pid = getpid(); - - len = readlink("/proc/self/exe", self_path, sizeof(self_path)); - if (len < 0 || (size_t) len >= sizeof(self_path)) { - E("ERROR: readlink(): /proc/self/exe: %s", strerror(errno)); - return -1; - } - self_path[len] = 0; - - procfs = opendir("/proc"); - if (!procfs) { - E("ERROR: opendir(): /proc: %s", strerror(errno)); - return -1; - } - - matched = err = 0; - while ((entry = readdir(procfs))) { - pid = strtoull(entry->d_name, NULL, 0); - if (pid <= 1 || pid == self_pid) { - continue; - } - - res = snprintf(exe_path, sizeof(exe_path), "/proc/%s/exe", - entry->d_name); - if (res < 0 || (size_t) res >= sizeof(exe_path)) { - continue; - } - - len = readlink(exe_path, proc_path, sizeof(proc_path)); - if (len < 0 || (size_t) len >= sizeof(self_path)) { - continue; - } - proc_path[len] = 0; - - if (strcmp(self_path, proc_path) == 0) { - matched = 1; - - if (signal) { - res = kill(pid, signal); - if (res < 0) { - E("ERROR: kill(): %llu: %s", pid, strerror(errno)); - err = 1; - } - } - } - } - - res = closedir(procfs); - if (res < 0) { - E("ERROR: closedir(): %s", strerror(errno)); - err = 1; - } - - if (matched && !err) { - return 0; - } - - return -1; -} - - -static int execute_command(char **argv, int silent, char *input) -{ - int res, pipefd[2], status, fd, i; - size_t input_len, written; - ssize_t n; - pid_t pid; - - if (input) { - res = pipe(pipefd); - if (res < 0) { - E("ERROR: pipe(): %s", strerror(errno)); - return -1; - } - } - - pid = fork(); - if (pid < 0) { - E("ERROR: fork(): %s", strerror(errno)); - if (input) { - close(pipefd[0]); - close(pipefd[1]); - } - return -1; - } - - if (!pid) { - fd = -1; - - if (silent) { - fd = open("/dev/null", O_WRONLY); - if (fd < 0) { - E("ERROR: open(): %s", strerror(errno)); - _exit(EXIT_FAILURE); - } - } else if (g_logfp) { - fd = fileno(g_logfp); - if (fd < 0) { - E("ERROR: fileno(): %s", strerror(errno)); - _exit(EXIT_FAILURE); - } - } - - if (fd >= 0) { - res = dup2(fd, STDOUT_FILENO); - if (res < 0) { - E("ERROR: dup2(): %s", strerror(errno)); - _exit(EXIT_FAILURE); - } - res = dup2(fd, STDERR_FILENO); - if (res < 0) { - E("ERROR: dup2(): %s", strerror(errno)); - _exit(EXIT_FAILURE); - } - close(fd); - } - - if (input) { - close(pipefd[1]); - res = dup2(pipefd[0], STDIN_FILENO); - if (res < 0) { - E("ERROR: dup2(): %s", strerror(errno)); - _exit(EXIT_FAILURE); - } - close(pipefd[0]); - } - - execvp(argv[0], argv); - - E("ERROR: execvp(): %s: %s", argv[0], strerror(errno)); - - _exit(EXIT_FAILURE); - } - - if (input) { - close(pipefd[0]); - input_len = strlen(input); - for (written = 0; written < input_len; written += n) { - n = write(pipefd[1], input + written, input_len - written); - if (n < 0) { - E("ERROR: write(): %s", strerror(errno)); - break; - } - } - close(pipefd[1]); - } - - if (waitpid(pid, &status, 0) < 0) { - E("ERROR: waitpid(): %s", strerror(errno)); - goto child_failed; - } - - if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { - return 0; - } - -child_failed: - if (!silent) { - E_RAW("[*] failed command is: %s", argv[0]); - for (i = 1; argv[i]; i++) { - E_RAW(" %s", argv[i]); - } - E_RAW("\n"); - } - - return -1; -} - - -static int nft_is_working(void) -{ - char *nft_ver_cmd[] = {"nft", "--version", NULL}; - - return !execute_command(nft_ver_cmd, 1, NULL); -} - - -static int nft_rules_flush(int auto_create) -{ - int res; - char *nft_flush_cmd[] = {"nft", "flush table fakehttp", NULL}; - char *nft_cmd[] = {"nft", "-f", "-", NULL}; - char *nft_create_conf = - "table ip fakehttp {\n" - " chain fh_input {\n" - " type filter hook input priority mangle - 5;\n" - " policy accept;\n" - " }\n" - "\n" - " chain fh_output {\n" - " type filter hook forward priority mangle - 5;\n" - " policy accept;\n" - " }\n" - "\n" - " chain fh_rules {\n" - " }\n" - "}\n"; - - res = execute_command(nft_flush_cmd, 1, NULL); - if (res < 0) { - if (!auto_create) { - return -1; - } - - res = execute_command(nft_cmd, 0, nft_create_conf); - if (res) { - E("ERROR: execute_command()"); - return -1; - } - } - - return 0; -} - - -static int nft_rules_setup(void) -{ - size_t i, nft_opt_cmds_cnt; - int res; - char *nft_cmd[] = {"nft", "-f", "-", NULL}; - char nft_conf_buff[2048]; - char *nft_conf_fmt = - "table ip fakehttp {\n" - " chain fh_input {\n" - " jump fh_rules;\n" - " }\n" - "\n" - " chain fh_output {\n" - " jump fh_rules;\n" - " }\n" - "\n" - " chain fh_rules {\n" - - /* - exclude marked packets - */ - " meta mark and %" PRIu32 " == %" PRIu32 - " ct mark set ct mark and %" PRIu32 " xor %" PRIu32 ";\n" - " ct mark and %" PRIu32 " == %" PRIu32 - " meta mark set mark and %" PRIu32 " xor %" PRIu32 ";\n" - " meta mark and %" PRIu32 " == %" PRIu32 " return;\n" - - /* - exclude local IPs - */ - " ip saddr 0.0.0.0/8 return;\n" - " ip saddr 10.0.0.0/8 return;\n" - " ip saddr 100.64.0.0/10 return;\n" - " ip saddr 127.0.0.0/8 return;\n" - " ip saddr 169.254.0.0/16 return;\n" - " ip saddr 172.16.0.0/12 return;\n" - " ip saddr 192.168.0.0/16 return;\n" - " ip saddr 224.0.0.0/3 return;\n" - - /* - send to nfqueue - */ - " iifname \"%s\" tcp flags & (fin | rst | ack) == ack queue " - "num %" PRIu32 " bypass;\n" - " }\n" - "}\n"; - - char *nft_opt_cmds[][32] = { - /* - exclude packets from connections with more than 32 packets - */ - {"nft", "insert rule ip fakehttp fh_rules ct packets > 32 return", - NULL}, - - /* - exclude big packets - */ - {"nft", "insert rule ip fakehttp fh_rules meta length > 120 return", - NULL}}; - - nft_opt_cmds_cnt = sizeof(nft_opt_cmds) / sizeof(*nft_opt_cmds); - - res = snprintf(nft_conf_buff, sizeof(nft_conf_buff), nft_conf_fmt, - g_fwmask, g_fwmark, ~g_fwmask, g_fwmark, g_fwmask, g_fwmark, - ~g_fwmask, g_fwmark, g_fwmask, g_fwmark, g_iface, g_nfqnum); - if (res < 0 || (size_t) res >= sizeof(nft_conf_buff)) { - E("ERROR: snprintf()"); - return -1; - } - - res = execute_command(nft_cmd, 1, nft_conf_buff); - if (res) { - E("ERROR: execute_command()"); - return -1; - } - - for (i = 0; i < nft_opt_cmds_cnt; i++) { - execute_command(nft_opt_cmds[i], 1, NULL); - } - - return 0; -} - - -static int ipt_rules_flush(int auto_create) -{ - int res; - size_t i, cnt; - char *ipt_flush_cmd[] = {"iptables", "-w", "-t", "mangle", - "-F", "FAKEHTTP", NULL}; - char *ipt_create_cmds[][32] = { - {"iptables", "-w", "-t", "mangle", "-N", "FAKEHTTP", NULL}, - - {"iptables", "-w", "-t", "mangle", "-I", "INPUT", "-j", "FAKEHTTP", - NULL}, - - {"iptables", "-w", "-t", "mangle", "-I", "FORWARD", "-j", "FAKEHTTP", - NULL}}; - - res = execute_command(ipt_flush_cmd, 1, NULL); - if (res < 0) { - if (!auto_create) { - return -1; - } - - cnt = sizeof(ipt_create_cmds) / sizeof(*ipt_create_cmds); - for (i = 0; i < cnt; i++) { - res = execute_command(ipt_create_cmds[i], 0, NULL); - if (res) { - E("ERROR: execute_command()"); - return -1; - } - } - } - - return 0; -} - - -static int ipt_rules_setup(void) -{ - char xmark_str[64], nfqnum_str[32], iface_str[32]; - size_t i, ipt_cmds_cnt, ipt_opt_cmds_cnt; - int res; - char *ipt_cmds[][32] = { - /* - exclude marked packets - */ - {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP", "-m", "mark", - "--mark", xmark_str, "-j", "CONNMARK", "--set-xmark", xmark_str, - NULL}, - - {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP", "-m", "connmark", - "--mark", xmark_str, "-j", "MARK", "--set-xmark", xmark_str, NULL}, - - {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP", "-m", "mark", - "--mark", xmark_str, "-j", "RETURN", NULL}, - - /* - exclude local IPs - */ - {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP", "-s", "0.0.0.0/8", - "-j", "RETURN", NULL}, - - {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP", "-s", - "10.0.0.0/8", "-j", "RETURN", NULL}, - - {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP", "-s", - "100.64.0.0/10", "-j", "RETURN", NULL}, - - {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP", "-s", - "127.0.0.0/8", "-j", "RETURN", NULL}, - - {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP", "-s", - "169.254.0.0/16", "-j", "RETURN", NULL}, - - {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP", "-s", - "172.16.0.0/12", "-j", "RETURN", NULL}, - - {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP", "-s", - "192.168.0.0/16", "-j", "RETURN", NULL}, - - {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP", "-s", - "224.0.0.0/3", "-j", "RETURN", NULL}, - - /* - send to nfqueue - */ - {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP", "-i", iface_str, - "-p", "tcp", "--tcp-flags", "ACK,FIN,RST", "ACK", "-j", "NFQUEUE", - "--queue-bypass", "--queue-num", nfqnum_str, NULL}}; - - char *ipt_opt_cmds[][32] = { - /* - exclude packets from connections with more than 32 packets - */ - {"iptables", "-w", "-t", "mangle", "-I", "FAKEHTTP", "-m", "connbytes", - "!", "--connbytes", "0:32", "--connbytes-dir", "both", - "--connbytes-mode", "packets", "-j", "RETURN", NULL}, - - /* - exclude big packets - */ - {"iptables", "-w", "-t", "mangle", "-I", "FAKEHTTP", "-m", "length", - "!", "--length", "0:120", "-j", "RETURN", NULL}}; - - ipt_cmds_cnt = sizeof(ipt_cmds) / sizeof(*ipt_cmds); - ipt_opt_cmds_cnt = sizeof(ipt_opt_cmds) / sizeof(*ipt_opt_cmds); - - res = snprintf(xmark_str, sizeof(xmark_str), "%" PRIu32 "/%" PRIu32, - g_fwmark, g_fwmask); - if (res < 0 || (size_t) res >= sizeof(xmark_str)) { - E("ERROR: snprintf()"); - return -1; - } - - res = snprintf(nfqnum_str, sizeof(nfqnum_str), "%" PRIu32, g_nfqnum); - if (res < 0 || (size_t) res >= sizeof(nfqnum_str)) { - E("ERROR: snprintf()"); - return -1; - } - - res = snprintf(iface_str, sizeof(iface_str), "%s", g_iface); - if (res < 0 || (size_t) res >= sizeof(iface_str)) { - E("ERROR: snprintf()"); - return -1; - } - - for (i = 0; i < ipt_cmds_cnt; i++) { - res = execute_command(ipt_cmds[i], 0, NULL); - if (res) { - E("ERROR: execute_command()"); - return -1; - } - } - - for (i = 0; i < ipt_opt_cmds_cnt; i++) { - execute_command(ipt_opt_cmds[i], 1, NULL); - } - - return 0; -} - - -static uint16_t chksum(void *pseudo, size_t pseudo_count, void *data, - size_t count) -{ - uint32_t sum = 0; - uint8_t *ptr, b1, b2; - - if (pseudo_count % 2 != 0) { - return 0; - } - - ptr = pseudo; - while (pseudo_count > 1) { - b1 = *ptr++; - b2 = *ptr++; - sum += (b2 << 8) + b1; - pseudo_count -= 2; - } - - ptr = data; - while (count > 1) { - b1 = *ptr++; - b2 = *ptr++; - sum += (b2 << 8) + b1; - count -= 2; - } - if (count > 0) { - sum += *ptr; - } - while (sum >> 16) { - sum = (sum & 0xffff) + (sum >> 16); - } - - return ~sum; -} - - -static uint16_t chksum_pseudo_ipv4(uint8_t protonum, void *data, size_t count, - uint32_t saddr_be, uint32_t daddr_be) -{ - struct { - uint32_t saddr; - uint32_t daddr; - uint8_t zero; - uint8_t protocol; - uint16_t len; - } __attribute__((packed)) pseudo; - - pseudo.saddr = saddr_be; - pseudo.daddr = daddr_be; - pseudo.zero = 0; - pseudo.protocol = protonum; - pseudo.len = htons(count); - - return chksum(&pseudo, sizeof(pseudo), data, count); -} - - -static int make_pkt(char *buffer, size_t buffer_size, uint32_t saddr_be, - uint32_t daddr_be, uint16_t sport_be, uint16_t dport_be, - uint32_t seq_be, uint32_t ackseq_be, int psh, - char *tcp_payload, size_t tcp_payload_size) -{ - size_t pkt_len; - struct iphdr *iph; - struct tcphdr *tcph; - char *tcppl; - - pkt_len = sizeof(*iph) + sizeof(*tcph) + tcp_payload_size; - if (buffer_size < pkt_len + 1) { - return -1; - } - - iph = (struct iphdr *) buffer; - tcph = (struct tcphdr *) (buffer + sizeof(*iph)); - tcppl = buffer + sizeof(*iph) + sizeof(*tcph); - - memset(iph, 0, sizeof(*iph)); - iph->version = 4; - iph->ihl = sizeof(*iph) / 4; - iph->tos = 0; - iph->tot_len = htons(pkt_len); - iph->id = ((rand() & 0xff) << 8) | (rand() & 0xff); - iph->frag_off = htons(1 << 14 /* DF */); - iph->ttl = g_ttl; - iph->protocol = IPPROTO_TCP; - iph->check = 0; - iph->saddr = saddr_be; - iph->daddr = daddr_be; - - memset(tcph, 0, sizeof(*tcph)); - tcph->source = sport_be; - tcph->dest = dport_be; - tcph->seq = seq_be; - tcph->ack_seq = ackseq_be; - tcph->doff = sizeof(*tcph) / 4; - tcph->psh = psh; - tcph->ack = 1; - tcph->window = htons(0x0080); - tcph->check = 0; - tcph->urg_ptr = 0; - - if (tcp_payload_size) { - memcpy(tcppl, tcp_payload, tcp_payload_size); - } - - iph->check = chksum(NULL, 0, iph, sizeof(*iph)); - tcph->check = chksum_pseudo_ipv4(IPPROTO_TCP, tcph, - sizeof(*tcph) + tcp_payload_size, - saddr_be, daddr_be); - return pkt_len; -} - - -static int send_ack(uint32_t saddr_be, uint32_t daddr_be, uint16_t sport_be, - uint16_t dport_be, uint32_t seq_be, uint32_t ackseq_be) -{ - int pkt_len; - ssize_t nbytes; - char pkt_buff[1024]; - struct sockaddr_in dstaddr; - - memset(&dstaddr, 0, sizeof(dstaddr)); - dstaddr.sin_family = AF_INET; - dstaddr.sin_addr.s_addr = daddr_be; - - pkt_len = make_pkt(pkt_buff, sizeof(pkt_buff), saddr_be, daddr_be, - sport_be, dport_be, seq_be, ackseq_be, 0, NULL, 0); - if (pkt_len < 0) { - E("ERROR: make_pkt()"); - return -1; - } - - nbytes = sendto(g_sockfd, pkt_buff, pkt_len, 0, - (struct sockaddr *) &dstaddr, sizeof(dstaddr)); - if (nbytes < 0) { - E("ERROR: sendto(): %s", strerror(errno)); - return -1; - } - - return 0; -} - - -static int send_http(uint32_t saddr_be, uint32_t daddr_be, uint16_t sport_be, - uint16_t dport_be, uint32_t seq_be, uint32_t ackseq_be) -{ - static const char *http_fmt = "GET / HTTP/1.1\r\n" - "Host: %s\r\n" - "Accept: */*\r\n" - "\r\n"; - - int http_len, pkt_len; - ssize_t nbytes; - char http_buff[512], pkt_buff[1024]; - struct sockaddr_in dstaddr; - - memset(&dstaddr, 0, sizeof(dstaddr)); - dstaddr.sin_family = AF_INET; - dstaddr.sin_addr.s_addr = daddr_be; - - http_len = snprintf(http_buff, sizeof(http_buff), http_fmt, g_hostname); - if (http_len < 0 || (size_t) http_len >= sizeof(http_buff)) { - E("ERROR: snprintf()"); - return -1; - } - - pkt_len = make_pkt(pkt_buff, sizeof(pkt_buff), saddr_be, daddr_be, - sport_be, dport_be, seq_be, ackseq_be, 1, http_buff, - http_len); - if (pkt_len < 0) { - E("ERROR: make_pkt()"); - return -1; - } - - nbytes = sendto(g_sockfd, pkt_buff, pkt_len, 0, - (struct sockaddr *) &dstaddr, sizeof(dstaddr)); - if (nbytes < 0) { - E("ERROR: sendto(): %s", strerror(errno)); - return -1; - } - - return 0; -} - - -static int callback(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, - struct nfq_data *nfa, void *data) -{ - uint32_t pkt_id, ack_new; - int res, i, pkt_len, iph_len, tcph_len, tcp_payload_len; - struct nfqnl_msg_packet_hdr *ph; - struct iphdr *iph; - struct tcphdr *tcph; - unsigned char *pkt_data; - char src_ip[INET_ADDRSTRLEN], dst_ip[INET_ADDRSTRLEN]; - - (void) nfmsg; - (void) data; - - ph = nfq_get_msg_packet_hdr(nfa); - if (!ph) { - E("ERROR: nfq_get_msg_packet_hdr()"); - return -1; - } - - pkt_id = ntohl(ph->packet_id); - pkt_data = NULL; - pkt_len = nfq_get_payload(nfa, &pkt_data); - if (pkt_len < 0 || !pkt_data) { - E("ERROR: nfq_get_payload()"); - goto ret_accept; - } - - if ((size_t) pkt_len < sizeof(*iph)) { - E("ERROR: invalid packet length: %d", pkt_len); - goto ret_accept; - } - - iph = (struct iphdr *) pkt_data; - iph_len = iph->ihl * 4; - - if ((size_t) iph_len < sizeof(*iph)) { - E("ERROR: invalid IP header length: %d", iph_len); - goto ret_accept; - } - - if (iph->protocol != IPPROTO_TCP) { - E("ERROR: not a TCP packet (protocol %d)", (int) iph->protocol); - goto ret_accept; - } - - if ((size_t) pkt_len < iph_len + sizeof(*tcph)) { - E("ERROR: invalid packet length: %d", pkt_len); - goto ret_accept; - } - - tcph = (struct tcphdr *) (pkt_data + iph_len); - tcph_len = tcph->doff * 4; - tcp_payload_len = pkt_len - iph_len - tcph_len; - - if (!g_silent) { - if (!inet_ntop(AF_INET, &iph->saddr, src_ip, sizeof(src_ip))) { - strncpy(src_ip, "INVALID", sizeof(src_ip) - 1); - src_ip[sizeof(src_ip) - 1] = '\0'; - } - if (!inet_ntop(AF_INET, &iph->daddr, dst_ip, sizeof(dst_ip))) { - strncpy(dst_ip, "INVALID", sizeof(dst_ip) - 1); - src_ip[sizeof(src_ip) - 1] = '\0'; - } - } - - if (tcp_payload_len > 0) { - E_INFO("%s:%u ===PAYLOAD(?)===> %s:%u", src_ip, ntohs(tcph->source), - dst_ip, ntohs(tcph->dest)); - goto ret_mark_repeat; - } else if (tcph->syn && tcph->ack) { - E_INFO("%s:%u ===SYN-ACK===> %s:%u", src_ip, ntohs(tcph->source), - dst_ip, ntohs(tcph->dest)); - - ack_new = ntohl(tcph->seq); - ack_new++; - ack_new = htonl(ack_new); - - for (i = 0; i < g_repeat; i++) { - res = send_ack(iph->daddr, iph->saddr, tcph->dest, tcph->source, - tcph->ack_seq, ack_new); - if (res < 0) { - E("ERROR: send_ack()"); - goto ret_accept; - } - } - E_INFO("%s:%u <===ACK(*)=== %s:%u", src_ip, ntohs(tcph->source), - dst_ip, ntohs(tcph->dest)); - - for (i = 0; i < g_repeat; i++) { - res = send_http(iph->daddr, iph->saddr, tcph->dest, tcph->source, - tcph->ack_seq, ack_new); - if (res < 0) { - E("ERROR: send_http()"); - goto ret_accept; - } - } - E_INFO("%s:%u <===HTTP(*)=== %s:%u", src_ip, ntohs(tcph->source), - dst_ip, ntohs(tcph->dest)); - - goto ret_mark_repeat; - } else if (tcph->ack) { - E_INFO("%s:%u ===ACK===> %s:%u", src_ip, ntohs(tcph->source), dst_ip, - ntohs(tcph->dest)); - - for (i = 0; i < g_repeat; i++) { - res = send_http(iph->daddr, iph->saddr, tcph->dest, tcph->source, - tcph->ack_seq, tcph->seq); - if (res < 0) { - E("ERROR: send_http()"); - goto ret_accept; - } - } - E_INFO("%s:%u <===HTTP(*)=== %s:%u", src_ip, ntohs(tcph->source), - dst_ip, ntohs(tcph->dest)); - - goto ret_mark_repeat; - } else { - E_INFO("%s:%u ===(?)===> %s:%u", src_ip, ntohs(tcph->source), dst_ip, - ntohs(tcph->dest)); - goto ret_accept; - } - -ret_accept: - return nfq_set_verdict(qh, pkt_id, NF_ACCEPT, 0, NULL); - -ret_mark_repeat: - return nfq_set_verdict2(qh, pkt_id, NF_REPEAT, g_fwmark, 0, NULL); -} - - -int main(int argc, char *argv[]) -{ - static const size_t buffsize = UINT16_MAX; - - unsigned long long tmp; - struct nfq_handle *h; - struct nfq_q_handle *qh; - int res, fd, opt, exitcode, err_cnt; - socklen_t opt_len; - ssize_t recv_len; - char *buff, *err_hint; - - exitcode = EXIT_FAILURE; - - if (!argc) { - return EXIT_FAILURE; - } - - while ((opt = getopt(argc, argv, "dh:i:km:n:r:st:w:x:z")) != -1) { - switch (opt) { - case 'd': - g_daemon = 1; - break; - case 'h': - if (strlen(optarg) > _POSIX_HOST_NAME_MAX) { - fprintf(stderr, "%s: hostname is too long.\n", argv[0]); - print_usage(argv[0]); - return EXIT_FAILURE; - } - g_hostname = optarg; - break; - case 'i': - g_iface = optarg; - if (strlen(optarg) > IFNAMSIZ - 1) { - fprintf(stderr, "%s: interface name is too long.\n", - argv[0]); - print_usage(argv[0]); - return EXIT_FAILURE; - } - break; - case 'k': - g_killproc = 1; - break; - case 'm': - tmp = strtoull(optarg, NULL, 0); - if (!tmp || tmp > UINT32_MAX) { - fprintf(stderr, "%s: invalid value for -m.\n", argv[0]); - print_usage(argv[0]); - return EXIT_FAILURE; - } - g_fwmark = tmp; - break; - case 'n': - tmp = strtoull(optarg, NULL, 0); - if (!tmp || tmp > UINT32_MAX) { - fprintf(stderr, "%s: invalid value for -n.\n", argv[0]); - print_usage(argv[0]); - return EXIT_FAILURE; - } - g_nfqnum = tmp; - break; - case 'r': - tmp = strtoull(optarg, NULL, 0); - if (!tmp || tmp > 10) { - fprintf(stderr, "%s: invalid value for -r.\n", argv[0]); - print_usage(argv[0]); - return EXIT_FAILURE; - } - g_repeat = tmp; - break; - case 's': - g_silent = 1; - break; - case 't': - if (sscanf(optarg, "%llu", &tmp) != 1 || !tmp || - tmp > UINT8_MAX) { - fprintf(stderr, "%s: invalid value for -t.\n", argv[0]); - print_usage(argv[0]); - return EXIT_FAILURE; - } - g_ttl = tmp; - break; - case 'w': - g_logfp = fopen(optarg, "a"); - if (!g_logfp) { - fprintf(stderr, "%s: invalid value for -w: %s\n", argv[0], - strerror(errno)); - print_usage(argv[0]); - return EXIT_FAILURE; - } - break; - case 'x': - tmp = strtoull(optarg, NULL, 0); - if (!tmp || tmp > UINT32_MAX) { - fprintf(stderr, "%s: invalid value for -x.\n", argv[0]); - print_usage(argv[0]); - return EXIT_FAILURE; - } - g_fwmask = tmp; - break; - case 'z': - g_use_iptables = 1; - break; - default: - print_usage(argv[0]); - return EXIT_FAILURE; - } - } - - if (g_killproc) { - res = kill_running(SIGTERM); - return res ? EXIT_FAILURE : EXIT_SUCCESS; - } - - if (!g_fwmask) { - g_fwmask = g_fwmark; - } else if ((g_fwmark & g_fwmask) != g_fwmark) { - fprintf(stderr, "%s: invalid value for -m/-x.\n", argv[0]); - print_usage(argv[0]); - return EXIT_FAILURE; - } - - if (!g_hostname) { - fprintf(stderr, "%s: option -h is required.\n", argv[0]); - print_usage(argv[0]); - return EXIT_FAILURE; - } - - if (!g_iface) { - fprintf(stderr, "%s: option -i is required.\n", argv[0]); - print_usage(argv[0]); - return EXIT_FAILURE; - } - - if (g_daemon) { - res = daemon(0, 0); - if (res < 0) { - fprintf(stderr, "%s: failed to daemonize: %s\n", argv[0], - strerror(errno)); - return EXIT_FAILURE; - } - - if (!g_logfp) { - g_silent = 1; - } - } - - E("FakeHTTP version " VERSION); - - srand(time(NULL)); - - buff = malloc(buffsize); - if (!buff) { - E("ERROR: malloc(): %s", strerror(errno)); - return EXIT_FAILURE; - } - - /* - Raw Socket - */ - g_sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); - if (g_sockfd < 0) { - switch (errno) { - case EPERM: - err_hint = " (Are you root?)"; - break; - default: - err_hint = ""; - } - E("ERROR: socket(): %s%s", strerror(errno), err_hint); - goto free_buff; - } - - res = setsockopt(g_sockfd, SOL_SOCKET, SO_BINDTODEVICE, g_iface, - strlen(g_iface)); - if (res < 0) { - E("ERROR: setsockopt(): SO_BINDTODEVICE: %s", strerror(errno)); - goto close_socket; - } - - opt = 1; - res = setsockopt(g_sockfd, IPPROTO_IP, IP_HDRINCL, &opt, sizeof(opt)); - if (res < 0) { - E("ERROR: setsockopt(): IP_HDRINCL: %s", strerror(errno)); - goto close_socket; - } - - res = setsockopt(g_sockfd, SOL_SOCKET, SO_MARK, &g_fwmark, - sizeof(g_fwmark)); - if (res < 0) { - E("ERROR: setsockopt(): SO_MARK: %s", strerror(errno)); - goto close_socket; - } - - opt = 7; - res = setsockopt(g_sockfd, SOL_SOCKET, SO_PRIORITY, &opt, sizeof(opt)); - if (res < 0) { - E("ERROR: setsockopt(): SO_PRIORITY: %s", strerror(errno)); - goto close_socket; - } - - /* - Netfilter Queue - */ - h = nfq_open(); - if (!h) { - switch (errno) { - case EPERM: - err_hint = " (Are you root?)"; - break; - case EINVAL: - err_hint = " (Missing kernel module?)"; - break; - default: - err_hint = ""; - } - E("ERROR: nfq_open(): %s%s", strerror(errno), err_hint); - goto close_socket; - } - - qh = nfq_create_queue(h, g_nfqnum, &callback, NULL); - if (!qh) { - switch (errno) { - case EPERM: - res = kill_running(0); - errno = EPERM; - if (res) { - err_hint = " (Another process is running / Are you root?)"; - } else { - err_hint = " (Another process is running)"; - } - break; - case EINVAL: - err_hint = " (Missing kernel module?)"; - break; - default: - err_hint = ""; - } - E("ERROR: nfq_create_queue(): %s%s", strerror(errno), err_hint); - goto close_nfq; - } - - res = nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff); - if (res < 0) { - E("ERROR: nfq_set_mode(): NFQNL_COPY_PACKET: %s", strerror(errno)); - goto destroy_queue; - } - - res = nfq_set_queue_flags(qh, NFQA_CFG_F_FAIL_OPEN, NFQA_CFG_F_FAIL_OPEN); - if (res < 0) { - E("ERROR: nfq_set_queue_flags(): NFQA_CFG_F_FAIL_OPEN: %s", - strerror(errno)); - goto destroy_queue; - } - - fd = nfq_fd(h); - - opt_len = sizeof(opt); - res = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, &opt_len); - if (res < 0) { - E("ERROR: getsockopt(): SO_RCVBUF: %s", strerror(errno)); - goto destroy_queue; - } - - if (opt < 1048576 /* 1 MB */) { - opt = 1048576; - res = setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &opt, sizeof(opt)); - if (res < 0) { - E("ERROR: setsockopt(): SO_RCVBUFFORCE: %s", strerror(errno)); - goto destroy_queue; - } - } - - /* - Firewall - */ - if (!g_use_iptables) { - if (!nft_is_working()) { - E("WARNING: Falling back to iptables command, as nft command is " - "not working."); - g_use_iptables = 1; - } - } - - if (g_use_iptables) { - res = ipt_rules_flush(1); - if (res) { - E("ERROR: ipt_rules_flush()"); - goto destroy_queue; - } - - res = ipt_rules_setup(); - if (res) { - E("ERROR: ipt_rules_setup()"); - goto flush_rules; - } - } else { - res = nft_rules_flush(1); - if (res) { - E("ERROR: nft_rules_flush()"); - goto destroy_queue; - } - - res = nft_rules_setup(); - if (res) { - E("ERROR: nft_rules_setup()"); - goto flush_rules; - } - } - - /* - Process priority - */ - res = setpriority(PRIO_PROCESS, getpid(), -20); - if (res) { - E("ERROR: setpriority(): %s", strerror(errno)); - /* ignored */ - } - - /* - Signals - */ - res = signal_setup(); - if (res) { - E("ERROR: signal_setup()"); - goto flush_rules; - } - - E("listening on %s, netfilter queue number %" PRIu32 "...", g_iface, - g_nfqnum); - - /* - Main Loop - */ - err_cnt = 0; - while (!g_exit) { - if (err_cnt >= 20) { - E("too many errors, exiting..."); - goto flush_rules; - } - - recv_len = recv(fd, buff, buffsize, 0); - if (recv_len < 0) { - switch (errno) { - case EINTR: - continue; - case EAGAIN: - case ETIMEDOUT: - case ENOBUFS: - E("ERROR: recv(): %s", strerror(errno)); - err_cnt++; - continue; - default: - E("ERROR: recv(): %s", strerror(errno)); - err_cnt++; - goto flush_rules; - } - } - - res = nfq_handle_packet(h, buff, recv_len); - if (res < 0) { - E("ERROR: nfq_handle_packet()"); - err_cnt++; - continue; - } - - err_cnt = 0; - } - - E("exiting normally..."); - exitcode = EXIT_SUCCESS; - -flush_rules: - if (g_use_iptables) { - ipt_rules_flush(0); - } else { - nft_rules_flush(0); - } - -destroy_queue: - nfq_destroy_queue(qh); - -close_nfq: - nfq_close(h); - -close_socket: - close(g_sockfd); - -free_buff: - free(buff); - - if (g_logfp) { - fclose(g_logfp); - } - - return exitcode; -} diff --git a/src/globvar.c b/src/globvar.c new file mode 100644 index 0000000..6fe8339 --- /dev/null +++ b/src/globvar.c @@ -0,0 +1,41 @@ +/* + * globvar.c - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#define _GNU_SOURCE +#include "globvar.h" + +#include +#include + +struct fh_context g_ctx = {.exit = 0, + .sockfd = -1, + .logfp = NULL, + + /* -d */ .daemon = 0, + /* -h */ .hostname = NULL, + /* -i */ .iface = NULL, + /* -k */ .killproc = 0, + /* -m */ .fwmark = 0x8000, + /* -n */ .nfqnum = 512, + /* -r */ .repeat = 3, + /* -s */ .silent = 0, + /* -t */ .ttl = 3, + /* -w */ .logpath = NULL, + /* -x */ .fwmask = 0, + /* -z */ .use_iptables = 0}; diff --git a/src/ipv4ipt.c b/src/ipv4ipt.c new file mode 100644 index 0000000..e90eff3 --- /dev/null +++ b/src/ipv4ipt.c @@ -0,0 +1,167 @@ +/* + * ipv4ipt.c - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#define _GNU_SOURCE +#include "ipv4ipt.h" + +#include +#include + +#include "globvar.h" +#include "logging.h" +#include "process.h" + +int fh_ipt4_flush(int auto_create) +{ + int res; + size_t i, cnt; + char *ipt_flush_cmd[] = {"iptables", "-w", "-t", "mangle", + "-F", "FAKEHTTP", NULL}; + char *ipt_create_cmds[][32] = { + {"iptables", "-w", "-t", "mangle", "-N", "FAKEHTTP", NULL}, + + {"iptables", "-w", "-t", "mangle", "-I", "INPUT", "-j", "FAKEHTTP", + NULL}, + + {"iptables", "-w", "-t", "mangle", "-I", "FORWARD", "-j", "FAKEHTTP", + NULL}}; + + res = fh_execute_command(ipt_flush_cmd, 1, NULL); + if (res < 0) { + if (!auto_create) { + return -1; + } + + cnt = sizeof(ipt_create_cmds) / sizeof(*ipt_create_cmds); + for (i = 0; i < cnt; i++) { + res = fh_execute_command(ipt_create_cmds[i], 0, NULL); + if (res < 0) { + E("ERROR: fh_execute_command()"); + return -1; + } + } + } + + return 0; +} + + +int fh_ipt4_add(void) +{ + char xmark_str[64], nfqnum_str[32], iface_str[32]; + size_t i, ipt_cmds_cnt, ipt_opt_cmds_cnt; + int res; + char *ipt_cmds[][32] = { + /* + exclude marked packets + */ + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP", "-m", "mark", + "--mark", xmark_str, "-j", "CONNMARK", "--set-xmark", xmark_str, + NULL}, + + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP", "-m", "connmark", + "--mark", xmark_str, "-j", "MARK", "--set-xmark", xmark_str, NULL}, + + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP", "-m", "mark", + "--mark", xmark_str, "-j", "RETURN", NULL}, + + /* + exclude local IPs + */ + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP", "-s", "0.0.0.0/8", + "-j", "RETURN", NULL}, + + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP", "-s", + "10.0.0.0/8", "-j", "RETURN", NULL}, + + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP", "-s", + "100.64.0.0/10", "-j", "RETURN", NULL}, + + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP", "-s", + "127.0.0.0/8", "-j", "RETURN", NULL}, + + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP", "-s", + "169.254.0.0/16", "-j", "RETURN", NULL}, + + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP", "-s", + "172.16.0.0/12", "-j", "RETURN", NULL}, + + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP", "-s", + "192.168.0.0/16", "-j", "RETURN", NULL}, + + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP", "-s", + "224.0.0.0/3", "-j", "RETURN", NULL}, + + /* + send to nfqueue + */ + {"iptables", "-w", "-t", "mangle", "-A", "FAKEHTTP", "-i", iface_str, + "-p", "tcp", "--tcp-flags", "ACK,FIN,RST", "ACK", "-j", "NFQUEUE", + "--queue-bypass", "--queue-num", nfqnum_str, NULL}}; + + char *ipt_opt_cmds[][32] = { + /* + exclude packets from connections with more than 32 packets + */ + {"iptables", "-w", "-t", "mangle", "-I", "FAKEHTTP", "-m", "connbytes", + "!", "--connbytes", "0:32", "--connbytes-dir", "both", + "--connbytes-mode", "packets", "-j", "RETURN", NULL}, + + /* + exclude big packets + */ + {"iptables", "-w", "-t", "mangle", "-I", "FAKEHTTP", "-m", "length", + "!", "--length", "0:120", "-j", "RETURN", NULL}}; + + ipt_cmds_cnt = sizeof(ipt_cmds) / sizeof(*ipt_cmds); + ipt_opt_cmds_cnt = sizeof(ipt_opt_cmds) / sizeof(*ipt_opt_cmds); + + res = snprintf(xmark_str, sizeof(xmark_str), "%" PRIu32 "/%" PRIu32, + g_ctx.fwmark, g_ctx.fwmask); + if (res < 0 || (size_t) res >= sizeof(xmark_str)) { + E("ERROR: snprintf()"); + return -1; + } + + res = snprintf(nfqnum_str, sizeof(nfqnum_str), "%" PRIu32, g_ctx.nfqnum); + if (res < 0 || (size_t) res >= sizeof(nfqnum_str)) { + E("ERROR: snprintf()"); + return -1; + } + + res = snprintf(iface_str, sizeof(iface_str), "%s", g_ctx.iface); + if (res < 0 || (size_t) res >= sizeof(iface_str)) { + E("ERROR: snprintf()"); + return -1; + } + + for (i = 0; i < ipt_cmds_cnt; i++) { + res = fh_execute_command(ipt_cmds[i], 0, NULL); + if (res < 0) { + E("ERROR: fh_execute_command()"); + return -1; + } + } + + for (i = 0; i < ipt_opt_cmds_cnt; i++) { + fh_execute_command(ipt_opt_cmds[i], 1, NULL); + } + + return 0; +} diff --git a/src/ipv4nft.c b/src/ipv4nft.c new file mode 100644 index 0000000..8f4f400 --- /dev/null +++ b/src/ipv4nft.c @@ -0,0 +1,153 @@ +/* + * ipv4nft.c - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#define _GNU_SOURCE +#include "ipv4nft.h" + +#include +#include + +#include "globvar.h" +#include "logging.h" +#include "process.h" + +int fh_nft4_flush(int auto_create) +{ + int res; + char *nft_flush_cmd[] = {"nft", "flush table fakehttp", NULL}; + char *nft_cmd[] = {"nft", "-f", "-", NULL}; + char *nft_create_conf = + "table ip fakehttp {\n" + " chain fh_input {\n" + " type filter hook input priority mangle - 5;\n" + " policy accept;\n" + " }\n" + "\n" + " chain fh_output {\n" + " type filter hook forward priority mangle - 5;\n" + " policy accept;\n" + " }\n" + "\n" + " chain fh_rules {\n" + " }\n" + "}\n"; + + res = fh_execute_command(nft_flush_cmd, 1, NULL); + if (res < 0) { + if (!auto_create) { + return -1; + } + + res = fh_execute_command(nft_cmd, 0, nft_create_conf); + if (res < 0) { + E("ERROR: fh_execute_command()"); + return -1; + } + } + + return 0; +} + + +int fh_nft4_add(void) +{ + size_t i, nft_opt_cmds_cnt; + int res; + char *nft_cmd[] = {"nft", "-f", "-", NULL}; + char nft_conf_buff[2048]; + char *nft_conf_fmt = + "table ip fakehttp {\n" + " chain fh_input {\n" + " jump fh_rules;\n" + " }\n" + "\n" + " chain fh_output {\n" + " jump fh_rules;\n" + " }\n" + "\n" + " chain fh_rules {\n" + + /* + exclude marked packets + */ + " meta mark and %" PRIu32 " == %" PRIu32 + " ct mark set ct mark and %" PRIu32 " xor %" PRIu32 ";\n" + + " ct mark and %" PRIu32 " == %" PRIu32 + " meta mark set mark and %" PRIu32 " xor %" PRIu32 ";\n" + + " meta mark and %" PRIu32 " == %" PRIu32 " return;\n" + + /* + exclude local IPs + */ + " ip saddr 0.0.0.0/8 return;\n" + " ip saddr 10.0.0.0/8 return;\n" + " ip saddr 100.64.0.0/10 return;\n" + " ip saddr 127.0.0.0/8 return;\n" + " ip saddr 169.254.0.0/16 return;\n" + " ip saddr 172.16.0.0/12 return;\n" + " ip saddr 192.168.0.0/16 return;\n" + " ip saddr 224.0.0.0/3 return;\n" + + /* + send to nfqueue + */ + " iifname \"%s\" tcp flags & (fin | rst | ack) == ack queue " + "num %" PRIu32 " bypass;\n" + + " }\n" + "}\n"; + + char *nft_opt_cmds[][32] = { + /* + exclude packets from connections with more than 32 packets + */ + {"nft", "insert rule ip fakehttp fh_rules ct packets > 32 return", + NULL}, + + /* + exclude big packets + */ + {"nft", "insert rule ip fakehttp fh_rules meta length > 120 return", + NULL}}; + + nft_opt_cmds_cnt = sizeof(nft_opt_cmds) / sizeof(*nft_opt_cmds); + + res = snprintf(nft_conf_buff, sizeof(nft_conf_buff), nft_conf_fmt, + g_ctx.fwmask, g_ctx.fwmark, ~g_ctx.fwmask, g_ctx.fwmark, + g_ctx.fwmask, g_ctx.fwmark, ~g_ctx.fwmask, g_ctx.fwmark, + g_ctx.fwmask, g_ctx.fwmark, g_ctx.iface, g_ctx.nfqnum); + if (res < 0 || (size_t) res >= sizeof(nft_conf_buff)) { + E("ERROR: snprintf()"); + return -1; + } + + res = fh_execute_command(nft_cmd, 1, nft_conf_buff); + if (res < 0) { + E("ERROR: fh_execute_command()"); + return -1; + } + + for (i = 0; i < nft_opt_cmds_cnt; i++) { + fh_execute_command(nft_opt_cmds[i], 1, NULL); + } + + return 0; +} diff --git a/src/ipv4pkt.c b/src/ipv4pkt.c new file mode 100644 index 0000000..5f965c4 --- /dev/null +++ b/src/ipv4pkt.c @@ -0,0 +1,85 @@ +/* + * ipv4pkt.c - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#define _GNU_SOURCE +#include "ipv4pkt.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "globvar.h" + +int fh_pkt4_make(char *buffer, size_t buffer_size, uint32_t saddr_be, + uint32_t daddr_be, uint16_t sport_be, uint16_t dport_be, + uint32_t seq_be, uint32_t ackseq_be, int psh, + char *tcp_payload, size_t tcp_payload_size) +{ + size_t pkt_len; + struct iphdr *iph; + struct tcphdr *tcph; + char *tcppl; + + pkt_len = sizeof(*iph) + sizeof(*tcph) + tcp_payload_size; + if (buffer_size < pkt_len + 1) { + return -1; + } + + iph = (struct iphdr *) buffer; + tcph = (struct tcphdr *) (buffer + sizeof(*iph)); + tcppl = buffer + sizeof(*iph) + sizeof(*tcph); + + memset(iph, 0, sizeof(*iph)); + iph->version = 4; + iph->ihl = sizeof(*iph) / 4; + iph->tos = 0; + iph->tot_len = htons(pkt_len); + iph->id = ((rand() & 0xff) << 8) | (rand() & 0xff); + iph->frag_off = htons(1 << 14 /* DF */); + iph->ttl = g_ctx.ttl; + iph->protocol = IPPROTO_TCP; + iph->check = 0; + iph->saddr = saddr_be; + iph->daddr = daddr_be; + + memset(tcph, 0, sizeof(*tcph)); + tcph->source = sport_be; + tcph->dest = dport_be; + tcph->seq = seq_be; + tcph->ack_seq = ackseq_be; + tcph->doff = sizeof(*tcph) / 4; + tcph->psh = psh; + tcph->ack = 1; + tcph->window = htons(0x0080); + tcph->check = 0; + tcph->urg_ptr = 0; + + if (tcp_payload_size) { + memcpy(tcppl, tcp_payload, tcp_payload_size); + } + + nfq_ip_set_checksum(iph); + nfq_tcp_compute_checksum_ipv4(tcph, iph); + + return pkt_len; +} diff --git a/src/ipv6ipt.c b/src/ipv6ipt.c new file mode 100644 index 0000000..bce7b31 --- /dev/null +++ b/src/ipv6ipt.c @@ -0,0 +1,43 @@ +/* + * ipv6ipt.c - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#define _GNU_SOURCE +#include "ipv6ipt.h" + +#include +#include + +#include "globvar.h" +#include "logging.h" +#include "process.h" + +/* TODO: NOT IMPLEMENTED */ +int fh_ipt6_flush(int auto_create) +{ + (void) auto_create; + + return -1; +} + + +/* TODO: NOT IMPLEMENTED */ +int fh_ipt6_add(void) +{ + return -1; +} diff --git a/src/ipv6nft.c b/src/ipv6nft.c new file mode 100644 index 0000000..a465ae5 --- /dev/null +++ b/src/ipv6nft.c @@ -0,0 +1,43 @@ +/* + * ipv6nft.c - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#define _GNU_SOURCE +#include "ipv6nft.h" + +#include +#include + +#include "globvar.h" +#include "logging.h" +#include "process.h" + +/* TODO: NOT IMPLEMENTED */ +int fh_nft6_flush(int auto_create) +{ + (void) auto_create; + + return -1; +} + + +/* TODO: NOT IMPLEMENTED */ +int fh_nft6_add(void) +{ + return -1; +} diff --git a/src/ipv6pkt.c b/src/ipv6pkt.c new file mode 100644 index 0000000..5c9b61e --- /dev/null +++ b/src/ipv6pkt.c @@ -0,0 +1,52 @@ +/* + * ipv6pkt.c - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#define _GNU_SOURCE +#include "ipv6pkt.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "globvar.h" + +/* TODO: NOT IMPLEMENTED */ +int fh_pkt6_make(char *buffer, size_t buffer_size, uint8_t *saddr_be, + uint8_t *daddr_be, uint16_t sport_be, uint16_t dport_be, + uint32_t seq_be, uint32_t ackseq_be, int psh, + char *tcp_payload, size_t tcp_payload_size) +{ + (void) buffer; + (void) buffer_size; + (void) saddr_be; + (void) daddr_be; + (void) sport_be; + (void) dport_be; + (void) seq_be; + (void) ackseq_be; + (void) psh; + (void) tcp_payload; + (void) tcp_payload_size; + + return -1; +} diff --git a/src/logging.c b/src/logging.c new file mode 100644 index 0000000..06af7ee --- /dev/null +++ b/src/logging.c @@ -0,0 +1,89 @@ +/* + * logging.c - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#define _GNU_SOURCE +#include "logging.h" + +#include +#include +#include +#include +#include +#include + +#include "globvar.h" + +int fh_logger_setup(void) +{ + if (g_ctx.logpath) { + g_ctx.logfp = fopen(g_ctx.logpath, "a"); + if (!g_ctx.logfp) { + g_ctx.logfp = stderr; + E("ERROR: fopen(): %s: %s", g_ctx.logpath, strerror(errno)); + return -1; + } + } else { + g_ctx.logfp = stderr; + } + + return 0; +} + + +void fh_logger_cleanup(void) +{ + if (g_ctx.logfp && g_ctx.logfp != stderr) { + fclose(g_ctx.logfp); + g_ctx.logfp = NULL; + } +} + + +void fh_logger(const char *funcname, const char *filename, unsigned long line, + const char *fmt, ...) +{ + va_list args; + time_t t; + char *stime; + + t = time(NULL); + stime = ctime(&t); + if (stime) { + stime[strlen(stime) - 1] = '\0'; + fprintf(g_ctx.logfp, "%s ", stime); + } + + fprintf(g_ctx.logfp, "[%s() - %s:%lu] ", funcname, filename, line); + va_start(args, fmt); + vfprintf(g_ctx.logfp, fmt, args); + va_end(args); + fputc('\n', g_ctx.logfp); + fflush(g_ctx.logfp); +} + + +void fh_logger_raw(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vfprintf(g_ctx.logfp, fmt, args); + va_end(args); + fflush(g_ctx.logfp); +} diff --git a/src/mainfun.c b/src/mainfun.c new file mode 100644 index 0000000..e55acb2 --- /dev/null +++ b/src/mainfun.c @@ -0,0 +1,297 @@ +/* + * mainfun.c - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#define _GNU_SOURCE +#include "mainfun.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "globvar.h" +#include "logging.h" +#include "nfqueue.h" +#include "nfrules.h" +#include "process.h" +#include "rawsock.h" +#include "signals.h" + +#ifndef VERSION +#define VERSION "dev" +#endif /* VERSION */ + +static void print_usage(const char *name) +{ + fprintf(stderr, + "Usage: %s [options]\n" + "\n" + "Options:\n" + " -d run as a daemon\n" + " -h hostname for obfuscation (required)\n" + " -i network interface name (required)\n" + " -k kill the running process\n" + " -m fwmark for bypassing the queue\n" + " -n netfilter queue number\n" + " -r duplicate generated packets for " + "times\n" + " -s enable silent mode\n" + " -t TTL for generated packets\n" + " -w write log to instead of stderr\n" + " -x set the mask for fwmark\n" + " -z use iptables commands instead of nft\n" + "\n" + "FakeHTTP version " VERSION "\n", + name); +} + + +int main(int argc, char *argv[]) +{ + unsigned long long tmp; + int res, opt, exitcode; + + if (!argc || !argv[0]) { + return EXIT_FAILURE; + } + + exitcode = EXIT_FAILURE; + + while ((opt = getopt(argc, argv, "dh:i:km:n:r:st:w:x:z")) != -1) { + switch (opt) { + case 'd': + g_ctx.daemon = 1; + break; + + case 'h': + if (strlen(optarg) > _POSIX_HOST_NAME_MAX) { + fprintf(stderr, "%s: hostname is too long.\n", argv[0]); + print_usage(argv[0]); + return EXIT_FAILURE; + } + g_ctx.hostname = optarg; + break; + + case 'i': + g_ctx.iface = optarg; + if (strlen(optarg) > IFNAMSIZ - 1) { + fprintf(stderr, "%s: interface name is too long.\n", + argv[0]); + print_usage(argv[0]); + return EXIT_FAILURE; + } + break; + + case 'k': + g_ctx.killproc = 1; + break; + + case 'm': + tmp = strtoull(optarg, NULL, 0); + if (!tmp || tmp > UINT32_MAX) { + fprintf(stderr, "%s: invalid value for -m.\n", argv[0]); + print_usage(argv[0]); + return EXIT_FAILURE; + } + g_ctx.fwmark = tmp; + break; + + case 'n': + tmp = strtoull(optarg, NULL, 0); + if (!tmp || tmp > UINT32_MAX) { + fprintf(stderr, "%s: invalid value for -n.\n", argv[0]); + print_usage(argv[0]); + return EXIT_FAILURE; + } + g_ctx.nfqnum = tmp; + break; + + case 'r': + tmp = strtoull(optarg, NULL, 0); + if (!tmp || tmp > 10) { + fprintf(stderr, "%s: invalid value for -r.\n", argv[0]); + print_usage(argv[0]); + return EXIT_FAILURE; + } + g_ctx.repeat = tmp; + break; + + case 's': + g_ctx.silent = 1; + break; + + case 't': + tmp = strtoull(optarg, NULL, 0); + if (!tmp || tmp > UINT8_MAX) { + fprintf(stderr, "%s: invalid value for -t.\n", argv[0]); + print_usage(argv[0]); + return EXIT_FAILURE; + } + g_ctx.ttl = tmp; + break; + + case 'w': + g_ctx.logpath = optarg; + if (strlen(g_ctx.logpath) > PATH_MAX - 1) { + fprintf(stderr, "%s: path of log file is too long.\n", + argv[0]); + print_usage(argv[0]); + return EXIT_FAILURE; + } + break; + + case 'x': + tmp = strtoull(optarg, NULL, 0); + if (!tmp || tmp > UINT32_MAX) { + fprintf(stderr, "%s: invalid value for -x.\n", argv[0]); + print_usage(argv[0]); + return EXIT_FAILURE; + } + g_ctx.fwmask = tmp; + break; + + case 'z': + g_ctx.use_iptables = 1; + break; + + default: + print_usage(argv[0]); + return EXIT_FAILURE; + } + } + + if (g_ctx.killproc) { + res = fh_logger_setup(); + if (res < 0) { + E("ERROR: fh_logger_setup()"); + return EXIT_FAILURE; + } + res = fh_kill_running(SIGTERM); + fh_logger_cleanup(); + + return res < 0 ? EXIT_FAILURE : EXIT_SUCCESS; + } + + if (!g_ctx.fwmask) { + g_ctx.fwmask = g_ctx.fwmark; + } else if ((g_ctx.fwmark & g_ctx.fwmask) != g_ctx.fwmark) { + fprintf(stderr, "%s: invalid value for -m/-x.\n", argv[0]); + print_usage(argv[0]); + return EXIT_FAILURE; + } + + if (!g_ctx.hostname) { + fprintf(stderr, "%s: option -h is required.\n", argv[0]); + print_usage(argv[0]); + return EXIT_FAILURE; + } + + if (!g_ctx.iface) { + fprintf(stderr, "%s: option -i is required.\n", argv[0]); + print_usage(argv[0]); + return EXIT_FAILURE; + } + + if (g_ctx.daemon) { + res = daemon(0, 0); + if (res < 0) { + fprintf(stderr, "%s: failed to daemonize: %s\n", argv[0], + strerror(errno)); + return EXIT_FAILURE; + } + + if (g_ctx.logfp == stderr) { + g_ctx.silent = 1; + } + } + + srand(time(NULL)); + + res = fh_logger_setup(); + if (res < 0) { + E("ERROR: fh_logger_setup()"); + return EXIT_FAILURE; + } + + E("FakeHTTP version " VERSION); + + res = fh_rawsock_setup(); + if (res < 0) { + E("from fh_rawsock_setup()"); + goto cleanup_logger; + } + + res = fh_nfq_setup(); + if (res < 0) { + E("ERROR: fh_nfq_setup()"); + goto cleanup_rawsock; + } + + res = fh_nfrules_setup(); + if (res < 0) { + E("ERROR: fh_nfrules_setup()"); + goto cleanup_nfq; + } + + res = fh_signal_setup(); + if (res < 0) { + E("ERROR: fh_signal_setup()"); + goto cleanup_nfrules; + } + + res = setpriority(PRIO_PROCESS, getpid(), -20); + if (res < 0) { + E("WARNING: setpriority(): %s", strerror(errno)); + } + + E("listening on %s, netfilter queue number %" PRIu32 "...", g_ctx.iface, + g_ctx.nfqnum); + + /* + Main Loop + */ + res = fh_nfq_loop(); + if (res < 0) { + E("ERROR: fh_nfq_loop()"); + goto cleanup_nfrules; + } + + E("exiting normally..."); + exitcode = EXIT_SUCCESS; + +cleanup_nfrules: + fh_nfrules_cleanup(); + +cleanup_nfq: + fh_nfq_cleanup(); + +cleanup_rawsock: + fh_rawsock_cleanup(); + +cleanup_logger: + fh_logger_cleanup(); + + return exitcode; +} diff --git a/src/nfqueue.c b/src/nfqueue.c new file mode 100644 index 0000000..5e97860 --- /dev/null +++ b/src/nfqueue.c @@ -0,0 +1,407 @@ +/* + * nfqueue.c - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#define _GNU_SOURCE +#include "nfqueue.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "globvar.h" +#include "ipv4pkt.h" +#include "logging.h" +#include "process.h" +#include "signals.h" + +static int fd = -1; +static struct nfq_handle *h = NULL; +static struct nfq_q_handle *qh = NULL; + +static int send_ack(uint32_t saddr_be, uint32_t daddr_be, uint16_t sport_be, + uint16_t dport_be, uint32_t seq_be, uint32_t ackseq_be) +{ + int pkt_len; + ssize_t nbytes; + char pkt_buff[1024]; + struct sockaddr_in dstaddr; + + memset(&dstaddr, 0, sizeof(dstaddr)); + dstaddr.sin_family = AF_INET; + dstaddr.sin_addr.s_addr = daddr_be; + + pkt_len = fh_pkt4_make(pkt_buff, sizeof(pkt_buff), saddr_be, daddr_be, + sport_be, dport_be, seq_be, ackseq_be, 0, NULL, 0); + if (pkt_len < 0) { + E("ERROR: fh_pkt4_make()"); + return -1; + } + + nbytes = sendto(g_ctx.sockfd, pkt_buff, pkt_len, 0, + (struct sockaddr *) &dstaddr, sizeof(dstaddr)); + if (nbytes < 0) { + E("ERROR: sendto(): %s", strerror(errno)); + return -1; + } + + return 0; +} + + +static int send_http(uint32_t saddr_be, uint32_t daddr_be, uint16_t sport_be, + uint16_t dport_be, uint32_t seq_be, uint32_t ackseq_be) +{ + static const char *http_fmt = "GET / HTTP/1.1\r\n" + "Host: %s\r\n" + "Accept: */*\r\n" + "\r\n"; + + int http_len, pkt_len; + ssize_t nbytes; + char http_buff[512], pkt_buff[1024]; + struct sockaddr_in dstaddr; + + memset(&dstaddr, 0, sizeof(dstaddr)); + dstaddr.sin_family = AF_INET; + dstaddr.sin_addr.s_addr = daddr_be; + + http_len = snprintf(http_buff, sizeof(http_buff), http_fmt, + g_ctx.hostname); + if (http_len < 0 || (size_t) http_len >= sizeof(http_buff)) { + E("ERROR: snprintf()"); + return -1; + } + + pkt_len = fh_pkt4_make(pkt_buff, sizeof(pkt_buff), saddr_be, daddr_be, + sport_be, dport_be, seq_be, ackseq_be, 1, http_buff, + http_len); + if (pkt_len < 0) { + E("ERROR: fh_pkt4_make()"); + return -1; + } + + nbytes = sendto(g_ctx.sockfd, pkt_buff, pkt_len, 0, + (struct sockaddr *) &dstaddr, sizeof(dstaddr)); + if (nbytes < 0) { + E("ERROR: sendto(): %s", strerror(errno)); + return -1; + } + + return 0; +} + + +static int callback(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, + struct nfq_data *nfa, void *data) +{ + uint32_t pkt_id, ack_new; + int res, i, pkt_len, iph_len, tcph_len, tcp_payload_len; + struct nfqnl_msg_packet_hdr *ph; + struct iphdr *iph; + struct tcphdr *tcph; + unsigned char *pkt_data; + char src_ip[INET_ADDRSTRLEN], dst_ip[INET_ADDRSTRLEN]; + + (void) nfmsg; + (void) data; + + ph = nfq_get_msg_packet_hdr(nfa); + if (!ph) { + E("ERROR: nfq_get_msg_packet_hdr()"); + return -1; + } + + pkt_id = ntohl(ph->packet_id); + pkt_data = NULL; + pkt_len = nfq_get_payload(nfa, &pkt_data); + if (pkt_len < 0 || !pkt_data) { + E("ERROR: nfq_get_payload()"); + goto ret_accept; + } + + if ((size_t) pkt_len < sizeof(*iph)) { + E("ERROR: invalid packet length: %d", pkt_len); + goto ret_accept; + } + + iph = (struct iphdr *) pkt_data; + iph_len = iph->ihl * 4; + + if ((size_t) iph_len < sizeof(*iph)) { + E("ERROR: invalid IP header length: %d", iph_len); + goto ret_accept; + } + + if (iph->protocol != IPPROTO_TCP) { + E("ERROR: not a TCP packet (protocol %d)", (int) iph->protocol); + goto ret_accept; + } + + if ((size_t) pkt_len < iph_len + sizeof(*tcph)) { + E("ERROR: invalid packet length: %d", pkt_len); + goto ret_accept; + } + + tcph = (struct tcphdr *) (pkt_data + iph_len); + tcph_len = tcph->doff * 4; + tcp_payload_len = pkt_len - iph_len - tcph_len; + + if (!g_ctx.silent) { + if (!inet_ntop(AF_INET, &iph->saddr, src_ip, sizeof(src_ip))) { + strncpy(src_ip, "INVALID", sizeof(src_ip) - 1); + src_ip[sizeof(src_ip) - 1] = '\0'; + } + if (!inet_ntop(AF_INET, &iph->daddr, dst_ip, sizeof(dst_ip))) { + strncpy(dst_ip, "INVALID", sizeof(dst_ip) - 1); + src_ip[sizeof(src_ip) - 1] = '\0'; + } + } + + if (tcp_payload_len > 0) { + E_INFO("%s:%u ===PAYLOAD(?)===> %s:%u", src_ip, ntohs(tcph->source), + dst_ip, ntohs(tcph->dest)); + goto ret_mark_repeat; + } else if (tcph->syn && tcph->ack) { + E_INFO("%s:%u ===SYN-ACK===> %s:%u", src_ip, ntohs(tcph->source), + dst_ip, ntohs(tcph->dest)); + + ack_new = ntohl(tcph->seq); + ack_new++; + ack_new = htonl(ack_new); + + for (i = 0; i < g_ctx.repeat; i++) { + res = send_ack(iph->daddr, iph->saddr, tcph->dest, tcph->source, + tcph->ack_seq, ack_new); + if (res < 0) { + E("ERROR: send_ack()"); + goto ret_accept; + } + } + E_INFO("%s:%u <===ACK(*)=== %s:%u", src_ip, ntohs(tcph->source), + dst_ip, ntohs(tcph->dest)); + + for (i = 0; i < g_ctx.repeat; i++) { + res = send_http(iph->daddr, iph->saddr, tcph->dest, tcph->source, + tcph->ack_seq, ack_new); + if (res < 0) { + E("ERROR: send_http()"); + goto ret_accept; + } + } + E_INFO("%s:%u <===HTTP(*)=== %s:%u", src_ip, ntohs(tcph->source), + dst_ip, ntohs(tcph->dest)); + + goto ret_mark_repeat; + } else if (tcph->ack) { + E_INFO("%s:%u ===ACK===> %s:%u", src_ip, ntohs(tcph->source), dst_ip, + ntohs(tcph->dest)); + + for (i = 0; i < g_ctx.repeat; i++) { + res = send_http(iph->daddr, iph->saddr, tcph->dest, tcph->source, + tcph->ack_seq, tcph->seq); + if (res < 0) { + E("ERROR: send_http()"); + goto ret_accept; + } + } + E_INFO("%s:%u <===HTTP(*)=== %s:%u", src_ip, ntohs(tcph->source), + dst_ip, ntohs(tcph->dest)); + + goto ret_mark_repeat; + } else { + E_INFO("%s:%u ===(?)===> %s:%u", src_ip, ntohs(tcph->source), dst_ip, + ntohs(tcph->dest)); + goto ret_accept; + } + +ret_accept: + return nfq_set_verdict(qh, pkt_id, NF_ACCEPT, 0, NULL); + +ret_mark_repeat: + return nfq_set_verdict2(qh, pkt_id, NF_REPEAT, g_ctx.fwmark, 0, NULL); +} + + +int fh_nfq_setup(void) +{ + int res, opt; + char *err_hint; + socklen_t opt_len; + + h = nfq_open(); + if (!h) { + switch (errno) { + case EPERM: + err_hint = " (Are you root?)"; + break; + case EINVAL: + err_hint = " (Missing kernel module?)"; + break; + default: + err_hint = ""; + } + E("ERROR: nfq_open(): %s%s", strerror(errno), err_hint); + return -1; + } + + qh = nfq_create_queue(h, g_ctx.nfqnum, &callback, NULL); + if (!qh) { + switch (errno) { + case EPERM: + res = fh_kill_running(0); + errno = EPERM; + if (res < 0) { + err_hint = " (Another process is running / Are you root?)"; + } else { + err_hint = " (Another process is running)"; + } + break; + case EINVAL: + err_hint = " (Missing kernel module?)"; + break; + default: + err_hint = ""; + } + E("ERROR: nfq_create_queue(): %s%s", strerror(errno), err_hint); + goto close_nfq; + } + + res = nfq_set_mode(qh, NFQNL_COPY_PACKET, 0xffff); + if (res < 0) { + E("ERROR: nfq_set_mode(): NFQNL_COPY_PACKET: %s", strerror(errno)); + goto destroy_queue; + } + + res = nfq_set_queue_flags(qh, NFQA_CFG_F_FAIL_OPEN, NFQA_CFG_F_FAIL_OPEN); + if (res < 0) { + E("ERROR: nfq_set_queue_flags(): NFQA_CFG_F_FAIL_OPEN: %s", + strerror(errno)); + goto destroy_queue; + } + + fd = nfq_fd(h); + + opt_len = sizeof(opt); + res = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, &opt_len); + if (res < 0) { + E("ERROR: getsockopt(): SO_RCVBUF: %s", strerror(errno)); + goto destroy_queue; + } + + if (opt < 1048576 /* 1 MB */) { + opt = 1048576; + res = setsockopt(fd, SOL_SOCKET, SO_RCVBUFFORCE, &opt, sizeof(opt)); + if (res < 0) { + E("ERROR: setsockopt(): SO_RCVBUFFORCE: %s", strerror(errno)); + goto destroy_queue; + } + } + + return 0; + +destroy_queue: + nfq_destroy_queue(qh); + +close_nfq: + nfq_close(h); + + return -1; +} + + +void fh_nfq_cleanup(void) +{ + if (qh) { + nfq_destroy_queue(qh); + qh = NULL; + } + + if (h) { + nfq_close(h); + h = NULL; + fd = -1; + } +} + + +int fh_nfq_loop(void) +{ + static const size_t buffsize = UINT16_MAX; + + int res, ret, err_cnt; + ssize_t recv_len; + char *buff; + + buff = malloc(buffsize); + if (!buff) { + E("ERROR: malloc(): %s", strerror(errno)); + return -1; + } + + err_cnt = 0; + + while (!g_ctx.exit) { + if (err_cnt >= 20) { + E("too many errors, exiting..."); + ret = -1; + goto free_buff; + } + + recv_len = recv(fd, buff, buffsize, 0); + if (recv_len < 0) { + err_cnt++; + switch (errno) { + case EINTR: + continue; + case EAGAIN: + case ETIMEDOUT: + case ENOBUFS: + E("ERROR: recv(): %s", strerror(errno)); + continue; + default: + E("ERROR: recv(): %s", strerror(errno)); + ret = -1; + goto free_buff; + } + } + + res = nfq_handle_packet(h, buff, recv_len); + if (res < 0) { + err_cnt++; + E("ERROR: nfq_handle_packet()"); + continue; + } + + err_cnt = 0; + } + + ret = 0; + +free_buff: + free(buff); + + return ret; +} diff --git a/src/nfrules.c b/src/nfrules.c new file mode 100644 index 0000000..14982dd --- /dev/null +++ b/src/nfrules.c @@ -0,0 +1,88 @@ +/* + * nfrules.c - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#define _GNU_SOURCE +#include "nfrules.h" + +#include + +#include "globvar.h" +#include "ipv4ipt.h" +#include "ipv4nft.h" +#include "logging.h" +#include "process.h" + +static int nft_is_working(void) +{ + char *nft_ver_cmd[] = {"nft", "--version", NULL}; + + return !fh_execute_command(nft_ver_cmd, 1, NULL); +} + + +int fh_nfrules_setup(void) +{ + int res; + + if (!g_ctx.use_iptables && !nft_is_working()) { + E("WARNING: Falling back to iptables command, as nft command is not " + "working."); + g_ctx.use_iptables = 1; + } + + if (g_ctx.use_iptables) { + res = fh_ipt4_flush(1); + if (res < 0) { + E("ERROR: fh_ipt4_flush()"); + return -1; + } + + res = fh_ipt4_add(); + if (res < 0) { + E("ERROR: fh_ipt4_add()"); + fh_ipt4_flush(0); + return -1; + } + } else { + res = fh_nft4_flush(1); + if (res < 0) { + E("ERROR: fh_nft4_flush()"); + return -1; + } + + res = fh_nft4_add(); + if (res < 0) { + E("ERROR: fh_nft4_add()"); + fh_nft4_flush(0); + return -1; + } + } + + return 0; +} + + +void fh_nfrules_cleanup(void) +{ + if (g_ctx.use_iptables) { + fh_ipt4_flush(0); + } else { + fh_nft4_flush(0); + } +} diff --git a/src/process.c b/src/process.c new file mode 100644 index 0000000..edf8d5b --- /dev/null +++ b/src/process.c @@ -0,0 +1,139 @@ +/* + * process.c - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#define _GNU_SOURCE +#include "process.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "globvar.h" +#include "logging.h" + +int fh_execute_command(char **argv, int silent, char *input) +{ + int res, pipefd[2], status, fd, i; + size_t input_len, written; + ssize_t n; + pid_t pid; + + if (input) { + res = pipe(pipefd); + if (res < 0) { + E("ERROR: pipe(): %s", strerror(errno)); + return -1; + } + } + + pid = fork(); + if (pid < 0) { + E("ERROR: fork(): %s", strerror(errno)); + if (input) { + close(pipefd[0]); + close(pipefd[1]); + } + return -1; + } + + if (!pid) { + fd = -1; + + if (silent) { + fd = open("/dev/null", O_WRONLY); + if (fd < 0) { + E("ERROR: open(): %s", strerror(errno)); + _exit(EXIT_FAILURE); + } + } else if (g_ctx.logfp && g_ctx.logfp != stderr) { + fd = fileno(g_ctx.logfp); + if (fd < 0) { + E("ERROR: fileno(): %s", strerror(errno)); + _exit(EXIT_FAILURE); + } + } + + if (fd >= 0) { + res = dup2(fd, STDOUT_FILENO); + if (res < 0) { + E("ERROR: dup2(): %s", strerror(errno)); + _exit(EXIT_FAILURE); + } + res = dup2(fd, STDERR_FILENO); + if (res < 0) { + E("ERROR: dup2(): %s", strerror(errno)); + _exit(EXIT_FAILURE); + } + close(fd); + } + + if (input) { + close(pipefd[1]); + res = dup2(pipefd[0], STDIN_FILENO); + if (res < 0) { + E("ERROR: dup2(): %s", strerror(errno)); + _exit(EXIT_FAILURE); + } + close(pipefd[0]); + } + + execvp(argv[0], argv); + + E("ERROR: execvp(): %s: %s", argv[0], strerror(errno)); + + _exit(EXIT_FAILURE); + } + + if (input) { + close(pipefd[0]); + input_len = strlen(input); + for (written = 0; written < input_len; written += n) { + n = write(pipefd[1], input + written, input_len - written); + if (n < 0) { + E("ERROR: write(): %s", strerror(errno)); + break; + } + } + close(pipefd[1]); + } + + if (waitpid(pid, &status, 0) < 0) { + E("ERROR: waitpid(): %s", strerror(errno)); + goto child_failed; + } + + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + return 0; + } + +child_failed: + if (!silent) { + E_RAW("[*] failed command is: %s", argv[0]); + for (i = 1; argv[i]; i++) { + E_RAW(" %s", argv[i]); + } + E_RAW("\n"); + } + + return -1; +} diff --git a/src/rawsock.c b/src/rawsock.c new file mode 100644 index 0000000..589efae --- /dev/null +++ b/src/rawsock.c @@ -0,0 +1,94 @@ +/* + * rawsock.c - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#define _GNU_SOURCE +#include "rawsock.h" + +#include +#include +#include +#include +#include + +#include "globvar.h" +#include "logging.h" + +int fh_rawsock_setup(void) +{ + int res, opt; + const char *err_hint; + + g_ctx.sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + if (g_ctx.sockfd < 0) { + switch (errno) { + case EPERM: + err_hint = " (Are you root?)"; + break; + default: + err_hint = ""; + } + E("ERROR: socket(): %s%s", strerror(errno), err_hint); + return -1; + } + + res = setsockopt(g_ctx.sockfd, SOL_SOCKET, SO_BINDTODEVICE, g_ctx.iface, + strlen(g_ctx.iface)); + if (res < 0) { + E("ERROR: setsockopt(): SO_BINDTODEVICE: %s", strerror(errno)); + goto close_socket; + } + + opt = 1; + res = setsockopt(g_ctx.sockfd, IPPROTO_IP, IP_HDRINCL, &opt, sizeof(opt)); + if (res < 0) { + E("ERROR: setsockopt(): IP_HDRINCL: %s", strerror(errno)); + goto close_socket; + } + + res = setsockopt(g_ctx.sockfd, SOL_SOCKET, SO_MARK, &g_ctx.fwmark, + sizeof(g_ctx.fwmark)); + if (res < 0) { + E("ERROR: setsockopt(): SO_MARK: %s", strerror(errno)); + goto close_socket; + } + + opt = 7; + res = setsockopt(g_ctx.sockfd, SOL_SOCKET, SO_PRIORITY, &opt, sizeof(opt)); + if (res < 0) { + E("ERROR: setsockopt(): SO_PRIORITY: %s", strerror(errno)); + goto close_socket; + } + + return 0; + +close_socket: + close(g_ctx.sockfd); + g_ctx.sockfd = -1; + + return -1; +} + + +void fh_rawsock_cleanup(void) +{ + if (g_ctx.sockfd >= 0) { + close(g_ctx.sockfd); + g_ctx.sockfd = -1; + } +} diff --git a/src/signals.c b/src/signals.c new file mode 100644 index 0000000..e7f6193 --- /dev/null +++ b/src/signals.c @@ -0,0 +1,153 @@ +/* + * signals.c - FakeHTTP: https://github.com/MikeWang000000/FakeHTTP + * + * Copyright (C) 2025 MikeWang000000 + * + * 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 . + */ + +#define _GNU_SOURCE +#include "signals.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "globvar.h" +#include "logging.h" + +static void signal_handler(int sig) +{ + switch (sig) { + case SIGINT: + case SIGTERM: + g_ctx.exit = 1; + break; + default: + break; + } +} + + +int fh_signal_setup(void) +{ + struct sigaction sa; + int res; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_IGN; + + res = sigaction(SIGPIPE, &sa, NULL); + if (res < 0) { + E("ERROR: sigaction(): %s", strerror(errno)); + return -1; + } + + res = sigaction(SIGHUP, &sa, NULL); + if (res < 0) { + E("ERROR: sigaction(): %s", strerror(errno)); + return -1; + } + + sa.sa_handler = signal_handler; + + res = sigaction(SIGINT, &sa, NULL); + if (res < 0) { + E("ERROR: sigaction(): %s", strerror(errno)); + return -1; + } + + res = sigaction(SIGTERM, &sa, NULL); + if (res < 0) { + E("ERROR: sigaction(): %s", strerror(errno)); + return -1; + } + + return 0; +} + + +int fh_kill_running(int signal) +{ + int res, matched, err; + ssize_t len; + DIR *procfs; + struct dirent *entry; + pid_t pid, self_pid; + char self_path[PATH_MAX], proc_path[PATH_MAX], exe_path[PATH_MAX]; + + self_pid = getpid(); + + len = readlink("/proc/self/exe", self_path, sizeof(self_path)); + if (len < 0 || (size_t) len >= sizeof(self_path)) { + E("ERROR: readlink(): /proc/self/exe: %s", strerror(errno)); + return -1; + } + self_path[len] = 0; + + procfs = opendir("/proc"); + if (!procfs) { + E("ERROR: opendir(): /proc: %s", strerror(errno)); + return -1; + } + + matched = err = 0; + while ((entry = readdir(procfs))) { + pid = strtoull(entry->d_name, NULL, 0); + if (pid <= 1 || pid == self_pid) { + continue; + } + + res = snprintf(exe_path, sizeof(exe_path), "/proc/%s/exe", + entry->d_name); + if (res < 0 || (size_t) res >= sizeof(exe_path)) { + continue; + } + + len = readlink(exe_path, proc_path, sizeof(proc_path)); + if (len < 0 || (size_t) len >= sizeof(self_path)) { + continue; + } + proc_path[len] = 0; + + if (strcmp(self_path, proc_path) == 0) { + matched = 1; + + if (signal) { + res = kill(pid, signal); + if (res < 0) { + E("ERROR: kill(): %llu: %s", (unsigned long long) pid, + strerror(errno)); + err = 1; + } + } + } + } + + res = closedir(procfs); + if (res < 0) { + E("ERROR: closedir(): %s", strerror(errno)); + err = 1; + } + + if (matched && !err) { + return 0; + } + + return -1; +}