gpt4 book ai didi

c - 如何在同一台机器上以编程方式获取通过AF_INET套接字连接到我的代理的进程的PID?

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:33:34 25 4
gpt4 key购买 nike

我正在Linux机器(具体来说是Ubuntu 18.04.1)上编写一个小型的http 代理服务器(用C语言编写),并且我一直在尝试寻找一种方法来获取与其连接的进程的 pid。 。

可能需要提及的是,代理仅用于为同一台计算机上运行的进程代理连接,因此我想这应该可以完成此任务。

服务器使用 AF_INET 系列套接字以及读/写操作来完成它的工作。我之所以这样说是因为经过一些研究,我确实遇到了有关“辅助数据”的线索,例如:Is there a way to get the uid of the other end of a unix socket connection

辅助数据包含连接套接字的凭据(例如PID),但仅在用于本地IPC的 AF_UNIX 套接字上起作用,并且要求我们在两侧(客户端/服务器)显式发送/接收它。就我而言,尽管如上所述,服务器只会在与服务器相同的计算机上代理流量,但我需要使用 AF_INET 套接字,因此每个人(例如Web浏览器)都可以连接到它。

性能并不是那么关键;因此,任何建议(包括使用系统调用的解决方法等)都非常受欢迎。

最佳答案

我们可以使用netstat -nptW输出来查看哪些本地进程的TCP连接。由于输出可能对安全性敏感,因此需要 super 用户特权才能查看属于所有用户的进程。

由于没有理由以提升的特权运行代理服务(可能需要CAP_NET_BIND_SERVICE),因此需要特权帮助程序。

我仔细考虑了一个合适的安全模型,得出的结论是,一个助手检查提供给它的连接套接字(例如标准输入),并仅输出对等的PID,这将是最安全的:很难滥用它,即使可能,也只会显示对等进程ID。

这是示例帮助程序 tcp-peer-pids.c :

#define _POSIX_C_SOURCE  200809L
#define _GNU_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

#define EXITCODE_OK 0
#define EXITCODE_STDIN_INVALID 1
#define EXITCODE_UNKNOWN_ADDRESS 2
#define EXITCODE_NETSTAT 3
#define EXITCODE_NETSTAT_OUTPUT 4
#define EXITCODE_WRITE_ERROR 5
#define EXITCODE_PRIVILEGES 6

static pid_t *pids = NULL;
static size_t num_pids = 0;
static size_t max_pids = 0;

static int add_pid(const pid_t p)
{
size_t i;

/* Check if already listed. */
for (i = 0; i < num_pids; i++)
if (pids[i] == p)
return 0;

/* Ensure enough room in pids array. */
if (num_pids >= max_pids) {
const size_t max_temp = (num_pids | 1023) + 1025 - 8;
pid_t *temp;

temp = realloc(pids, max_temp * sizeof pids[0]);
if (!temp)
return ENOMEM;

pids = temp;
max_pids = max_temp;
}

pids[num_pids++] = p;

return 0;
}

