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;
+}