Unix domain sockets in Phy<>

This commit is contained in:
Adam Ierymenko 2015-09-02 14:32:01 -07:00
parent b11ffc9635
commit 4838cbc350
3 changed files with 186 additions and 13 deletions

View File

@ -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

View File

@ -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()
{

View File

@ -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);