int main(void)
{
struct sockaddr_storage sock_addr;
socklen_t sock_addrlen = sizeof sock_addr;
char sock_match[128], sock_host[64], sock_port[32];

struct sockaddr_storage peer_addr;
socklen_t peer_addrlen = sizeof peer_addr;
char peer_match[128], peer_host[64], peer_port[32];

FILE *cmd;
char *line = NULL;
size_t size = 0;
ssize_t len;

int status;

/* Socket address is *remote*, and peer address is *local*.
This is because the variables are named after their matching netstat lines. */
if (getsockname(STDIN_FILENO, (struct sockaddr *)&sock_addr, &sock_addrlen) == -1) {
fprintf(stderr, "Standard input is not a valid socket.\n");
exit(EXITCODE_STDIN_INVALID);
}
if (getpeername(STDIN_FILENO, (struct sockaddr *)&peer_addr, &peer_addrlen) == -1) {
fprintf(stderr, "Standard input is not a connected socket.\n");
exit(EXITCODE_STDIN_INVALID);
}
if ((sock_addr.ss_family != AF_INET && sock_addr.ss_family != AF_INET6) ||
(peer_addr.ss_family != AF_INET && peer_addr.ss_family != AF_INET6)) {
fprintf(stderr, "Standard input is not an IP socket.\n");
exit(EXITCODE_STDIN_INVALID);
}

/* For security, we close the standard input descriptor, */
close(STDIN_FILENO);

/* and redirect it from /dev/null, if possible. */
{
int fd = open("/dev/null", O_RDONLY);
if (fd != -1 && fd != STDIN_FILENO) {
dup2(fd, STDIN_FILENO);
close(fd);
}
}

/* Convert sockets to numerical host and port strings. */
if (getnameinfo((const struct sockaddr *)&sock_addr, sock_addrlen,
sock_host, sizeof sock_host, sock_port, sizeof sock_port,
NI_NUMERICHOST | NI_NUMERICSERV)) {
fprintf(stderr, "Unknown socket address.\n");
exit(EXITCODE_UNKNOWN_ADDRESS);
}
if (getnameinfo((const struct sockaddr *)&peer_addr, peer_addrlen,
peer_host, sizeof peer_host, peer_port, sizeof peer_port,
NI_NUMERICHOST | NI_NUMERICSERV)) {
fprintf(stderr, "Unknown peer address.\n");
exit(EXITCODE_UNKNOWN_ADDRESS);
}

/* Combine to the host:port format netstat uses. */
snprintf(sock_match, sizeof sock_match, "%s:%s", sock_host, sock_port);
snprintf(peer_match, sizeof peer_match, "%s:%s", peer_host, peer_port);

/* Switch to privileged user, if installed as setuid. */
{
uid_t real_uid = getuid();
gid_t real_gid = getgid();
uid_t effective_uid = geteuid();
gid_t effective_gid = getegid();
if (real_gid != effective_gid || real_uid != effective_uid) {
/* SetUID or SetGID in effect. Switch privileges. */
if (setresgid(effective_gid, effective_gid, effective_gid) == -1 ||
setresuid(effective_uid, effective_uid, effective_uid) == -1) {
fprintf(stderr, "Error in privileges: %s.\n", strerror(errno));
exit(EXITCODE_PRIVILEGES);
}
}
}

/* Run netstat to obtain the data; redirect standard error to standard output. */
cmd = popen("LANG=C LC_ALL=C /bin/netstat -nptW 2>&1", "r");
if (!cmd) {
fprintf(stderr, "Cannot run netstat.\n");
exit(EXITCODE_NETSTAT);
}

/* Input line loop. */
while (1) {
char *field[8], *ends;
long val;
pid_t p;

len = getline(&line, &size, cmd);
if (len < 1)
break;

/* Split each line into fields. */
field[0] = strtok(line, "\t\n\v\f\r "); /* Protocol */

/* We are only interested in tcp ("tcp" and "tcp6" protocols). */
if (strcmp(field[0], "tcp") && strcmp(field[0], "tcp6"))
continue;

field[1] = strtok(NULL, "\t\n\v\f\r "); /* Recv-Q */
field[2] = strtok(NULL, "\t\n\v\f\r "); /* Send-Q */
field[3] = strtok(NULL, "\t\n\v\f\r "); /* Local address (peer) */
field[4] = strtok(NULL, "\t\n\v\f\r "); /* Remote address (sock) */
field[5] = strtok(NULL, "\t\n\v\f\r "); /* State */
field[6] = strtok(NULL, "\t\n\v\f\r /"); /* PID */
field[7] = strtok(NULL, "\t\n\v\f\r "); /* Process name */

/* Local address must match peer_match, and foreign/remote sock_match. */
if (strcmp(field[3], peer_match) || strcmp(field[4], sock_match))
continue;

/* This line corresponds to the process we are looking for. */

/* Missing PID field is an error at this point. */
if (!field[6])
break;

/* Parse the PID. Parsing errors are fatal. */
ends = field[6];
errno = 0;
val = strtol(field[6], &ends, 10);
if (errno || ends == field[6] || *ends != '\0' || val < 1)
break;
p = (pid_t)val;
if ((long)p != val)
break;

/* Add the pid to the known pids list. */
if (add_pid(p))
break;
}

/* The line buffer is no longer needed. */
free(line);

/* I/O error? */
if (!feof(cmd) || ferror(cmd)) {
fprintf(stderr, "Error reading netstat output.\n");
exit(EXITCODE_NETSTAT_OUTPUT);
}

/* Reap the netstat process. */
status = pclose(cmd);
if (status == -1) {
fprintf(stderr, "Error reading netstat output: %s.\n", strerror(errno));
exit(EXITCODE_NETSTAT_OUTPUT);
}
if (!WIFEXITED(status)) {
fprintf(stderr, "Netstat died unexpectedly.\n");
exit(EXITCODE_NETSTAT_OUTPUT);
}
if (WEXITSTATUS(status)) {
fprintf(stderr, "Netstat failed with exit status %d.\n", WEXITSTATUS(status));
exit(EXITCODE_NETSTAT_OUTPUT);
}

/* Output the array of pids as binary data. */
if (num_pids > 0) {
const char *head = (const char *)pids;
const char *const ends = (const char *)(pids + num_pids);
ssize_t n;

while (head < ends) {
n = write(STDOUT_FILENO, head, (size_t)(ends - head));
if (n > 0)
head += n;
else
if (n != -1)
exit(EXITCODE_WRITE_ERROR);
else
if (errno != EINTR)
exit(EXITCODE_WRITE_ERROR);
}
}

/* Discard the pids array. */
free(pids);
exit(EXITCODE_OK);
}

