Dual-stack related bugfixes for ipv4/ipv6
This commit is contained in:
@@ -184,6 +184,11 @@ namespace ZeroTier {
|
|||||||
* SEE: ZT_HOUSEKEEPING_INTERVAL in ZeroTierSDK.h
|
* SEE: ZT_HOUSEKEEPING_INTERVAL in ZeroTierSDK.h
|
||||||
*/
|
*/
|
||||||
std::time_t last_housekeeping_ts;
|
std::time_t last_housekeeping_ts;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Whether our picoTCP device has been initialized
|
||||||
|
*/
|
||||||
|
bool picodev_initialized = false;
|
||||||
|
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
/* Guarded RX Frame Buffer for picoTCP */
|
/* Guarded RX Frame Buffer for picoTCP */
|
||||||
|
|||||||
@@ -429,7 +429,7 @@ Darwin:
|
|||||||
[ ] [ECONNRESET] Remote host reset the connection request.
|
[ ] [ECONNRESET] Remote host reset the connection request.
|
||||||
*/
|
*/
|
||||||
int zts_connect(ZT_CONNECT_SIG) {
|
int zts_connect(ZT_CONNECT_SIG) {
|
||||||
//DEBUG_INFO("fd = %d", fd);
|
DEBUG_INFO("fd = %d", fd);
|
||||||
int err = 0;
|
int err = 0;
|
||||||
if(fd < 0) {
|
if(fd < 0) {
|
||||||
errno = EBADF;
|
errno = EBADF;
|
||||||
@@ -460,8 +460,8 @@ int zts_connect(ZT_CONNECT_SIG) {
|
|||||||
// TODO: This is a hack, determine a proper way to do this
|
// TODO: This is a hack, determine a proper way to do this
|
||||||
iaddr.fromString(ipstr + std::string("/88"));
|
iaddr.fromString(ipstr + std::string("/88"));
|
||||||
}
|
}
|
||||||
// DEBUG_INFO("ipstr= %s", ipstr);
|
DEBUG_INFO("ipstr= %s", ipstr);
|
||||||
// DEBUG_INFO("iaddr= %s", iaddr.toString().c_str());
|
DEBUG_INFO("iaddr= %s", iaddr.toString().c_str());
|
||||||
tap = zt1Service->getTap(iaddr);
|
tap = zt1Service->getTap(iaddr);
|
||||||
if(!tap) {
|
if(!tap) {
|
||||||
DEBUG_ERROR("no route to host");
|
DEBUG_ERROR("no route to host");
|
||||||
@@ -471,7 +471,7 @@ int zts_connect(ZT_CONNECT_SIG) {
|
|||||||
else {
|
else {
|
||||||
// pointer to tap we use in callbacks from the stack
|
// pointer to tap we use in callbacks from the stack
|
||||||
conn->picosock->priv = new ZeroTier::ConnectionPair(tap, conn);
|
conn->picosock->priv = new ZeroTier::ConnectionPair(tap, conn);
|
||||||
// DEBUG_INFO("found appropriate SocketTap");
|
DEBUG_INFO("found appropriate SocketTap");
|
||||||
// Semantically: tap->stack->connect
|
// Semantically: tap->stack->connect
|
||||||
err = tap->Connect(conn, fd, addr, addrlen);
|
err = tap->Connect(conn, fd, addr, addrlen);
|
||||||
if(err == 0) {
|
if(err == 0) {
|
||||||
@@ -558,7 +558,7 @@ Darwin:
|
|||||||
address space.
|
address space.
|
||||||
*/
|
*/
|
||||||
int zts_bind(ZT_BIND_SIG) {
|
int zts_bind(ZT_BIND_SIG) {
|
||||||
DEBUG_INFO("fd = %d", fd);
|
//DEBUG_EXTRA("fd = %d", fd);
|
||||||
int err = 0;
|
int err = 0;
|
||||||
if(fd < 0) {
|
if(fd < 0) {
|
||||||
errno = EBADF;
|
errno = EBADF;
|
||||||
@@ -627,7 +627,7 @@ Darwin:
|
|||||||
[ ] [EOPNOTSUPP] The socket is not of a type that supports the operation listen().
|
[ ] [EOPNOTSUPP] The socket is not of a type that supports the operation listen().
|
||||||
*/
|
*/
|
||||||
int zts_listen(ZT_LISTEN_SIG) {
|
int zts_listen(ZT_LISTEN_SIG) {
|
||||||
DEBUG_INFO("fd = %d", fd);
|
DEBUG_EXTRA("fd = %d", fd);
|
||||||
int err = 0;
|
int err = 0;
|
||||||
if(fd < 0) {
|
if(fd < 0) {
|
||||||
errno = EBADF;
|
errno = EBADF;
|
||||||
@@ -672,7 +672,7 @@ Darwin:
|
|||||||
[ ] [ENFILE] The system file table is full.
|
[ ] [ENFILE] The system file table is full.
|
||||||
*/
|
*/
|
||||||
int zts_accept(ZT_ACCEPT_SIG) {
|
int zts_accept(ZT_ACCEPT_SIG) {
|
||||||
DEBUG_INFO("fd = %d", fd);
|
DEBUG_EXTRA("fd = %d", fd);
|
||||||
int err = 0;
|
int err = 0;
|
||||||
if(fd < 0) {
|
if(fd < 0) {
|
||||||
errno = EBADF;
|
errno = EBADF;
|
||||||
@@ -861,7 +861,7 @@ Darwin:
|
|||||||
|
|
||||||
int zts_close(ZT_CLOSE_SIG)
|
int zts_close(ZT_CLOSE_SIG)
|
||||||
{
|
{
|
||||||
//DEBUG_INFO("fd = %d", fd);
|
//DEBUG_EXTRA("fd = %d", fd);
|
||||||
int err = 0;
|
int err = 0;
|
||||||
if(fd < 0) {
|
if(fd < 0) {
|
||||||
errno = EBADF;
|
errno = EBADF;
|
||||||
@@ -911,7 +911,7 @@ int zts_close(ZT_CLOSE_SIG)
|
|||||||
conn = p->first;
|
conn = p->first;
|
||||||
ZeroTier::SocketTap *tap = p->second;
|
ZeroTier::SocketTap *tap = p->second;
|
||||||
|
|
||||||
DEBUG_ERROR("close...., conn = %p, fd = %d", conn, fd);
|
//DEBUG_ERROR("close...., conn = %p, fd = %d", conn, fd);
|
||||||
|
|
||||||
// For cases where data might still need to pass through the library
|
// For cases where data might still need to pass through the library
|
||||||
// before socket closure
|
// before socket closure
|
||||||
@@ -1016,12 +1016,12 @@ ssize_t zts_recvmsg(ZT_RECVMSG_SIG)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int zts_read(ZT_READ_SIG) {
|
int zts_read(ZT_READ_SIG) {
|
||||||
DEBUG_INFO("fd = %d", fd);
|
//DEBUG_EXTRA("fd = %d", fd);
|
||||||
return read(fd, buf, len);
|
return read(fd, buf, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
int zts_write(ZT_WRITE_SIG) {
|
int zts_write(ZT_WRITE_SIG) {
|
||||||
DEBUG_INFO("fd = %d", fd);
|
//DEBUG_EXTRA("fd = %d", fd);
|
||||||
return write(fd, buf, len);
|
return write(fd, buf, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -83,64 +83,40 @@ struct pico_socket * pico_socket_accept(PICO_SOCKET_ACCEPT_SIG);
|
|||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
// FIXME: Determine why stack interrupt code fails when picodev is a mmember of a SocketTap
|
// TODO: Determine why stack interrupt code fails when picodev is a mmember of a SocketTap
|
||||||
|
|
||||||
struct pico_device picodev;
|
struct pico_device picodev;
|
||||||
|
|
||||||
bool picoTCP::pico_init_interface(SocketTap *tap, const InetAddress &ip)
|
bool picoTCP::pico_init_interface(SocketTap *tap, const InetAddress &ip)
|
||||||
{
|
{
|
||||||
//DEBUG_INFO();
|
|
||||||
if (std::find(tap->_ips.begin(),tap->_ips.end(),ip) == tap->_ips.end()) {
|
if (std::find(tap->_ips.begin(),tap->_ips.end(),ip) == tap->_ips.end()) {
|
||||||
tap->_ips.push_back(ip);
|
tap->_ips.push_back(ip);
|
||||||
std::sort(tap->_ips.begin(),tap->_ips.end());
|
std::sort(tap->_ips.begin(),tap->_ips.end());
|
||||||
if(ip.isV4())
|
|
||||||
|
if(!tap->picodev_initialized)
|
||||||
{
|
{
|
||||||
//tap->picodev = new struct pico_device;
|
picodev.send = pico_eth_send; // tx
|
||||||
struct pico_ip4 ipaddr, netmask;
|
|
||||||
ipaddr.addr = *((uint32_t *)ip.rawIpData());
|
|
||||||
netmask.addr = *((uint32_t *)ip.netmask().rawIpData());
|
|
||||||
picodev.send = pico_eth_send; // tx
|
|
||||||
picodev.poll = pico_eth_poll; // rx
|
picodev.poll = pico_eth_poll; // rx
|
||||||
picodev.mtu = tap->_mtu;
|
picodev.mtu = tap->_mtu;
|
||||||
picodev.tap = tap;
|
picodev.tap = tap;
|
||||||
uint8_t mac[PICO_SIZE_ETH];
|
uint8_t mac[PICO_SIZE_ETH];
|
||||||
tap->_mac.copyTo(mac, PICO_SIZE_ETH);
|
tap->_mac.copyTo(mac, PICO_SIZE_ETH);
|
||||||
if(pico_device_init(&picodev, "p4", mac) != 0) {
|
if(pico_device_init(&picodev, "pz", mac) != 0) {
|
||||||
DEBUG_ERROR("dev init failed");
|
DEBUG_ERROR("dev init failed");
|
||||||
delete &picodev;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
tap->picodev_initialized = true;
|
||||||
|
}
|
||||||
|
if(ip.isV4())
|
||||||
|
{
|
||||||
|
struct pico_ip4 ipaddr, netmask;
|
||||||
|
ipaddr.addr = *((uint32_t *)ip.rawIpData());
|
||||||
|
netmask.addr = *((uint32_t *)ip.netmask().rawIpData());
|
||||||
pico_ipv4_link_add(&picodev, ipaddr, netmask);
|
pico_ipv4_link_add(&picodev, ipaddr, netmask);
|
||||||
DEBUG_INFO("addr = %s", ip.toString().c_str());
|
DEBUG_INFO("addr = %s", ip.toString().c_str());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if(ip.isV6())
|
if(ip.isV6())
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
char ipv6_str[INET6_ADDRSTRLEN], nm_str[INET6_ADDRSTRLEN];
|
|
||||||
inet_ntop(AF_INET6, ip.rawIpData(), ipv6_str, INET6_ADDRSTRLEN);
|
|
||||||
inet_ntop(AF_INET6, ip.netmask().rawIpData(), nm_str, INET6_ADDRSTRLEN);
|
|
||||||
|
|
||||||
//tap->picodev6 = new struct pico_device;
|
|
||||||
struct pico_ip6 ipaddr, netmask;
|
|
||||||
pico_string_to_ipv6(ipv6_str, ipaddr.addr);
|
|
||||||
pico_string_to_ipv6(nm_str, netmask.addr);
|
|
||||||
picodev6.send = pico_eth_send; // tx
|
|
||||||
picodev6.poll = pico_eth_poll; // rx
|
|
||||||
picodev6.mtu = tap->_mtu;
|
|
||||||
picodev6.tap = tap;
|
|
||||||
uint8_t mac[PICO_SIZE_ETH];
|
|
||||||
tap->_mac.copyTo(mac, PICO_SIZE_ETH);
|
|
||||||
if(pico_device_init(&picodev6, "p6", mac) != 0) {
|
|
||||||
DEBUG_ERROR("dev init failed");
|
|
||||||
delete &picodev6;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
pico_ipv6_link_add(&picodev6, ipaddr, netmask);
|
|
||||||
DEBUG_INFO("addr6 = %s", ip.toString().c_str());
|
|
||||||
return true;
|
|
||||||
*/
|
|
||||||
|
|
||||||
char ipv6_str[INET6_ADDRSTRLEN], nm_str[INET6_ADDRSTRLEN];
|
char ipv6_str[INET6_ADDRSTRLEN], nm_str[INET6_ADDRSTRLEN];
|
||||||
inet_ntop(AF_INET6, ip.rawIpData(), ipv6_str, INET6_ADDRSTRLEN);
|
inet_ntop(AF_INET6, ip.rawIpData(), ipv6_str, INET6_ADDRSTRLEN);
|
||||||
inet_ntop(AF_INET6, ip.netmask().rawIpData(), nm_str, INET6_ADDRSTRLEN);
|
inet_ntop(AF_INET6, ip.netmask().rawIpData(), nm_str, INET6_ADDRSTRLEN);
|
||||||
@@ -148,7 +124,8 @@ namespace ZeroTier {
|
|||||||
pico_string_to_ipv6(ipv6_str, ipaddr.addr);
|
pico_string_to_ipv6(ipv6_str, ipaddr.addr);
|
||||||
pico_string_to_ipv6(nm_str, netmask.addr);
|
pico_string_to_ipv6(nm_str, netmask.addr);
|
||||||
pico_ipv6_link_add(&picodev, ipaddr, netmask);
|
pico_ipv6_link_add(&picodev, ipaddr, netmask);
|
||||||
return true;
|
DEBUG_INFO("addr6 = %s", ipv6_str);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -343,11 +320,11 @@ namespace ZeroTier {
|
|||||||
DEBUG_ERROR("PICO_ERR_ECONNRESET");
|
DEBUG_ERROR("PICO_ERR_ECONNRESET");
|
||||||
conn->state = PICO_ERR_ECONNRESET;
|
conn->state = PICO_ERR_ECONNRESET;
|
||||||
}
|
}
|
||||||
DEBUG_INFO("PICO_SOCK_EV_ERR (socket error received) err=%d, picosock=%p", pico_err, s);
|
//DEBUG_INFO("PICO_SOCK_EV_ERR (socket error received) err=%d, picosock=%p", pico_err, s);
|
||||||
}
|
}
|
||||||
if (ev & PICO_SOCK_EV_CLOSE) {
|
if (ev & PICO_SOCK_EV_CLOSE) {
|
||||||
err = pico_socket_close(s);
|
err = pico_socket_close(s);
|
||||||
DEBUG_INFO("PICO_SOCK_EV_CLOSE (socket closure) err = %d, picosock=%p, conn=%p", err, s, conn);
|
//DEBUG_INFO("PICO_SOCK_EV_CLOSE (socket closure) err = %d, picosock=%p, conn=%p", err, s, conn);
|
||||||
conn->closure_ts = std::time(nullptr);
|
conn->closure_ts = std::time(nullptr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -365,7 +342,7 @@ namespace ZeroTier {
|
|||||||
|
|
||||||
int pico_eth_send(struct pico_device *dev, void *buf, int len)
|
int pico_eth_send(struct pico_device *dev, void *buf, int len)
|
||||||
{
|
{
|
||||||
//DEBUG_INFO();
|
DEBUG_INFO("len = %d", len);
|
||||||
SocketTap *tap = (SocketTap*)(dev->tap);
|
SocketTap *tap = (SocketTap*)(dev->tap);
|
||||||
if(!tap) {
|
if(!tap) {
|
||||||
DEBUG_ERROR("invalid dev->tap");
|
DEBUG_ERROR("invalid dev->tap");
|
||||||
@@ -379,14 +356,13 @@ namespace ZeroTier {
|
|||||||
dest_mac.setTo(ethhdr->daddr, 6);
|
dest_mac.setTo(ethhdr->daddr, 6);
|
||||||
tap->_handler(tap->_arg,NULL,tap->_nwid,src_mac,dest_mac,
|
tap->_handler(tap->_arg,NULL,tap->_nwid,src_mac,dest_mac,
|
||||||
Utils::ntoh((uint16_t)ethhdr->proto),0, ((char*)buf) + sizeof(struct pico_eth_hdr),len - sizeof(struct pico_eth_hdr));
|
Utils::ntoh((uint16_t)ethhdr->proto),0, ((char*)buf) + sizeof(struct pico_eth_hdr),len - sizeof(struct pico_eth_hdr));
|
||||||
//DEBUG_INFO("len = %d", len);
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
||||||
void picoTCP::pico_rx(SocketTap *tap, const MAC &from,const MAC &to,unsigned int etherType,
|
void picoTCP::pico_rx(SocketTap *tap, const MAC &from,const MAC &to,unsigned int etherType,
|
||||||
const void *data,unsigned int len)
|
const void *data,unsigned int len)
|
||||||
{
|
{
|
||||||
//DEBUG_INFO();
|
DEBUG_INFO("len = %d", len);
|
||||||
if(!tap) {
|
if(!tap) {
|
||||||
DEBUG_ERROR("invalid tap");
|
DEBUG_ERROR("invalid tap");
|
||||||
return;
|
return;
|
||||||
@@ -459,7 +435,7 @@ namespace ZeroTier {
|
|||||||
return ZT_ERR_GENERAL_FAILURE;
|
return ZT_ERR_GENERAL_FAILURE;
|
||||||
}
|
}
|
||||||
int err = 0;
|
int err = 0;
|
||||||
#if defined(SDK_IPV4)
|
if(conn->socket_family == AF_INET) {
|
||||||
struct pico_ip4 zaddr;
|
struct pico_ip4 zaddr;
|
||||||
struct sockaddr_in *in4 = (struct sockaddr_in*)addr;
|
struct sockaddr_in *in4 = (struct sockaddr_in*)addr;
|
||||||
char ipv4_str[INET_ADDRSTRLEN];
|
char ipv4_str[INET_ADDRSTRLEN];
|
||||||
@@ -468,8 +444,8 @@ namespace ZeroTier {
|
|||||||
//DEBUG_ATTN("addr=%s:%d", ipv4_str, Utils::ntoh( in4->sin_port ));
|
//DEBUG_ATTN("addr=%s:%d", ipv4_str, Utils::ntoh( in4->sin_port ));
|
||||||
err = pico_socket_connect(conn->picosock, &zaddr, in4->sin_port);
|
err = pico_socket_connect(conn->picosock, &zaddr, in4->sin_port);
|
||||||
//DEBUG_INFO("connect_err = %d", err);
|
//DEBUG_INFO("connect_err = %d", err);
|
||||||
|
}
|
||||||
#elif defined(SDK_IPV6)
|
if(conn->socket_family == AF_INET6) {
|
||||||
struct pico_ip6 zaddr;
|
struct pico_ip6 zaddr;
|
||||||
struct sockaddr_in6 *in6 = (struct sockaddr_in6*)addr;
|
struct sockaddr_in6 *in6 = (struct sockaddr_in6*)addr;
|
||||||
char ipv6_str[INET6_ADDRSTRLEN];
|
char ipv6_str[INET6_ADDRSTRLEN];
|
||||||
@@ -477,7 +453,7 @@ namespace ZeroTier {
|
|||||||
pico_string_to_ipv6(ipv6_str, zaddr.addr);
|
pico_string_to_ipv6(ipv6_str, zaddr.addr);
|
||||||
//DEBUG_ATTN("addr=%s:%d", ipv6_str, Utils::ntoh(in6->sin6_port));
|
//DEBUG_ATTN("addr=%s:%d", ipv6_str, Utils::ntoh(in6->sin6_port));
|
||||||
err = pico_socket_connect(conn->picosock, &zaddr, in6->sin6_port);
|
err = pico_socket_connect(conn->picosock, &zaddr, in6->sin6_port);
|
||||||
#endif
|
}
|
||||||
|
|
||||||
memcpy(&(conn->peer_addr), &addr, sizeof(struct sockaddr_storage));
|
memcpy(&(conn->peer_addr), &addr, sizeof(struct sockaddr_storage));
|
||||||
|
|
||||||
@@ -498,7 +474,7 @@ namespace ZeroTier {
|
|||||||
return ZT_ERR_GENERAL_FAILURE;
|
return ZT_ERR_GENERAL_FAILURE;
|
||||||
}
|
}
|
||||||
int err = 0;
|
int err = 0;
|
||||||
#if defined(SDK_IPV4)
|
if(conn->socket_family == AF_INET) {
|
||||||
struct pico_ip4 zaddr;
|
struct pico_ip4 zaddr;
|
||||||
struct sockaddr_in *in4 = (struct sockaddr_in*)addr;
|
struct sockaddr_in *in4 = (struct sockaddr_in*)addr;
|
||||||
char ipv4_str[INET_ADDRSTRLEN];
|
char ipv4_str[INET_ADDRSTRLEN];
|
||||||
@@ -506,17 +482,17 @@ namespace ZeroTier {
|
|||||||
pico_string_to_ipv4(ipv4_str, &(zaddr.addr));
|
pico_string_to_ipv4(ipv4_str, &(zaddr.addr));
|
||||||
// DEBUG_ATTN("addr=%s: %d ntoh()=%d", ipv4_str, in4->sin_port, Utils::ntoh(in4->sin_port));
|
// DEBUG_ATTN("addr=%s: %d ntoh()=%d", ipv4_str, in4->sin_port, Utils::ntoh(in4->sin_port));
|
||||||
err = pico_socket_bind(conn->picosock, &zaddr, (uint16_t *)&(in4->sin_port));
|
err = pico_socket_bind(conn->picosock, &zaddr, (uint16_t *)&(in4->sin_port));
|
||||||
#endif
|
}
|
||||||
#if defined(SDK_IPV6)
|
if(conn->socket_family == AF_INET6) {
|
||||||
struct pico_ip6 pip6;
|
struct pico_ip6 pip6;
|
||||||
struct sockaddr_in6 *in6 = (struct sockaddr_in6*)addr;
|
struct sockaddr_in6 *in6 = (struct sockaddr_in6*)addr;
|
||||||
char ipv6_str[INET6_ADDRSTRLEN];
|
char ipv6_str[INET6_ADDRSTRLEN];
|
||||||
inet_ntop(AF_INET6, &(in6->sin6_addr), ipv6_str, INET6_ADDRSTRLEN);
|
inet_ntop(AF_INET6, &(in6->sin6_addr), ipv6_str, INET6_ADDRSTRLEN);
|
||||||
// TODO: This isn't proper
|
// TODO: This isn't proper
|
||||||
pico_string_to_ipv6("::", pip6.addr);
|
pico_string_to_ipv6("::", pip6.addr);
|
||||||
//DEBUG_ATTN("addr=%s:%d, picosock=%p", ipv6_str, Utils::ntoh(in6->sin6_port), (conn->picosock));
|
DEBUG_ATTN("addr=%s:%d", ipv6_str, Utils::ntoh(in6->sin6_port));
|
||||||
err = pico_socket_bind(conn->picosock, &pip6, (uint16_t *)&(in6->sin6_port));
|
err = pico_socket_bind(conn->picosock, &pip6, (uint16_t *)&(in6->sin6_port));
|
||||||
#endif
|
}
|
||||||
if(err < 0) {
|
if(err < 0) {
|
||||||
if(pico_err < 0)
|
if(pico_err < 0)
|
||||||
DEBUG_ERROR("pico_err = %d", pico_err);
|
DEBUG_ERROR("pico_err = %d", pico_err);
|
||||||
@@ -703,7 +679,7 @@ namespace ZeroTier {
|
|||||||
|
|
||||||
int picoTCP::pico_Close(Connection *conn)
|
int picoTCP::pico_Close(Connection *conn)
|
||||||
{
|
{
|
||||||
DEBUG_INFO("conn = %p, picosock=%p, fd = %d", conn, conn->picosock, conn->app_fd);
|
//DEBUG_INFO("conn = %p, picosock=%p, fd = %d", conn, conn->picosock, conn->app_fd);
|
||||||
if(!conn || !conn->picosock)
|
if(!conn || !conn->picosock)
|
||||||
return ZT_ERR_GENERAL_FAILURE;
|
return ZT_ERR_GENERAL_FAILURE;
|
||||||
int err;
|
int err;
|
||||||
|
|||||||
Reference in New Issue
Block a user