Browse Source

Unix domain sockets in Phy<>

Adam Ierymenko 10 years ago
parent
commit
4838cbc350
3 changed files with 186 additions and 13 deletions
  1. 174 13
      osdep/Phy.hpp
  2. 7 0
      selftest.cpp
  3. 5 0
      service/OneService.cpp

+ 174 - 13
osdep/Phy.hpp

@@ -93,6 +93,10 @@ typedef void PhySocket;
  * phyOnTcpClose(PhySocket *sock,void **uptr)
  * phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len)
  * phyOnTcpWritable(PhySocket *sock,void **uptr)
+ * phyOnUnixAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN)
+ * phyOnUnixClose(PhySocket *sock,void **uptr)
+ * phyOnUnixData(PhySocket *sock,void **uptr,void *data,unsigned long len)
+ * phyOnUnixWritable(PhySocket *sock,void **uptr)
  *
  * These templates typically refer to function objects. Templates are used to
  * avoid the call overhead of indirection, which is surprisingly high for high
@@ -105,6 +109,9 @@ typedef void PhySocket;
  * uptr: sockL and uptrL for the listen socket, and sockN and uptrN for
  * the new TCP connection socket that has just been created.
  *
+ * Note that phyOnUnix*() are only required and will only be used on systems
+ * that support Unix domain sockets.
+ *
  * Handlers are always called. On outgoing TCP connection, CONNECT is always
  * called on either success or failure followed by DATA and/or WRITABLE as
  * indicated. On socket close, handlers are called unless close() is told
@@ -129,7 +136,9 @@ private:
 		ZT_PHY_SOCKET_TCP_IN = 0x03,
 		ZT_PHY_SOCKET_TCP_LISTEN = 0x04,
 		ZT_PHY_SOCKET_RAW = 0x05,
-		ZT_PHY_SOCKET_UDP = 0x06
+		ZT_PHY_SOCKET_UDP = 0x06,
+		ZT_PHY_SOCKET_UNIX_IN = 0x07,
+		ZT_PHY_SOCKET_UNIX_LISTEN = 0x08
 	};
 
 	struct PhySocketImpl
@@ -358,6 +367,64 @@ public:
 #endif
 	}
 
