如何使用socat proxy拦截Unix域套接字发送辅助数据?

我有两个程序,一个服务器和一个客户端。 服务器打开一个文件,向其写入数据,然后通过unix域套接字将其文件描述符发送到客户端。 一切正常,直到我介绍一个社会代理之间。

socat -x -v UNIX-LISTEN:/tmp/unixSockSendFe,mode=775,reuseaddr,fork UNIX-CONNECT:/tmp/unixSockSendFd 

说明
服务器监听/tmp/unixSockSendFd ,socat连接到它( UNIX-CONNECT:/tmp/unixSockSendFd ),并创build另一个Unix域套接字( UNIX-LISTEN:/tmp/unixSockSendFe,mode=775,reuseaddr,fork )客户端连接。 客户端和服务器之间的任何通信都通过socat进行中继, socat会打印以二进制( -x选项)和ascii( -v选项)forms发送的字节。

如果我不使用socat,并且客户端直接连接到服务器(on /tmp/unixSockSendFd套接字),一切工作正常,但是当socat用作代理时,客户端崩溃与分段错误。

服务器

 /*Server code - sendfd.c*/ #include <sys/socket.h> #include <sys/un.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <fcntl.h> #include <sys/stat.h> #include <errno.h> char *socket_path = "/tmp/unixSockSendFd"; char *file="/tmp/abcd.txt" ;/*file whose fd is to be sent*/ int sendfd(int sock, int fd); int recvfd(int s); char data[]="sahil\0"; int main(int argc, char *argv[]) { struct sockaddr_un addr; char buf[100]; buf[0]='\n'; int fd,rc,confd; int fd_to_send; int temp,len; temp=1; fd_to_send=open(file,O_TRUNC|O_RDWR|O_CREAT,S_IRWXU|S_IRWXG|S_IRWXO); if(fd_to_send==-1) { perror("file open error"); return -1; } if (argc > 1) socket_path=argv[1]; if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { perror("socket error"); exit(-1); } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; if (*socket_path == '\0') { *addr.sun_path = '\0'; strncpy(addr.sun_path+1, socket_path+1, sizeof(addr.sun_path)-2); } else { strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1); } unlink(socket_path); if(bind(fd,(struct sockaddr*)&addr,sizeof(addr))==-1){ perror("bind error"); return -1; } /*Writing data to file before sending fd*/ len=write(fd_to_send,data,(int)strlen(data)); fsync(fd_to_send); printf("(len=%d)data written in file(content between ## marks) ##%s##\n",len,data); listen(fd,1); for(;;){ confd=accept(fd,NULL,NULL); if(confd==-1) { perror("accept error"); continue; } else{ printf("new client connected ... sending fd ... \n"); sendfd(confd,fd_to_send); close(confd); } } return 0; } int sendfd(int sock, int fd) { struct msghdr hdr; struct iovec data; char cmsgbuf[CMSG_SPACE(sizeof(int))]; char dummy = '*'; data.iov_base = &dummy; data.iov_len = sizeof(dummy); memset(&hdr, 0, sizeof(hdr)); hdr.msg_name = NULL; hdr.msg_namelen = 0; hdr.msg_iov = &data; hdr.msg_iovlen = 1; hdr.msg_flags = 0; hdr.msg_control = cmsgbuf; hdr.msg_controllen = CMSG_LEN(sizeof(int)); struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr); cmsg->cmsg_len = CMSG_LEN(sizeof(int)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; *(int*)CMSG_DATA(cmsg) = fd; //memcpy((CMSG_DATA(cmsg)), &fd, sizeof(fd)); -- from ivshmem server code - this too works instead of previous line int n = sendmsg(sock, &hdr, 0); if(n == -1) printf("sendmsg() failed: %s (socket fd = %d)\n", strerror(errno), sock); return n; } int recvfd(int s) { int n; int fd; char buf[1]; struct iovec iov; struct msghdr msg; struct cmsghdr *cmsg; char cms[CMSG_SPACE(sizeof(int))]; iov.iov_base = buf; iov.iov_len = 1; memset(&msg, 0, sizeof msg); msg.msg_name = 0; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = (caddr_t)cms; msg.msg_controllen = sizeof cms; if((n=recvmsg(s, &msg, 0)) < 0) return -1; if(n == 0){ perror("unexpected EOF"); return -1; } cmsg = CMSG_FIRSTHDR(&msg); memmove(&fd, CMSG_DATA(cmsg), sizeof(int)); return fd; } 

