A process' file descriptors can be seen in /proc/pid/fd dir, where some of them can be opened and read or written. For example, if I pipe something to a process on stdin, it will show up as /proc/pid/fd/0: "0 -> 'pipe:[19918]'|" (ls -lF); I can then read it from another process, with data sometimes going to one process and somethings to the other.
If the file descriptor is a socket, however, this does not work. It shows up as /proc/pid/fd/3: "3 -> 'socket:[8577]'=", but opening it results in an "No such device or address" error.
With the first scenario the fd appears to become a named pipe, but in the second it does not appear to become a named socket. Now, while there are named sockets, they are created by bind and must be connected to. The socket above would be the result of accept or socket/connect and it is unclear how to attach to it.
So open does not work and connect can not work because the fd is not being listened on. Is there something that would work? In my examples, both fd types show a number in the ls listing - 19918 for the pipe, and 8577 for the socket, indicating that there is a specific kernel endpoint to attach to (?), but I do not know which API can be used to attach to it. Is there one?
TheDiveO's answer gave me a lead. His golang NewSocketFd uses the Linux syscall pidfd_getfd, available since kernel 5.4. It can dup and fd of the process, opened by fd created by pidfd_open. The pidfd_getfd operation requires PTRACE_MODE_ATTACH_REALCREDS permissions, meaning that unless you are running as root, this will only work for getting the fd of a child process, not just any random one. Here is example code to write to another process's socket:
#include <stdio.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
static int pidfd_open (pid_t pid, unsigned flags)
{ return syscall (SYS_pidfd_open, pid, flags); }
static int pidfd_getfd (pid_t pid, unsigned targetfd, unsigned flags)
{ return syscall (SYS_pidfd_getfd, pid, targetfd, flags); }
int main (int argc, const char* const* argv)
{
if (argc != 3) {
printf ("Usage: %s <pid> <fd>\n", argv[0]);
return EXIT_SUCCESS;
}
int pidfd = pidfd_open (atoi(argv[1]), 0);
if (pidfd < 0) {
perror ("pidfd_open");
return EXIT_FAILURE;
}
int tfd = pidfd_getfd (pidfd, atoi(argv[2]), 0);
if (tfd < 0)
perror ("pidfd_getfd");
else {
static const char c_test_data[] = "hello world";
if (sizeof(c_test_data) != write (tfd, c_test_data, sizeof(c_test_data)))
perror ("write");
close (tfd);
}
close (pidfd);
return EXIT_SUCCESS;
}