From 4ff34a227f6cc4ad5cb11d5fb0c74b5121d4f058 Mon Sep 17 00:00:00 2001
From: Philippe PITTOLI
Date: Sun, 24 Sep 2017 23:46:02 +0200
Subject: [PATCH] passing file descriptors into unix sockets
---
drop/pass-socket_client.c | 270 ++++++++++++++++++++++++++++++++
drop/pass-socket_server.c | 322 ++++++++++++++++++++++++++++++++++++++
2 files changed, 592 insertions(+)
create mode 100644 drop/pass-socket_client.c
create mode 100644 drop/pass-socket_server.c
diff --git a/drop/pass-socket_client.c b/drop/pass-socket_client.c
new file mode 100644
index 0000000..1b96401
--- /dev/null
+++ b/drop/pass-socket_client.c
@@ -0,0 +1,270 @@
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#define USOCK "./.socket"
+
+#define handle_error(msg) \
+ do { log_error (msg); exit(EXIT_FAILURE); } while (0)
+
+#define handle_err(fun,msg)\
+ do { log_error ("%s: file %s line %d %s", fun, __FILE__, __LINE__, msg); } while (0)
+
+void log_format (const char* tag, const char* message, va_list args) {
+ time_t now;
+
+ time(&now);
+
+ char * date =ctime(&now);
+ date[strlen(date) - 1] = '\0';
+ printf("%s:%s: ", date, tag);
+ vprintf(message, args);
+
+ printf("\n");
+}
+
+void log_error (const char* message, ...) {
+ va_list args;
+ va_start(args, message);
+
+ log_format("error", message, args);
+
+ va_end(args);
+}
+
+void log_info (const char* message, ...) {
+ va_list args;
+ va_start(args, message);
+
+ log_format("info", message, args);
+ va_end(args);
+}
+
+void log_debug (const char* message, ...) {
+ va_list args;
+ va_start(args, message);
+
+ log_format("debug", message, args);
+
+ va_end(args);
+}
+
+static
+int recvsockfd (int socket) // receive fd from socket
+{
+ struct msghdr msg = {0};
+
+ /* On Mac OS X, the struct iovec is needed, even if it points to minimal data */
+ char m_buffer[1];
+ struct iovec io = { .iov_base = m_buffer, .iov_len = sizeof(m_buffer) };
+ msg.msg_iov = &io;
+ msg.msg_iovlen = 1;
+
+ char c_buffer[256];
+ msg.msg_control = c_buffer;
+ msg.msg_controllen = sizeof(c_buffer);
+
+ if (recvmsg(socket, &msg, 0) < 0)
+ handle_err ("recvsockfd", "Failed to receive message\n");
+
+ struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
+
+ printf ("About to extract fd\n");
+ int fd;
+ memmove(&fd, CMSG_DATA(cmsg), sizeof(fd));
+ printf ("Extracted fd %d\n", fd);
+
+ return fd;
+}
+
+int usock_connect (int *fd, const char *path)
+{
+ assert (fd != NULL);
+ assert (path != NULL);
+
+ if (fd == NULL) {
+ handle_err ("usock_connect", "fd == NULL");
+ return -1;
+ }
+
+ if (path == NULL) {
+ handle_err ("usock_connect", "path == NULL");
+ return -1;
+ }
+
+ int sfd;
+ struct sockaddr_un my_addr;
+ socklen_t peer_addr_size;
+
+ sfd = socket (AF_UNIX, SOCK_STREAM, 0);
+ if (sfd == -1) {
+ handle_err ("usock_connect", "sfd == -1");
+ return -1;
+ }
+
+ // clear structure
+ memset(&my_addr, 0, sizeof(struct sockaddr_un));
+
+ my_addr.sun_family = AF_UNIX;
+ strncpy(my_addr.sun_path, path, strlen (path));
+
+ peer_addr_size = sizeof(struct sockaddr_un);
+ if(connect(sfd, (struct sockaddr *) &my_addr, peer_addr_size) == -1) {
+ handle_err ("usock_connect", "connect == -1");
+ perror("connect()");
+ exit(errno);
+ }
+
+ *fd = sfd;
+
+ return 0;
+}
+
+int usock_init (int *fd, const char *path)
+{
+ assert (fd != NULL);
+ assert (path != NULL);
+
+ if (fd == NULL) {
+ handle_err ("usock_init", "fd == NULL");
+ return -1;
+ }
+
+ if (path == NULL) {
+ handle_err ("usock_init", "path == NULL");
+ return -1;
+ }
+
+ int sfd;
+ struct sockaddr_un my_addr;
+ socklen_t peer_addr_size;
+
+ sfd = socket (AF_UNIX, SOCK_STREAM, 0);
+ if (sfd == -1) {
+ handle_err ("usock_init", "sfd == -1");
+ return -1;
+ }
+
+ // clear structure
+ memset(&my_addr, 0, sizeof(struct sockaddr_un));
+
+ my_addr.sun_family = AF_UNIX;
+ strncpy(my_addr.sun_path, path, strlen (path));
+
+ // TODO FIXME
+ // delete the unix socket if already created
+
+ peer_addr_size = sizeof(struct sockaddr_un);
+
+ if (bind (sfd, (struct sockaddr *) &my_addr, peer_addr_size) == -1) {
+ handle_err ("usock_init", "bind == -1");
+ perror("bind()");
+ return -1;
+ }
+
+ if (listen (sfd, 5) == -1) {
+ handle_err ("usock_init", "listen == -1");
+ perror("listen()");
+ return -1;
+ }
+
+ *fd = sfd;
+
+ return 0;
+}
+
+int usock_accept (int fd, int *pfd)
+{
+ assert (pfd != NULL);
+
+ if (pfd == NULL) {
+ handle_err ("usock_accept", "pfd == NULL");
+ return -1;
+ }
+
+ struct sockaddr_un peer_addr;
+ memset (&peer_addr, 0, sizeof (struct sockaddr_un));
+ socklen_t peer_addr_size = 0;
+
+ *pfd = accept (fd, (struct sockaddr *) &peer_addr, &peer_addr_size);
+ if (*pfd < 0) {
+ handle_err ("usock_accept", "accept < 0");
+ perror("listen()");
+ return -1;
+ }
+
+ return 0;
+}
+
+int usock_close (int fd)
+{
+ int ret;
+ ret = close (fd);
+ if (ret < 0) {
+ handle_err ("usock_close", "close ret < 0");
+ perror ("closing");
+ }
+ return ret;
+}
+
+int usock_remove (const char *path)
+{
+ return unlink (path);
+}
+
+int main(int argc, char * argv[])
+{
+ int tcpsockfd;
+ int usockfd;
+ // check the number of args on command line
+ if(argc != 1)
+ {
+ fprintf (stderr, "USAGE: %s\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Connection to the unix socket\n");
+
+ // 1. unix socket connection
+ int ret = usock_connect (&usockfd, USOCK);
+ if (ret != 0) {
+ fprintf (stderr, "error: usock_connect\n");
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Receiving the tcp socket\n");
+
+ // 2. receive the tcp socket
+ tcpsockfd = recvsockfd (usockfd);
+
+ printf("Sending 'hello world' to the tcp socket\n");
+
+ // 3. send a message to check the connection is effective
+ if (write(tcpsockfd, "hello world\n", strlen("hello world\n")) == -1) {
+ perror("write");
+ close(tcpsockfd);
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Disconnection of both sockets\n");
+
+ // 4. close sockets
+ close(usockfd);
+ close(tcpsockfd);
+
+ return EXIT_SUCCESS;
+}
diff --git a/drop/pass-socket_server.c b/drop/pass-socket_server.c
new file mode 100644
index 0000000..fc35280
--- /dev/null
+++ b/drop/pass-socket_server.c
@@ -0,0 +1,322 @@
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+#define USOCK "./.socket"
+
+#define handle_error(msg) \
+ do { log_error (msg); exit(EXIT_FAILURE); } while (0)
+
+#define handle_err(fun,msg)\
+ do { log_error ("%s: file %s line %d %s", fun, __FILE__, __LINE__, msg); } while (0)
+
+void log_format (const char* tag, const char* message, va_list args) {
+ time_t now;
+
+ time(&now);
+
+ char * date =ctime(&now);
+ date[strlen(date) - 1] = '\0';
+ printf("%s:%s: ", date, tag);
+ vprintf(message, args);
+
+ printf("\n");
+}
+
+void log_error (const char* message, ...) {
+ va_list args;
+ va_start(args, message);
+
+ log_format("error", message, args);
+
+ va_end(args);
+}
+
+void log_info (const char* message, ...) {
+ va_list args;
+ va_start(args, message);
+
+ log_format("info", message, args);
+ va_end(args);
+}
+
+void log_debug (const char* message, ...) {
+ va_list args;
+ va_start(args, message);
+
+ log_format("debug", message, args);
+
+ va_end(args);
+}
+
+int build_socket (char *servername, char * serverport)
+{
+ int sockfd;
+ struct sockaddr_in6 server;
+ socklen_t addrlen;
+
+ // socket factory
+ if((sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)) == -1)
+ {
+ perror("socket");
+ exit(EXIT_FAILURE);
+ }
+
+ // init remote addr structure and other params
+ server.sin6_family = AF_INET6;
+ server.sin6_port = htons(atoi(serverport));
+ addrlen = sizeof(struct sockaddr_in6);
+
+ // get addr from command line and convert it
+ if(inet_pton(AF_INET6, servername, &server.sin6_addr) != 1)
+ {
+ perror("inet_pton");
+ close(sockfd);
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Trying to connect to the remote host\n");
+ if(connect(sockfd, (struct sockaddr *) &server, addrlen) == -1)
+ {
+ perror("connect");
+ exit(EXIT_FAILURE);
+ }
+
+ return sockfd;
+}
+
+int usock_connect (int *fd, const char *path)
+{
+ assert (fd != NULL);
+ assert (path != NULL);
+
+ if (fd == NULL) {
+ handle_err ("usock_connect", "fd == NULL");
+ return -1;
+ }
+
+ if (path == NULL) {
+ handle_err ("usock_connect", "path == NULL");
+ return -1;
+ }
+
+ int sfd;
+ struct sockaddr_un my_addr;
+ socklen_t peer_addr_size;
+
+ sfd = socket (AF_UNIX, SOCK_STREAM, 0);
+ if (sfd == -1) {
+ handle_err ("usock_connect", "sfd == -1");
+ return -1;
+ }
+
+ // clear structure
+ memset(&my_addr, 0, sizeof(struct sockaddr_un));
+
+ my_addr.sun_family = AF_UNIX;
+ strncpy(my_addr.sun_path, path, strlen (path));
+
+ peer_addr_size = sizeof(struct sockaddr_un);
+ if(connect(sfd, (struct sockaddr *) &my_addr, peer_addr_size) == -1) {
+ handle_err ("usock_connect", "connect == -1");
+ perror("connect()");
+ exit(errno);
+ }
+
+ *fd = sfd;
+
+ return 0;
+}
+
+int usock_init (int *fd, const char *path)
+{
+ assert (fd != NULL);
+ assert (path != NULL);
+
+ if (fd == NULL) {
+ handle_err ("usock_init", "fd == NULL");
+ return -1;
+ }
+
+ if (path == NULL) {
+ handle_err ("usock_init", "path == NULL");
+ return -1;
+ }
+
+ int sfd;
+ struct sockaddr_un my_addr;
+ socklen_t peer_addr_size;
+
+ sfd = socket (AF_UNIX, SOCK_STREAM, 0);
+ if (sfd == -1) {
+ handle_err ("usock_init", "sfd == -1");
+ return -1;
+ }
+
+ // clear structure
+ memset(&my_addr, 0, sizeof(struct sockaddr_un));
+
+ my_addr.sun_family = AF_UNIX;
+ strncpy(my_addr.sun_path, path, strlen (path));
+
+ // TODO FIXME
+ // delete the unix socket if already created
+
+ peer_addr_size = sizeof(struct sockaddr_un);
+
+ if (bind (sfd, (struct sockaddr *) &my_addr, peer_addr_size) == -1) {
+ handle_err ("usock_init", "bind == -1");
+ perror("bind()");
+ return -1;
+ }
+
+ if (listen (sfd, 5) == -1) {
+ handle_err ("usock_init", "listen == -1");
+ perror("listen()");
+ return -1;
+ }
+
+ *fd = sfd;
+
+ return 0;
+}
+
+int usock_accept (int fd, int *pfd)
+{
+ assert (pfd != NULL);
+
+ if (pfd == NULL) {
+ handle_err ("usock_accept", "pfd == NULL");
+ return -1;
+ }
+
+ struct sockaddr_un peer_addr;
+ memset (&peer_addr, 0, sizeof (struct sockaddr_un));
+ socklen_t peer_addr_size = 0;
+
+ *pfd = accept (fd, (struct sockaddr *) &peer_addr, &peer_addr_size);
+ if (*pfd < 0) {
+ handle_err ("usock_accept", "accept < 0");
+ perror("listen()");
+ return -1;
+ }
+
+ return 0;
+}
+
+int usock_close (int fd)
+{
+ int ret;
+ ret = close (fd);
+ if (ret < 0) {
+ handle_err ("usock_close", "close ret < 0");
+ perror ("closing");
+ }
+ return ret;
+}
+
+int usock_remove (const char *path)
+{
+ return unlink (path);
+}
+
+int build_unix_socket (char * path)
+{
+ int remotefd, localfd;
+
+ usock_init (&localfd, path);
+ usock_accept (localfd, &remotefd);
+
+ return remotefd;
+}
+
+static
+void sendfd (int socket, int fd) // send fd by socket
+{
+ struct msghdr msg = { 0 };
+ char buf[CMSG_SPACE(sizeof(fd))];
+ memset(buf, '\0', sizeof(buf));
+
+ /* On Mac OS X, the struct iovec is needed, even if it points to minimal data */
+ struct iovec io = { .iov_base = "", .iov_len = 1 };
+
+ msg.msg_iov = &io;
+ msg.msg_iovlen = 1;
+ msg.msg_control = buf;
+ msg.msg_controllen = sizeof(buf);
+
+ struct cmsghdr * cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+
+ memmove(CMSG_DATA(cmsg), &fd, sizeof(fd));
+
+ msg.msg_controllen = cmsg->cmsg_len;
+
+ if (sendmsg(socket, &msg, 0) < 0)
+ handle_err("sendfd", "Failed to send message\n");
+}
+
+int main(int argc, char * argv[])
+{
+ // check the number of args on command line
+ if(argc != 3)
+ {
+ fprintf (stderr, "USAGE: %s @server port_num\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ char *servername = argv[1];
+ char *serverport = argv[2];
+
+ printf("Connection to the tcp socket\n");
+ // 1. socket creation (tcp), connection to the server
+ int sockfd = build_socket (servername, serverport);
+
+ printf("Sending 'coucou' to the tcp socket\n");
+ // send a message to check the connection is effective
+ if (write(sockfd, "coucou\n", strlen("coucou\n")) == -1) {
+ perror("write");
+ close(sockfd);
+ exit(EXIT_FAILURE);
+ }
+
+ printf ("Connection to the unix socket\n");
+ // 2. socket creation (unix)
+ int usockfd = build_unix_socket (USOCK);
+
+ printf ("Passing the tcp socket to the unix socket\n");
+ // 3. tcp socket passing to the client
+ sendfd (usockfd, sockfd);
+
+ // send a message to check the connection is (still) effective
+ if (write(sockfd, "bye\n", strlen("bye\n")) == -1) {
+ perror("write");
+ close(sockfd);
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Disconnection\n");
+
+ // 4. close sockets
+ close(usockfd);
+ close(sockfd);
+
+ usock_remove (USOCK);
+
+ return EXIT_SUCCESS;
+}