3

I am trying to pass socket descriptor between two process through a shared memory zone between the two process and Linux kernel, my goal is to open a TCP socket with one process and write inside the same socket using the second process. Is this possible without socket pipe between the two process?

The socket descriptor is an int, how to get the native form of the descriptor in Linux? And if I just pass the native form of descriptor from process to other can I write data to the opened TCP socket.

alk
  • 69,737
  • 10
  • 105
  • 255
Mondher123
  • 67
  • 1
  • 9
  • 3
    It's just a number. You need to pass it the correct way in order for it to *mean* anything. – Ignacio Vazquez-Abrams Jul 21 '16 at 12:24
  • 4
    "*Passing socket descriptor between two processes using shared memory*" is not possible. Use the `sendmsg()` call on a UNIX-domain-socket (`AF_UNIX`). – alk Jul 21 '16 at 12:28
  • Related: http://stackoverflow.com/questions/909064/portable-way-to-pass-file-descriptor-between-different-processes – alk Jul 21 '16 at 12:30
  • "*And if I just pass the native form of descriptor from process to other*" this won't work either. Please also see here: http://stackoverflow.com/questions/5256599/what-are-file-descriptors-explained-in-simple-terms – alk Jul 21 '16 at 12:34
  • correct me if i am wrong: the unix domain socket is just a solution to interprocess communication and it communicate some information about the file descriptor or the socket descriptor in my case, if i put the full information transmitted by the unix socket in to shared memory and read this information by the second process will this work ? – Mondher123 Jul 21 '16 at 12:50
  • No, it won't. The *only* way to transfer a file descriptor from one process to another is by asking the kernel to do that specific thing, using `sendmsg`. – zwol Jul 21 '16 at 15:48

3 Answers3

3

No, you can't just use some alternate method to transfer the same "stuff" that would have gone into the sendmsg call. When you "pass a file descriptor", what you're really transferring is access to the kernel-internal file object.

The cmsg structure is just a way of formatting a request to the kernel, in which you say "I want to duplicate this open file object, and allow the process that reads this socket to gain access to it". The name SCM_RIGHTS is a clue that what you're transferring is in essence a permission.

Since the request is for manipulation of a kernel-internal object with security implications, you can't sneak around it. You have to make a syscall. And sendmsg is it. (There have been other fd-passing APIs... something with Streams on SysV I think. I don't know if that one is still alive in any recent OSes. For BSD and Linux at least, sendmsg with SCM_RIGHTS is the way to go.)

In general, this is exactly the difference between msg and cmsg: cmsg is used for operations where the kernel is doing more than just copying some bytes from one end of the socket to the other.

  • I think there is another aspect to the question which is less so about skipping `sendmsg()`, but rather whether one could ultimately `sendmsg()` and `recvmsg()` on the same file, one that would already be common to both processes (i.e. avoiding `pipe()`/`socketpair()`). – init_js Aug 01 '18 at 08:21
0

Sending a file descriptor using unix socket

static int
send_file_descriptor(
  int socket, /* Socket through which the file descriptor is passed */
  int fd_to_send) /* File descriptor to be passed, could be another socket */
{
 struct msghdr message;
 struct iovec iov[1];
 struct cmsghdr *control_message = NULL;
 char ctrl_buf[CMSG_SPACE(sizeof(int))];
 char data[1];

 memset(&message, 0, sizeof(struct msghdr));
 memset(ctrl_buf, 0, CMSG_SPACE(sizeof(int)));

 /* We are passing at least one byte of data so that recvmsg() will not return 0 */
 data[0] = ' ';
 iov[0].iov_base = data;
 iov[0].iov_len = sizeof(data);

 message.msg_name = NULL;
 message.msg_namelen = 0;
 message.msg_iov = iov;
 message.msg_iovlen = 1;
 message.msg_controllen =  CMSG_SPACE(sizeof(int));
 message.msg_control = ctrl_buf;

 control_message = CMSG_FIRSTHDR(&message);
 control_message->cmsg_level = SOL_SOCKET;
 control_message->cmsg_type = SCM_RIGHTS;
 control_message->cmsg_len = CMSG_LEN(sizeof(int));

 *((int *) CMSG_DATA(control_message)) = fd_to_send;

 return sendmsg(socket, &message, 0);
}

i mean puting the full "message" information inside the shared memory

Mondher123
  • 67
  • 1
  • 9
0

Adding to the previous answer, you must establish a socket pair between the two processes

pid_t pid;
int streamfd[2];
int n;
int conn_fd;
char buffer; // dummy byte character received from parent

if (socketpair(AF_UNIX, SOCK_STREAM, 0, streamfd) < 0) {
        return -1;
}

Then, one process must send the descriptor and the other must receive it. This is the receiving end (this code was adapted from : https://www.amazon.ca/UNIX-Network-Programming-Richard-Stevens/dp/0139498761 )

int receiveDescriptor(int streamPipe, int* descriptor, void* pBuffer, size_t bytes) {

  struct msghdr msgInstance;
  struct iovec iov[1];
  int n;

  // ancillary (control) data
  // This is where the descriptor will be held

  #ifdef MSGHDR_MSG_CONTROL

  union {
    struct cmsghdr cm;
    char control[CMSG_SPACE(sizeof(int))];
  } control_un;

  struct cmsghdr* cmptr;

  msgInstance.msg_control = control_un.control;
  msgInstance.msg_controllen = sizeof(control_un.control);

#else

  int receivedFD;
  msgInstance.msg_accrights = (caddr_t)&receivedFD;
  msgInstance.msg_accrightslen = sizeof(int);

#endif

  msgInstance.msg_name = NULL;
  msgInstance.msg_namelen = 0;

  iov[0].iov_base = pBuffer;
  iov[0].iov_len = bytes;

  msgInstance.msg_iov = iov;
  msgInstance.msg_iovlen = 1;

  n = recvmsg(streamPipe, &msgInstance, 0);

  if ( n <= 0) {
    return n;
  }

  // assume descriptor will not be received

  *descriptor = -1;

  // get the descriptor

#ifdef MSGHDR_MSG_CONTROL

  if ( (cmptr = CMSG_FIRSTHDR(&msgInstance)) != NULL) {

    if (cmptr->cmsg_len == CMSG_LEN(sizeof(int))) {

        if (cmptr->cmsg_level == SOL_SOCKET && cmptr->cmsg_type == SCM_RIGHTS) {

            *descriptor = *((int*)CMSG_DATA(cmptr));

        }
    }
  }

#else

  if (msgInstance.msg_accrightslen == sizeof(int)) {

    *descriptor = receivedFD
  }

#endif

  return n;
}
clarasoft-it
  • 201
  • 1
  • 5