wider selftest coverage
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -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
|
||||
|
||||
22
README.md
22
README.md
@@ -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
|
||||
@@ -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
416
examples/csharp/DotNetWrapper.cs
Executable 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;
|
||||
}
|
||||
}
|
||||
@@ -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`
|
||||
@@ -1,2 +1,3 @@
|
||||
/MyClass$1.class
|
||||
/zerotier/
|
||||
/MyClass.class
|
||||
|
||||
77
examples/swift/Apple-Bridging-Header.h
Normal file
77
examples/swift/Apple-Bridging-Header.h
Normal 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 */
|
||||
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
Swift Language Binding API for the ZeroTier SDK
|
||||
======
|
||||
|
||||
137
examples/swift/XcodeWrapper.cpp
Executable file
137
examples/swift/XcodeWrapper.cpp
Executable 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
30
examples/swift/XcodeWrapper.hpp
Executable 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
216
examples/swift/ztsdk.swift
Normal 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()));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 ##
|
||||
##############################################################################
|
||||
|
||||
44
make-mac.mk
44
make-mac.mk
@@ -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 ##
|
||||
@@ -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 ##
|
||||
##############################################################################
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -37,6 +37,9 @@
|
||||
#include <sys/types.h>
|
||||
#include <pthread.h>
|
||||
|
||||
// stack
|
||||
#include "pico_stack.h"
|
||||
|
||||
// ZT
|
||||
#include "OneService.hpp"
|
||||
#include "Utils.hpp"
|
||||
@@ -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,12 +328,25 @@ 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();
|
||||
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;
|
||||
}
|
||||
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 err = 0, protocol_version = 0;
|
||||
// set up pico_socket
|
||||
int protocol_version = 0;
|
||||
struct pico_socket *psock;
|
||||
|
||||
// TODO: check ifdef logic here
|
||||
@@ -356,14 +371,17 @@ int zts_socket(ZT_SOCKET_SIG) {
|
||||
conn->socket_type = socket_type;
|
||||
conn->picosock = psock;
|
||||
memset(conn->rxbuf, 0, ZT_UDP_RX_BUF_SZ);
|
||||
ZeroTier::UnassignedConnections[conn->app_fd] = conn;
|
||||
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"));
|
||||
}
|
||||
// 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,15 +484,21 @@ 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
|
||||
// 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 && true) {
|
||||
if(err == 0 && blocking) {
|
||||
DEBUG_INFO("BLOCKING!");
|
||||
bool complete = false;
|
||||
while(true)
|
||||
{
|
||||
@@ -497,6 +524,7 @@ int zts_connect(ZT_CONNECT_SIG) {
|
||||
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,29 +647,40 @@ 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;
|
||||
}
|
||||
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) {
|
||||
@@ -650,6 +698,7 @@ int zts_accept(ZT_ACCEPT_SIG) {
|
||||
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,27 +835,63 @@ 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;
|
||||
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());
|
||||
|
||||
// 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;
|
||||
@@ -791,47 +901,76 @@ int zts_close(ZT_CLOSE_SIG)
|
||||
}
|
||||
}
|
||||
// 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[fd] = NULL; // erase?
|
||||
ZeroTier::fdmap.erase(fd);
|
||||
err = 0;
|
||||
}
|
||||
}
|
||||
//DEBUG_INFO(" unmap=%d, fdmap=%d", ZeroTier::unmap.size(), ZeroTier::fdmap.size());
|
||||
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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
Basic RX/TX connect()/accept() Functionality:
|
||||
|
||||
Throughput
|
||||
Memory Usage
|
||||
Processor usage
|
||||
[ ?] 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
|
||||
|
||||
socket API semantics
|
||||
- Proper socket closure
|
||||
- Proper handling of blocking/non-blocking behaviour
|
||||
- replicate specific errno conditions and verify correctness
|
||||
Performance:
|
||||
|
||||
Network semantics
|
||||
- Multi-network handling
|
||||
- Address handling
|
||||
[ ] Throughput - Test maximum RX/TX speeds
|
||||
[ ] Memory Usage - Test memory consumption profile
|
||||
[ ] CPU Usage - Test processor usage
|
||||
[ ] Multithreaded Throughput -
|
||||
[ ] Multithreaded CPU Usage -
|
||||
|
||||
ZeroTier-specific functionality
|
||||
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)
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user