Created
July 25, 2025 04:22
-
-
Save aabccd021/9b9995adafea055aaadff0e7226e1f49 to your computer and use it in GitHub Desktop.
Mock domain/host with LD_PRELOAD
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define _GNU_SOURCE | |
#include <dlfcn.h> | |
#include <netdb.h> | |
#include <string.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <arpa/inet.h> | |
#include <sys/socket.h> | |
#include <netinet/in.h> | |
#define MAX_MAPPINGS 128 | |
#define MAX_LINE 256 | |
typedef struct { | |
char hostname[128]; | |
char ip[64]; | |
int port; | |
} hostmap_t; | |
static hostmap_t mappings[MAX_MAPPINGS]; | |
static int mappings_count = 0; | |
static int loaded = 0; | |
void load_mappings() { | |
if (loaded) return; | |
loaded = 1; | |
const char *file = getenv("HOSTMAP_FILE"); | |
if (!file) file = "map.txt"; | |
FILE *f = fopen(file, "r"); | |
if (!f) return; | |
char line[MAX_LINE]; | |
while (fgets(line, sizeof(line), f)) { | |
char host[128], ip[64]; | |
int port = 0; | |
if (sscanf(line, "%127s %63[^:]:%d", host, ip, &port) == 3) { | |
strncpy(mappings[mappings_count].hostname, host, sizeof(mappings[0].hostname)-1); | |
strncpy(mappings[mappings_count].ip, ip, sizeof(mappings[0].ip)-1); | |
mappings[mappings_count].port = port; | |
mappings_count++; | |
if (mappings_count >= MAX_MAPPINGS) break; | |
} | |
} | |
fclose(f); | |
} | |
hostmap_t *find_mapping_by_hostname(const char *hostname) { | |
for (int i = 0; i < mappings_count; ++i) { | |
if (strcmp(mappings[i].hostname, hostname) == 0) { | |
return &mappings[i]; | |
} | |
} | |
return NULL; | |
} | |
hostmap_t *find_mapping_by_ip(const char *ip) { | |
for (int i = 0; i < mappings_count; ++i) { | |
if (strcmp(mappings[i].ip, ip) == 0) { | |
return &mappings[i]; | |
} | |
} | |
return NULL; | |
} | |
// Interpose getaddrinfo | |
typedef int (*orig_getaddrinfo_f_type)(const char *, const char *, const struct addrinfo *, struct addrinfo **); | |
int getaddrinfo(const char *node, const char *service, | |
const struct addrinfo *hints, | |
struct addrinfo **res) { | |
load_mappings(); | |
static orig_getaddrinfo_f_type orig_getaddrinfo = NULL; | |
if (!orig_getaddrinfo) | |
orig_getaddrinfo = (orig_getaddrinfo_f_type)dlsym(RTLD_NEXT, "getaddrinfo"); | |
if (node) { | |
hostmap_t *mapping = find_mapping_by_hostname(node); | |
if (mapping) { | |
// Use mapped IP | |
return orig_getaddrinfo(mapping->ip, service, hints, res); | |
} | |
} | |
return orig_getaddrinfo(node, service, hints, res); | |
} | |
// Interpose connect | |
typedef int (*orig_connect_f_type)(int, const struct sockaddr *, socklen_t); | |
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { | |
load_mappings(); | |
static orig_connect_f_type orig_connect = NULL; | |
if (!orig_connect) | |
orig_connect = (orig_connect_f_type)dlsym(RTLD_NEXT, "connect"); | |
if (addr->sa_family == AF_INET) { | |
struct sockaddr_in *in_addr = (struct sockaddr_in *)addr; | |
char ipstr[INET_ADDRSTRLEN]; | |
inet_ntop(AF_INET, &(in_addr->sin_addr), ipstr, sizeof(ipstr)); | |
hostmap_t *mapping = find_mapping_by_ip(ipstr); | |
if (mapping && ntohs(in_addr->sin_port) == 80) { | |
struct sockaddr_in new_addr = *in_addr; | |
new_addr.sin_port = htons(mapping->port); | |
return orig_connect(sockfd, (struct sockaddr *)&new_addr, addrlen); | |
} | |
} | |
// TODO: add IPv6 support if needed | |
return orig_connect(sockfd, addr, addrlen); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
tmpdir=$(mktemp -d) | |
echo 'fetch("http://example.com").then(response => console.log(response.status))' > "$tmpdir/fetch.js" | |
echo "example.com 127.0.0.1:4002" > "$tmpdir/hosts" | |
export HOSTMAP_FILE="$tmpdir/hosts" | |
nix run nixpkgs#gcc_multi -- -shared -fPIC -o "$tmpdir/libmockhost.so" ./mock_host.c -ldl | |
timeout 10 nix run nixpkgs#simple-http-server -- --port 4002 >/dev/null 2>&1 & | |
pid=$! | |
trap "kill $pid" EXIT | |
nix shell nixpkgs#nodejs --command sh -c "LD_PRELOAD=$tmpdir/libmockhost.so node fetch.js" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment