How to use sendmsg() to send a file-descriptor via sockets between 2 processes?

穿精又带淫゛_ 提交于 2019-11-29 02:26:50

This is extremely hard to get right. I'd recommend just using a library that does it for you. One of the simplest is libancillary. It gives you two functions, one to send a file descriptor over a UNIX-domain socket and one to receive one. They are absurdly simple to use.

The problem is you are passing the file descriptor in a msg_name field. This is an address field, and it is not intended to pass arbitrary data.

In fact, the file descriptors should be passed in a special way so the kernel could duplicate the file descriptor for the receiving process (and maybe the descriptor will have another value after the duplicating). That's why there is a special ancillary message type (SCM_RIGHTS) to pass file descriptors.

The following would work (I omitted some of the error handling). In client:

memset(&child_msg,   0, sizeof(child_msg));
char cmsgbuf[CMSG_SPACE(sizeof(int))];
child_msg.msg_control = cmsgbuf; // make place for the ancillary message to be received
child_msg.msg_controllen = sizeof(cmsgbuf);

printf("Waiting on recvmsg\n");
rc = recvmsg(worker_sd, &child_msg, 0);
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&child_msg);
if (cmsg == NULL || cmsg -> cmsg_type != SCM_RIGHTS) {
     printf("The first control structure contains no file descriptor.\n");
     exit(0);
}
memcpy(&pass_sd, CMSG_DATA(cmsg), sizeof(pass_sd));
printf("Received descriptor = %d\n", pass_sd);

In server:

memset(&parent_msg, 0, sizeof(parent_msg));
struct cmsghdr *cmsg;
char cmsgbuf[CMSG_SPACE(sizeof(accepted_socket_fd))];
parent_msg.msg_control = cmsgbuf;
parent_msg.msg_controllen = sizeof(cmsgbuf); // necessary for CMSG_FIRSTHDR to return the correct value
cmsg = CMSG_FIRSTHDR(&parent_msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(accepted_socket_fd));
memcpy(CMSG_DATA(cmsg), &accepted_socket_fd, sizeof(accepted_socket_fd));
parent_msg.msg_controllen = cmsg->cmsg_len; // total size of all control blocks

if((sendmsg(server_sd, &parent_msg, 0)) < 0)
{
    perror("sendmsg()");
    exit(EXIT_FAILURE);
}

See also man 3 cmsg, there are some examples.

You cannot send file descriptors over AF_INET. Use a UNIX domain socket.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!