+#ifdef __UNIX_LIKE__
+	/**
+	 * Listen for connections on a Unix domain socket
+	 *
+	 * @param path Path to Unix domain socket
+	 * @param uptr Arbitrary pointer to associate
+	 * @return PhySocket or NULL if cannot bind
+	 */
+	inline PhySocket *unixListen(const char *path,void *uptr = (void *)0)
+	{
+		struct sockaddr_un sun;
+
+		if (_socks.size() >= ZT_PHY_MAX_SOCKETS)
+			return (PhySocket *)0;
+
+		memset(&sun,0,sizeof(sun));
+		sun.sun_family = AF_UNIX;
+		if (strlen(path) >= sizeof(sun.sun_path))
+			return (PhySocket *)0;
+		strcpy(sun.sun_path,path);
+
+		ZT_PHY_SOCKFD_TYPE s = ::socket(PF_UNIX,SOCK_STREAM,0);
+		if (!ZT_PHY_SOCKFD_VALID(s))
+			return (PhySocket *)0;
+
+		::fcntl(s,F_SETFL,O_NONBLOCK);
+
+		::unlink(path);
+		if (::bind(s,(struct sockaddr *)&sun,sizeof(struct sockaddr_un)) != 0) {
+			ZT_PHY_CLOSE_SOCKET(s);
+			return (PhySocket *)0;
+		}
+		if (::listen(s,128) != 0) {
+			ZT_PHY_CLOSE_SOCKET(s);
+			return (PhySocket *)0;
+		}
+
+		try {
+			_socks.push_back(PhySocketImpl());
+		} catch ( ... ) {
+			ZT_PHY_CLOSE_SOCKET(s);
+			return (PhySocket *)0;
+		}
+		PhySocketImpl &sws = _socks.back();
+
+		if ((long)s > _nfds)
+			_nfds = (long)s;
+		FD_SET(s,&_readfds);
+		sws.type = ZT_PHY_SOCKET_UNIX_LISTEN;
+		sws.sock = s;
+		sws.uptr = uptr;
+		memset(&(sws.saddr),0,sizeof(struct sockaddr_storage));
+		memcpy(&(sws.saddr),&sun,sizeof(struct sockaddr_un));
+
+		return (PhySocket *)&sws;
+	}
+#endif // __UNIX_LIKE__
+
 	/**
 	 * Bind a local listen socket to listen for new TCP connections
 	 *
@@ -573,6 +640,45 @@ public:
 		return n;
 	}
 
+#ifdef __UNIX_LIKE__
+	/**
+	 * Attempt to send data to a Unix domain socket connection (non-blocking)
+	 *
+	 * If -1 is returned, the socket should no longer be used as it is now
+	 * destroyed. If callCloseHandler is true, the close handler will be
+	 * called before the function returns.
+	 *
+	 * @param sock An open Unix socket (other socket types will fail)
+	 * @param data Data to send
+	 * @param len Length of data
+	 * @param callCloseHandler If true, call close handler on socket closing failure condition (default: true)
+	 * @return Number of bytes actually sent or -1 on fatal error (socket closure)
+	 */
+	inline long unixSend(PhySocket *sock,const void *data,unsigned long len,bool callCloseHandler = true)
+	{
+		PhySocketImpl &sws = *(reinterpret_cast<PhySocketImpl *>(sock));
+		long n = (long)::write(sws.sock,data,len);
+		if (n < 0) {
+			switch(errno) {
+#ifdef EAGAIN
+				case EAGAIN:
+#endif
+#if defined(EWOULDBLOCK) && ( !defined(EAGAIN) || (EWOULDBLOCK != EAGAIN) )
+				case EWOULDBLOCK:
+#endif
+#ifdef EINTR
+				case EINTR:
+#endif
+					return 0;
+				default:
+					this->close(sock,callCloseHandler);
+					return -1;
+			}
+		}
+		return n;
+	}
+#endif // __UNIX_LIKE__
+
 	/**
 	 * Set whether we want to be notified via the TCP writability handler when a socket is writable
 	 *
@@ -727,6 +833,56 @@ public:
 					}
 					break;
 
+				case ZT_PHY_SOCKET_UNIX_IN: {
+#ifdef __UNIX_LIKE__
+					ZT_PHY_SOCKFD_TYPE sock = s->sock; // if closed, s->sock becomes invalid as s is no longer dereferencable
+					if (FD_ISSET(sock,&rfds)) {
+						long n = (long)::read(sock,buf,sizeof(buf));
+						if (n <= 0) {
+							this->close((PhySocket *)&(*s),true);
+						} else {
+							try {
+								_handler->phyOnUnixData((PhySocket *)&(*s),&(s->uptr),(void *)buf,(unsigned long)n);
+							} catch ( ... ) {}
+						}
+					}
+					if ((FD_ISSET(sock,&wfds))&&(FD_ISSET(sock,&_writefds))) {
+						try {
+							_handler->phyOnUnixWritable((PhySocket *)&(*s),&(s->uptr));
+						} catch ( ... ) {}
+					}
+#endif // __UNIX_LIKE__
+				}	break;
+
+				case ZT_PHY_SOCKET_UNIX_LISTEN:
+#ifdef __UNIX_LIKE__
+					if (FD_ISSET(s->sock,&rfds)) {
+						memset(&ss,0,sizeof(ss));
+						socklen_t slen = sizeof(ss);
+						ZT_PHY_SOCKFD_TYPE newSock = ::accept(s->sock,(struct sockaddr *)&ss,&slen);
+						if (ZT_PHY_SOCKFD_VALID(newSock)) {
+							if (_socks.size() >= ZT_PHY_MAX_SOCKETS) {
+								ZT_PHY_CLOSE_SOCKET(newSock);
+							} else {
+								fcntl(newSock,F_SETFL,O_NONBLOCK);
+								_socks.push_back(PhySocketImpl());
+								PhySocketImpl &sws = _socks.back();
+								FD_SET(newSock,&_readfds);
+								if ((long)newSock > _nfds)
+									_nfds = (long)newSock;
+								sws.type = ZT_PHY_SOCKET_UNIX_IN;
+								sws.sock = newSock;
+								sws.uptr = (void *)0;
+								memcpy(&(sws.saddr),&ss,sizeof(struct sockaddr_storage));
+								try {
+									_handler->phyOnUnixAccept((PhySocket *)&(*s),(PhySocket *)&(_socks.back()),&(s->uptr),&(sws.uptr));
+								} catch ( ... ) {}
+							}
+						}
+					}
+#endif // __UNIX_LIKE__
+					break;
+
 				default:
 					break;
 
@@ -758,24 +914,29 @@ public:
 
 		ZT_PHY_CLOSE_SOCKET(sws.sock);
 
-		switch(sws.type) {
-			case ZT_PHY_SOCKET_TCP_OUT_PENDING:
-				if (callHandlers) {
+		if (callHandlers) {
+			switch(sws.type) {
+				case ZT_PHY_SOCKET_TCP_OUT_PENDING:
 					try {
 						_handler->phyOnTcpConnect(sock,&(sws.uptr),false);
 					} catch ( ... ) {}
-				}
-				break;
-			case ZT_PHY_SOCKET_TCP_OUT_CONNECTED:
-			case ZT_PHY_SOCKET_TCP_IN:
-				if (callHandlers) {
+					break;
+				case ZT_PHY_SOCKET_TCP_OUT_CONNECTED:
+				case ZT_PHY_SOCKET_TCP_IN:
 					try {
 						_handler->phyOnTcpClose(sock,&(sws.uptr));
 					} catch ( ... ) {}
-				}
-				break;
-			default:
-				break;
+					break;
+				case ZT_PHY_SOCKET_UNIX_IN:
+#ifdef __UNIX_LIKE__
+					try {
+						_handler->phyOnUnixClose(sock,&(sws.uptr));
+					} catch ( ... ) {}
+#endif // __UNIX_LIKE__
+					break;
+				default:
+					break;
+			}
 		}
 
 		// Causes entry to be deleted from list in poll(), ignored elsewhere

+ 7 - 0
selftest.cpp

@@ -791,6 +791,13 @@ struct TestPhyHandlers
 			testPhyInstance->close(sock,true);
 		}
 	}
+
+#ifdef __UNIX_LIKE__
+	inline void phyOnUnixAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN) {}
+	inline void phyOnUnixClose(PhySocket *sock,void **uptr) {}
+	inline void phyOnUnixData(PhySocket *sock,void **uptr,void *data,unsigned long len) {}
+	inline void phyOnUnixWritable(PhySocket *sock,void **uptr) {}
+#endif // __UNIX_LIKE__
 };
 static int testPhy()
 {

+ 5 - 0
service/OneService.cpp

@@ -924,6 +924,11 @@ public:
 		}
 	}
 
+	inline void phyOnUnixAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN) {}
+	inline void phyOnUnixClose(PhySocket *sock,void **uptr) {}
+	inline void phyOnUnixData(PhySocket *sock,void **uptr,void *data,unsigned long len) {}
+	inline void phyOnUnixWritable(PhySocket *sock,void **uptr) {}
+
 	inline int nodeVirtualNetworkConfigFunction(uint64_t nwid,enum ZT1_VirtualNetworkConfigOperation op,const ZT1_VirtualNetworkConfig *nwc)
 	{
 		Mutex::Lock _l(_taps_m);