它可以使用普通用户特权(在这种情况下,它将只知道该用户拥有的进程),root特权或作为setuid root来运行。

如果与 sudo一起使用,请确保您使用规则 proxyuser ALL = NOPASSWD: /path/to/helper,因为 sudo无法在此处询问密码。我可能只是将助手以setuid root的身份安装在 /usr/lib/yourproxy/tcp-peer-pid,所有者root,对代理服务组进行分组,并且无法访问其他用户(root:proxygroup -r-sr-x---)。

该辅助程序与 netstat -nptW输出格式紧密耦合,但是确实明确设置了C语言环境以避免获取本地化输出。

使用数字格式的[ address:port( getpeername() )(使用 getsockname()标志),分别从 getnameinfo() http://man7.org/linux/man-pages/man3/getnameinfo.3.html返回的地址构造要与netstat输出中的“本地地址”和“外部地址”匹配的比较 NI_NUMERICHOST | NI_NUMERICSERV字符串。

辅助程序以二进制形式向服务器提供PID,因为服务器代码太长而无法容纳在此处的单个帖子中。

这是一个示例TCP服务 server.c ,它使用上述帮助程序来查找本地计算机上套接字对等端的PID。 (为避免拒绝服务攻击,您应该设置一个IP筛选器,以拒绝从计算机外部对代理服务端口的访问。)
#define _POSIX_C_SOURCE  200809L
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

#ifndef HELPER_PATH
#define HELPER_PATH "./tcp-peer-pids"
#endif
#ifndef HELPER_NAME
#define HELPER_NAME "tcp-peer-pids"
#endif

#ifndef SUDO_PATH
#define SUDO_PATH "/usr/bin/sudo"
#endif
#ifndef SUDO_NAME
#define SUDO_NAME "sudo"
#endif

/*
* Signal handler, to detect INT (Ctrl+C), HUP, and TERM signals.
*/

static volatile sig_atomic_t done = 0;

static void handle_done(int signum)
{
/* In Linux, all signals have signum > 0. */
__atomic_store_n(&done, (sig_atomic_t)signum, __ATOMIC_SEQ_CST);
}

static int install_done(int signum)
{
struct sigaction act;

memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_flags = SA_RESTART; /* Do not interrupt slow syscalls. */
act.sa_handler = handle_done;
if (sigaction(signum, &act, NULL) == -1)
return -1; /* errno set by getpeername() */

return 0;
}

/* Helper function: Move descriptors away from STDIN/STDOUT/STDERR.
Returns 0 if successful, -1 with errno set if an error occurs. */
static inline int normalfds(int fd[], const size_t n)
{
unsigned int closemask = 0;
int err = 0;
size_t i;
int newfd;

for (i = 0; i < n; i++)
while (fd[i] == STDIN_FILENO || fd[i] == STDOUT_FILENO || fd[i] == STDERR_FILENO) {
newfd = dup(fd[i]);
if (newfd == -1) {
err = errno;
break;
}
closemask |= 1u << fd[i];
fd[i] = newfd;
}

/* Close temporary descriptors. */
if (closemask & (1u << STDIN_FILENO)) close(STDIN_FILENO);
if (closemask & (1u << STDOUT_FILENO)) close(STDOUT_FILENO);
if (closemask & (1u << STDERR_FILENO)) close(STDERR_FILENO);

/* Success? */
if (!err)
return 0;

/* Report error. */
errno = err;
return -1;
}

/* Return the number of peer processes.
If an error occurs, returns zero; examine errno. */
size_t peer_pids(const int connfd, pid_t *const pids, size_t maxpids)
{
char *in_data = NULL;
size_t in_size = 0;
size_t in_used = 0;
size_t n;
int binpipe[2], status;
pid_t child, p;

/* Sanity check. */
if (connfd == -1) {
errno = EBADF;
return 0;
}

/* Create a pipe to transfer the PIDs (in binary). */
if (pipe(binpipe) == -1)
return 0; /* errno set by pipe(). */

/* Make sure the binary pipe descriptors do not conflict with standard descriptors. */
if (normalfds(binpipe, 2) == -1) {
const int saved_errno = errno;
close(binpipe[0]);
close(binpipe[1]);
errno = saved_errno;
return 0;
}

/* Fork a child process. */
child = fork();
if (child == -1) {
const int saved_errno = errno;
close(binpipe[0]);
close(binpipe[1]);
errno = saved_errno;
return 0;
}

if (!child) {
/* This is the child process. */

#ifdef USE_SUDO
const char *cmd_path = SUDO_PATH;
char *const cmd_args[3] = { SUDO_NAME, HELPER_PATH, NULL };
#else
const char *cmd_path = HELPER_PATH;
char *const cmd_args[2] = { HELPER_NAME, NULL };
#endif

/* The child runs in its own process group, for easier management. */
setsid();

/* Close read end of pipe. */
close(binpipe[0]);

/* Move established connection to standard input. */
if (connfd != STDIN_FILENO) {
if (dup2(connfd, STDIN_FILENO) != STDIN_FILENO)
_Exit(99);
close(connfd);
}

/* Move write end of pipe to standard output. */
if (dup2(binpipe[1], STDOUT_FILENO) != STDOUT_FILENO)
_Exit(99);
else
close(binpipe[1]);

/* Execute helper. */
execv(cmd_path, cmd_args);

/* Failed to execute helper. */
_Exit(98);
}

/* Parent process. */

/* Close write end of pipe, so we detect when child exits. */
close(binpipe[1]);

/* Read all output from child. */
status = 0;
while (1) {
ssize_t bytes;

if (in_used >= in_size) {
const size_t size = (in_used | 1023) + 1025 - 8;
char *temp;

temp = realloc(in_data, in_size);
if (!temp) {
status = ENOMEM;
break;
}
in_data = temp;
in_size = size;
}

bytes = read(binpipe[0], in_data + in_used, in_size - in_used);
if (bytes > 0) {
in_used += bytes;
} else
if (bytes == 0) {
/* End of input condition. */
break;
} else
if (bytes != -1) {
status = EIO;
break;
} else
if (errno != EINTR) {
status = errno;
break;
}
}

/* Close the pipe. */
close(binpipe[0]);

/* Abort, if an error occurred. */
if (status) {
free(in_data);
kill(-child, SIGKILL);
do {
p = waitpid(child, NULL, 0);
} while (p == -1 && errno == EINTR);
errno = status;
return 0;
}

/* Reap the child process. */
do {
status = 0;
p = waitpid(child, &status, 0);
} while (p == -1 && errno == EINTR);
if (p == -1) {
const int saved_errno = errno;
free(in_data);
errno = saved_errno;
return 0;
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
free(in_data);
errno = ESRCH; /* The helper command failed, really. */
return 0;
}

/* We expect an integer number of pid_t's. Check. */
n = in_used / sizeof (pid_t);
if ((in_used % sizeof (pid_t)) != 0) {
free(in_data);
errno = EIO;
return 0;
}

/* None found? */
if (!n) {
free(in_data);
errno = ENOENT; /* Not found, really. */
return 0;
}

/* Be paranoid, and verify the pids look sane. */
{
const pid_t *const pid = (const pid_t *const)in_data;
size_t i;

for (i = 0; i < n; i++)
if (pid[i] < 2) {
free(in_data);
errno = ESRCH; /* Helper failed */
return 0;
}
}

/* Copy to user buffer, if specified. */
if (maxpids > n)
memcpy(pids, in_data, n * sizeof (pid_t));
else
if (maxpids > 0)
memcpy(pids, in_data, maxpids * sizeof (pid_t));

/* The pid buffer is no longer needed. */
free(in_data);

/* Return the number of pids we actually received. */
return n;
}


int main(int argc, char *argv[])
{
struct addrinfo hints, *list, *curr;
const char *node, *serv;
int service_fd, err;

struct sockaddr_storage client_addr;
socklen_t client_addrlen;
int client_fd;

if (argc != 3) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s HOST PORT\n", argv[0]);
fprintf(stderr, "\n");
return EXIT_FAILURE;
}

/* Install signal handers for Ctrl+C, HUP, and TERM. */
if (install_done(SIGINT) ||
install_done(SIGHUP) ||
install_done(SIGTERM)) {
fprintf(stderr, "Cannot install signal handlers: %s.\n", strerror(errno));
return EXIT_FAILURE;
}

/* Empty or - or * is a wildcard host. */
if (argv[1][0] == '\0' || !strcmp(argv[1], "-") || !strcmp(argv[1], "*"))
node = NULL;
else
node = argv[1];
serv = argv[2];

memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; /* IPv4 or IPv6 */
hints.ai_socktype = SOCK_STREAM; /* TCP */
hints.ai_flags = AI_PASSIVE;
hints.ai_protocol = 0;
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
list = NULL;
err = getaddrinfo(node, serv, &hints, &list);
if (err) {
fprintf(stderr, "Invalid host and/or port: %s.\n", gai_strerror(err));
return EXIT_FAILURE;
}

service_fd = -1;
err = 0;
for (curr = list; curr != NULL; curr = curr->ai_next) {
service_fd = socket(curr->ai_family, curr->ai_socktype, curr->ai_protocol);
if (service_fd == -1)
continue;

errno = 0;
if (bind(service_fd, curr->ai_addr, curr->ai_addrlen) == -1) {
if (!err)
if (errno == EADDRINUSE || errno == EADDRNOTAVAIL || errno == EACCES)
err = errno;
close(service_fd);
service_fd = -1;
continue;
}

if (listen(service_fd, 5) == -1) {
if (!err)
if (errno == EADDRINUSE)
err = errno;
close(service_fd);
service_fd = -1;
continue;
}

/* This socket works. */
break;
}

freeaddrinfo(list);
list = curr = NULL;

if (service_fd == -1) {
if (err)
fprintf(stderr, "Cannot listen for incoming connections on the specified host and port: %s.\n", strerror(err));
else
fprintf(stderr, "Cannot listen for incoming connections on the specified host and port.\n");
return EXIT_FAILURE;
}

/* Do not leak the listening socket to child processes. */
fcntl(service_fd, F_SETFD, FD_CLOEXEC);

/* We also want the listening socket to be nonblocking. */
fcntl(service_fd, F_SETFL, O_NONBLOCK);

fprintf(stderr, "Process %ld is waiting for incoming TCP connections.\n", (long)getpid());

/* Incoming connection loop. */
while (!done) {
struct timeval t;
char client_host[64]; /* 64 for numeric, 1024 for non-numeric */
char client_port[32];
pid_t client_pid;
fd_set fds;

t.tv_sec = 0;
t.tv_usec = 100000; /* Max. 0.1s delay to react to done signal. */

FD_ZERO(&fds);
FD_SET(service_fd, &fds);

if (select(service_fd + 1, &fds, NULL, NULL, &t) < 1)
continue;

client_addrlen = sizeof client_addr;
client_fd = accept(service_fd, (struct sockaddr *)&client_addr, &client_addrlen);
if (client_fd == -1) {
if (errno == EINTR || errno == ECONNABORTED)
continue;
fprintf(stderr, "Error accepting an incoming connection: %s.\n", strerror(errno));
continue;
}

if (getnameinfo((const struct sockaddr *)&client_addr, client_addrlen,
client_host, sizeof client_host, client_port, sizeof client_port,
NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
fprintf(stderr, "Cannot resolve peer address for incoming connection, so dropping it.\n");
close(client_fd);
continue;
}

printf("Incoming connection from %s:%s", client_host, client_port);
fflush(stdout);

if (peer_pids(client_fd, &client_pid, 1) != 1) {
printf(", but cannot determine process ID. Dropped.\n");
close(client_fd);
continue;
}

printf(" from local process %ld.\n", (long)client_pid);
fflush(stdout);

/*
* Handle connection.
*/

printf("Closing connection.\n");
fflush(stdout);
close(client_fd);
}

/* Close service socket. */
close(service_fd);

switch (__atomic_load_n(&done, __ATOMIC_SEQ_CST)) {
case SIGINT:
fprintf(stderr, "Received INT signal.\n");
break;
case SIGHUP:
fprintf(stderr, "Received HUP signal.\n");
break;
case SIGTERM:
fprintf(stderr, "Received TERM signal.\n");
break;
}

return EXIT_SUCCESS;
}
peer_pids()函数与帮助程序进程进行通信。这很简单,尽管要小心,不要返回不可靠的数据:它会报告失败,而不是忽略错误或尝试从错误中恢复。这允许主程序执行 if (peer_pids(client_fd, &pid, 1) != 1) /* Don't know! */并丢弃服务器不确定的任何连接-我在这里认为这是一种理智的方法。
normalfds()辅助函数通常被忽略。如果有任何标准流被关闭,它可以帮助避免问题。它仅使用最多三个额外的描述符将描述符集从三个标准流中移开。

您可以在编译时定义 USE_SUDO,以使它在执行帮助程序时使用sudo。分别将 HELPER_PATHHELPER_NAME定义为帮助程序的绝对路径及其文件名。 (现在,它们默认为 ./tcp-peer-pidtcp-peer-pid,以便于测试。)

服务器确实为INT(Ctrl + C),HUP(在用户关闭终端时发送)或TERM信号安装了信号处理程序,所有这些信号处理程序都会导致它停止接受新连接并以受控方式退出。 (由于信号处理程序是使用 SA_RESTART标志安装的,因此它的传递不会中断慢速的系统调用或引起 errno == EINTR。这也意味着 accept()不应阻塞,否则信号传递将不会被注意到。因此,将 select()阻塞0.1s,并且至少在示例服务器中,检查信号是否在两者之间传递是一个不错的选择。)

在我的机器上,我使用以下命令在一个终端窗口中编译并测试了服务
gcc -Wall -O2 tcp-peer-pids.c -o tcp-peer-pids
gcc -Wall -O2 "-DHELPER_PATH=\"$PWD/tcp-peer-pids\"" server.c -o server
./server - 2400

那将报告 Process # is waiting for incoming TCP connections。在另一个窗口中,使用Bash或POSIX shell,我运行一个或多个test netcat命令:
nc localhost 2400 & wait

在后台运行命令并立即等待它看起来很愚蠢,但是那样您就可以看到 nc进程的PID。

在我的系统上,所有环回( 127.x.y.z),TCP/IPv4和TCP/IPv6(我的以太网和WiFi接口(interface)的地址)都可以正常工作,并可靠地报告了连接到示例服务器的进程的正确PID。

在许多情况下,所报告的PID数量可能会有所不同:例如,如果程序执行了子进程,但在子进程中也打开了连接的描述符。 (应该将其视为错误。)另一种典型情况是程序在 netstat命令执行之前已退出。

如果您发现任何错别字,错误或奇怪的行为,请在评论中告诉我,以便我进行验证和解决。我一次坐下来编写了这两个程序,因此它们很可能包含错误。正如我提到的那样,在同事(或者我自己几次,后来又用新鲜的眼睛)以批判/偏执的眼光审视产品之前,我也不信任生产。

我个人只会使用这种方法进行日志记录和统计,而不会使用访问控制本身。通过访问控制,我的意思是您应该配置IP过滤器(Linux内核内置的防火墙),以仅限制对受信任主机的访问。并且特别是如果只代理本地应用程序,则不允许任何传入的代理服务器连接到代理服务,而不是依靠此方法检测所有远程连接。

对于特定于应用程序的日志记录/限制,请在 readlink()伪符号链接(symbolic link)上使用 /proc/PID/exe 。这不能被伪造,但是如果可执行文件不可访问或目录树太深,则调用可能会失败。 (在那种情况下,我会完全拒绝代理连接。)

请注意,对于用户来说,将可执行文件复制到他们拥有的任何目录并从那里执行通常是很简单的。这意味着要完全发挥特定于应用程序的限制,默认情况下,您应对所有应用程序设置严格的限制,并放宽对特定可执行文件的限制。

关于c - 如何在同一台机器上以编程方式获取通过AF_INET套接字连接到我的代理的进程的PID?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53482678/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com