diff --git a/make-linux.mk b/make-linux.mk index 35e7c07e5..e30f210cf 100644 --- a/make-linux.mk +++ b/make-linux.mk @@ -75,7 +75,7 @@ ifeq ($(ZT_DEBUG),1) DEFS+=-DZT_TRACE CFLAGS+=-Wall -g -pthread $(INCLUDES) $(DEFS) CXXFLAGS+=-Wall -g -pthread $(INCLUDES) $(DEFS) - LDFLAGS= + LDFLAGS=-ldl STRIP=echo # The following line enables optimization for the crypto code, since # C25519 in particular is almost UNUSABLE in -O0 even on a 3ghz box! @@ -85,7 +85,7 @@ else CFLAGS+=-Wall -fPIE -fvisibility=hidden -pthread $(INCLUDES) -DNDEBUG $(DEFS) CXXFLAGS?=-O3 -fstack-protector CXXFLAGS+=-Wall -fPIE -fvisibility=hidden -fno-rtti -pthread $(INCLUDES) -DNDEBUG $(DEFS) - LDFLAGS=-pie -Wl,-z,relro,-z,now + LDFLAGS=-ldl -pie -Wl,-z,relro,-z,now STRIP=strip --strip-all endif diff --git a/netcon/Intercept.h b/netcon/Intercept.h index 97075dcbe..30d118cf9 100755 --- a/netcon/Intercept.h +++ b/netcon/Intercept.h @@ -175,6 +175,7 @@ struct shutdown_st /* LWIP error beautification */ +/* const char *lwiperror(int n) { switch(n) @@ -215,5 +216,6 @@ const char *lwiperror(int n) return "UNKNOWN_RET_VAL"; } } +*/ #endif diff --git a/netcon/NetconEthernetTap.cpp b/netcon/NetconEthernetTap.cpp index 723a5f55e..d3deb8ce3 100644 --- a/netcon/NetconEthernetTap.cpp +++ b/netcon/NetconEthernetTap.cpp @@ -29,6 +29,8 @@ #include #include +#include + #include "NetconEthernetTap.hpp" @@ -43,7 +45,7 @@ #include "lwip/ip_frag.h" #include "LWIPStack.hpp" -#include "NetconService.h" +#include "NetconService.hpp" #include "Intercept.h" #include "NetconUtilities.hpp" @@ -114,6 +116,7 @@ bool NetconEthernetTap::addIp(const InetAddress &ip) // TODO: alloc IP in LWIP //netif_set_addr(netif, ipaddr, netmask, gw); } + return true; // TODO: what is exapected? } bool NetconEthernetTap::removeIp(const InetAddress &ip) @@ -194,7 +197,7 @@ NetconClient *NetconEthernetTap::getClientByPCB(struct tcp_pcb *pcb) void NetconEthernetTap::closeClient(NetconClient *client) { // erase from clients vector - client->close(); + client->closeClient(); } @@ -219,7 +222,7 @@ void NetconEthernetTap::threadMain() unsigned long since_etharp; struct timeval tv; - struct timeval tv_sel; + //struct timeval tv_sel; while (_run) { gettimeofday(&tv, NULL); @@ -267,7 +270,7 @@ void NetconEthernetTap::phyOnUnixAccept(PhySocket *sockL,PhySocket *sockN,void * void NetconEthernetTap::phyOnUnixClose(PhySocket *sock,void **uptr) { - ((NetconClient*)*uptr)->close(); + ((NetconClient*)*uptr)->closeClient(); } void NetconEthernetTap::phyOnUnixData(PhySocket *sock,void **uptr,void *data,unsigned long len) @@ -313,7 +316,7 @@ void NetconEthernetTap::phyOnUnixData(PhySocket *sock,void **uptr,void *data,uns handle_bind(client, &bind_rpc); break; case RPC_KILL_INTERCEPT: - client->close(); + client->closeClient(); break; case RPC_CONNECT: struct connect_st connect_rpc; @@ -361,22 +364,17 @@ int NetconEthernetTap::send_return_value(NetconClient *client, int retval) --------------------------------- LWIP callbacks ------------------------------- ------------------------------------------------------------------------------*/ -err_t NetconEthernetTap::nc_poll(void* arg, struct tcp_pcb *tpcb) -{ - NetconConnection *c = getConnectionByPCB(tpcb); // TODO: make sure this works, if not, use arg to look up the connection - if(c) - handle_write(c); - return ERR_OK; -} - err_t NetconEthernetTap::nc_recved(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) { + Larg *l = (Larg*)arg; + NetconConnection *c = l->tap->getConnectionByPCB(tpcb); + NetconEthernetTap *tap = l->tap; + int n; struct pbuf* q = p; - NetconConnection *c = getConnectionByPCB(tpcb); // TODO: make sure this works, if not, use arg as "buf sock" - int our_fd = _phy.getDescriptor(c->sock); + int our_fd = tap->_phy.getDescriptor(c->sock); if(c) { //dwr(c->owner->tid, "nc_recved(%d)\n", (intptr_t)arg); @@ -386,47 +384,45 @@ err_t NetconEthernetTap::nc_recved(void *arg, struct tcp_pcb *tpcb, struct pbuf return ERR_OK; // ? } if(p == NULL) { - //dwr(c->owner->tid, "nc_recved() = %s\n", lwiperror(err)); - if(c) //dwr(c->owner->tid, "nc_recved()\n"); if(c) { //dwr(c->owner->tid, "closing connection\n"); nc_close(tpcb); close(our_fd); /* TODO: Check logic */ //nc_service->remove_connection(c); - c->owner->close(c); + c->owner->closeConnection(c); } else { - //dwr(-1, "can't locate connection via (arg)\n"); + // can't locate connection via (arg) } return err; } q = p; while(p != NULL) { // Cycle through pbufs and write them to the socket - //dwr(c->owner->tid, "writing data to mapped sock (%d)\n", c->our_fd); if(p->len <= 0) break; // ? if((n = write(our_fd, p->payload, p->len)) > 0) { if(n < p->len) { - //dwr(c->owner->tid, "ERROR: unable to write entire pbuf to buffer\n"); + // ERROR: unable to write entire pbuf to buffer } - lwipstack->tcp_recved(tpcb, n); + tap->lwipstack->tcp_recved(tpcb, n); } else { - //dwr(c->owner->tid, "ERROR: No data written to intercept buffer.\n"); + // Error: No data written to intercept buffer } p = p->next; } - lwipstack->pbuf_free(q); /* free pbufs */ + tap->lwipstack->pbuf_free(q); // free pbufs return ERR_OK; } void NetconEthernetTap::nc_err(void *arg, err_t err) { - NetconConnection *c = getConnectionByThisFD((intptr)arg); + Larg *l = (Larg*)arg; + NetconEthernetTap *tap = l->tap; + NetconConnection *c = tap->getConnectionByThisFD(tap->_phy.getDescriptor(l->sock)); if(c) { - //nc_service->remove_connection(c); - c->owner->close(c); + c->owner->closeConnection(c); //tcp_close(c->pcb); } else { @@ -436,13 +432,14 @@ void NetconEthernetTap::nc_err(void *arg, err_t err) void NetconEthernetTap::nc_close(struct tcp_pcb* tpcb) { - //NetconConnection *c = getConnectionByPCB(tpcb); + /* lwipstack->tcp_arg(tpcb, NULL); lwipstack->tcp_sent(tpcb, NULL); lwipstack->tcp_recv(tpcb, NULL); lwipstack->tcp_err(tpcb, NULL); lwipstack->tcp_poll(tpcb, NULL, 0); lwipstack->tcp_close(tpcb); + */ } err_t NetconEthernetTap::nc_send(struct tcp_pcb *tpcb) @@ -452,17 +449,17 @@ err_t NetconEthernetTap::nc_send(struct tcp_pcb *tpcb) err_t NetconEthernetTap::nc_sent(void* arg, struct tcp_pcb *tpcb, u16_t len) { - //NetconConnection *c = _phy->getConnectionByPCB(tpcb); - //if(c) - //c->data_sent += len; return len; } err_t NetconEthernetTap::nc_connected(void *arg, struct tcp_pcb *tpcb, err_t err) { - for(int i=0; itap; + + for(size_t i=0; iclients.size(); i++) { + if(tap->clients[i]->containsPCB(tpcb)) { + tap->send_return_value(tap->clients[i],err); } } return err; @@ -481,14 +478,16 @@ void NetconEthernetTap::handle_bind(NetconClient *client, struct bind_st *bind_r ip_addr_t conn_addr; IP4_ADDR(&conn_addr, 192,168,0,2); + /* int ip = connaddr->sin_addr.s_addr; unsigned char bytes[4]; bytes[0] = ip & 0xFF; bytes[1] = (ip >> 8) & 0xFF; bytes[2] = (ip >> 16) & 0xFF; bytes[3] = (ip >> 24) & 0xFF; - // "binding to: %d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3] - NetconConnection *c = client->getConnectionByTheirFD(bind_rpc->sockfd); + "binding to: %d.%d.%d.%d", bytes[0], bytes[1], bytes[2], bytes[3] + */ + NetconConnection *c = client->getConnectionByTheirFD(bind_rpc->sockfd); if(c) { if(c->pcb->state == CLOSED){ int err = lwipstack->tcp_bind(c->pcb, &conn_addr, conn_port); @@ -520,8 +519,7 @@ void NetconEthernetTap::handle_listen(NetconClient *client, struct listen_st *li if(listening_pcb != NULL) { c->pcb = listening_pcb; lwipstack->tcp_accept(listening_pcb, nc_accept); - int our_fd = _phy.getDescriptor(c->sock); - lwipstack->tcp_arg(listening_pcb, (void*)(intptr_t)our_fd); + lwipstack->tcp_arg(listening_pcb, new Larg(this, c->sock)); client->waiting_for_retval=true; } else { @@ -565,14 +563,13 @@ void NetconEthernetTap::handle_connect(NetconClient *client, struct connect_st* int conn_port = lwipstack->ntohs(connaddr->sin_port); ip_addr_t conn_addr = convert_ip((struct sockaddr_in *)&connect_rpc->__addr); NetconConnection *c = client->getConnectionByTheirFD(connect_rpc->__fd); - int our_fd = _phy.getDescriptor(c->sock); if(c!= NULL) { lwipstack->tcp_sent(c->pcb, nc_sent); // FIXME: Move? lwipstack->tcp_recv(c->pcb, nc_recved); lwipstack->tcp_err(c->pcb, nc_err); lwipstack->tcp_poll(c->pcb, nc_poll, APPLICATION_POLL_FREQ); - lwipstack->tcp_arg(c->pcb,(void*)(intptr_t)our_fd); + lwipstack->tcp_arg(c->pcb, new Larg(this, c->sock)); int err = 0; if((err = lwipstack->tcp_connect(c->pcb,&conn_addr,conn_port, nc_connected)) < 0) diff --git a/netcon/NetconEthernetTap.hpp b/netcon/NetconEthernetTap.hpp index c79a11c0d..3eb39eb66 100644 --- a/netcon/NetconEthernetTap.hpp +++ b/netcon/NetconEthernetTap.hpp @@ -44,7 +44,8 @@ #include "../osdep/Thread.hpp" #include "../osdep/Phy.hpp" -#include "NetconService.h" +#include "NetconService.hpp" +#include "NetconUtilities.hpp" namespace ZeroTier { @@ -84,6 +85,15 @@ public: throw(); private: + + // RPC handlers (from NetconIntercept) + void handle_bind(NetconClient *client, struct bind_st *bind_rpc); + void handle_listen(NetconClient *client, struct listen_st *listen_rpc); + void handle_retval(NetconClient *client, unsigned char* buf); + void handle_socket(NetconClient *client, struct socket_st* socket_rpc); + void handle_connect(NetconClient *client, struct connect_st* connect_rpc); + void handle_write(NetconConnection *c); + void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *from,void *data,unsigned long len); void phyOnTcpConnect(PhySocket *sock,void **uptr,bool success); void phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from); @@ -95,10 +105,26 @@ private: void phyOnUnixData(PhySocket *sock,void **uptr,void *data,unsigned long len); void phyOnUnixWritable(PhySocket *sock,void **uptr); + void phyOnSocketPairEndpointClose(void *sock, void **uptr); + void phyOnSocketPairEndpointData(PhySocket *sock, void **uptr, void *buf, unsigned long n); + void phyOnSocketPairEndpointWritable(PhySocket *sock, void **uptr); + + + int send_return_value(NetconClient *client, int retval); // For LWIP Callbacks - static err_t nc_poll(void *arg, struct tcp_pcb *tpcb); + static err_t nc_poll(void* arg, struct tcp_pcb *tpcb) + { + Larg *l = (Larg*)arg; + NetconConnection *c = l->tap->getConnectionByPCB(tpcb); + NetconEthernetTap *tap = l->tap; + if(c) + tap->handle_write(c); + return ERR_OK; + } + + static err_t nc_accept(void *arg, struct tcp_pcb *newpcb, err_t err); static err_t nc_recved(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err); static void nc_err(void *arg, err_t err); @@ -107,13 +133,7 @@ private: static err_t nc_sent(void *arg, struct tcp_pcb *tpcb, u16_t len); static err_t nc_connected(void *arg, struct tcp_pcb *tpcb, err_t err); - // RPC handlers (from NetconIntercept) - void handle_bind(NetconClient *client, struct bind_st *bind_rpc); - void handle_listen(NetconClient *client, struct listen_st *listen_rpc); - void handle_retval(NetconClient *client, unsigned char* buf); - void handle_socket(NetconClient *client, struct socket_st* socket_rpc); - void handle_connect(NetconClient *client, struct connect_st* connect_rpc); - void handle_write(NetconConnection *c); + void (*_handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int); void *_arg; diff --git a/netcon/NetconService.h b/netcon/NetconService.hpp similarity index 84% rename from netcon/NetconService.h rename to netcon/NetconService.hpp index 3ca37b112..93431f759 100644 --- a/netcon/NetconService.h +++ b/netcon/NetconService.hpp @@ -29,16 +29,29 @@ #include #include "../osdep/Phy.hpp" +#include "NetconEthernetTap.hpp" #include "Intercept.h" #include "LWIPStack.hpp" -#ifndef _NETCON_SERVICE_H_ -#define _NETCON_SERVICE_H_ +#ifndef _NETCON_SERVICE_HPP +#define _NETCON_SERVICE_HPP using namespace std; namespace ZeroTier { + + class NetconEthernetTap; + + // Helper class for passing reference to Phy to LWIP callbacks + class Larg + { + public: + NetconEthernetTap *tap; + PhySocket *sock; + Larg(NetconEthernetTap *_tap, PhySocket *_sock) : tap(_tap), sock(_sock) {} + }; + enum NetconConnectionType { RPC, BUFFER }; // prototypes @@ -103,7 +116,7 @@ namespace ZeroTier { NetconConnection *getConnectionByPCB(struct tcp_pcb *pcb) { for(size_t i=0; ipcb = pcb) { return connections[i]; } + if(connections[i]->pcb == pcb) { return connections[i]; } } return NULL; } @@ -111,17 +124,23 @@ namespace ZeroTier { NetconConnection *containsPCB(struct tcp_pcb *pcb) { for(size_t i=0; ipcb = pcb) { return connections[i]; } + if(connections[i]->pcb == pcb) { return connections[i]; } } return NULL; } - void close() + void closeConnection(NetconConnection *c) { // close all connections // -- pcb // -- PhySocket } + + void closeClient() + { + + } }; } // namespace ZeroTier + #endif diff --git a/netcon/NetconUtilities.cpp b/netcon/NetconUtilities.cpp index 8f501ce2e..bb870669b 100644 --- a/netcon/NetconUtilities.cpp +++ b/netcon/NetconUtilities.cpp @@ -7,126 +7,127 @@ #ifndef _NETCON_UTILITIES_CPP #define _NETCON_UTILITIES_CPP - - -ip_addr_t convert_ip(struct sockaddr_in * addr) +namespace ZeroTier { - ip_addr_t conn_addr; - struct sockaddr_in *ipv4 = addr; - short a = ip4_addr1(&(ipv4->sin_addr)); - short b = ip4_addr2(&(ipv4->sin_addr)); - short c = ip4_addr3(&(ipv4->sin_addr)); - short d = ip4_addr4(&(ipv4->sin_addr)); - IP4_ADDR(&conn_addr, a,b,c,d); - return conn_addr; -} - -ip_addr_t ip_addr_sin(register struct sockaddr_in *sin) { - ip_addr_t ip; - *((struct sockaddr_in*) &ip) = *sin; - return ip; -} - -// Functions used to pass file descriptors between processes - -ssize_t sock_fd_write(int sock, int fd); -ssize_t sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd); - -ssize_t sock_fd_write(int sock, int fd) -{ - ssize_t size; - struct msghdr msg; - struct iovec iov; - char buf = '\0'; - int buflen = 1; - - union + ip_addr_t convert_ip(struct sockaddr_in * addr) { - struct cmsghdr cmsghdr; - char control[CMSG_SPACE(sizeof (int))]; - } cmsgu; - struct cmsghdr *cmsg; - - iov.iov_base = &buf; - iov.iov_len = buflen; - - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - - if (fd != -1) { - msg.msg_control = cmsgu.control; - msg.msg_controllen = sizeof(cmsgu.control); - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_len = CMSG_LEN(sizeof (int)); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - *((int *) CMSG_DATA(cmsg)) = fd; - } else { - msg.msg_control = NULL; - msg.msg_controllen = 0; + ip_addr_t conn_addr; + struct sockaddr_in *ipv4 = addr; + short a = ip4_addr1(&(ipv4->sin_addr)); + short b = ip4_addr2(&(ipv4->sin_addr)); + short c = ip4_addr3(&(ipv4->sin_addr)); + short d = ip4_addr4(&(ipv4->sin_addr)); + IP4_ADDR(&conn_addr, a,b,c,d); + return conn_addr; } - size = sendmsg(sock, &msg, 0); - if (size < 0) - perror ("sendmsg"); - return size; -} + ip_addr_t ip_addr_sin(register struct sockaddr_in *sin) { + ip_addr_t ip; + *((struct sockaddr_in*) &ip) = *sin; + return ip; + } + + // Functions used to pass file descriptors between processes + + ssize_t sock_fd_write(int sock, int fd); + ssize_t sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd); + + ssize_t sock_fd_write(int sock, int fd) + { + ssize_t size; + struct msghdr msg; + struct iovec iov; + char buf = '\0'; + int buflen = 1; + + union + { + struct cmsghdr cmsghdr; + char control[CMSG_SPACE(sizeof (int))]; + } cmsgu; + struct cmsghdr *cmsg; + + iov.iov_base = &buf; + iov.iov_len = buflen; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + if (fd != -1) { + msg.msg_control = cmsgu.control; + msg.msg_controllen = sizeof(cmsgu.control); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof (int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + *((int *) CMSG_DATA(cmsg)) = fd; + } else { + msg.msg_control = NULL; + msg.msg_controllen = 0; + } + + size = sendmsg(sock, &msg, 0); + if (size < 0) + perror ("sendmsg"); + return size; + } -ssize_t sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd) -{ - ssize_t size; + ssize_t sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd) + { + ssize_t size; - if (fd) { - struct msghdr msg; - struct iovec iov; - union - { - struct cmsghdr cmsghdr; - char control[CMSG_SPACE(sizeof (int))]; - } cmsgu; - struct cmsghdr *cmsg; + if (fd) { + struct msghdr msg; + struct iovec iov; + union + { + struct cmsghdr cmsghdr; + char control[CMSG_SPACE(sizeof (int))]; + } cmsgu; + struct cmsghdr *cmsg; - iov.iov_base = buf; - iov.iov_len = bufsize; + iov.iov_base = buf; + iov.iov_len = bufsize; - msg.msg_name = NULL; - msg.msg_namelen = 0; - msg.msg_iov = &iov; - msg.msg_iovlen = 1; - msg.msg_control = cmsgu.control; - msg.msg_controllen = sizeof(cmsgu.control); - size = recvmsg (sock, &msg, 0); - if (size < 0) { - perror ("recvmsg"); - exit(1); - } - cmsg = CMSG_FIRSTHDR(&msg); - if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(int))) { - if (cmsg->cmsg_level != SOL_SOCKET) { - fprintf (stderr, "invalid cmsg_level %d\n", - cmsg->cmsg_level); - exit(1); - } - if (cmsg->cmsg_type != SCM_RIGHTS) { - fprintf (stderr, "invalid cmsg_type %d\n", - cmsg->cmsg_type); - exit(1); - } + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = cmsgu.control; + msg.msg_controllen = sizeof(cmsgu.control); + size = recvmsg (sock, &msg, 0); + if (size < 0) { + perror ("recvmsg"); + exit(1); + } + cmsg = CMSG_FIRSTHDR(&msg); + if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(int))) { + if (cmsg->cmsg_level != SOL_SOCKET) { + fprintf (stderr, "invalid cmsg_level %d\n", + cmsg->cmsg_level); + exit(1); + } + if (cmsg->cmsg_type != SCM_RIGHTS) { + fprintf (stderr, "invalid cmsg_type %d\n", + cmsg->cmsg_type); + exit(1); + } - *fd = *((int *) CMSG_DATA(cmsg)); - } else - *fd = -1; - } else { - size = read (sock, buf, bufsize); - if (size < 0) { - perror("read"); - exit(1); - } - } - return size; + *fd = *((int *) CMSG_DATA(cmsg)); + } else + *fd = -1; + } else { + size = read (sock, buf, bufsize); + if (size < 0) { + perror("read"); + exit(1); + } + } + return size; + } } #endif diff --git a/netcon/NetconUtilities.hpp b/netcon/NetconUtilities.hpp index 56b0ed945..5ce062f35 100644 --- a/netcon/NetconUtilities.hpp +++ b/netcon/NetconUtilities.hpp @@ -2,10 +2,12 @@ #ifndef _NETCON_UTILITIES_H #define _NETCON_UTILITIES_H -ip_addr_t convert_ip(struct sockaddr_in * addr); -ip_addr_t ip_addr_sin(register struct sockaddr_in *sin); - -ssize_t sock_fd_write(int sock, int fd); -ssize_t sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd); +namespace ZeroTier +{ + ip_addr_t convert_ip(struct sockaddr_in * addr); + ip_addr_t ip_addr_sin(register struct sockaddr_in *sin); + ssize_t sock_fd_write(int sock, int fd); + ssize_t sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd); +} #endif diff --git a/osdep/Phy.hpp b/osdep/Phy.hpp index e4209d3cd..7f790e5d2 100644 --- a/osdep/Phy.hpp +++ b/osdep/Phy.hpp @@ -289,7 +289,7 @@ public: * @param uptr Pointer to associate with local side of socket pair * @return PhySocket for local side of socket pair */ - static inline PhySocket *createSocketPair(ZT_PHY_SOCKFD_TYPE &remoteSocketDescriptor,void *uptr = (void *)0) + inline PhySocket *createSocketPair(ZT_PHY_SOCKFD_TYPE &remoteSocketDescriptor,void *uptr = (void *)0) { if (_socks.size() >= ZT_PHY_MAX_SOCKETS) return (PhySocket *)0;