wider selftest coverage

This commit is contained in:
Joseph Henry
2017-05-05 16:46:07 -07:00
parent d631e06d99
commit dcdc13204c
31 changed files with 1613 additions and 303 deletions

3
.gitignore vendored
View File

@@ -11,6 +11,9 @@
integrations/android/android_jni_lib/proj/local.properties
integrations/android/example_app/local.properties
# examples
examples/java/ZeroTierHelloWorld/lib/libzt.jnilib
# Unity
*.meta
integrations/Unity3D/Library

View File

@@ -11,10 +11,9 @@ Embed ZeroTier directly into your app or service. Connect everything without wor
```
string str = "welcome to the machine";
zts_start("./zt");
while(!zts_service_running())
while(!zts_running())
sleep(1);
zts_join_network(nwid);
int err, sockfd;
zts_join("e5cd7a7b1c0fa971");
while(!zts_has_address(nwid))
sleep(1);
if((fd = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) {
@@ -31,7 +30,7 @@ Bindings also exist for [many popular languages]().
## Build Targets
### Static Library
- `make static_lib`: Will output to `build/`
- `make static_lib SDK_IPV4=1`: Will output to `build/`
### Tests
- `make tests`: Will output to `build/tests/`
@@ -40,8 +39,8 @@ Then run the unit test suite with whatever configuration you need. For instance:
To run a single-test IPv4 client/server test:
- Host 1: `./build/test/unit zt1 c7cd7c9e1b0f52a2 simple 4 server 8787`
- Host 2: `./build/test/unit zt2 c7cd7c9e1b0f52a2 simple 4 client 10.9.9.40 8787`
- Host 1: `./build/selftest zt1 c7cd7c9e1b0f52a2 simple 4 server 8787`
- Host 2: `./build/selftest zt2 c7cd7c9e1b0f52a2 simple 4 client 10.9.9.40 8787`
To run a multi-message IPv4 client/server test:
- Host 1: ./build/test/unit zt2 c7cd7c9e1b0f52a2 simple 4 server 8787 n_bytes 100 50'
@@ -49,8 +48,17 @@ To run a multi-message IPv4 client/server test:
- For more unit test examples, see the [testing]() page
## IP version flags
`SDK_IPV4=1`
`SDK_IPV6=1`
## Using Language Bindings
`SDK_LANG_JAVA=1`
`SDK_LANG_JNI=1` - Enable JNI bindings for Java (produces a shared library)
`SDK_LANG_CSHARP=1`
`SDK_LANG_PYTHON=1`
`SDK_LANG_GO=1`
## Debugging flags
`SDK_DEBUG=1` - Enable SDK library debugging
`ZT_DEBUG=1` - Enable core ZeroTier service debugging

View File

@@ -39,13 +39,13 @@ int main()
printf("homePath = %s\n", homePath);
// Wait for ZeroTier service to start
while(!zts_service_running()) {
while(!zts_running()) {
printf("wating for service to start\n");
sleep(1);
}
// Join a network
zts_join_network(nwid);
zts_join(nwid);
// Wait for ZeroTier service to issue an address to the device on the given network
while(!zts_has_address("e5cd7a9e1c0fd272")) {

416
examples/csharp/DotNetWrapper.cs Executable file
View File

@@ -0,0 +1,416 @@
/*
* ZeroTier SDK - Network Virtualization Everywhere
* Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*
* You can be released from the requirements of the license by purchasing
* a commercial license. Buying such a license is mandatory as soon as you
* develop commercial closed-source software that incorporates or links
* directly against ZeroTier software without disclosing the source code
* of your own application.
*/
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using System;
using System.Collections;
using System.Runtime.InteropServices;
using System.Threading;
using System.Net.Sockets;
using System.Net;
using System.IO;
using System.Collections.Generic;
using System.Globalization;
// TODO:
/*
* check for mem leaks surrounding managed/unmanaged barrier
* find root of 2X buffer size requirement issue
* check that cross-thread oprations are handled correctly
* check that .IsRunning() doesn't bork the entire system anymore
* Allow max packet size configuration
* Handle exceptions from unmanaged code
* */
// Provides a bare-bones interface to ZeroTier-administered sockets
public class ZTSDK {
// ZeroTier background thread
protected Thread ztThread;
protected List<int> connections = new List<int> ();
protected int MaxPacketSize;
// Only allow one network at a time for BETA
protected bool joined_to_network = false;
protected string nwid = "";
protected string path = "";
// Platform-specific paths and bundle/libary names
#if UNITY_STANDALONE_OSX || UNITY_EDITOR_OSX
const string DLL_PATH = "ZeroTierSDK_Unity3D_OSX";
protected string rpc_path = "/Library/Application\\ Support/ZeroTier/SDK/";
#endif
#if UNITY_IOS || UNITY_IPHONE
const string DLL_PATH = "ZeroTierSDK_Unity3D_iOS";
protected string rpc_path = "ZeroTier/One/";
#endif
#if UNITY_STANDALONE_WIN || UNITY_EDITOR_WIN
const string DLL_PATH = "ZeroTierSDK_Unity3D_WIN";
protected string rpc_path = "";
#endif
#if UNITY_STANDALONE_LINUX
const string DLL_PATH = "ZeroTierSDK_Unity3D_LINUX";
protected string rpc_path = "";
#endif
#if UNITY_ANDROID
const string DLL_PATH = "ZeroTierSDK_Unity3D_ANDROID";
protected string rpc_path = "ZeroTier/One/";
#endif
#region DLL Imports
// ZeroTier service / debug initialization
[DllImport (DLL_PATH)]
public static extern void SetDebugFunction( IntPtr fp );
[DllImport (DLL_PATH)]
private static extern int unity_start_service(string path);
[DllImport (DLL_PATH)]
private static extern int unity_start_service_and_rpc(string path, string nwid);
[DllImport (DLL_PATH)]
protected static extern bool zts_is_running();
[DllImport (DLL_PATH)]
protected static extern void zts_stop_service();
// Connection calls
[DllImport (DLL_PATH)]
protected static extern int zts_socket(int family, int type, int protocol);
[DllImport (DLL_PATH)]
unsafe protected static extern int zts_bind(int sockfd, System.IntPtr addr, int addrlen);
[DllImport (DLL_PATH)]
unsafe protected static extern int zts_connect(int sockfd, System.IntPtr addr, int addrlen);
[DllImport (DLL_PATH)]
protected static extern int zts_accept(int sockfd);
[DllImport (DLL_PATH)]
protected static extern int zts_listen(int sockfd, int backlog);
[DllImport (DLL_PATH)]
protected static extern int zts_close(int sockfd);
// RX / TX
[DllImport (DLL_PATH)]
unsafe protected static extern int zts_recv(int sockfd, [In, Out] IntPtr buf, int len);
[DllImport (DLL_PATH)]
unsafe protected static extern int zts_send(int sockfd, IntPtr buf, int len);
[DllImport (DLL_PATH)]
unsafe protected static extern int zts_set_nonblock(int sockfd);
[DllImport (DLL_PATH)]
unsafe protected static extern int zts_sendto(int fd, IntPtr buf, int len, int flags, System.IntPtr addr, int addrlen);
[DllImport (DLL_PATH)]
unsafe protected static extern int zts_recvfrom(int fd, [In, Out] IntPtr buf, int len, int flags, System.IntPtr addr, int addrlen);
// ZT Network controls
[DllImport (DLL_PATH)]
protected static extern void zts_join_network(string nwid);
[DllImport (DLL_PATH)]
protected static extern void zts_leave_network(string nwid);
#endregion
// Interop structures
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential, CharSet=System.Runtime.InteropServices.CharSet.Ansi)]
public struct sockaddr {
/// u_short->unsigned short
public ushort sa_family;
/// char[14]
[System.Runtime.InteropServices.MarshalAsAttribute(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst=14)]
public string sa_data;
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void MyDelegate(string str);
// Debug output callback
static void CallBackFunction(string str) {
Debug.Log("ZeroTier: " + str);
}
// Returns a path for RPC communications to the service
private string rpcCommPath()
{
if(path != "" && nwid != "") {
return path + "nc_" + nwid;
}
return "";
}
// Thread which starts the ZeroTier service
protected void zt_service_thread()
{
// Set up debug callback
MyDelegate callback_delegate = new MyDelegate( CallBackFunction );
IntPtr intptr_delegate = Marshal.GetFunctionPointerForDelegate(callback_delegate);
SetDebugFunction( intptr_delegate );
// Start service
/* This new instance will communicate via a named pipe, so any
* API calls (ZeroTier.Connect(), ZeroTier.Send(), etc) will be sent to the service
* via this pipe.
*/
if(nwid.Length > 0) {
unity_start_service_and_rpc (path, nwid);
}
else {
unity_start_service(rpcCommPath());
}
}
// Returns the nwid of the network you're currently connected to
public string GetNetworkID() {
return nwid;
}
// Returns whether you're currently connected to a network
public bool IsConnected() {
return nwid != "";
}
// Start the ZeroTier service
protected void Init()
{
ztThread = new Thread(() => {
try {
zt_service_thread();
} catch(Exception e) {
Debug.Log(e.Message.ToString());
}
});
ztThread.IsBackground = true; // Allow the thread to be aborted safely
ztThread.Start();
}
// Initialize the ZeroTier service with a given path
public ZTSDK(string path, string nwid) {
Debug.Log("ZTSDK(): " + nwid);
this.path = path;
this.nwid = nwid;
Init();
}
public ZTSDK (string path) {
this.path = path;
Init();
}
// Initialize the ZeroTier service
public ZTSDK() {
Init();
}
#region Network Handling
// Joins a ZeroTier virtual network
public bool JoinNetwork(string nwid)
{
if(!joined_to_network) {
zts_join_network(nwid);
return true;
}
return false;
}
// Leaves a ZeroTier virtual network
public bool LeaveNetwork(string nwid)
{
if(!joined_to_network) {
return false;
}
else {
zts_leave_network(nwid);
return true;
}
}
#endregion
// Creates a new ZeroTier-administered socket
public int Socket(int family, int type, int protocol)
{
return zts_socket (family, type, protocol);
}
// Binds to a specific address
public int Bind(int fd, string addr, int port)
{
GCHandle sockaddr_ptr = Generate_unmananged_sockaddr(addr + ":" + port);
IntPtr pSockAddr = sockaddr_ptr.AddrOfPinnedObject ();
int addrlen = Marshal.SizeOf (pSockAddr);
return zts_bind (fd, pSockAddr, addrlen);
}
// Listens for an incoming connection request
public int Listen(int fd, int backlog)
{
return zts_listen(fd, backlog);
}
// Accepts an incoming connection
public int Accept(int fd)
{
return zts_accept (fd);
}
// Closes a connection
public int Close(int fd)
{
return Close (fd);
}
// Connects to a remote host
public int Connect(int fd, string addr, int port)
{
GCHandle sockaddr_ptr = Generate_unmananged_sockaddr(addr + ":" + port);
IntPtr pSockAddr = sockaddr_ptr.AddrOfPinnedObject ();
int addrlen = Marshal.SizeOf (pSockAddr);
return zts_connect (fd, pSockAddr, addrlen);
}
public int Read(int fd, ref char[] buf, int len)
{
GCHandle handle = GCHandle.Alloc(buf, GCHandleType.Pinned);
IntPtr ptr = handle.AddrOfPinnedObject();
int bytes_read = zts_recv (fd, ptr, len*2);
string str = Marshal.PtrToStringAuto(ptr);
//Marshal.Copy (ptr, buf, 0, bytes_read);
buf = Marshal.PtrToStringAnsi(ptr).ToCharArray();
return bytes_read;
}
public int Write(int fd, char[] buf, int len)
{
GCHandle handle = GCHandle.Alloc(buf, GCHandleType.Pinned);
IntPtr ptr = handle.AddrOfPinnedObject();
//error = 0;
int bytes_written;
// FIXME: Sending a length of 2X the buffer size seems to fix the object pinning issue
if((bytes_written = zts_send(fd, ptr, len*2)) < 0) {
//error = (byte)bytes_written;
}
return bytes_written;
}
// Sends data to an address
public int SendTo(int fd, char[] buf, int len, int flags, string addr, int port)
{
GCHandle handle = GCHandle.Alloc(buf, GCHandleType.Pinned);
IntPtr ptr = handle.AddrOfPinnedObject();
int bytes_written;
// Form address structure
GCHandle sockaddr_ptr = Generate_unmananged_sockaddr(addr + ":" + port);
IntPtr pSockAddr = sockaddr_ptr.AddrOfPinnedObject ();
int addrlen = Marshal.SizeOf (pSockAddr);
if((bytes_written = zts_sendto(fd, ptr, len*2, flags, pSockAddr, addrlen)) < 0) {
//error = (byte)bytes_written;
}
return bytes_written;
}
// Receives data from an address
public int RecvFrom(int fd, ref char[] buf, int len, int flags, string addr, int port)
{
GCHandle handle = GCHandle.Alloc(buf, GCHandleType.Pinned);
IntPtr ptr = handle.AddrOfPinnedObject();
// Form address structure
GCHandle sockaddr_ptr = Generate_unmananged_sockaddr(addr + ":" + port);
IntPtr pSockAddr = sockaddr_ptr.AddrOfPinnedObject ();
int addrlen = Marshal.SizeOf (pSockAddr);
int bytes_read = zts_recvfrom(fd, ptr, len*2, flags, pSockAddr, addrlen);
string str = Marshal.PtrToStringAuto(ptr);
//Marshal.Copy (ptr, buf, 0, bytes_read);
buf = Marshal.PtrToStringAnsi(ptr).ToCharArray();
return bytes_read;
}
#region Service-Related calls
// Returns whether the ZeroTier service is currently running
public bool IsRunning()
{
return zts_is_running ();
}
// Terminates the ZeroTier service
public void Terminate()
{
zts_stop_service ();
}
#endregion
// --- Utilities ---
// Handles IPv4 and IPv6 notation.
public static IPEndPoint CreateIPEndPoint(string endPoint)
{
string[] ep = endPoint.Split(':');
if (ep.Length < 2) throw new FormatException("Invalid endpoint format");
IPAddress ip;
if (ep.Length > 2) {
if (!IPAddress.TryParse(string.Join(":", ep, 0, ep.Length - 1), out ip)) {
throw new FormatException("Invalid ip-adress");
}
}
else {
if (!IPAddress.TryParse(ep[0], out ip)) {
throw new FormatException("Invalid ip-adress");
}
}
int port;
if (!int.TryParse(ep[ep.Length - 1], NumberStyles.None, NumberFormatInfo.CurrentInfo, out port)) {
throw new FormatException("Invalid port");
}
return new IPEndPoint(ip, port);
}
// Generates an unmanaged sockaddr structure from a string-formatted endpoint
public static GCHandle Generate_unmananged_sockaddr(string endpoint_str)
{
IPEndPoint ipEndPoint;
ipEndPoint = CreateIPEndPoint (endpoint_str);
SocketAddress socketAddress = ipEndPoint.Serialize ();
// use an array of bytes instead of the sockaddr structure
byte[] sockAddrStructureBytes = new byte[socketAddress.Size];
GCHandle sockAddrHandle = GCHandle.Alloc (sockAddrStructureBytes, GCHandleType.Pinned);
for (int i = 0; i < socketAddress.Size; ++i) {
sockAddrStructureBytes [i] = socketAddress [i];
}
return sockAddrHandle;
}
public static GCHandle Generate_unmanaged_buffer(byte[] buf)
{
// use an array of bytes instead of the sockaddr structure
GCHandle sockAddrHandle = GCHandle.Alloc (buf, GCHandleType.Pinned);
return sockAddrHandle;
}
}

View File

@@ -1,7 +1,9 @@
Java Examples
======
`make shared_jni_lib SDK_JNI=1 SDK_IPV4=1`
Uses API described in [api/java/README.md]
- Copy `ZeroTier.java` into `your_project/src/zerotier`
- Copy `Address.java` into `your_project/src/zerotier`
- Copy `src/ZeroTier.java` into `your_project/src/zerotier`
- Copy `src/Address.java` into `your_project/src/zerotier`
- Copy `libzt.jnilib` or `libzt.so` from `build/` into `your_project/lib`

View File

@@ -1,2 +1,3 @@
/MyClass$1.class
/zerotier/
/MyClass.class

View File

@@ -0,0 +1,77 @@
/*
* ZeroTier SDK - Network Virtualization Everywhere
* Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*
* You can be released from the requirements of the license by purchasing
* a commercial license. Buying such a license is mandatory as soon as you
* develop commercial closed-source software that incorporates or links
* directly against ZeroTier software without disclosing the source code
* of your own application.
*/
//
// Implementations located in src/wrappers/swift/XcodeWrapper.cpp
//
#ifndef Example_OSX_IOS_Bridging_Header_h
#define Example_OSX_IOS_Bridging_Header_h
#include <sys/socket.h>
// ZT INTERCEPT/RPC CONTROLS
int zt_init_rpc(const char *path, const char *nwid);
int zt_start_intercept();
void zt_disable_intercept();
void zt_enable_intercept();
// ZT SERVICE CONTROLS
void zt_start_service(const char * path);
void zt_stop_service();
void zt_start_service_and_rpc(const char * path, const char * nwid);
bool zt_service_is_running();
void zt_join_network(const char *nwid);
void zt_leave_network(const char *nwid);
void zt_get_ipv4_address(const char *nwid, char *addrstr);
void zt_get_ipv6_address(const char *nwid, char *addrstr);
// SOCKS5 PROXY CONTROLS
void zt_start_proxy_server(const char *nwid, struct sockaddr_storage addr);
void zt_stop_proxy_server(const char *nwid);
void zt_proxy_is_running(const char *nwid);
void zt_get_proxy_server_address(const char *nwid, struct sockaddr_storage addr);
// SOCKET API
int zt_connect(CONNECT_SIG);
int zt_bind(BIND_SIG);
int zt_accept(ACCEPT_SIG);
int zt_listen(LISTEN_SIG);
int zt_socket(SOCKET_SIG);
int zt_setsockopt(SETSOCKOPT_SIG);
int zt_getsockopt(GETSOCKOPT_SIG);
int zt_close(CLOSE_SIG);
int zt_getsockname(GETSOCKNAME_SIG);
int zt_getpeername(GETPEERNAME_SIG);
int zt_recvfrom(RECVFROM_SIG);
int zt_fcntl(FCNTL_SIG);
int zt_sendto(SENDTO_SIG);
#endif /* Example_OSX_IOS_Bridging_Header_h */

View File

@@ -1,3 +0,0 @@
Swift Language Binding API for the ZeroTier SDK
======

137
examples/swift/XcodeWrapper.cpp Executable file
View File

@@ -0,0 +1,137 @@
/*
* ZeroTier SDK - Network Virtualization Everywhere
* Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*
* You can be released from the requirements of the license by purchasing
* a commercial license. Buying such a license is mandatory as soon as you
* develop commercial closed-source software that incorporates or links
* directly against ZeroTier software without disclosing the source code
* of your own application.
*/
#include "sdk.h"
#include "XcodeWrapper.hpp"
#define INTERCEPT_ENABLED 111
#define INTERCEPT_DISABLED 222
// ZEROTIER CONTROLS
// Starts a ZeroTier service at the specified path
extern "C" void zt_start_service(const char *path, const char *nwid) {
zts_start_service(path);
//init_service(INTERCEPT_DISABLED, path);
}
// Starts a ZeroTier service at the specified path and initializes the RPC mechanism
//extern "C" void zt_start_service_and_rpc(const char *path, const char *nwid) {
// init_service_and_rpc(INTERCEPT_DISABLED, path, nwid);
//}
// Stops the core ZeroTier service
extern "C" void zt_stop_service() {
zts_stop_service();
}
// Returns whether the core ZeroTier service is running
extern "C" bool zt_service_is_running() {
return zts_service_is_running();
}
// Joins a ZeroTier virtual network
extern "C" void zt_join_network(const char *nwid) {
zts_join_network(nwid);
}
// Leaves a ZeroTier virtual network
extern "C" void zt_leave_network(const char *nwid) {
zts_leave_network(nwid);
}
// Returns a list of addresses associated with this device on the given network
extern "C" void zt_get_ipv4_address(const char *nwid, char *addrstr) {
zts_get_ipv4_address(nwid, addrstr);
}
// Returns a list of addresses associated with this device on the given network
extern "C" void zt_get_ipv6_address(const char *nwid, char *addrstr) {
zts_get_ipv6_address(nwid, addrstr);
}
// PROXY SERVER CONTROLS
//
extern "C" void zt_start_proxy_server(const char *homepath, const char *nwid, struct sockaddr_storage *addr) {
zts_start_proxy_server(homepath, nwid, addr);
}
//
extern "C" void zt_stop_proxy_server(const char *nwid) {
zts_stop_proxy_server(nwid);
}
//
extern "C" void zt_proxy_is_running(const char *nwid) {
zts_proxy_is_running(nwid);
}
//
extern "C" void zt_get_proxy_server_address(const char *nwid, struct sockaddr_storage *addr) {
zts_get_proxy_server_address(nwid, addr);
}
// Explicit ZT API wrappers
#if !defined(__IOS__)
// This isn't available for iOS since function interposition isn't as reliable
extern "C" void zt_init_rpc(const char *path, const char *nwid) {
zts_init_rpc(path, nwid);
}
#endif
// SOCKET API
// These are used in ZTSDK.swift to implement the higher-level API
extern "C" int zt_socket(SOCKET_SIG) {
return zts_socket(socket_family, socket_type, protocol);
}
extern "C" int zt_connect(CONNECT_SIG) {
return zts_connect(fd, addr, addrlen);
}
extern "C" int zt_bind(BIND_SIG){
return zts_bind(fd, addr, addrlen);
}
extern "C" int zt_accept(ACCEPT_SIG) {
return zts_accept(fd, addr, addrlen);
}
extern "C" int zt_listen(LISTEN_SIG) {
return zts_listen(fd, backlog);
}
extern "C" int zt_setsockopt(SETSOCKOPT_SIG) {
return zts_setsockopt(fd, level, optname, optval, optlen);
}
extern "C" int zt_getsockopt(GETSOCKOPT_SIG) {
return zts_getsockopt(fd, level, optname, optval, optlen);
}
extern "C" int zt_close(CLOSE_SIG) {
return zts_close(fd);
}
extern "C" int zt_getsockname(GETSOCKNAME_SIG) {
return zts_getsockname(fd, addr, addrlen);
}
extern "C" int zt_getpeername(GETPEERNAME_SIG) {
return zts_getpeername(fd, addr, addrlen);
}
extern "C" int zt_fcntl(FCNTL_SIG) {
return zts_fcntl(fd, cmd, flags);
}
extern "C" ssize_t zt_recvfrom(RECVFROM_SIG) {
return zts_recvfrom(fd, buf, len, flags, addr, addrlen);
}
extern "C" ssize_t zt_sendto(SENDTO_SIG) {
return zts_sendto(fd, buf, len, flags, addr, addrlen);
}

30
examples/swift/XcodeWrapper.hpp Executable file
View File

@@ -0,0 +1,30 @@
/*
* ZeroTier SDK - Network Virtualization Everywhere
* Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*
* You can be released from the requirements of the license by purchasing
* a commercial license. Buying such a license is mandatory as soon as you
* develop commercial closed-source software that incorporates or links
* directly against ZeroTier software without disclosing the source code
* of your own application.
*/
#ifndef SDK_XCODE_WRAPPER_HPP
#define SDK_XCODE_WRAPPER_HPP
#endif // SDK_XCODE_WRAPPER_HPP

216
examples/swift/ztsdk.swift Normal file
View File

@@ -0,0 +1,216 @@
/*
* ZeroTier SDK - Network Virtualization Everywhere
* Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*
* You can be released from the requirements of the license by purchasing
* a commercial license. Buying such a license is mandatory as soon as you
* develop commercial closed-source software that incorporates or links
* directly against ZeroTier software without disclosing the source code
* of your own application.
*/
import Foundation
// Convenience structure for getting address data to/from the native library
struct ZTAddress
{
var family: Int32
var addr: String
var port: Int16
var data: sockaddr_in?
init(_ family: Int32, _ addr: String, _ port: Int16) {
self.family = family
self.addr = addr
self.port = port
}
func to_sockaddr_in() -> UnsafePointer<sockaddr> {
var data = sockaddr_in(sin_len: UInt8(sizeof(sockaddr_in)),
sin_family: UInt8(AF_INET),
sin_port: UInt16(port).bigEndian,
sin_addr: in_addr(s_addr: 0),
sin_zero: (0,0,0,0,0,0,0,0))
inet_pton(AF_INET, addr, &(data.sin_addr));
return UnsafePointer<sockaddr>([data]);
}
func len() -> UInt8 {
return UInt8(sizeof(sockaddr_in))
}
}
// Convenience wrapper class for ZeroTier/SDK/Proxy controls
// Implemented in terms of SDK_XcodeWrapper.cpp
class ZTSDK : NSObject
{
var service_thread : NSThread!
private func ztnc_start_service(path: String?) {
if(path == nil) {
zt_start_service(
NSSearchPathForDirectoriesInDomains(
NSSearchPathDirectory.DocumentDirectory,NSSearchPathDomainMask.UserDomainMask,true)[0])
return;
}
zt_start_service(path!)
}
// Starts the ZeroTier background service
func start_service(path: String?) {
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(queue) {
self.ztnc_start_service(path)
}
sleep(3)
while(service_is_running() == false) { /* waiting for service to start */ }
}
// Stops the ZeroTier background service
func stop_service() {
zt_stop_service()
}
// Returns whether the ZeroTier background service is running
func service_is_running() -> Bool {
return zt_service_is_running()
}
// Joins a ZeroTier network
func join_network(nwid: String) {
zt_join_network(nwid)
}
// Leaves a ZeroTier network
func leave_network(nwid: String) {
zt_leave_network(nwid)
}
// Returns the IPV4 address of this device on a given ZeroTier network
func get_ipv4_address(nwid: String, inout _ addrbuf: [Int8]) {
zt_get_ipv4_address(nwid,&addrbuf)
}
// Returns the IPV6 address of this device on a given ZeroTier network
func get_ipv6_address(nwid: String, inout _ addrbuf: [Int8]) {
zt_get_ipv6_address(nwid,&addrbuf)
}
// PROXY SERVER CONTROLS
//
/*
func start_proxy_server(homepath: String, nwid: String, struct sockaddr_storage *addr) {
zt_start_proxy_server(homepath, nwid, addr);
}
//
func stop_proxy_server(nwid: String) {
zt_stop_proxy_server(nwid);
}
//
func proxy_is_running(const char *homepath, const char *nwid, struct sockaddr_storage *addr) {
zt_start_proxy_server(homepath, nwid, addr);
}
//
func get_proxy_server_address(const char *nwid, struct sockaddr_storage *addr) {
zt_get_proxy_server_address(nwid, addr);
}
// Explicit ZT API wrappers
#if !defined(__IOS__)
// This isn't available for iOS since function interposition isn't as reliable
func init_rpc(const char *path, const char *nwid) {
zt_init_rpc(path, nwid);
}
#endif
*/
// TODO: Verify this hasn't been broken. Should check for interface addresses based on
// protocol version. (important)
// SOCKET API
func socket(socket_family: Int32, _ socket_type: Int32, _ socket_protocol: Int32) -> Int32 {
return zt_socket(socket_family, socket_type, socket_protocol);
}
func connect(fd: Int32, _ addr: ZTAddress, _ nwid: String? = nil) -> Int32 {
if(nwid == nil) { // no nwid is provided to check for address, try once and fail
return zt_connect(Int32(fd), addr.to_sockaddr_in(), UInt32(addr.len()));
}
while(true) { // politely wait until an address is provided. simulates a blocking call
var addrbuf = [Int8](count: 16, repeatedValue: 0)
self.get_ipv4_address(nwid!, &addrbuf)
let addr_str:String = String.fromCString(addrbuf)!
if(addr_str != "-1.-1.-1.-1/-1") {
return zt_connect(Int32(fd), addr.to_sockaddr_in(), UInt32(addr.len()));
}
}
}
func bind(fd: Int32, _ addr: ZTAddress, _ nwid: String? = nil) -> Int32 {
if(nwid == nil) { // no nwid is provided to check for address, try once and fail
return zt_bind(Int32(fd), addr.to_sockaddr_in(), UInt32(addr.len()));
}
while(true) { // politely wait until an address is provided. simulates a blocking call
var addrbuf = [Int8](count: 16, repeatedValue: 0)
self.get_ipv4_address(nwid!, &addrbuf)
let addr_str:String = String.fromCString(addrbuf)!
if(addr_str != "-1.-1.-1.-1/-1") {
return zt_bind(Int32(fd), addr.to_sockaddr_in(), UInt32(addr.len()));
}
}
}
func accept(fd: Int32, _ addr: ZTAddress) -> Int32 {
return zt_accept(Int32(fd), UnsafeMutablePointer<sockaddr>([addr.data]), UnsafeMutablePointer<UInt32>([addr.len]));
}
func listen(fd: Int32, _ backlog: Int16) -> Int32 {
return zt_listen(Int32(fd), Int32(backlog));
}
func setsockopt(fd: Int32, _ level: Int32, _ optname: Int32, _ optval: UnsafePointer<Void>, _ optlen: Int32) -> Int32 {
return zt_setsockopt(fd, level, optname, optval, UInt32(optlen));
}
func getsockopt(fd: Int32, _ level: Int32, _ optname: Int32, _ optval: UnsafeMutablePointer<Void>, _ optlen: UInt32) -> Int32 {
return zt_getsockopt(fd, level, optname, optval, UnsafeMutablePointer<UInt32>([optlen]));
}
func close(fd: Int32) -> Int32 {
return zt_close(fd);
}
func getsockname(fd: Int32, _ addr: ZTAddress) -> Int32 {
return zt_getsockname(fd, UnsafeMutablePointer<sockaddr>([addr.data]), UnsafeMutablePointer<UInt32>([addr.len]));
}
func getpeername(fd: Int32, _ addr: ZTAddress) -> Int32 {
return zt_getpeername(fd, UnsafeMutablePointer<sockaddr>([addr.data]), UnsafeMutablePointer<UInt32>([addr.len]));
}
func fcntl(fd: Int32, _ cmd: Int32, _ flags: Int32) -> Int32 {
return zt_fcntl(fd, cmd, flags);
}
func recvfrom(fd: Int32, _ buf: UnsafeMutablePointer<Void>, _ len: Int32, _ flags: Int32, _ addr: ZTAddress) -> Int32 {
return zt_recvfrom(fd, buf, Int(len), flags, UnsafeMutablePointer<sockaddr>([addr.data]), UnsafeMutablePointer<UInt32>([addr.len]));
}
func sendto(fd: Int32, _ buf: UnsafePointer<Void>, _ len: Int32, _ flags: Int32, _ addr: ZTAddress) -> Int32 {
return zt_sendto(fd, buf, Int(len), flags, addr.to_sockaddr_in(), UInt32(addr.len()));
}
}

View File

@@ -9,7 +9,7 @@
#include "pico_config.h"
#include "pico_frame.h"
#define PICO_MAX_TIMERS 20
#define PICO_MAX_TIMERS 128
#define PICO_ETH_MRU (1514u)
#define PICO_IP_MRU (1500u)
@@ -78,6 +78,16 @@ int pico_notify_ttl_expired(struct pico_frame *f);
int pico_notify_frag_expired(struct pico_frame *f);
int pico_notify_pkt_too_big(struct pico_frame *f);
#ifdef __cplusplus
extern "C" {
#endif
int pico_ntimers();
#ifdef __cplusplus
}
#endif
/* Various. */
int pico_source_is_local(struct pico_frame *f);
int pico_frame_dst_is_unicast(struct pico_frame *f);

View File

@@ -871,6 +871,11 @@ int32_t pico_seq_compare(uint32_t a, uint32_t b)
return 0;
}
extern int pico_ntimers()
{
return Timers->n;
}
static void pico_check_timers(void)
{
struct pico_timer *t;

View File

@@ -73,7 +73,7 @@
// zwrite() might cause data loss, for this reason, sockets will SO_LINGER for
// a short period of time by default as a precaution.
#define ZT_SOCK_BEHAVIOR_LINGER true
#define ZT_SOCK_BEHAVIOR_LINGER false
#define ZT_SOCK_BEHAVIOR_LINGER_TIME 2 // s
/****************************************************************************/
@@ -358,10 +358,19 @@ namespace ZeroTier
extern ZeroTier::picoTCP *picostack;
}
/**
* Returns the number of sockets either already provisioned or waiting to be
* Some network stacks may have a limit on the number of sockets that they can
* safely handle due to timer construction, this is a way to check that we
* haven't passed that limit. Someday if multiple stacks are used simultaneously
* the logic for this function should change accordingly.
*/
int zts_nsockets();
/**
* Don't call this directly, use 'zts_start()'
*/
void *_start_service(void *thread_id);
void *zts_start_service(void *thread_id);
/****************************************************************************/
/* Debug */

View File

@@ -1,3 +1,28 @@
#
# ZeroTier SDK - Network Virtualization Everywhere
# Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# --
#
# You can be released from the requirements of the license by purchasing
# a commercial license. Buying such a license is mandatory as soon as you
# develop commercial closed-source software that incorporates or links
# directly against ZeroTier software without disclosing the source code
# of your own application.
#
# Automagically pick clang or gcc, with preference for clang
# This is only done if we have not overridden these with an environment or CLI variable
@@ -27,8 +52,7 @@ PICO_LIB = $(PICO_DIR)/build/lib/$(PICO_LIB_NAME)
SHARED_JNI_LIB = $(BUILD)/$(JNI_LIB_NAME)
#
TEST_BUILD_DIR = $(BUILD)/test
UNIT_TEST_SRC_DIR = test/unit
DUMB_TEST_SRC_DIR = test/dumb
UNIT_TEST_SRC_DIR = test
##############################################################################
## General Configuration ##
@@ -136,7 +160,7 @@ PICO_OBJS+= ext/picotcp/build/lib/pico_device.o \
all:
tests: dumb_tests unit_tests
tests: unit_tests
##############################################################################
## User-Space Stack ##
@@ -177,20 +201,6 @@ $(TEST_BUILD_DIR)/%: $(UNIT_TEST_SRC_DIR)/%.cpp
unit_tests: $(UNIT_TEST_OBJ_FILES)
##############################################################################
## Non-ZT Client/Server Tests ##
##############################################################################
DUMB_TEST_SRC_FILES := $(wildcard $(DUMB_TEST_SRC_DIR)/*.cpp)
DUMB_TEST_OBJ_FILES := $(addprefix $(TEST_BUILD_DIR)/,$(notdir $(DUMB_TEST_SRC_FILES:.cpp=)))
$(TEST_BUILD_DIR)/%: $(DUMB_TEST_SRC_DIR)/%.cpp
@mkdir -p $(TEST_BUILD_DIR)
@-$(CC) -o $@ $<
@-./check.sh $@
dumb_tests: $(DUMB_TEST_OBJ_FILES)
##############################################################################
## Misc ##
##############################################################################

View File

@@ -1,4 +1,29 @@
#
# ZeroTier SDK - Network Virtualization Everywhere
# Copyright (C) 2011-2017 ZeroTier, Inc. https://www.zerotier.com/
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# --
#
# You can be released from the requirements of the license by purchasing
# a commercial license. Buying such a license is mandatory as soon as you
# develop commercial closed-source software that incorporates or links
# directly against ZeroTier software without disclosing the source code
# of your own application.
#
# Automagically pick clang or gcc, with preference for clang
# This is only done if we have not overridden these with an environment or CLI variable
ifeq ($(origin CC),default)
@@ -27,8 +52,7 @@ PICO_LIB = $(PICO_DIR)/build/lib/$(PICO_LIB_NAME)
SHARED_JNI_LIB = $(BUILD)/$(JNI_LIB_NAME)
#
TEST_BUILD_DIR = $(BUILD)/test
UNIT_TEST_SRC_DIR = test/unit
DUMB_TEST_SRC_DIR = test/dumb
UNIT_TEST_SRC_DIR = test
##############################################################################
## General Configuration ##
@@ -135,7 +159,7 @@ PICO_OBJS+= ext/picotcp/build/lib/pico_device.o \
all:
tests: dumb_tests unit_tests
tests: unit_tests
##############################################################################
## User-Space Stack ##
@@ -176,20 +200,6 @@ $(TEST_BUILD_DIR)/%: $(UNIT_TEST_SRC_DIR)/%.cpp
unit_tests: $(UNIT_TEST_OBJ_FILES)
##############################################################################
## Non-ZT Client/Server Tests ##
##############################################################################
DUMB_TEST_SRC_FILES := $(wildcard $(DUMB_TEST_SRC_DIR)/*.cpp)
DUMB_TEST_OBJ_FILES := $(addprefix $(TEST_BUILD_DIR)/,$(notdir $(DUMB_TEST_SRC_FILES:.cpp=)))
$(TEST_BUILD_DIR)/%: $(DUMB_TEST_SRC_DIR)/%.cpp
@mkdir -p $(TEST_BUILD_DIR)
@-$(CC) -o $@ $<
@-./check.sh $@
dumb_tests: $(DUMB_TEST_OBJ_FILES)
##############################################################################
## Misc ##
##############################################################################

View File

@@ -75,7 +75,7 @@ namespace ZeroTier {
_unixListenSocket((PhySocket *)0),
_phy(this,false,true)
{
_thread = Thread::start(this);
_thread = Thread::start(this);
}
SocketTap::~SocketTap()
@@ -98,7 +98,7 @@ namespace ZeroTier {
return _enabled;
}
bool SocketTap::addIp(const InetAddress &ip)
bool SocketTap::registerIpWithStack(const InetAddress &ip)
{
if(picostack) {
if(ip.isV4())
@@ -123,6 +123,17 @@ namespace ZeroTier {
return false;
}
bool SocketTap::addIp(const InetAddress &ip)
{
if(registerIpWithStack(ip))
{
// only start the stack if we successfully registered and initialized a device to
// the given address
return true;
}
return false;
}
bool SocketTap::removeIp(const InetAddress &ip)
{
DEBUG_INFO();
@@ -269,11 +280,11 @@ namespace ZeroTier {
DEBUG_ERROR("invalid connection");
return;
}
picostack->pico_Close(conn);
if(!conn->sock) {
DEBUG_EXTRA("invalid PhySocket");
return;
}
picostack->pico_Close(conn);
// Here we assume _tcpconns_m is already locked by caller
// FIXME: is this assumption still valid
if(conn->sock)

View File

@@ -81,8 +81,14 @@ namespace ZeroTier {
void setEnabled(bool en);
bool enabled() const;
/*
* Registers a device with the given address
*/
bool registerIpWithStack(const InetAddress &ip);
/*
* Adds an address to the userspace stack interface associated with this SocketTap
* - Starts SocketTap main thread ONLY if successful
*/
bool addIp(const InetAddress &ip);

View File

@@ -37,6 +37,9 @@
#include <sys/types.h>
#include <pthread.h>
// stack
#include "pico_stack.h"
// ZT
#include "OneService.hpp"
#include "Utils.hpp"
@@ -55,8 +58,8 @@ extern "C" {
static ZeroTier::OneService *zt1Service;
namespace ZeroTier {
std::string homeDir; // The resultant platform-specific dir we *must* use internally
std::string netDir; // Where network .conf files are to be written
std::string homeDir; // The resultant platform-specific dir we *must* use internally
std::string netDir; // Where network .conf files are to be written
/*
* Global reference to stack
@@ -66,9 +69,8 @@ namespace ZeroTier {
/*
* "sockets" that have been created but not bound to a SocketTap interface yet
*/
std::map<int, Connection*> UnassignedConnections;
std::map<int, Connection*> unmap;
// FIXME: make sure these are properly deleted
/*
* For fast lookup of Connections and SocketTaps via given file descriptor
*/
@@ -96,7 +98,7 @@ void zts_start(const char *path)
if(path)
ZeroTier::homeDir = path;
pthread_t service_thread;
pthread_create(&service_thread, NULL, _start_service, (void *)(path));
pthread_create(&service_thread, NULL, zts_start_service, (void *)(path));
}
void zts_stop() {
@@ -316,8 +318,8 @@ Darwin:
[ ] [EACCES] Permission to create a socket of the specified type and/or protocol is denied.
[ ] [EAFNOSUPPORT] The specified address family is not supported.
[ ] [EMFILE] The per-process descriptor table is full.
[ ] [ENFILE] The system file table is full.
[--] [EMFILE] The per-process descriptor table is full.
[NA] [ENFILE] The system file table is full.
[ ] [ENOBUFS] Insufficient buffer space is available. The socket cannot be created until sufficient resources are freed.
[ ] [ENOMEM] Insufficient memory was available to fulfill the request.
[ ] [EPROTONOSUPPORT] The protocol type or the specified protocol is not supported within this domain.
@@ -326,44 +328,60 @@ Darwin:
// int socket_family, int socket_type, int protocol
int zts_socket(ZT_SOCKET_SIG) {
DEBUG_INFO("UnassConn=%d, fdmap=%d", ZeroTier::UnassignedConnections.size(), ZeroTier::fdmap.size());
DEBUG_INFO();
ZeroTier::_multiplexer_lock.lock();
ZeroTier::Connection *conn = new ZeroTier::Connection();
int err = 0, protocol_version = 0;
// set up pico_socket
struct pico_socket * psock;
// TODO: check ifdef logic here
#if defined(SDK_IPV4)
protocol_version = PICO_PROTO_IPV4;
#endif
#if defined(SDK_IPV6)
protocol_version = PICO_PROTO_IPV6;
#endif
if(socket_type == SOCK_DGRAM) {
psock = pico_socket_open(
protocol_version, PICO_PROTO_UDP, &ZeroTier::picoTCP::pico_cb_socket_activity);
}
if(socket_type == SOCK_STREAM) {
psock = pico_socket_open(
protocol_version, PICO_PROTO_TCP, &ZeroTier::picoTCP::pico_cb_socket_activity);
}
// set up Unix Domain socketpair (used for data later on)
if(psock) {
conn->socket_family = socket_family;
conn->socket_type = socket_type;
conn->picosock = psock;
memset(conn->rxbuf, 0, ZT_UDP_RX_BUF_SZ);
ZeroTier::UnassignedConnections[conn->app_fd] = conn;
err = conn->app_fd; // return one end of the socketpair
}
else {
DEBUG_ERROR("failed to create pico_socket");
int err = 0;
if(!zt1Service) {
DEBUG_ERROR("cannot create socket, no service running. call zts_start() first.");
errno = EMFILE; // could also be ENFILE
err = -1;
}
ZeroTier::_multiplexer_lock.unlock();
else {
ZeroTier::_multiplexer_lock.lock();
//DEBUG_INFO("unmap=%d, fdmap=%d", ZeroTier::unmap.size(), ZeroTier::fdmap.size());
DEBUG_INFO("timers = %d, max = %d", pico_ntimers(), PICO_MAX_TIMERS);
if(pico_ntimers() >= PICO_MAX_TIMERS) {
DEBUG_ERROR("cannot provision additional socket due to limitation of PICO_MAX_TIMERS. current = %d", pico_ntimers());
errno = EMFILE;
err = -1;
}
else
{
ZeroTier::Connection *conn = new ZeroTier::Connection();
int protocol_version = 0;
struct pico_socket *psock;
// TODO: check ifdef logic here
#if defined(SDK_IPV4)
protocol_version = PICO_PROTO_IPV4;
#endif
#if defined(SDK_IPV6)
protocol_version = PICO_PROTO_IPV6;
#endif
if(socket_type == SOCK_DGRAM) {
psock = pico_socket_open(
protocol_version, PICO_PROTO_UDP, &ZeroTier::picoTCP::pico_cb_socket_activity);
}
if(socket_type == SOCK_STREAM) {
psock = pico_socket_open(
protocol_version, PICO_PROTO_TCP, &ZeroTier::picoTCP::pico_cb_socket_activity);
}
// set up Unix Domain socketpair (used for data later on)
if(psock) {
conn->socket_family = socket_family;
conn->socket_type = socket_type;
conn->picosock = psock;
memset(conn->rxbuf, 0, ZT_UDP_RX_BUF_SZ);
ZeroTier::unmap[conn->app_fd] = conn;
err = conn->app_fd; // return one end of the socketpair
}
else {
DEBUG_ERROR("failed to create pico_socket");
err = -1;
}
}
//DEBUG_INFO(" unmap=%d, fdmap=%d", ZeroTier::unmap.size(), ZeroTier::fdmap.size());
ZeroTier::_multiplexer_lock.unlock();
}
return err;
}
@@ -377,16 +395,16 @@ Darwin:
[ ] [EADDRNOTAVAIL] The specified address is not available on this machine.
[ ] [EAFNOSUPPORT] Addresses in the specified address family cannot be used with this socket.
[ ] [EALREADY] The socket is non-blocking and a previous connection attempt has not yet been completed.
[ ] [EBADF] socket is not a valid descriptor.
[--] [EBADF] socket is not a valid descriptor.
[ ] [ECONNREFUSED] The attempt to connect was ignored (because the target is not listening for connections) or explicitly rejected.
[ ] [EFAULT] The address parameter specifies an area outside the process address space.
[ ] [EHOSTUNREACH] The target host cannot be reached (e.g., down, disconnected).
[ ] [EINPROGRESS] The socket is non-blocking and the connection cannot be completed immediately. It is possible to select(2) for completion by selecting the socket for writing.
[ ] [EINTR] Its execution was interrupted by a signal.
[--] [EINPROGRESS] The socket is non-blocking and the connection cannot be completed immediately. It is possible to select(2) for completion by selecting the socket for writing.
[NA] [EINTR] Its execution was interrupted by a signal.
[ ] [EINVAL] An invalid argument was detected (e.g., address_len is not valid for the address family, the specified address family is invalid).
[ ] [EISCONN] The socket is already connected.
[ ] [ENETDOWN] The local network interface is not functioning.
[ ] [ENETUNREACH] The network isn't reachable from this host.
[--] [ENETUNREACH] The network isn't reachable from this host.
[ ] [ENOBUFS] The system call was unable to allocate a needed memory buffer.
[ ] [ENOTSOCK] socket is not a file descriptor for a socket.
[ ] [EOPNOTSUPP] Because socket is listening, no connection is allowed.
@@ -396,14 +414,18 @@ Darwin:
*/
int zts_connect(ZT_CONNECT_SIG) {
DEBUG_INFO("fd = %d", fd);
int err = 0;
if(fd < 0) {
errno = EBADF;
err = -1;
}
if(!zt1Service) {
DEBUG_ERROR("Service not started. Call zts_start(path) first");
// errno = ?
errno = EBADF;
return -1;
}
ZeroTier::_multiplexer_lock.lock();
int err;
ZeroTier::Connection *conn = ZeroTier::UnassignedConnections[fd];
ZeroTier::Connection *conn = ZeroTier::unmap[fd];
ZeroTier::SocketTap *tap;
if(conn) {
@@ -412,23 +434,22 @@ int zts_connect(ZT_CONNECT_SIG) {
ZeroTier::InetAddress iaddr;
if(conn->socket_family == AF_INET) {
// FIXME: Fix this typecast mess
inet_ntop(AF_INET,
(const void *)&((struct sockaddr_in *)addr)->sin_addr.s_addr, ipstr, INET_ADDRSTRLEN);
iaddr.fromString(ipstr);
}
if(conn->socket_family == AF_INET6) {
// FIXME: Fix this typecast mess
inet_ntop(AF_INET6,
(const void *)&((struct sockaddr_in6 *)addr)->sin6_addr.s6_addr, ipstr, INET6_ADDRSTRLEN);
// 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("iaddr= %s", iaddr.toString().c_str());
// DEBUG_INFO("ipstr= %s", ipstr);
// DEBUG_INFO("iaddr= %s", iaddr.toString().c_str());
tap = zt1Service->getTap(iaddr);
if(!tap) {
// TODO: More canonical error?
DEBUG_ERROR("no route to host");
// errno = ?
errno = ENETUNREACH;
err = -1;
}
else {
@@ -450,10 +471,10 @@ int zts_connect(ZT_CONNECT_SIG) {
}
else {
DEBUG_ERROR("unable to locate connection");
// errno = ?
errno = EBADF;
err = -1;
}
ZeroTier::UnassignedConnections.erase(fd);
ZeroTier::unmap.erase(fd);
ZeroTier::fdmap[fd] = new std::pair<ZeroTier::Connection*,ZeroTier::SocketTap*>(conn, tap);
ZeroTier::_multiplexer_lock.unlock();
@@ -463,38 +484,45 @@ int zts_connect(ZT_CONNECT_SIG) {
// to the multiplexer logic that this connection is complete and a success value can be sent to the
// user application
// non-blocking
if(err == 0 && false) {
errno = EINPROGRESS;
return -1;
}
socklen_t optlen;
socklen_t blocking;
zts_getsockopt(fd, SOL_SOCKET, O_NONBLOCK, &blocking, &optlen);
// blocking
// FIXME: Double check that accept/connect queues in multithreaded apps don't get mixed up
if(err == 0 && true) {
bool complete = false;
while(true)
{
// FIXME: locking and unlocking so often might cause a performance bottleneck while outgoing connections
// are being established (also applies to accept())
usleep(ZT_CONNECT_RECHECK_DELAY * 1000);
tap->_tcpconns_m.lock();
for(int i=0; i<tap->_Connections.size(); i++)
// non-blocking
if(err == 0 && !blocking) {
DEBUG_INFO("NONBLOCKING!");
errno = EINPROGRESS;
err = -1;
}
else // blocking
{
// FIXME: Double check that accept/connect queues in multithreaded apps don't get mixed up
if(err == 0 && blocking) {
DEBUG_INFO("BLOCKING!");
bool complete = false;
while(true)
{
if(tap->_Connections[i]->state == PICO_ERR_ECONNRESET) {
errno = ECONNRESET;
err = -1;
}
if(tap->_Connections[i]->state == ZT_SOCK_STATE_UNHANDLED_CONNECTED) {
tap->_Connections[i]->state = ZT_SOCK_STATE_CONNECTED;
errno = 0;
err = 0; // complete
complete = true;
// FIXME: locking and unlocking so often might cause a performance bottleneck while outgoing connections
// are being established (also applies to accept())
usleep(ZT_CONNECT_RECHECK_DELAY * 1000);
tap->_tcpconns_m.lock();
for(int i=0; i<tap->_Connections.size(); i++)
{
if(tap->_Connections[i]->state == PICO_ERR_ECONNRESET) {
errno = ECONNRESET;
err = -1;
}
if(tap->_Connections[i]->state == ZT_SOCK_STATE_UNHANDLED_CONNECTED) {
tap->_Connections[i]->state = ZT_SOCK_STATE_CONNECTED;
errno = 0;
err = 0; // complete
complete = true;
}
}
tap->_tcpconns_m.unlock();
if(complete)
break;
}
tap->_tcpconns_m.unlock();
if(complete)
break;
}
}
return err;
@@ -504,9 +532,9 @@ int zts_connect(ZT_CONNECT_SIG) {
Darwin:
[ ] [EBADF] S is not a valid descriptor.
[--] [EBADF] S is not a valid descriptor.
[ ] [ENOTSOCK] S is not a socket.
[ ] [EADDRNOTAVAIL] The specified address is not available from the local
[--] [EADDRNOTAVAIL] The specified address is not available from the local
machine.
[ ] [EADDRINUSE] The specified address is already in use.
[ ] [EINVAL] The socket is already bound to an address.
@@ -517,14 +545,18 @@ Darwin:
*/
int zts_bind(ZT_BIND_SIG) {
DEBUG_INFO("fd = %d", fd);
int err = 0;
if(fd < 0) {
errno = EBADF;
err = -1;
}
if(!zt1Service) {
DEBUG_ERROR("Service not started. Call zts_start(path) first");
// errno = ?
return -1;
errno = EBADF;
err = -1;
}
ZeroTier::_multiplexer_lock.lock();
int err;
ZeroTier::Connection *conn = ZeroTier::UnassignedConnections[fd];
ZeroTier::Connection *conn = ZeroTier::unmap[fd];
ZeroTier::SocketTap *tap;
if(conn) {
@@ -544,26 +576,27 @@ int zts_bind(ZT_BIND_SIG) {
tap = zt1Service->getTap(iaddr);
if(!tap) {
// TODO: More canonical error?
DEBUG_ERROR("no appropriate interface to bind to");
// errno = ?
DEBUG_ERROR("no matching interface to bind to");
errno = EADDRNOTAVAIL;
err = -1;
}
else {
//DEBUG_INFO("found appropriate SocketTap");
//DEBUG_INFO("conn->picosock = %p", conn->picosock);
conn->picosock->priv = new ZeroTier::ConnectionPair(tap, conn);
tap->_Connections.push_back(conn); // Give this Connection to the tap we decided on
err = tap->Bind(conn, fd, addr, addrlen); // Semantically: tap->stack->connect
conn->tap = tap;
if(err == 0) { // success
ZeroTier::unmap.erase(fd);
ZeroTier::fdmap[fd] = new std::pair<ZeroTier::Connection*,ZeroTier::SocketTap*>(conn, tap);
}
}
}
else {
DEBUG_ERROR("unable to locate connection");
// errno = ?
errno = EBADF;
err = -1;
}
ZeroTier::fdmap[fd] = new std::pair<ZeroTier::Connection*,ZeroTier::SocketTap*>(conn, tap);
ZeroTier::_multiplexer_lock.unlock();
return err;
}
@@ -572,8 +605,8 @@ int zts_bind(ZT_BIND_SIG) {
Darwin:
[ ] [EACCES] The current process has insufficient privileges.
[ ] [EBADF] The argument socket is not a valid file descriptor.
[--] [EACCES] The current process has insufficient privileges.
[--] [EBADF] The argument socket is not a valid file descriptor.
[ ] [EDESTADDRREQ] The socket is not bound to a local address and the protocol does not support listening on an unbound socket.
[ ] [EINVAL] socket is already connected.
[ ] [ENOTSOCK] The argument socket does not reference a socket.
@@ -581,12 +614,16 @@ Darwin:
*/
int zts_listen(ZT_LISTEN_SIG) {
DEBUG_INFO("fd = %d", fd);
int err = 0;
if(fd < 0) {
errno = EBADF;
err = -1;
}
if(!zt1Service) {
DEBUG_ERROR("Service not started. Call zts_start(path) first");
// errno = ?
DEBUG_ERROR("service not started. call zts_start(path) first");
errno = EACCES;
return -1;
}
int err;
ZeroTier::_multiplexer_lock.lock();
std::pair<ZeroTier::Connection*, ZeroTier::SocketTap*> *p = ZeroTier::fdmap[fd];
if(!p) {
@@ -610,45 +647,57 @@ int zts_listen(ZT_LISTEN_SIG) {
Darwin:
[ ] [EBADF] The descriptor is invalid.
[--] [EBADF] The descriptor is invalid.
[ ] [ENOTSOCK] The descriptor references a file, not a socket.
[ ] [EOPNOTSUPP] The referenced socket is not of type SOCK_STREAM.
[ ] [EFAULT] The addr parameter is not in a writable part of the
user address space.
[ ] [EWOULDBLOCK] The socket is marked non-blocking and no connections
are present to be accepted.
[ ] [EMFILE] The per-process descriptor table is full.
[--] [EMFILE] The per-process descriptor table is full.
[ ] [ENFILE] The system file table is full.
*/
int zts_accept(ZT_ACCEPT_SIG) {
DEBUG_INFO("fd = %d", fd);
int err = 0;
if(fd < 0) {
errno = EBADF;
err = -1;
}
// +1 since we'll be creating a new pico_socket when we accept the connection
if(pico_ntimers()+1 >= PICO_MAX_TIMERS) {
DEBUG_ERROR("cannot provision additional socket due to limitation of PICO_MAX_TIMERS.");
errno = EMFILE;
err = -1;
}
ZeroTier::_multiplexer_lock.lock();
int err = -1;
std::pair<ZeroTier::Connection*, ZeroTier::SocketTap*> *p = ZeroTier::fdmap[fd];
if(!p) {
DEBUG_ERROR("unable to locate connection pair (did you zbind())?");
return -1;
DEBUG_ERROR("unable to locate connection pair (did you zts_bind())?");
errno = EBADF;
err = -1;
}
ZeroTier::Connection *conn = p->first;
ZeroTier::SocketTap *tap = p->second;
ZeroTier::Connection *accepted_conn;
// BLOCKING: loop and keep checking until we find a newly accepted connection
if(true) {
while(true) {
usleep(ZT_ACCEPT_RECHECK_DELAY * 1000);
accepted_conn = tap->Accept(conn);
if(accepted_conn)
break; // accepted fd = err
else {
ZeroTier::Connection *conn = p->first;
ZeroTier::SocketTap *tap = p->second;
ZeroTier::Connection *accepted_conn;
// BLOCKING: loop and keep checking until we find a newly accepted connection
if(true) {
while(true) {
usleep(ZT_ACCEPT_RECHECK_DELAY * 1000);
accepted_conn = tap->Accept(conn);
if(accepted_conn)
break; // accepted fd = err
}
}
}
// NON-BLOCKING: only check for a new connection once
else
accepted_conn = tap->Accept(conn);
// NON-BLOCKING: only check for a new connection once
else
accepted_conn = tap->Accept(conn);
if(accepted_conn) {
ZeroTier::fdmap[accepted_conn->app_fd] = new std::pair<ZeroTier::Connection*,ZeroTier::SocketTap*>(accepted_conn, tap);
err = accepted_conn->app_fd;
if(accepted_conn) {
ZeroTier::fdmap[accepted_conn->app_fd] = new std::pair<ZeroTier::Connection*,ZeroTier::SocketTap*>(accepted_conn, tap);
err = accepted_conn->app_fd;
}
}
ZeroTier::_multiplexer_lock.unlock();
return err;
@@ -659,19 +708,19 @@ int zts_accept(ZT_ACCEPT_SIG) {
Linux accept() (and accept4()) passes already-pending network errors on the new socket as an error code from accept(). This behavior differs from other BSD socket implementations. For reliable operation the application should detect the network errors defined for the protocol after accept() and treat them like EAGAIN by retrying. In the case of TCP/IP, these are ENETDOWN, EPROTO, ENOPROTOOPT, EHOSTDOWN, ENONET, EHOSTUNREACH, EOPNOTSUPP, and ENETUNREACH.
Errors
EAGAIN or EWOULDBLOCK The socket is marked nonblocking and no connections are present to be accepted. POSIX.1-2001 allows either error to be returned for this case, and does not require these constants to have the same value, so a portable application should check for both possibilities.
EBADF The descriptor is invalid.
ECONNABORTED A connection has been aborted.
EFAULT The addr argument is not in a writable part of the user address space.
EINTR The system call was interrupted by a signal that was caught before a valid connection arrived; see signal(7).
EINVAL Socket is not listening for connections, or addrlen is invalid (e.g., is negative).
EINVAL (accept4()) invalid value in flags.
EMFILE The per-process limit of open file descriptors has been reached.
ENFILE The system limit on the total number of open files has been reached.
ENOBUFS, ENOMEM Not enough free memory. This often means that the memory allocation is limited by the socket buffer limits, not by the system memory.
ENOTSOCK The descriptor references a file, not a socket.
EOPNOTSUPP The referenced socket is not of type SOCK_STREAM.
EPROTO Protocol error.
[ ] EAGAIN or EWOULDBLOCK The socket is marked nonblocking and no connections are present to be accepted. POSIX.1-2001 allows either error to be returned for this case, and does not require these constants to have the same value, so a portable application should check for both possibilities.
[--] EBADF The descriptor is invalid.
[ ] ECONNABORTED A connection has been aborted.
[ ] EFAULT The addr argument is not in a writable part of the user address space.
[NA] EINTR The system call was interrupted by a signal that was caught before a valid connection arrived; see signal(7).
[ ] EINVAL Socket is not listening for connections, or addrlen is invalid (e.g., is negative).
[ ] EINVAL (accept4()) invalid value in flags.
[ ] EMFILE The per-process limit of open file descriptors has been reached.
[ ] ENFILE The system limit on the total number of open files has been reached.
[ ] ENOBUFS, ENOMEM Not enough free memory. This often means that the memory allocation is limited by the socket buffer limits, not by the system memory.
[ ] ENOTSOCK The descriptor references a file, not a socket.
[ ] EOPNOTSUPP The referenced socket is not of type SOCK_STREAM.
[ ] EPROTO Protocol error.
In addition, Linux accept() may fail if:
@@ -681,14 +730,18 @@ EPERM Firewall rules forbid connection.
int zts_accept4(ZT_ACCEPT4_SIG)
{
DEBUG_INFO("fd = %d", fd);
// TODO
int err = 0;
if(fd < 0) {
errno = EBADF;
err = -1;
}
return 0;
}
#endif
/*
[ ] [EBADF] The argument s is not a valid descriptor.
[--] [EBADF] The argument s is not a valid descriptor.
[ ] [ENOTSOCK] The argument s is a file, not a socket.
[ ] [ENOPROTOOPT] The option is unknown at the level indicated.
[ ] [EFAULT] The address pointed to by optval is not in a valid
@@ -700,13 +753,18 @@ EPERM Firewall rules forbid connection.
int zts_setsockopt(ZT_SETSOCKOPT_SIG)
{
DEBUG_INFO("fd = %d", fd);
int err = setsockopt(fd, level, optname, optval, optlen);
int err = 0;
if(fd < 0) {
errno = EBADF;
err = -1;
}
err = setsockopt(fd, level, optname, optval, optlen);
DEBUG_INFO("err = %d", err);
return err;
}
/*
[ ] [EBADF] The argument s is not a valid descriptor.
[--] [EBADF] The argument s is not a valid descriptor.
[ ] [ENOTSOCK] The argument s is a file, not a socket.
[ ] [ENOPROTOOPT] The option is unknown at the level indicated.
[ ] [EFAULT] The address pointed to by optval is not in a valid
@@ -719,11 +777,17 @@ int zts_setsockopt(ZT_SETSOCKOPT_SIG)
int zts_getsockopt(ZT_GETSOCKOPT_SIG)
{
DEBUG_INFO("fd = %d", fd);
return getsockopt(fd, level, optname, optval, optlen);
int err = 0;
if(fd < 0) {
errno = EBADF;
err = -1;
}
err = getsockopt(fd, level, optname, optval, optlen);
return err;
}
/*
[ ] [EBADF] The argument s is not a valid descriptor.
[--] [EBADF] The argument s is not a valid descriptor.
[ ] [ENOTSOCK] The argument s is a file, not a socket.
[ ] [ENOBUFS] Insufficient resources were available in the system to
perform the operation.
@@ -733,12 +797,17 @@ int zts_getsockopt(ZT_GETSOCKOPT_SIG)
int zts_getsockname(ZT_GETSOCKNAME_SIG)
{
DEBUG_INFO("fd = %d", fd);
int err = 0;
if(fd < 0) {
errno = EBADF;
err = -1;
}
// TODO
return 0;
return err;
}
/*
[ ] [EBADF] The argument s is not a valid descriptor.
[--] [EBADF] The argument s is not a valid descriptor.
[ ] [ENOTSOCK] The argument s is a file, not a socket.
[ ] [ENOTCONN] The socket is not connected.
[ ] [ENOBUFS] Insufficient resources were available in the system to
@@ -749,8 +818,13 @@ int zts_getsockname(ZT_GETSOCKNAME_SIG)
int zts_getpeername(ZT_GETPEERNAME_SIG)
{
DEBUG_INFO("fd = %d", fd);
int err = 0;
if(fd < 0) {
errno = EBADF;
err = -1;
}
// TODO
return 0;
return err;
}
/*
@@ -761,77 +835,142 @@ Linux:
Darwin:
[OK] [EBADF] fildes is not a valid, active file descriptor.
[ ] [EINTR] Its execution was interrupted by a signal.
[--] [EBADF] fildes is not a valid, active file descriptor.
[NA] [EINTR] Its execution was interrupted by a signal.
[ ] [EIO] A previously-uncommitted write(2) encountered an input/output error.
*/
// FIXME: See pico_socket_setoption's LINGER functionality
int zts_close(ZT_CLOSE_SIG)
{
DEBUG_INFO("fd = %d", fd);
// TODO: Close should handle situations where data is still being
// processed by the SocketTap (such as a zwrite() call)
// check out SO_LINGER
ZeroTier::_multiplexer_lock.lock();
std::pair<ZeroTier::Connection*, ZeroTier::SocketTap*> *p = ZeroTier::fdmap[fd];
if(!p) {
DEBUG_ERROR("unable to locate connection pair.?");
//DEBUG_INFO("fd = %d", fd);
int err = 0;
if(fd < 0) {
errno = EBADF;
return -1;
err = -1;
}
ZeroTier::Connection *conn = p->first;
ZeroTier::SocketTap *tap = p->second;
if(!zt1Service) {
DEBUG_ERROR("cannot close socket. service not started. call zts_start(path) first");
errno = EBADF;
err = -1;
}
else
{
ZeroTier::_multiplexer_lock.lock();
//DEBUG_INFO("unmap=%d, fdmap=%d", ZeroTier::unmap.size(), ZeroTier::fdmap.size());
if(ZT_SOCK_BEHAVIOR_LINGER) {
socklen_t optlen;
struct linger so_linger;
zts_getsockopt(fd, SOL_SOCKET, SO_LINGER, &so_linger, &optlen);
if (so_linger.l_linger != 0) {
sleep(so_linger.l_linger); // do the linger!
}
// First, look for for unassigned connections
ZeroTier::Connection *conn = ZeroTier::unmap[fd];
// Since we found an unassigned connection, we don't need to consult the stack or tap
// during closure - it isn't yet stitched into the clockwork
if(conn) // unassigned
{
if((err = pico_socket_close(conn->picosock)) < 0)
DEBUG_ERROR("error calling pico_socket_close()");
if((err = close(conn->app_fd)) < 0)
DEBUG_ERROR("error closing app_fd");
if((err = close(conn->sdk_fd)) < 0)
DEBUG_ERROR("error closing sdk_fd");
delete conn;
ZeroTier::unmap.erase(fd);
}
else // assigned
{
std::pair<ZeroTier::Connection*, ZeroTier::SocketTap*> *p = ZeroTier::fdmap[fd];
if(!p)
{
//DEBUG_ERROR("unable to locate connection pair.");
errno = EBADF;
err = -1;
}
else
{
conn = p->first;
ZeroTier::SocketTap *tap = p->second;
// For cases where data might still need to pass through the library
// before socket closure
if(ZT_SOCK_BEHAVIOR_LINGER) {
socklen_t optlen;
struct linger so_linger;
zts_getsockopt(fd, SOL_SOCKET, SO_LINGER, &so_linger, &optlen);
if (so_linger.l_linger != 0) {
sleep(so_linger.l_linger); // do the linger!
}
}
// Tell the tap to stop monitoring this PhySocket
//if((err = pico_socket_close(conn->picosock)) < 0)
// DEBUG_ERROR("error calling pico_socket_close()");
tap->Close(conn);
// delete objects
// FIXME: double check this
delete p;
ZeroTier::fdmap.erase(fd);
err = 0;
}
}
//DEBUG_INFO(" unmap=%d, fdmap=%d", ZeroTier::unmap.size(), ZeroTier::fdmap.size());
ZeroTier::_multiplexer_lock.unlock();
}
// Tell the tap to stop monitoring this PhySocket
tap->Close(conn);
// delete objects
// FIXME: double check this
delete p;
ZeroTier::fdmap[fd] = NULL; // erase?
ZeroTier::_multiplexer_lock.unlock();
return 0;
return err;
}
int zts_fcntl(ZT_FCNTL_SIG)
{
DEBUG_INFO("fd = %d", fd);
return fcntl(fd, cmd, flags);
int err;
if(fd < 0) {
errno = EBADF;
err = -1;
}
err = fcntl(fd, cmd, flags);
return err;
}
ssize_t zts_sendto(ZT_SENDTO_SIG)
{
DEBUG_INFO("fd = %d", fd);
// TODO
return 0;
int err;
if(fd < 0) {
errno = EBADF;
err = -1;
}
return err;
}
ssize_t zts_sendmsg(ZT_SENDMSG_SIG)
{
DEBUG_INFO("fd = %d", fd);
// TODO
return 0;
int err;
if(fd < 0) {
errno = EBADF;
err = -1;
}
return err;
}
ssize_t zts_recvfrom(ZT_RECVFROM_SIG)
{
DEBUG_INFO("fd = %d", fd);
// TODO
return 0;
int err;
if(fd < 0) {
errno = EBADF;
err = -1;
}
return err;
}
ssize_t zts_recvmsg(ZT_RECVMSG_SIG)
{
DEBUG_INFO("fd = %d", fd);
// TODO
return 0;
int err;
if(fd < 0) {
errno = EBADF;
err = -1;
}
return err;
}
int zts_read(ZT_READ_SIG) {
@@ -844,8 +983,6 @@ int zts_write(ZT_WRITE_SIG) {
return write(fd, buf, len);
}
/****************************************************************************/
/* SDK Socket API (Java Native Interface JNI) */
/* JNI naming convention: Java_PACKAGENAME_CLASSNAME_METHODNAME */
@@ -1108,8 +1245,16 @@ namespace ZeroTier {
/* SDK Socket API Helper functions --- DONT CALL THESE DIRECTLY */
/****************************************************************************/
int zts_nsockets()
{
ZeroTier::_multiplexer_lock.unlock();
int num = ZeroTier::unmap.size() + ZeroTier::fdmap.size();
ZeroTier::_multiplexer_lock.unlock();
return num;
}
// Starts a ZeroTier service in the background
void *_start_service(void *thread_id) {
void *zts_start_service(void *thread_id) {
DEBUG_INFO("homeDir=%s", ZeroTier::homeDir.c_str());
// Where network .conf files will be stored

View File

@@ -81,6 +81,10 @@ struct pico_socket * pico_socket_accept(PICO_SOCKET_ACCEPT_SIG);
namespace ZeroTier {
// FIXME: Determine why stack interrupt code fails when picodev is a mmember of a SocketTap
struct pico_device picodev;
bool picoTCP::pico_init_interface(SocketTap *tap, const InetAddress &ip)
{
DEBUG_INFO();
@@ -89,22 +93,22 @@ namespace ZeroTier {
std::sort(tap->_ips.begin(),tap->_ips.end());
if(ip.isV4())
{
tap->picodev = new struct pico_device;
//tap->picodev = new struct pico_device;
struct pico_ip4 ipaddr, netmask;
ipaddr.addr = *((uint32_t *)ip.rawIpData());
netmask.addr = *((uint32_t *)ip.netmask().rawIpData());
tap->picodev->send = pico_eth_send; // tx
tap->picodev->poll = pico_eth_poll; // rx
tap->picodev->mtu = tap->_mtu;
tap->picodev->tap = tap;
picodev.send = pico_eth_send; // tx
picodev.poll = pico_eth_poll; // rx
picodev.mtu = tap->_mtu;
picodev.tap = tap;
uint8_t mac[PICO_SIZE_ETH];
tap->_mac.copyTo(mac, PICO_SIZE_ETH);
if(pico_device_init(tap->picodev, "p4", mac) != 0) {
if(pico_device_init(&picodev, "p4", mac) != 0) {
DEBUG_ERROR("dev init failed");
delete tap->picodev;
delete &picodev;
return false;
}
pico_ipv4_link_add(tap->picodev, ipaddr, netmask);
pico_ipv4_link_add(&picodev, ipaddr, netmask);
DEBUG_INFO("addr = %s", ip.toString().c_str());
return true;
}
@@ -114,22 +118,22 @@ namespace ZeroTier {
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;
//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);
tap->picodev6->send = pico_eth_send; // tx
tap->picodev6->poll = pico_eth_poll; // rx
tap->picodev6->mtu = tap->_mtu;
tap->picodev6->tap = tap;
picodev.send = pico_eth_send; // tx
picodev.poll = pico_eth_poll; // rx
picodev.mtu = tap->_mtu;
picodev.tap = tap;
uint8_t mac[PICO_SIZE_ETH];
tap->_mac.copyTo(mac, PICO_SIZE_ETH);
if(pico_device_init(tap->picodev6, "p6", mac) != 0) {
if(pico_device_init(&picodev, "p6", mac) != 0) {
DEBUG_ERROR("dev init failed");
delete tap->picodev6;
delete &picodev;
return false;
}
pico_ipv6_link_add(tap->picodev6, ipaddr, netmask);
pico_ipv6_link_add(&picodev, ipaddr, netmask);
DEBUG_INFO("addr6 = %s", ip.toString().c_str());
return true;
}
@@ -141,7 +145,7 @@ namespace ZeroTier {
{
while(tap->_run)
{
tap->_phy.poll(ZT_PHY_POLL_INTERVAL); // in ms
tap->_phy.poll(ZT_PHY_POLL_INTERVAL);
pico_stack_tick();
}
}
@@ -162,7 +166,6 @@ namespace ZeroTier {
if(avail) {
r = pico_socket_recvfrom(s, conn->rxbuf + (conn->rxsz), ZT_SDK_MTU,
(void *)&peer.ip4.addr, &port);
//tap->_phy.setNotifyWritable(conn->sock, true);
if (r > 0)
conn->rxsz += r;
picostack->pico_Read(tap, conn->sock, conn, true);
@@ -312,16 +315,6 @@ namespace ZeroTier {
conn->_AcceptedConnections.push(newConn);
// For I/O loop participation and referencing the PhySocket's parent Connection in callbacks
newConn->sock = tap->_phy.wrapSocket(newConn->sdk_fd, newConn);
/*
DEBUG_INFO("wrapping newConn->sdk_fd = %d", newConn->sdk_fd);
DEBUG_INFO(" newConn->app_fd = %d", newConn->app_fd);
DEBUG_INFO(" newConn->sock = %p", newConn->sock);
DEBUG_INFO(" conn = %p", conn);
DEBUG_INFO(" newConn = %p", newConn);
DEBUG_INFO(" oldConn->sock = %p", conn->sock);
*/
}
if(conn->state != ZT_SOCK_STATE_LISTENING) {
// set state so socket multiplexer logic will pick this up
@@ -656,6 +649,10 @@ namespace ZeroTier {
void picoTCP::pico_Write(Connection *conn, void *data, ssize_t len)
{
if(!conn) {
DEBUG_ERROR("invalid connection");
return;
}
unsigned char *buf = (unsigned char*)data;
memcpy(conn->txbuf + conn->txsz, buf, len);
conn->txsz += len;
@@ -699,8 +696,7 @@ namespace ZeroTier {
if((err = pico_socket_close(conn->picosock)) < 0) {
errno = pico_err;
DEBUG_ERROR("error closing pico_socket(%p)", (void*)(conn->picosock));
return err;
}
return -1;
return err;
}
}

View File

@@ -39,6 +39,7 @@
#include <fcntl.h>
#include <iostream>
#include <vector>
#include "ZeroTierSDK.h"
@@ -46,6 +47,7 @@
#define FAILED -1
#define ECHO_INTERVAL 100000 // us
#define SLAM_INTERVAL 50000
#define STR_SIZE 32
#define TEST_OP_N_BYTES 10
@@ -58,39 +60,47 @@
#define TEST_TYPE_SIMPLE 30
#define TEST_TYPE_SUSTAINED 31
#define MIN_PORT 5000
#define MAX_PORT 50000
char str[STR_SIZE];
// [] random
// [OK] simple client ipv4
// [OK] simple server ipv4
// [?] simple client ipv6
// [?] simple server ipv6
// [OK] sustained client ipv4
// [OK] sustained server ipv4
// [?] sustained client ipv6
// [?] sustained server ipv6
// [] comprehensive client ipv4
// [] comprehensive server ipv6
/* Tests in this file:
/* Performance Tests
Throughput
Memory Usage
Processor usage
Basic RX/TX connect()/accept() Functionality:
socket API semantics
- Proper socket closure
- Proper handling of blocking/non-blocking behaviour
- replicate specific errno conditions and verify correctness
Network semantics
- Multi-network handling
- Address handling
[ ?] slam - perform thousands of the same call per second
[ ] random - act like a monkey, press all the buttons
[OK] simple client ipv4 - connect, send one message and wait for an echo
[OK] simple server ipv4 - accept, read one message and echo it back
[OK] simple client ipv6 - connect, send one message and wait for an echo
[OK] simple server ipv6 - accept, read one message and echo it back
[OK] sustained client ipv4 - connect and rx/tx many messages
[OK] sustained server ipv4 - accept and echo messages
[ ?] sustained client ipv6 - connect and rx/tx many messages
[ ?] sustained server ipv6 - accept and echo messages
[ ] comprehensive client ipv4 - test all ipv4/6 client simple/sustained modes
[ ] comprehensive server ipv6 - test all ipv4/6 server simple/sustained modes
ZeroTier-specific functionality
Performance:
[ ] Throughput - Test maximum RX/TX speeds
[ ] Memory Usage - Test memory consumption profile
[ ] CPU Usage - Test processor usage
[ ] Multithreaded Throughput -
[ ] Multithreaded CPU Usage -
Correctness:
[ ] Block/Non-block - Test that blocking and non-blocking behaviour is consistent
[ ] Release of resources - Test that all destructor methods/blocks function properly
[ ] Multi-network handling - Test internal Tap multiplexing works for multiple networks
[ ] Address handling - Test that addresses are copied/parsed/returned properly
*/
/****************************************************************************/
/* SIMPLE CLIENT */
/****************************************************************************/
@@ -399,6 +409,207 @@ int ipv6_tcp_server_sustained_test(struct sockaddr_in6 *addr, int port, int oper
/****************************************************************************/
/* SLAM API (multiple of each api call and/or plausible call sequence) */
/****************************************************************************/
#define SLAM_NUMBER 16
#define SLAM_REPEAT 1
int slam_api_test()
{
int err = 0;
struct hostent *server;
struct sockaddr_in6 addr6;
struct sockaddr_in addr;
// TESTS:
// socket()
// close()
if(false)
{
// open and close SLAM_NUMBER*SLAM_REPEAT sockets
for(int j=0; j<SLAM_REPEAT; j++) {
std::cout << "slamming " << j << " time(s)" << std::endl;
usleep(SLAM_INTERVAL);
// create sockets
int fds[SLAM_NUMBER];
for(int i = 0; i<SLAM_NUMBER; i++) {
if((err = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) {
std::cout << "error creating socket (errno = " << errno << ")" << std::endl;
if(errno == EMFILE)
break;
else
return -1;
}
else
fds[i] = err;
std::cout << "\tcreating " << i << " socket(s) fd = " << err << std::endl;
}
// close sockets
for(int i = 0; i<SLAM_NUMBER; i++) {
//std::cout << "\tclosing " << i << " socket(s)" << std::endl;
if((err = zts_close(fds[i])) < 0) {
std::cout << "error closing socket (errno = " << errno << ")" << std::endl;
//return -1;
}
else
fds[i] = -1;
}
}
if(zts_nsockets() == 0)
std::cout << "PASSED [slam open and close]" << std::endl;
else
std::cout << "FAILED [slam open and close] - sockets left unclosed" << std::endl;
}
// ---
// TESTS:
// socket()
// bind()
// listen()
// accept()
// close()
if(false)
{
int sock = 0;
std::vector<int> used_ports;
for(int j=0; j<SLAM_REPEAT; j++) {
std::cout << "slamming " << j << " time(s)" << std::endl;
usleep(SLAM_INTERVAL);
for(int i = 0; i<SLAM_NUMBER; i++) {
if((sock = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) {
std::cout << "error creating socket (errno = " << errno << ")" << std::endl;
if(errno == EMFILE)
break;
else
return -1;
}
std::cout << "socket() = " << sock << std::endl;
usleep(SLAM_INTERVAL);
int port;
while(!(std::find(used_ports.begin(),used_ports.end(),port) == used_ports.end())) {
port = MIN_PORT + (rand() % (int)(MAX_PORT - MIN_PORT + 1));
}
used_ports.push_back(port);
std::cout << "port = " << port << std::endl;
if(false) {
server = gethostbyname2("::",AF_INET6);
memset((char *) &addr6, 0, sizeof(addr6));
addr6.sin6_flowinfo = 0;
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(port);
addr6.sin6_addr = in6addr_any;
err = zts_bind(sock, (struct sockaddr *)&addr6, (socklen_t)(sizeof addr6));
}
if(true) {
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr("10.9.9.50");
//addr.sin_addr.s_addr = htons(INADDR_ANY);
addr.sin_family = AF_INET;
err = zts_bind(sock, (struct sockaddr *)&addr, (socklen_t)(sizeof addr));
}
if(err < 0) {
std::cout << "error binding socket (errno = " << errno << ")" << std::endl;
return -1;
}
if(sock > 0) {
if((err = zts_close(sock)) < 0) {
std::cout << "error closing socket (errno = " << errno << ")" << std::endl;
//return -1;
}
}
}
}
used_ports.clear();
if(zts_nsockets() == 0)
std::cout << "PASSED [slam open, bind, listen, accept, close]" << std::endl;
else
std::cout << "FAILED [slam open, bind, listen, accept, close]" << std::endl;
}
// TESTS:
// (1) socket()
// (2) connect()
// (3) close()
if(true)
{
// open, bind, listen, accept, close
int sock = 0;
for(int j=0; j<SLAM_REPEAT; j++) {
std::cout << "slamming " << j << " time(s)" << std::endl;
usleep(SLAM_INTERVAL);
for(int i = 0; i<SLAM_NUMBER; i++) {
if((sock = zts_socket(AF_INET, SOCK_STREAM, 0)) < 0) {
std::cout << "error creating socket (errno = " << errno << ")" << std::endl;
if(errno == EMFILE)
break;
else
return -1;
}
std::cout << "socket() = " << sock << std::endl;
usleep(SLAM_INTERVAL);
int port = 4545;
if((err = zts_fcntl(sock, F_SETFL, O_NONBLOCK) < 0)) {
std::cout << "error setting O_NONBLOCK on sock=" << sock << std::endl;
}
if(false) {
server = gethostbyname2("::",AF_INET6);
memset((char *) &addr6, 0, sizeof(addr6));
addr6.sin6_flowinfo = 0;
addr6.sin6_family = AF_INET6;
addr6.sin6_port = htons(port);
addr6.sin6_addr = in6addr_any;
err = zts_connect(sock, (struct sockaddr *)&addr6, (socklen_t)(sizeof addr6));
}
if(true) {
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr("10.9.9.51");
//addr.sin_addr.s_addr = htons(INADDR_ANY);
addr.sin_family = AF_INET;
err = zts_connect(sock, (struct sockaddr *)&addr, (socklen_t)(sizeof addr));
}
if(err < 0) {
std::cout << "error connecting socket (errno = " << errno << ")" << std::endl;
if(errno == EINPROGRESS)
break;
else
return -1;
}
if(sock > 0) {
if((err = zts_close(sock)) < 0) {
std::cout << "error closing socket (errno = " << errno << ")" << std::endl;
//return -1;
}
}
}
}
if(zts_nsockets() == 0)
std::cout << "PASSED [slam open, connect, close]" << std::endl;
else
std::cout << "FAILED [slam open, connect, close]" << std::endl;
}
}
/****************************************************************************/
/* RANDOMIZED API TEST */
/****************************************************************************/
@@ -573,7 +784,7 @@ int do_test(std::string path, std::string nwid, int type, int protocol, int mode
addr6.sin6_family = AF_INET6;
memmove((char *) &addr6.sin6_addr.s6_addr, (char *) server->h_addr, server->h_length);
addr6.sin6_port = htons(port);
return ipv6_tcp_server_sustained_test(&addr6, port, operation, n_count, delay);
return ipv6_tcp_client_sustained_test(&addr6, port, operation, n_count, delay);
}
}
@@ -654,6 +865,9 @@ int main(int argc , char *argv[])
printf("complete\n");
}
slam_api_test();
return 0;
// SIMPLE
// performs a one-off test of a particular subset of the API
// For instance (ipv4 client, ipv6 server, etc)

View File

@@ -71,7 +71,12 @@
*
* OneService also does this on detected restarts.
*/
#ifdef ZT_SDK
#define ZT_BINDER_REFRESH_PERIOD 5000
#else
#define ZT_BINDER_REFRESH_PERIOD 30000
#endif
namespace ZeroTier {

View File

@@ -1022,7 +1022,9 @@ public:
for(it = _nets.begin(); it != _nets.end(); it++) {
if(it->second.tap) {
for(int j=0; j<it->second.tap->_ips.size(); j++) {
if(it->second.tap->_ips[j].isEqualPrefix(addr) || it->second.tap->_ips[j].ipsEqual(addr)) {
if(it->second.tap->_ips[j].isEqualPrefix(addr)
|| it->second.tap->_ips[j].containsAddress(addr)
|| it->second.tap->_ips[j].ipsEqual(addr)) {
return it->second.tap;
}
}