diff --git a/CProxy.ini b/CProxy.ini index 9b195d0..fa8b160 100644 --- a/CProxy.ini +++ b/CProxy.ini @@ -1,6 +1,6 @@ [server] uid=3004; -process=2; +process=3; timer=60; local_port=9606; pid_file=CProxy.pid; @@ -10,13 +10,11 @@ http_ip=192.168.1.102; http_port=1080; http_del="x-online-host,X-Online-Host,host,Host"; http_first="[M] [U] [V]\r\nHost: [host]\r\n"; -;strrep = "Windows NT 10.0; Win64; x64->Linux"; -;regrep = "Host*.+?->Host: iread.wo.cn:443"; [https] https_ip=192.168.1.102; https_port=1080; https_del="Host,host"; -https_first="[M] [U]?aixiao.me [V]\r\nHost: [U]\r\n"; +https_first="[M] [H] [V]\r\nHost: [host]\r\n"; ;strrep = "Windows NT 10.0; Win64; x64->Linux"; ;regrep = "Host*.+?->Host: hu60.cn:443"; diff --git a/Makefile b/Makefile index 699d8e3..c20dd6f 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ CFLAGS += -g -O2 -Wall -I../iniparser/src -L../iniparser LIBS = -liniparser -pthread -static OBJ := CProxy -all: proxy.o http.o request.o picohttpparser.o conf.o timeout.o help.o +all: proxy.o http.o request.o picohttpparser.o conf.o timeout.o kill.o help.o $(CC) $(CFLAGS) -o $(OBJ) $^ $(LIBS) .c.o: $(CC) $(CFLAGS) -c $< $(LIBS) diff --git a/kill.c b/kill.c new file mode 100644 index 0000000..c4263e2 --- /dev/null +++ b/kill.c @@ -0,0 +1,489 @@ +#include "kill.h" + +static pid_t opt_ns_pid = 0; + +static int exact = 1, reg = 0, wait_until_dead = 1, process_group = + 0, ignore_case = 0; +static long younger_than = 0, older_than = 0; + +typedef struct NAMEINFO { + const char *name; + int name_length; + struct stat st; +} NAMEINFO; + +static double uptime() +{ + char *savelocale; + char buf[2048]; + FILE *file; + if (!(file = fopen(PROC_BASE "/uptime", "r"))) { + exit(1); + } + savelocale = setlocale(LC_NUMERIC, "C"); + if (fscanf(file, "%2047s", buf) == EOF) + perror("uptime"); + fclose(file); + setlocale(LC_NUMERIC, savelocale); + return atof(buf); +} + +static double process_age(const unsigned long long jf) +{ + double age; + double sc_clk_tck = sysconf(_SC_CLK_TCK); + assert(sc_clk_tck > 0); + age = uptime() - jf / sc_clk_tck; + if (age < 0L) + return 0L; + return age; +} + +enum ns_type { + IPCNS = 0, + MNTNS, + NETNS, + PIDNS, + USERNS, + UTSNS +}; + +static const char *ns_names[] = { + [IPCNS] = "ipc", + [MNTNS] = "mnt", + [NETNS] = "net", + [PIDNS] = "pid", + [USERNS] = "user", + [UTSNS] = "uts", +}; + +const char *get_ns_name(int id) +{ + if (id >= 6) + return NULL; + return ns_names[id]; +} + +static int get_ns(pid_t pid, int id) +{ + struct stat st; + char buff[50]; + snprintf(buff, sizeof(buff), "/proc/%i/ns/%s", pid, get_ns_name(id)); + if (stat(buff, &st)) + return 0; + else + return st.st_ino; +} + +static int match_process_uid(pid_t pid, uid_t uid) +{ + char buf[128]; + uid_t puid; + FILE *f; + int re = -1; + + snprintf(buf, sizeof buf, PROC_BASE "/%d/status", pid); + if (!(f = fopen(buf, "r"))) + return 0; + + while (fgets(buf, sizeof buf, f)) { + if (sscanf(buf, "Uid:\t%d", &puid)) { + re = uid == puid; + break; + } + } + fclose(f); + if (re == -1) { + exit(1); + } + return re; +} + +static void free_regexp_list(regex_t * reglist, int names) +{ + int i; + for (i = 0; i < names; i++) + regfree(®list[i]); + free(reglist); +} + +static regex_t *build_regexp_list(int names, char **namelist) +{ + int i; + regex_t *reglist; + int flag = REG_EXTENDED | REG_NOSUB; + + if (!(reglist = malloc(sizeof(regex_t) * names))) { + perror("malloc"); + exit(1); + } + + if (ignore_case) + flag |= REG_ICASE; + + for (i = 0; i < names; i++) { + if (regcomp(®list[i], namelist[i], flag) != 0) { + free_regexp_list(reglist, i); + exit(1); + } + } + return reglist; +} + +static NAMEINFO *build_nameinfo(const int names, char **namelist) +{ + int i; + NAMEINFO *ni = NULL; + if ((ni = malloc(sizeof(NAMEINFO) * names)) == NULL) + return NULL; + + for (i = 0; i < names; i++) { + ni[i].name = namelist[i]; + ni[i].st.st_dev = 0; + if (!strchr(namelist[i], '/')) { + ni[i].name_length = strlen(namelist[i]); + } else if (stat(namelist[i], &(ni[i].st)) < 0) { + perror(namelist[i]); + free(ni); + return NULL; + } + } + return ni; +} + +static int +load_process_name_and_age(char *comm, double *process_age_sec, + const pid_t pid, int load_age) +{ + FILE *file; + char *path; + char buf[1024]; + char *startcomm, *endcomm; + unsigned lencomm; + *process_age_sec = 0; + + if (asprintf(&path, PROC_BASE "/%d/stat", pid) < 0) + return -1; + if (!(file = fopen(path, "r"))) { + free(path); + return -1; + } + free(path); + if (fgets(buf, 1024, file) == NULL) { + fclose(file); + return -1; + } + fclose(file); + startcomm = strchr(buf, '(') + 1; + endcomm = strrchr(startcomm, ')'); + lencomm = endcomm - startcomm; + if (lencomm < 0) + lencomm = 0; + if (lencomm > COMM_LEN - 1) + lencomm = COMM_LEN - 1; + strncpy(comm, startcomm, lencomm); + comm[lencomm] = '\0'; + + endcomm += 2; // skip ") " + if (load_age) { + unsigned long long proc_stt_jf = 0; + if (sscanf + (endcomm, + "%*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %*s %Lu", + &proc_stt_jf) != 1) { + return -1; + } + *process_age_sec = process_age(proc_stt_jf); + } + return lencomm; +} + +static int +load_proc_cmdline(const pid_t pid, const char *comm, char **command, + int *got_long) +{ + FILE *file; + char *path, *p, *command_buf; + int cmd_size = 128; + int okay; + + if (asprintf(&path, PROC_BASE "/%d/cmdline", pid) < 0) + return -1; + if (!(file = fopen(path, "r"))) { + free(path); + return -1; + } + free(path); + + if ((command_buf = (char *)malloc(cmd_size)) == NULL) + exit(1); + + while (1) { + for (p = command_buf;; p++) { + int c; + if (p == (command_buf + cmd_size)) { + char *new_command_buf; + int cur_size = cmd_size; + cmd_size *= 2; + new_command_buf = (char *)realloc(command_buf, cmd_size); + if (!new_command_buf) { + if (command_buf) + free(command_buf); + exit(1); + } + command_buf = new_command_buf; + p = command_buf + cur_size; + } + c = fgetc(file); + if (c == EOF || c == '\0') { + *p = '\0'; + break; + } else { + *p = c; + } + } + if (strlen(command_buf) == 0) { + okay = 0; + break; + } + p = strrchr(command_buf, '/'); + p = p ? p + 1 : command_buf; + if (strncmp(p, comm, COMM_LEN - 1) == 0) { + okay = 1; + if (!(*command = strdup(p))) { + free(command_buf); + exit(1); + } + break; + } + } + (void)fclose(file); + free(command_buf); + command_buf = NULL; + + if (exact && !okay) { + *got_long = okay; + return -1; + } + *got_long = okay; + return 0; +} + +static pid_t *create_pid_table(int *max_pids, int *pids) +{ + pid_t self, *pid_table; + int pid; + DIR *dir; + struct dirent *de; + + self = getpid(); + if (!(dir = opendir(PROC_BASE))) { + perror(PROC_BASE); + exit(1); + } + *max_pids = 256; + pid_table = malloc(*max_pids * sizeof(pid_t)); + if (!pid_table) { + perror("malloc"); + exit(1); + } + *pids = 0; + while ((de = readdir(dir)) != NULL) { + if (!(pid = (pid_t) atoi(de->d_name)) || pid == self) + continue; + if (*pids == *max_pids) { + if (!(pid_table = realloc(pid_table, 2 * *pids * sizeof(pid_t)))) { + perror("realloc"); + exit(1); + } + *max_pids *= 2; + } + pid_table[(*pids)++] = pid; + } + (void)closedir(dir); + return pid_table; +} + +#define strcmp2(A,B,I) (I? strcasecmp((A),(B)):strcmp((A),(B))) +#define strncmp2(A,B,L,I) (I? strncasecmp((A),(B),(L)):strncmp((A),(B),(L))) +static int match_process_name(const char *proc_comm, + const int comm_len, + const char *proc_cmdline, + const char *match_name, + const int match_len, const int got_long) +{ + if (comm_len == OLD_COMM_LEN - 1 && match_len >= OLD_COMM_LEN - 1) { + if (got_long) { + return (0 == strncmp2(match_name, proc_cmdline, OLD_COMM_LEN - 1, + ignore_case)); + } else { + return (0 == strncmp2(match_name, proc_comm, OLD_COMM_LEN - 1, + ignore_case)); + } + } + + if (comm_len == COMM_LEN - 1 && match_len >= COMM_LEN - 1) { + if (got_long) { + return (0 == strncmp2(match_name, proc_cmdline, COMM_LEN - 1, + ignore_case)); + } else { + return (0 == strncmp2(match_name, proc_comm, COMM_LEN - 1, + ignore_case)); + } + } + if (got_long) { + return (0 == strcmp2(match_name, proc_cmdline, ignore_case)); + } + return (0 == strcmp2(match_name, proc_comm, ignore_case)); +} + +int kill_all(int signal, int name_count, char **namelist, struct passwd *pwent) +{ + struct stat st; + NAMEINFO *name_info = NULL; + char *path, comm[COMM_LEN]; + char *command = NULL; + pid_t *pid_table, *pid_killed; + pid_t *pgids = NULL; + int i, j, length, got_long, error; + int pids, max_pids, pids_killed; + unsigned long found; + regex_t *reglist = NULL; + long ns_ino = 0; + + if (opt_ns_pid) + ns_ino = get_ns(opt_ns_pid, PIDNS); + + if (name_count && reg) + reglist = build_regexp_list(name_count, namelist); + else if ((name_info = build_nameinfo(name_count, namelist)) == NULL) + exit(1); + + pid_table = create_pid_table(&max_pids, &pids); + found = 0; + pids_killed = 0; + pid_killed = malloc(max_pids * sizeof(pid_t)); + if (!pid_killed) { + perror("malloc"); + exit(1); + } + if (process_group) { + pgids = calloc(pids, sizeof(pid_t)); + if (!pgids) { + perror("malloc"); + exit(1); + } + } + got_long = 0; + for (i = 0; i < pids; i++) { + pid_t id; + int found_name = -1; + double process_age_sec = 0; + if (pwent && match_process_uid(pid_table[i], pwent->pw_uid) == 0) + continue; + if (opt_ns_pid && ns_ino && ns_ino != get_ns(pid_table[i], PIDNS)) + continue; + length = + load_process_name_and_age(comm, &process_age_sec, pid_table[i], + (younger_than || older_than)); + if (length < 0) + continue; + if (younger_than && (process_age_sec > younger_than)) + continue; + if (older_than && (process_age_sec < older_than)) + continue; + + got_long = 0; + if (command) { + free(command); + command = NULL; + } + if (length == COMM_LEN - 1) + if (load_proc_cmdline(pid_table[i], comm, &command, &got_long) < 0) + continue; + for (j = 0; j < name_count; j++) { + if (reg) { + if (regexec(®list[j], got_long ? command : comm, 0, NULL, 0) + != 0) + continue; + } else { + if (!name_info[j].st.st_dev) { + if (!match_process_name(comm, length, command, namelist[j], + name_info[j].name_length, got_long)) + continue; + + } else { + int ok = 1; + if (asprintf(&path, PROC_BASE "/%d/exe", pid_table[i]) < 0) + continue; + if (stat(path, &st) < 0) + ok = 0; + else if (name_info[j].st.st_dev != st.st_dev || + name_info[j].st.st_ino != st.st_ino) { + size_t len = strlen(namelist[j]); + char *linkbuf = malloc(len + 1); + + if (!linkbuf || + readlink(path, linkbuf, len + 1) != (ssize_t) len || + memcmp(namelist[j], linkbuf, len)) + ok = 0; + free(linkbuf); + } + free(path); + if (!ok) + continue; + } + } + found_name = j; + break; + } + if (name_count && found_name == -1) + continue; + if (!process_group) + id = pid_table[i]; + else { + int j; + + id = getpgid(pid_table[i]); + pgids[i] = id; + for (j = 0; j < i; j++) + if (pgids[j] == id) + break; + if (j < i) + continue; + } + + if (kill(process_group ? -id : id, signal) >= 0) { + if (found_name >= 0) + found |= 1UL << found_name; + pid_killed[pids_killed++] = id; + } + } + if (command) + free(command); + if (reglist) + free_regexp_list(reglist, name_count); + free(pgids); + if (name_count) + error = + found == + ((1UL << (name_count - 1)) | ((1UL << (name_count - 1)) - 1)) ? 0 : + 1; + else + error = pids_killed ? 0 : 1; + while (pids_killed && wait_until_dead) { + for (i = 0; i < pids_killed;) { + if (kill(process_group ? -pid_killed[i] : pid_killed[i], 0) < 0 && + errno == ESRCH) { + pid_killed[i] = pid_killed[--pids_killed]; + continue; + } + i++; + } + //sleep(1); + } + free(pid_killed); + free(pid_table); + free(name_info); + return error; +} diff --git a/kill.h b/kill.h new file mode 100644 index 0000000..f2c7d31 --- /dev/null +++ b/kill.h @@ -0,0 +1,60 @@ +#ifndef KILL_H +#define KILL_H + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define COMM_LEN 64 +#define OLD_COMM_LEN 16 +#define _(String) (String) +#define rpmatch(line) \ + ( (line == NULL)? -1 : \ + (*line == 'y' || *line == 'Y')? 1 : \ + (*line == 'n' || *line == 'N')? 0 : \ + -1 ) + +#define PROC_BASE "/proc" +#define MAX_NAMES (int)(sizeof(unsigned long)*8) + +#define TSECOND "s" +#define TMINUTE "m" +#define THOUR "h" +#define TDAY "d" +#define TWEEK "w" +#define TMONTH "M" +#define TYEAR "y" + +#define TMAX_SECOND 31536000 +#define TMAX_MINUTE 525600 +#define TMAX_HOUR 8760 +#define TMAX_DAY 365 +#define TMAX_WEEK 48 +#define TMAX_MONTH 12 +#define TMAX_YEAR 1 + +#define ER_REGFAIL -1 +#define ER_NOMEM -2 +#define ER_UNKWN -3 +#define ER_OOFRA -4 + +int kill_all(int signal, int name_count, char **namelist, struct passwd *pwent); + +#endif diff --git a/proxy.c b/proxy.c index 80ec720..763217b 100644 --- a/proxy.c +++ b/proxy.c @@ -3,6 +3,7 @@ #include "request.h" #include "timeout.h" #include "conf.h" +#include "kill.h" #include "help.h" #define SERVER_STOP 1 @@ -154,8 +155,12 @@ process_signal(int signal, char *process_name) for (; n >= 0; n--) { if (signal == SERVER_STATUS) printf("\t%d\n", num[n]); - if (signal == SERVER_STOP || signal == SERVER_RELOAD) - kill(num[n], SIGTERM); + } + if (signal == SERVER_STOP || signal == SERVER_RELOAD) { + //kill(num[n], SIGTERM); + struct passwd *pwent = NULL; + pwent = getpwnam("root"); + return kill_all(15, 1, &process_name, pwent); } closedir(dir); return 0; @@ -263,8 +268,13 @@ int _main(int argc, char *argv[]) } } + if (daemon(1, 1)) { + perror("daemon"); + return 1; + } + while (process-- > 0 && fork() == 0) - + epollfd = epoll_create(MAX_CONNECTION); if (epollfd == -1) { perror("epoll_create"); @@ -276,12 +286,7 @@ int _main(int argc, char *argv[]) if (-1 == epoll_ctl(epollfd, EPOLL_CTL_ADD, server_sock, &event)) { exit(1); } - - if (daemon(1, 1)) { - perror("daemon"); - return 1; - } - + if (setegid(configure->uid) == -1 || seteuid(configure->uid) == -1) // 设置uid exit(1); diff --git a/request.c b/request.c index 6172493..508f448 100644 --- a/request.c +++ b/request.c @@ -123,7 +123,7 @@ static char *regrep(char *str, int *str_len, const char *src, char *dest, return str; } -// 删除字符串head中第一位到character处并拼接string, character为空返回原字符串.(string字符替换第一个字符到character处) +// 删除字符串head中第一位到 character 处并拼接 string, character 为空返回原字符串.(string 字符替换第一个字符到 character 处) char *splice_head(char *head, const char *character, char *string) { int len_first = strlen(string); @@ -137,7 +137,7 @@ char *splice_head(char *head, const char *character, char *string) return strcat(new_string, p1); } -// 删除字符串head中character到string处, character为空返回原字符串. +// 删除字符串 head 中 character 到 string 处, character 为空返回原字符串. char *delete_head(char *head, const char *character, int string) { int head_len = strlen(head); @@ -158,9 +158,9 @@ char *delete_head(char *head, const char *character, int string) int extract_host(char *header, char *host, char *port, char *H) { - bzero(host, 0); - bzero(port, 0); - bzero(H, 0); + bzero(host, strlen(host)); + bzero(port, strlen(port)); + bzero(H, strlen(H)); char *_p = strstr(header, "CONNECT"); // 在 CONNECT 方法中解析 隧道主机名称及端口号 if (_p) { char *_p1 = strchr(_p, ' '); @@ -170,8 +170,8 @@ int extract_host(char *header, char *host, char *port, char *H) if (_p2) { char s_port[10]; bzero(s_port, 10); - strncpy(host, _p1 + 1, (int)(_p2 - _p1) - 1); - strncpy(s_port, _p2 + 1, (int)(_p3 - _p2) - 1); + strncpy_(host, _p1 + 1, (int)(_p2 - _p1) - 1); + strncpy_(s_port, _p2 + 1, (int)(_p3 - _p2) - 1); strcpy(port, s_port); } else { @@ -206,7 +206,7 @@ int extract_host(char *header, char *host, char *port, char *H) host[h_len] = '\0'; } else { int h_len = (int)(p1 - p - 5 - 1 - 1); - strncpy(host, p + 5 + 1, h_len); + strncpy_(host, p + 5 + 1, h_len); host[h_len] = '\0'; strcpy(port, "80"); } @@ -216,6 +216,18 @@ int extract_host(char *header, char *host, char *port, char *H) return 0; } +char *get_path(char *url, char *path) +{ + if (url) { + char *p0 = strstr(url+7, "/"); + if (p0) + return strncpy_(path, p0, strlen(p0)); + else + return NULL; + } + return NULL; +} + char *request_head(conn *in, conf *configure) { const char *method, *path; @@ -245,6 +257,8 @@ char *request_head(conn *in, conf *configure) char port[path_len]; char H[path_len*2]; extract_host(in->header_buffer, host, port, H); + + if (strncmp(M, "CONNECT", 7) == 0) { char https_del_copy[configure->https_del_len]; @@ -276,8 +290,10 @@ char *request_head(conn *in, conf *configure) incomplete_head = replace(incomplete_head, &incomplete_head_len, "\\r", 2, "\r", 1); incomplete_head = replace(incomplete_head, &incomplete_head_len, "\\n", 2, "\n", 1); incomplete_head = replace(incomplete_head, &incomplete_head_len, "[M]", 3, M, M_len); + incomplete_head = replace(incomplete_head, &incomplete_head_len, "[method]", 8, M, M_len); incomplete_head = replace(incomplete_head, &incomplete_head_len, "[U]", 3, U, U_len); incomplete_head = replace(incomplete_head, &incomplete_head_len, "[V]", 3, V, V_len); + incomplete_head = replace(incomplete_head, &incomplete_head_len, "[version]", 9, V, V_len); incomplete_head = replace(incomplete_head, &incomplete_head_len, "[host]", 6, host, (int)strlen(host)); incomplete_head = replace(incomplete_head, &incomplete_head_len, "[port]", 6, port, (int)strlen(port)); incomplete_head = replace(incomplete_head, &incomplete_head_len, "[H]", 3, H, (int)strlen(H)); @@ -300,7 +316,14 @@ char *request_head(conn *in, conf *configure) char https_del_copy[configure->http_del_len]; char *result = NULL; int incomplete_head_len; - + char url[U_len], uri[U_len]; + int uri_len; + + + strcpy(url, U); + get_path(url, uri); + uri_len = strlen(uri); + //printf("%s\n", uri); if (configure->http_port > 0) remote_port = configure->http_port; if (configure->https_ip != NULL) @@ -325,8 +348,11 @@ char *request_head(conn *in, conf *configure) incomplete_head = replace(incomplete_head, &incomplete_head_len, "\\r", 2, "\r", 1); incomplete_head = replace(incomplete_head, &incomplete_head_len, "\\n", 2, "\n", 1); incomplete_head = replace(incomplete_head, &incomplete_head_len, "[M]", 3, M, M_len); + incomplete_head = replace(incomplete_head, &incomplete_head_len, "[method]", 8, M, M_len); incomplete_head = replace(incomplete_head, &incomplete_head_len, "[U]", 3, U, U_len); + incomplete_head = replace(incomplete_head, &incomplete_head_len, "[uri]", 5, uri, uri_len); incomplete_head = replace(incomplete_head, &incomplete_head_len, "[V]", 3, V, V_len); + incomplete_head = replace(incomplete_head, &incomplete_head_len, "[version]", 9, V, V_len); incomplete_head = replace(incomplete_head, &incomplete_head_len, "[host]", 6, host, (int)strlen(host)); incomplete_head = replace(incomplete_head, &incomplete_head_len, "[port]", 6, port, (int)strlen(port)); incomplete_head = replace(incomplete_head, &incomplete_head_len, "[H]", 3, H, (int)strlen(H));