客户

 /*Client code - recvfd.c*/ #include <sys/socket.h> #include <sys/un.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <fcntl.h> #include <sys/stat.h> #include <errno.h> char *socket_path = "/tmp/unixSockSendFe"; int sendfd(int sock, int fd); int recvfd(int s); int fd_received; int main(int argc, char *argv[]) { struct sockaddr_un addr; char buf[100]; buf[0]='\n'; int fd,rc,confd; int temp,len; temp=1; if (argc > 1) socket_path=argv[1]; if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { perror("socket error"); exit(-1); } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; if (*socket_path == '\0') { *addr.sun_path = '\0'; strncpy(addr.sun_path+1, socket_path+1, sizeof(addr.sun_path)-2); } else { strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1); } if(connect(fd,(struct sockaddr*)&addr,sizeof(addr))==-1) { perror("connect error"); exit(-1); } fd_received=recvfd(fd); lseek(fd_received,0,SEEK_SET); len=read(fd_received,buf,5); if(len<0) { perror("read error"); } printf("(fd_received=%d,len=%d) first %d characters read from the file whoes fd was received(content within ##) ##%.*s##\n",fd_received,len,5,5,buf); return 0; } int sendfd(int sock, int fd) { struct msghdr hdr; struct iovec data; char cmsgbuf[CMSG_SPACE(sizeof(int))]; char dummy = '*'; data.iov_base = &dummy; data.iov_len = sizeof(dummy); memset(&hdr, 0, sizeof(hdr)); hdr.msg_name = NULL; hdr.msg_namelen = 0; hdr.msg_iov = &data; hdr.msg_iovlen = 1; hdr.msg_flags = 0; hdr.msg_control = cmsgbuf; hdr.msg_controllen = CMSG_LEN(sizeof(int)); struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr); cmsg->cmsg_len = CMSG_LEN(sizeof(int)); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; *(int*)CMSG_DATA(cmsg) = fd; int n = sendmsg(sock, &hdr, 0); if(n == -1) printf("sendmsg() failed: %s (socket fd = %d)\n", strerror(errno), sock); return n; } int recvfd(int s) { int n; int fd; char buf[1]; struct iovec iov; struct msghdr msg; struct cmsghdr *cmsg; char cms[CMSG_SPACE(sizeof(int))]; iov.iov_base = buf; iov.iov_len = 1; memset(&msg, 0, sizeof msg); msg.msg_name = 0; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = (caddr_t)cms; msg.msg_controllen = sizeof cms; if((n=recvmsg(s, &msg, 0)) < 0) return -1; if(n == 0){ perror("unexpected EOF"); return -1; } cmsg = CMSG_FIRSTHDR(&msg); memmove(&fd, CMSG_DATA(cmsg), sizeof(int)); return fd; } 

在运行客户端(recvfd)我得到分段错误。

 ./recvfd [1] 6104 segmentation fault (core dumped) ./recvfd 

这里是运行gdb和coredump的代码

 Core was generated by `./recvfd'. Program terminated with signal SIGSEGV, Segmentation fault. #0 0x0000000000400cf9 in recvfd (s=3) at recvfd.c:146 146 memmove(&fd, CMSG_DATA(cmsg), sizeof(int)); 

这是核心转储 – 链接 。

我想在发送文件描述符时嗅探两个进程之间的通信。 我无法弄清楚为什么客户端在与socat运行时崩溃,但没有它时运行。

更新1

使用socat来嗅探一个已经build立的开源项目( ivshmem – 用于在运行的虚拟机之间共享内存,也是英特尔DPDK 链接的一部分)两个进程之间的通信,我观察到以下内容。

  • 没有任何进程崩溃使用socat
  • 当使用socat ,文件描述符没有正确发送,并且不会被添加到收件人进程中。
  • 当不使用socat ,并且两个进程直接连接时,文件描述符会正确发送,并被添加到收件人进程中。