diff --git a/docs/unity3d_osx_zt_sdk.md b/docs/unity3d_osx_zt_sdk.md index 64a1fe4..e71839c 100644 --- a/docs/unity3d_osx_zt_sdk.md +++ b/docs/unity3d_osx_zt_sdk.md @@ -12,14 +12,14 @@ Our implementation currently intends to be the bare minimum required to get your - `Join(nwid)`: Joins a ZeroTier virtual network - `Leave(nwid)`: Leaves a ZeroTier virtual network -- `Socket(family, type, protocol)`: Creates a ZeroTier-administered socket +- `Socket(family, type, protocol)`: Creates a ZeroTier-administered socket (returns an `fd`) - `Bind(fd, addr, port)`: Binds to that socket on the address and port given - `Listen(fd, backlog)`: Puts a socket into a listening state - `Accept(fd)`: Accepts an incoming connection - `Connect(fd, addr, port)`: Connects to an endpoint associated with the given `fd` - `Write(fd, buf, len)`: Sends data to the endpoint associated with the given `fd` - `Read(fd, buf, len)`: Receives data from an endpoint associated with the given `fd` -- `CLose(fd)`: Closes a connection with an endpoint +- `CLose(fd)`: Closes a connection to an endpoint *** ## Adding ZeroTier to your Unity app @@ -46,13 +46,16 @@ public class Example public void example_server() { + zt = new ZeroTierNetworkInterface (); // Start interface + zt.Join("565799d8f6e1c11a"); // Join your network + Thread connectThread = new Thread(() => { // Create ZeroTier-administered socket int sock = zt.Socket ((int)AddressFamily.InterNetwork, (int)SocketType.Stream, (int)ProtocolType.Unspecified); zt.Bind(sock, "0.0.0.0", 8000); zt.Listen(sock, 1); - // Accept() client connection + // Accept client connection int accept_sock = -1; while(accept_res < 0) { accept_sock = zt.Accept(sock); @@ -81,6 +84,9 @@ public class Example public void example_client() { + zt = new ZeroTierNetworkInterface (); + zt.Join("565799d8f6e1c11a"); + Thread connectThread = new Thread(() => { // Create ZeroTier-administered socket int sock = zt.Socket ((int)AddressFamily.InterNetwork, (int)SocketType.Stream, (int)ProtocolType.Unspecified); diff --git a/integrations/Apple/ZeroTierSDK_Apple/ZeroTierSDK_Apple.xcodeproj/project.xcworkspace/xcuserdata/Joseph.xcuserdatad/UserInterfaceState.xcuserstate b/integrations/Apple/ZeroTierSDK_Apple/ZeroTierSDK_Apple.xcodeproj/project.xcworkspace/xcuserdata/Joseph.xcuserdatad/UserInterfaceState.xcuserstate index 794599c..b1e27ce 100644 Binary files a/integrations/Apple/ZeroTierSDK_Apple/ZeroTierSDK_Apple.xcodeproj/project.xcworkspace/xcuserdata/Joseph.xcuserdatad/UserInterfaceState.xcuserstate and b/integrations/Apple/ZeroTierSDK_Apple/ZeroTierSDK_Apple.xcodeproj/project.xcworkspace/xcuserdata/Joseph.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/integrations/Unity3D/Assets/ZeroTierNetworkInterface.cs b/integrations/Unity3D/Assets/ZeroTierNetworkInterface.cs new file mode 100755 index 0000000..d56fe67 --- /dev/null +++ b/integrations/Unity3D/Assets/ZeroTierNetworkInterface.cs @@ -0,0 +1,323 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2015 ZeroTier, Inc. + * + * 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 . + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +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; + +// 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 ZeroTierNetworkInterface { + + // ZeroTier background thread + protected Thread ztThread; + protected List connections = new List (); + protected int MaxPacketSize; + + // Only allow one network at a time for BETA + protected bool joined_to_network = false; + protected string nwid = ""; + + // 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); + + // Connection calls + [DllImport (DLL_PATH)] + protected static extern int zt_socket(int family, int type, int protocol); + + [DllImport (DLL_PATH)] + unsafe protected static extern int zt_bind(int sockfd, System.IntPtr addr, int addrlen); + [DllImport (DLL_PATH)] + unsafe protected static extern int zt_connect(int sockfd, System.IntPtr addr, int addrlen); + + [DllImport (DLL_PATH)] + protected static extern int zt_accept(int sockfd); + [DllImport (DLL_PATH)] + protected static extern int zt_listen(int sockfd, int backlog); + [DllImport (DLL_PATH)] + protected static extern int zt_close(int sockfd); + + // RX / TX + [DllImport (DLL_PATH)] + unsafe protected static extern int zt_recv(int sockfd, [In, Out] IntPtr buf, int len); + [DllImport (DLL_PATH)] + unsafe protected static extern int zt_send(int sockfd, IntPtr buf, int len); + [DllImport (DLL_PATH)] + unsafe protected static extern int zt_set_nonblock(int sockfd); + + // ZT Thread controls + [DllImport (DLL_PATH)] + protected static extern bool zt_is_running(); + [DllImport (DLL_PATH)] + protected static extern void zt_terminate(); + + // ZT Network controls + [DllImport (DLL_PATH)] + protected static extern bool zt_join_network(string nwid); + [DllImport (DLL_PATH)] + protected static extern void zt_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(rpc_path != "" && nwid != "") { + return rpc_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 ); + Debug.Log ("RPC = " + rpcCommPath()); + // Start service + unity_start_service(rpcCommPath()); + /* 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. + */ + } + + // 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 ZeroTierNetworkInterface(string nwid) + { + this.nwid = nwid; + Init(); + } + + // Initialize the ZeroTier service + public ZeroTierNetworkInterface() + { + Init(); + } + + // Initialize the ZeroTier service + // Use the GlobalConfig to set things like the max packet size + /* + public ZeroTierNetworkInterface(GlobalConfig gConfig) + { + MaxPacketSize = gConfig.MaxPacketSize; // TODO: Do something with this! + Init(); + } + */ + +#region Network Handling + // Joins a ZeroTier virtual network + public bool JoinNetwork(string nwid) + { + if(!joined_to_network) { + zt_join_network(nwid); + return true; + } + return false; + } + + // Leaves a ZeroTier virtual network + public bool LeaveNetwork(string nwid) + { + if(!joined_to_network) { + return false; + } + else { + zt_leave_network(nwid); + return true; + } + } +#endregion + + // Creates a new ZeroTier-administered socket + public int Socket(int family, int type, int protocol) + { + return zt_socket (family, type, protocol); + } + + // Binds to a specific address + public int Bind(int fd, string addr, int port) + { + GCHandle sockaddr_ptr = ZeroTierUtils.Generate_unmananged_sockaddr(addr + ":" + port); + IntPtr pSockAddr = sockaddr_ptr.AddrOfPinnedObject (); + int addrlen = Marshal.SizeOf (pSockAddr); + return zt_bind (fd, pSockAddr, addrlen); + } + + // Listens for an incoming connection request + public int Listen(int fd, int backlog) + { + return zt_listen(fd, backlog); + } + + // Accepts an incoming connection + public int Accept(int fd) + { + return zt_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 = ZeroTierUtils.Generate_unmananged_sockaddr(addr + ":" + port); + IntPtr pSockAddr = sockaddr_ptr.AddrOfPinnedObject (); + int addrlen = Marshal.SizeOf (pSockAddr); + return zt_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 = zt_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 = zt_send(fd, ptr, len*2)) < 0) { + //error = (byte)bytes_written; + } + return bytes_written; + } + +#region Service-Related calls + // Returns whether the ZeroTier service is currently running + public bool IsRunning() + { + return zt_is_running (); + } + + // Terminates the ZeroTier service + public void Terminate() + { + zt_terminate (); + } +#endregion +} diff --git a/integrations/Unity3D/Assets/ZeroTierSockets_Demo.cs b/integrations/Unity3D/Assets/ZeroTierSockets_Demo.cs index 60dfdb4..8c81b1a 100644 --- a/integrations/Unity3D/Assets/ZeroTierSockets_Demo.cs +++ b/integrations/Unity3D/Assets/ZeroTierSockets_Demo.cs @@ -186,12 +186,7 @@ public class ZeroTierSockets_Demo : MonoBehaviour input.text = "Welcome to the machine"; // Create new instance of ZeroTier in separate thread - zt = new ZeroTierLLAPI ("/Users/Joseph/utest2/nc_565799d8f6e1c11a"); - - /* 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. - */ + zt = new ZeroTierNetworkInterface (); } // Terminate the ZeroTier service when the application quits