2025-11-05 08:43:17 +00:00
|
|
|
|
#define _GNU_SOURCE
|
2025-11-06 03:03:48 +00:00
|
|
|
|
#include <dlfcn.h>
|
|
|
|
|
|
#include <netdb.h>
|
|
|
|
|
|
#include <arpa/inet.h>
|
2025-11-06 07:41:35 +00:00
|
|
|
|
#include <string.h>
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
#include <regex.h>
|
|
|
|
|
|
#include <unistd.h>
|
2025-11-06 03:03:48 +00:00
|
|
|
|
#include <sys/socket.h>
|
2025-11-06 07:41:35 +00:00
|
|
|
|
#include <errno.h>
|
2025-11-07 01:47:35 +00:00
|
|
|
|
#include <limits.h>
|
|
|
|
|
|
#include <libgen.h>
|
2025-11-06 03:03:48 +00:00
|
|
|
|
|
2025-11-07 01:47:35 +00:00
|
|
|
|
static const char *BANNED_FILE_NAME = ".SANDBOX_BANNED_HOSTS";
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 从 .so 文件所在目录读取 .SANDBOX_BANNED_HOSTS 文件内容
|
|
|
|
|
|
* 返回 malloc 出的字符串(需 free),读取失败则返回空字符串
|
|
|
|
|
|
*/
|
|
|
|
|
|
static char *load_banned_hosts() {
|
|
|
|
|
|
Dl_info info;
|
|
|
|
|
|
if (dladdr((void *)load_banned_hosts, &info) == 0 || !info.dli_fname) {
|
|
|
|
|
|
fprintf(stderr, "[sandbox] ⚠️ Unable to locate shared object path — allowing all hosts\n");
|
|
|
|
|
|
return strdup("");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char so_path[PATH_MAX];
|
|
|
|
|
|
strncpy(so_path, info.dli_fname, sizeof(so_path));
|
|
|
|
|
|
so_path[sizeof(so_path) - 1] = '\0';
|
|
|
|
|
|
|
|
|
|
|
|
char *dir = dirname(so_path);
|
|
|
|
|
|
char file_path[PATH_MAX];
|
|
|
|
|
|
snprintf(file_path, sizeof(file_path), "%s/%s", dir, BANNED_FILE_NAME);
|
|
|
|
|
|
|
|
|
|
|
|
FILE *fp = fopen(file_path, "r");
|
|
|
|
|
|
if (!fp) {
|
|
|
|
|
|
fprintf(stderr, "[sandbox] ⚠️ Cannot open %s — allowing all hosts\n", file_path);
|
|
|
|
|
|
return strdup("");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char *buf = malloc(4096);
|
|
|
|
|
|
if (!buf) {
|
|
|
|
|
|
fclose(fp);
|
|
|
|
|
|
fprintf(stderr, "[sandbox] ⚠️ Memory allocation failed — allowing all hosts\n");
|
|
|
|
|
|
return strdup("");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
size_t len = fread(buf, 1, 4095, fp);
|
|
|
|
|
|
buf[len] = '\0';
|
|
|
|
|
|
fclose(fp);
|
|
|
|
|
|
return buf;
|
|
|
|
|
|
}
|
2025-11-06 07:41:35 +00:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 精确匹配黑名单
|
|
|
|
|
|
*/
|
|
|
|
|
|
static int match_env_patterns(const char *target, const char *env_val) {
|
|
|
|
|
|
if (!target || !env_val || !*env_val) return 0;
|
2025-11-05 08:43:17 +00:00
|
|
|
|
|
|
|
|
|
|
char *patterns = strdup(env_val);
|
|
|
|
|
|
char *token = strtok(patterns, ",");
|
2025-11-06 07:41:35 +00:00
|
|
|
|
int matched = 0;
|
2025-11-05 08:43:17 +00:00
|
|
|
|
|
|
|
|
|
|
while (token) {
|
2025-11-06 07:41:35 +00:00
|
|
|
|
// 去掉前后空格
|
2025-11-05 08:43:17 +00:00
|
|
|
|
while (*token == ' ' || *token == '\t') token++;
|
|
|
|
|
|
char *end = token + strlen(token) - 1;
|
|
|
|
|
|
while (end > token && (*end == ' ' || *end == '\t')) *end-- = '\0';
|
|
|
|
|
|
|
|
|
|
|
|
if (*token) {
|
2025-11-06 07:41:35 +00:00
|
|
|
|
regex_t regex;
|
|
|
|
|
|
char fullpattern[512];
|
|
|
|
|
|
snprintf(fullpattern, sizeof(fullpattern), "^%s$", token);
|
|
|
|
|
|
|
|
|
|
|
|
if (regcomp(®ex, fullpattern, REG_EXTENDED | REG_NOSUB | REG_ICASE) == 0) {
|
|
|
|
|
|
if (regexec(®ex, target, 0, NULL, 0) == 0) {
|
|
|
|
|
|
matched = 1;
|
2025-11-06 03:03:48 +00:00
|
|
|
|
regfree(®ex);
|
2025-11-06 07:41:35 +00:00
|
|
|
|
break;
|
2025-11-06 03:03:48 +00:00
|
|
|
|
}
|
2025-11-06 07:41:35 +00:00
|
|
|
|
regfree(®ex);
|
2025-11-06 03:03:48 +00:00
|
|
|
|
} else {
|
2025-11-06 07:41:35 +00:00
|
|
|
|
fprintf(stderr, "[sandbox] ⚠️ Invalid regex '%s' — allowing host by default\n", token);
|
2025-11-05 08:43:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
token = strtok(NULL, ",");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
free(patterns);
|
2025-11-06 07:41:35 +00:00
|
|
|
|
return matched;
|
2025-11-06 03:03:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-06 07:41:35 +00:00
|
|
|
|
/** 拦截 connect() —— 精确匹配 IP */
|
2025-11-06 03:03:48 +00:00
|
|
|
|
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
|
2025-11-06 07:41:35 +00:00
|
|
|
|
static int (*real_connect)(int, const struct sockaddr *, socklen_t) = NULL;
|
2025-11-06 03:03:48 +00:00
|
|
|
|
if (!real_connect)
|
|
|
|
|
|
real_connect = dlsym(RTLD_NEXT, "connect");
|
2025-11-05 08:43:17 +00:00
|
|
|
|
|
2025-11-07 01:47:35 +00:00
|
|
|
|
static char *banned_env = NULL;
|
|
|
|
|
|
if (!banned_env) banned_env = load_banned_hosts();
|
2025-11-06 03:03:48 +00:00
|
|
|
|
|
2025-11-06 07:41:35 +00:00
|
|
|
|
char ip[INET6_ADDRSTRLEN] = {0};
|
2025-11-06 03:03:48 +00:00
|
|
|
|
if (addr->sa_family == AF_INET)
|
2025-11-05 08:43:17 +00:00
|
|
|
|
inet_ntop(AF_INET, &((struct sockaddr_in *)addr)->sin_addr, ip, sizeof(ip));
|
2025-11-06 03:03:48 +00:00
|
|
|
|
else if (addr->sa_family == AF_INET6)
|
2025-11-05 08:43:17 +00:00
|
|
|
|
inet_ntop(AF_INET6, &((struct sockaddr_in6 *)addr)->sin6_addr, ip, sizeof(ip));
|
2025-11-06 03:03:48 +00:00
|
|
|
|
|
2025-11-07 01:47:35 +00:00
|
|
|
|
if (banned_env && *banned_env && match_env_patterns(ip, banned_env)) {
|
2025-11-06 07:41:35 +00:00
|
|
|
|
fprintf(stderr, "[sandbox] 🚫 Access to host %s is banned\n", ip);
|
|
|
|
|
|
errno = EACCES;
|
2025-11-05 08:43:17 +00:00
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return real_connect(sockfd, addr, addrlen);
|
|
|
|
|
|
}
|
2025-11-06 07:41:35 +00:00
|
|
|
|
|
2025-11-10 02:08:57 +00:00
|
|
|
|
/** 拦截 getaddrinfo() —— 只拦截域名,不拦截纯 IP */
|
2025-11-06 07:41:35 +00:00
|
|
|
|
int getaddrinfo(const char *node, const char *service,
|
|
|
|
|
|
const struct addrinfo *hints, struct addrinfo **res) {
|
|
|
|
|
|
static int (*real_getaddrinfo)(const char *, const char *,
|
|
|
|
|
|
const struct addrinfo *, struct addrinfo **) = NULL;
|
|
|
|
|
|
if (!real_getaddrinfo)
|
|
|
|
|
|
real_getaddrinfo = dlsym(RTLD_NEXT, "getaddrinfo");
|
|
|
|
|
|
|
2025-11-07 01:47:35 +00:00
|
|
|
|
static char *banned_env = NULL;
|
|
|
|
|
|
if (!banned_env) banned_env = load_banned_hosts();
|
2025-11-06 07:41:35 +00:00
|
|
|
|
|
2025-11-10 02:08:57 +00:00
|
|
|
|
if (banned_env && *banned_env && node) {
|
|
|
|
|
|
// 检测 node 是否是 IP
|
|
|
|
|
|
struct in_addr ipv4;
|
|
|
|
|
|
struct in6_addr ipv6;
|
|
|
|
|
|
int is_ip = (inet_pton(AF_INET, node, &ipv4) == 1) ||
|
|
|
|
|
|
(inet_pton(AF_INET6, node, &ipv6) == 1);
|
|
|
|
|
|
|
|
|
|
|
|
// 只对“非IP的域名”进行屏蔽
|
|
|
|
|
|
if (!is_ip && match_env_patterns(node, banned_env)) {
|
|
|
|
|
|
fprintf(stderr, "[sandbox] 🚫 Access to host %s is banned (DNS blocked)\n", node);
|
|
|
|
|
|
return EAI_FAIL; // 模拟 DNS 层禁止
|
|
|
|
|
|
}
|
2025-11-06 07:41:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return real_getaddrinfo(node, service, hints, res);
|
2025-11-10 02:08:57 +00:00
|
|
|
|
}
|