UNIXworkcode

  1  /*
  2   *             DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
  3   *                     Version 2, December 2004
  4   * 
  5   *  Copyright (C) 2017 Olaf Wintermann <olaf.wintermann@gmail.com>
  6   * 
  7   *  Everyone is permitted to copy and distribute verbatim or modified
  8   *  copies of this license document, and changing it is allowed as long
  9   *  as the name is changed.
 10   * 
 11   *             DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
 12   *    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 13   * 
 14   *   0. You just DO WHAT THE FUCK YOU WANT TO.
 15   */
 16  
 17  #define _POSIX_C_SOURCE 200112L
 18  
 19  #include <stdio.h>
 20  #include <stdlib.h>
 21  #include <string.h>
 22  #include <unistd.h>
 23  #include <sys/socket.h>
 24  #include <sys/un.h>
 25  #include <sys/types.h>
 26  #include <sys/wait.h>
 27  #include <sys/stat.h>
 28  #include <fcntl.h>
 29  
 30  
 31  /*
 32   * sends a buffer and an additional file descriptor to a socket 
 33   */
 34  ssize_t send_fd(int sock, const char *buf, size_t len, int fd);
 35  
 36  /*
 37   * receives a buffer and an additional file descriptor from a socket
 38   */
 39  ssize_t recv_fd(int sock, char *buf, size_t len, int *fd);
 40  
 41  void child_process();
 42  
 43  const char *socket_path = "fd.sock";
 44  
 45  int main(int argc, char **argv) {  
 46      // create unix domain socket
 47      struct sockaddr_un addr;
 48      memset(&addr, 0, sizeof(struct sockaddr_un));
 49      addr.sun_family = AF_UNIX;
 50      strcpy(addr.sun_path, socket_path);
 51      
 52      int sock = socket(AF_UNIX, SOCK_STREAM, 0);
 53      if(sock == -1) {
 54          perror("socket");
 55          return -1;
 56      }
 57      
 58      if(bind(sock, (struct sockaddr*)&addr, sizeof(addr))) {
 59          perror("bind");
 60          return -1;
 61      }
 62      
 63      if(listen(sock, 8) == -1) {
 64          perror("listen");
 65          return -1;
 66      }
 67      
 68      // create new process
 69      pid_t pid = fork();
 70      if(pid == 0) {
 71          close(sock);
 72          child_process();
 73          return 0;
 74      }
 75      
 76      // accept clients
 77      int fd = accept(sock, NULL, 0);
 78      if(fd == -1) {
 79          perror("accept");
 80          return -1;
 81      }
 82      
 83      printf("client connected\n");
 84      
 85      // open test file
 86      mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
 87      int file = open("testfile", O_WRONLY | O_CREAT, mode);
 88      printf("testfile fd = %d\n", file);
 89      
 90      // send fd
 91      const char *msg = "Hello World!";
 92      send_fd(fd, msg, strlen(msg), file);
 93      
 94      int status;
 95      waitpid(pid, &status, 0);
 96      
 97      // cleanup
 98      close(file);
 99      close(fd);
100      close(sock);
101      
102      if(unlink(socket_path)) {
103          perror("unlink");
104      }
105      
106      return 0;
107  }
108  
109  void child_process() {
110      // connect to a unix domain socket
111      struct sockaddr_un addr;
112      memset(&addr, 0, sizeof(addr));
113      addr.sun_family = AF_UNIX;
114      strcpy(addr.sun_path, socket_path);
115      
116      int client = socket(AF_UNIX, SOCK_STREAM, 0);
117      if(client == -1) {
118          perror("client: socket");
119          return;
120      }
121      
122      if(connect(client, (struct sockaddr*)&addr, sizeof(addr))) {
123          perror("client: connect");
124          return;
125      }
126      
127      printf("client connected\n");
128      
129      // receive data and fd
130      char buf[1024];
131      int fd;
132      ssize_t r = recv_fd(client, buf, 1024, &fd);
133      printf("msg: {%.*s}\n", (int)r, buf);
134      printf("fd: %d\n", fd);
135      
136      if(r > 0 && fd != -1) {
137          printf("write msg to fd\n");
138          write(fd, buf, r);
139          close(fd);
140      }
141      
142      close(client);
143  }
144  
145  ssize_t send_fd(int sock, const char *buf, size_t len, int fd) {
146      struct msghdr msg;
147      msg.msg_name = NULL; // unused in this case
148      msg.msg_namelen = 0;
149      
150      // a message contains an iovec for the normal payload data
151      struct iovec iov[1] ;
152      iov[0].iov_base = (void*)buf;
153      iov[0].iov_len = len;
154      
155      msg.msg_iov = iov;
156      msg.msg_iovlen = 1;
157      
158      // set ancillary data
159      
160      // the ancillary data is a struct cmsghdr followed by additional data
161      // in this case just an int for the file descriptor, but you can send
162      // multiple file descriptors with an array of ints
163      // CMSG_SPACE returns the number of bytes needed for the struct and the
164      // payload data
165      char cmsg_buf[CMSG_SPACE(sizeof(int))];
166      
167      struct cmsghdr *cmsg = (struct cmsghdr*)cmsg_buf;
168      cmsg->cmsg_len = CMSG_LEN(sizeof(int));
169      cmsg->cmsg_level = SOL_SOCKET;
170      cmsg->cmsg_type = SCM_RIGHTS; // indicates fd payload data
171      
172      int *data = (int*)CMSG_DATA(cmsg);
173      *data = fd;
174      
175      msg.msg_control = cmsg;
176      msg.msg_controllen = CMSG_LEN(sizeof(int));
177      
178      msg.msg_flags = 0;
179      
180      return sendmsg(sock, &msg, 0);
181  }
182  
183  ssize_t recv_fd(int sock, char *buf, size_t len, int *fd) {
184      // just fill in the msghdr fields like in the send_fd function
185      struct msghdr msg;
186      msg.msg_name = NULL;
187      msg.msg_namelen = 0;
188      
189      struct iovec iov[1];
190      iov[0].iov_base = buf;
191      iov[0].iov_len = len;
192      
193      msg.msg_iov = iov;
194      msg.msg_iovlen = 1;
195      
196      char cmsg_buf[CMSG_SPACE(sizeof(int))];
197      memset(cmsg_buf, 0, CMSG_SPACE(sizeof(int)));
198      
199      struct cmsghdr *cmsg = (struct cmsghdr*)cmsg_buf;
200      cmsg->cmsg_len = CMSG_LEN(sizeof(int));
201      cmsg->cmsg_level = SOL_SOCKET;
202      cmsg->cmsg_type = SCM_RIGHTS;
203      
204      msg.msg_control = cmsg;
205      msg.msg_controllen = CMSG_LEN(sizeof(int));
206      
207      msg.msg_flags = 0;
208      
209      ssize_t r = recvmsg(sock, &msg, 0);
210      if(r != -1) {
211          // get file descriptor
212          int *data = (int*)CMSG_DATA(cmsg);
213          *fd = *data;
214      } else {
215          *fd = -1;
216      }
217      return r;
218  }