initial sources
This commit is contained in:
227
src/README.md
Normal file
227
src/README.md
Normal file
@@ -0,0 +1,227 @@
|
||||
ZeroTier SDK (beta)
|
||||
======
|
||||
|
||||
The ZeroTier SDK offers a microkernel-like networking paradigm for containerized applications and application-specific virtual networking.
|
||||
|
||||
The SDK couples the ZeroTier core Ethernet virtualization engine with a user-space TCP/IP stack and a library that intercepts calls to the Posix network API. This allows servers and applications to be used without modification or recompilation. It can be used to run services on virtual networks without elevated privileges, special configuration of the physical host, kernel support, or any other application specific configuration. It's ideal for use with [Docker](http://http://www.docker.com), [LXC](https://linuxcontainers.org), or [Rkt](https://coreos.com/rkt/docs/latest/) to build containerized microservices that automatically connect to a virtual network when deployed. It can also be used on a plain un-containerized Linux system to run applications on virtual networks without elevated privileges or system modification.
|
||||
|
||||
More discussion can be found in our [original blog announcement](https://www.zerotier.com/blog/?p=490) and [the SDK product page](https://www.zerotier.com/product-netcon.shtml).
|
||||
|
||||
The SDK is currently in **BETA** and is suitable for testing and experimentation. Only Linux is supported. Future updates will focus on compatibility, full stack support, and improved performance, and may also port to other OSes.
|
||||
|
||||
# Limitations and Compatibility
|
||||
|
||||
The beta version of the SDK **only supports IPv4**. There is no IPv6 support and no support for ICMP (or RAW sockets). That means network-containerizing *ping* won't work.
|
||||
|
||||
The virtual TCP/IP stack will respond to *incoming* ICMP ECHO requests, which means that you can ping it from another host on the same ZeroTier virtual network. This is useful for testing.
|
||||
|
||||
## Controlling traffic
|
||||
|
||||
**Network Containers are currently all or nothing.** If engaged, the intercept library intercepts all network I/O calls and redirects them through the new path. A network-containerized application cannot communicate over the regular network connection of its host or container or with anything else except other hosts on its ZeroTier virtual LAN. Support for optional "fall-through" to the host IP stack for outgoing connections outside the virtual network and for gateway routes within the virtual network is planned. (It will be optional since in some cases total network isolation might be considered a nice security feature.)
|
||||
|
||||
#### Compatibility Test Results
|
||||
|
||||
The following applications have been tested and confirmed to work for the beta release:
|
||||
|
||||
Fedora 23:
|
||||
|
||||
httpstub.c
|
||||
nginx 1.8.0
|
||||
http 2.4.16, 2.4.17
|
||||
darkhttpd 1.11
|
||||
python 2.7.10 (python -m SimpleHTTPServer)
|
||||
python 3.4.3 (python -m http.server)
|
||||
redis 3.0.4
|
||||
node 6.0.0-pre
|
||||
sshd
|
||||
|
||||
CentOS 7:
|
||||
|
||||
httpstub.c
|
||||
nginx 1.6.3
|
||||
httpd 2.4.6 (debug mode -X)
|
||||
darkhttpd 1.11
|
||||
node 4.2.2
|
||||
redis 2.8.19
|
||||
sshd
|
||||
|
||||
Ubuntu 14.04.3:
|
||||
|
||||
httpstub.c
|
||||
nginx 1.4.6
|
||||
python 2.7.6 (python -m SimpleHTTPServer)
|
||||
python 3.4.0 (python -m http.server)
|
||||
node 5.2.0
|
||||
redis 2.8.4
|
||||
sshd
|
||||
|
||||
It is *likely* to work with other things but there are no guarantees.
|
||||
|
||||
# Building the SDK
|
||||
|
||||
The SDK works on Linux and has been lightly tested on OSX. To build the service host, IP stack, and intercept library, from the base of the ZeroTier One tree run:
|
||||
|
||||
make clean
|
||||
make netcon
|
||||
|
||||
This will build a binary called *zerotier-netcon-service* and a library called *libzerotierintercept.so*. It will also build the IP stack as *netcon/liblwip.so*.
|
||||
|
||||
To enable debug trace statements for Network Containers, use *-D\_NETCON\_DEBUG*
|
||||
|
||||
The *zerotier-netcon-service* binary is almost the same as a regular ZeroTier One build except instead of creating virtual network ports using Linux's */dev/net/tun* interface, it creates instances of a user-space TCP/IP stack for each virtual network and provides RPC access to this stack via a Unix domain socket. The latter is a library that can be loaded with the Linux *LD\_PRELOAD* environment variable or by placement into */etc/ld.so.preload* on a Linux system or container. Additional magic involving nameless Unix domain socket pairs and interprocess socket handoff is used to emulate TCP sockets with extremely low overhead and in a way that's compatible with select, poll, epoll, and other I/O event mechanisms.
|
||||
|
||||
The intercept library does nothing unless the *ZT\_NC\_NETWORK* environment variable is set. If on program launch (or fork) it detects the presence of this environment variable, it will attempt to connect to a running *zerotier-netcon-service* at the specified Unix domain socket path.
|
||||
|
||||
Unlike *zerotier-one*, *zerotier-netcon-service* does not need to be run with root privileges and will not modify the host's network configuration in any way. It can be run alongside *zerotier-one* on the same host with no ill effect, though this can be confusing since you'll have to remember the difference between "real" host interfaces (tun/tap) and network containerized endpoints. The latter are completely unknown to the kernel and will not show up in *ifconfig*.
|
||||
|
||||
# Linking into an application on Mac OSX
|
||||
|
||||
Example:
|
||||
|
||||
gcc myapp.c -o myapp libzerotierintercept.so
|
||||
export ZT_NC_NETWORK=/tmp/netcon-test-home/nc_8056c2e21c000001
|
||||
|
||||
Start service
|
||||
|
||||
./zerotier-netcon-service -d -p8000 /tmp/netcon-test-home
|
||||
|
||||
Run application
|
||||
|
||||
./myapp
|
||||
|
||||
|
||||
# Starting the Network Containers Service
|
||||
|
||||
You don't need Docker or any other container engine to try Network Containers. A simple test can be performed in user space (no root) in your own home directory.
|
||||
|
||||
First, build the netcon service and intercept library as described above. Then create a directory to act as a temporary ZeroTier home for your test netcon service instance. You'll need to move the *liblwip.so* binary that was built with *make netcon* into there, since the service must be able to find it there and load it.
|
||||
|
||||
mkdir /tmp/netcon-test-home
|
||||
cp -f ./netcon/liblwip.so /tmp/netcon-test-home
|
||||
|
||||
Now you can run the service (no sudo needed, and *-d* tells it to run in the background):
|
||||
|
||||
./zerotier-netcon-service -d -p8000 /tmp/netcon-test-home
|
||||
|
||||
As with ZeroTier One in its normal incarnation, you'll need to join a network for anything interesting to happen:
|
||||
|
||||
./zerotier-cli -D/tmp/netcon-test-home join 8056c2e21c000001
|
||||
|
||||
If you don't want to use [Earth](https://www.zerotier.com/public.shtml) for this test, replace 8056c2e21c000001 with a different network ID. The *-D* option tells *zerotier-cli* not to look in /var/lib/zerotier-one for information about a running instance of the ZeroTier system service but instead to look in */tmp/netcon-test-home*.
|
||||
|
||||
Now type:
|
||||
|
||||
./zerotier-cli -D/tmp/netcon-test-home listnetworks
|
||||
|
||||
Try it a few times until you see that you've successfully joined the network and have an IP address. Instead of a *zt#* device, a path to a Unix domain socket will be listed for the network's port.
|
||||
|
||||
Now you will want to have ZeroTier One (the normal *zerotier-one* build, not network containers) running somewhere else, such as on another Linux system or VM. Technically you could run it on the *same* Linux system and it wouldn't matter at all, but many people find this intensely confusing until they grasp just what exactly is happening here.
|
||||
|
||||
On the other Linux system, join the same network if you haven't already (8056c2e21c000001 if you're using Earth) and wait until you have an IP address. Then try pinging the IP address your netcon instance received. You should see ping replies.
|
||||
|
||||
Back on the host that's running *zerotier-netcon-service*, type *ip addr list* or *ifconfig* (ifconfig is technically deprecated so some Linux systems might not have it). Notice that the IP address of the network containers endpoint is not listed and no network device is listed for it either. That's because as far as the Linux kernel is concerned it doesn't exist.
|
||||
|
||||
What are you pinging? What is happening here?
|
||||
|
||||
The *zerotier-netcon-service* binary has joined a *virtual* network and is running a *virtual* TCP/IP stack entirely in user space. As far as your system is concerned it's just another program exchanging UDP packets with a few other hosts on the Internet and nothing out of the ordinary is happening at all. That's why you never had to type *sudo*. It didn't change anything on the host.
|
||||
|
||||
Now you can run an application inside your network container.
|
||||
|
||||
export LD_PRELOAD=`pwd`/libzerotierintercept.so
|
||||
export ZT_NC_NETWORK=/tmp/netcon-test-home/nc_8056c2e21c000001
|
||||
node netcon/httpserver.js
|
||||
|
||||
Also note that the "pwd" in LD_PRELOAD assumes you are in the ZeroTier source root and have built netcon there. If not, substitute the full path to *libzerotierintercept.so*. If you want to remove those environment variables later, use "unset LD_PRELOAD" and "unset ZT_NC_NETWORK".
|
||||
|
||||
If you don't have node.js installed, an alternative test using python would be:
|
||||
|
||||
python -m SimpleHTTPServer 80
|
||||
|
||||
If you are running Python 3, use "-m http.server".
|
||||
|
||||
If all went well a small static HTTP server is now serving up the current directory, but only inside the network container. Going to port 80 on your machine won't work. To reach it, go to the other system where you joined the same network with a conventional ZeroTier instance and try:
|
||||
|
||||
curl http://NETCON.INSTANCE.IP/
|
||||
|
||||
Replace *NETCON.INSTANCE.IP* with the IP address that *zerotier-netcon-service* was assigned on the virtual network. (This is the same IP you pinged in your first test.) If everything works, you should get back a copy of ZeroTier One's main README.md file.
|
||||
|
||||
# Installing in a Docker container (or any other container engine)
|
||||
|
||||
If it's not immediately obvious, installation into a Docker container is easy. Just install *zerotier-netcon-service*, *libzerotierintercept.so*, and *liblwip.so* into the container at an appropriate locations. We suggest putting it all in */var/lib/zerotier-one* since this is the default ZeroTier home and will eliminate the need to supply a path to any of ZeroTier's services or utilities. Then, in your Docker container entry point script launch the service with *-d* to run it in the background, set the appropriate environment variables as described above, and launch your container's main application.
|
||||
|
||||
The only bit of complexity is configuring which virtual network to join. ZeroTier's service automatically joins networks that have *.conf* files in *ZTHOME/networks.d* even if the *.conf* file is empty. So one way of doing this very easily is to add the following commands to your Dockerfile or container entry point script:
|
||||
|
||||
mkdir -p /var/lib/zerotier-one/networks.d
|
||||
touch /var/lib/zerotier-one/networks.d/8056c2e21c000001.conf
|
||||
|
||||
Replace 8056c2e21c000001 with the network ID of the network you want your container to automatically join. It's also a good idea in your container's entry point script to add a small loop to wait until the container's instance of ZeroTier generates an identity and comes online. This could be something like:
|
||||
|
||||
/var/lib/zerotier-one/zerotier-netcon-service -d
|
||||
while [ ! -f /var/lib/zerotier-one/identity.secret ]; do
|
||||
sleep 0.1
|
||||
done
|
||||
# zerotier-netcon-service is now running and has generated an identity
|
||||
|
||||
(Be sure you don't bundle the identity into the container, otherwise every container will try to be the same device and they will "fight" over the device's address.)
|
||||
|
||||
Now each new instance of your container will automatically join the specified network on startup. Authorizing the container on a private network still requires a manual authorization step either via the ZeroTier Central web UI or the API. We're working on some ideas to automate this via bearer token auth or similar since doing this manually or with scripts for large deployments is tedious.
|
||||
|
||||
# Docker-based Unit Tests
|
||||
|
||||
Each unit test will temporarily copy all required ZeroTier binaries into its local directory, then build the *netcon_dockerfile* and *monitor_dockerfile*. Once built, each container will be run and perform tests and monitoring specified in *netcon_entrypoint.sh* and *monitor_entrypoint.sh*
|
||||
|
||||
Results will be written to the *netcon/docker-test/_results/* directory which is a common shared volume between all containers involved in the test and will be a combination of raw and formatted dumps to files whose names reflect the test performed. In the event of failure, *FAIL.* will be prepended to the result file's name (e.g. *FAIL.my_application_1.0.2.x86_64*), likewise in the event of success, *OK.* will be prepended.
|
||||
|
||||
To run unit tests:
|
||||
|
||||
1) Disable SELinux. This is so the containers can use a shared volume to exchange MD5 sums and address information.
|
||||
|
||||
2) Set up your own network at [https://my.zerotier.com/](https://my.zerotier.com/). For our example we'll just use the Earth network (8056c2e21c000001). Use its network id as follows:
|
||||
|
||||
3) Generate two pairs of identity keys. Each public/private pair will be used by the *netcon* and *monitor* containers:
|
||||
|
||||
mkdir -p /tmp/netcon_first
|
||||
cp -f ./netcon/liblwip.so /tmp/netcon_first
|
||||
./zerotier-netcon-service -d -p8100 /tmp/netcon_first
|
||||
while [ ! -f /tmp/netcon_first/identity.secret ]; do
|
||||
sleep 0.1
|
||||
done
|
||||
./zerotier-cli -D/tmp/netcon_first join 8056c2e21c000001
|
||||
kill `cat /tmp/netcon_first/zerotier-one.pid`
|
||||
|
||||
mkdir -p /tmp/netcon_second
|
||||
cp -f ./netcon/liblwip.so /tmp/netcon_second
|
||||
./zerotier-netcon-service -d -p8101 /tmp/netcon_second
|
||||
while [ ! -f /tmp/netcon_second/identity.secret ]; do
|
||||
sleep 0.1
|
||||
done
|
||||
./zerotier-cli -D/tmp/netcon_second join 8056c2e21c000001
|
||||
kill `cat /tmp/netcon_second/zerotier-one.pid`
|
||||
|
||||
4) Copy the identity files to your *docker-test* directory. Names will be altered during copy step so the dockerfiles know which identities to use for each image/container:
|
||||
|
||||
cp /tmp/netcon_first/identity.public ./netcon/docker-test/netcon_identity.public
|
||||
cp /tmp/netcon_first/identity.secret ./netcon/docker-test/netcon_identity.secret
|
||||
|
||||
cp /tmp/netcon_second/identity.public ./netcon/docker-test/monitor_identity.public
|
||||
cp /tmp/netcon_second/identity.secret ./netcon/docker-test/monitor_identity.secret
|
||||
|
||||
|
||||
5) Place a blank network config file in the *netcon/docker-test* directory (e.g. "8056c2e21c000001.conf")
|
||||
- This will be used to inform test-specific scripts what network to use for testing
|
||||
|
||||
After you've created your network and placed its blank config file in *netcon/docker-test* run the following to perform unit tests for httpd:
|
||||
|
||||
./build.sh httpd
|
||||
./test.sh httpd
|
||||
|
||||
It's useful to note that the keyword *httpd* in this example is merely a substring for a test name, this means that if we replaced it with *x86_64* or *fc23*, it would run all unit tests for *x86_64* systems or *Fedora 23* respectively.
|
||||
|
||||
# Mobile App Embedding
|
||||
|
||||
API | Hookable | Proxy-able |
|
||||
----------------- | ------------- | ------------ |
|
||||
iOS (NSStream) | NO | YES |
|
||||
iOS (BSD socket) | YES | NO |
|
||||
Android (socket) | Not reliably | NO |
|
||||
Android (Socket) | Not reliably | YES |
|
||||
106
src/SDK.h
Normal file
106
src/SDK.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* 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/
|
||||
*/
|
||||
|
||||
#ifndef _ZT_SDK_H
|
||||
#define _ZT_SDK_H 1
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include "SDK_Signatures.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define INTERCEPT_ENABLED 111
|
||||
#define INTERCEPT_DISABLED 222
|
||||
|
||||
void zt_init_rpc(const char *nwid);
|
||||
const char *get_netpath();
|
||||
void zt_init_rpc(const char *nwid);
|
||||
bool check_intercept_enabled_for_thread();
|
||||
|
||||
#if defined(__linux__)
|
||||
static int (*realaccept4)(ACCEPT4_SIG) = 0;
|
||||
#if !defined(__ANDROID__)
|
||||
static int (*realsyscall)(SYSCALL_SIG) = 0;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
static int (*realbind)(BIND_SIG) = 0;
|
||||
static int (*realsendmsg)(SENDMSG_SIG) = 0;
|
||||
static ssize_t (*realsendto)(SENDTO_SIG) = 0;
|
||||
static int (*realrecvmsg)(RECVMSG_SIG) = 0;
|
||||
static int (*realrecvfrom)(RECVFROM_SIG) = 0;
|
||||
#endif
|
||||
static int (*realconnect)(CONNECT_SIG) = 0;
|
||||
static int (*realaccept)(ACCEPT_SIG) = 0;
|
||||
static int (*reallisten)(LISTEN_SIG) = 0;
|
||||
static int (*realsocket)(SOCKET_SIG) = 0;
|
||||
static int (*realsetsockopt)(SETSOCKOPT_SIG) = 0;
|
||||
static int (*realgetsockopt)(GETSOCKOPT_SIG) = 0;
|
||||
static int (*realclose)(CLOSE_SIG) = 0;
|
||||
static int (*realgetsockname)(GETSOCKNAME_SIG) = 0;
|
||||
|
||||
ssize_t zt_sendto(SENDTO_SIG);
|
||||
ssize_t zt_sendmsg(SENDMSG_SIG);
|
||||
ssize_t zt_recvfrom(RECVFROM_SIG);
|
||||
ssize_t zt_recvmsg(RECVMSG_SIG);
|
||||
|
||||
|
||||
#if defined(__UNITY_3D__)
|
||||
|
||||
struct UnityArrayInput
|
||||
{
|
||||
void *array;
|
||||
int len;
|
||||
};
|
||||
|
||||
ssize_t zt_send(int fd, struct UnityArrayInput *buf, int len);
|
||||
ssize_t zt_recv(int fd, struct UnityArrayInput *buf, int len);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
int zt_socket(SOCKET_SIG);
|
||||
int zt_connect(CONNECT_SIG);
|
||||
int zt_bind(BIND_SIG);
|
||||
#if defined(__linux__)
|
||||
int zt_accept(ACCEPT_SIG);
|
||||
#endif
|
||||
int zt_accept(ACCEPT_SIG);
|
||||
int zt_listen(LISTEN_SIG);
|
||||
int zt_setsockopt(SETSOCKOPT_SIG);
|
||||
int zt_getsockopt(GETSOCKOPT_SIG);
|
||||
int zt_getsockname(GETSOCKNAME_SIG);
|
||||
int zt_close(CLOSE_SIG);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // _ZT_SDK_H
|
||||
112
src/SDK_Debug.c
Normal file
112
src/SDK_Debug.c
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* 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/
|
||||
*/
|
||||
|
||||
#ifndef _COMMON_DEBUG_
|
||||
#define _COMMON_DEBUG_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <netdb.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <pthread.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#define DEBUG_LEVEL 5 // Set this to adjust what you'd like to see in the debug traces
|
||||
|
||||
#define MSG_TRANSFER 1 // RX/TX specific statements
|
||||
#define MSG_ERROR 2 // Errors
|
||||
#define MSG_INFO 3 // Information which is generally useful to any user
|
||||
#define MSG_DEBUG 4 // Information which is only useful to someone debugging
|
||||
#define MSG_DEBUG_EXTRA 5 // If nothing in your world makes sense
|
||||
|
||||
#define DEBUG_TO_FILE 1
|
||||
|
||||
void dwr(int level, const char *fmt, ... );
|
||||
|
||||
void dwr(int level, const char *fmt, ... )
|
||||
{
|
||||
#if defined(_SDK_DEBUG)
|
||||
if(level > DEBUG_LEVEL)
|
||||
return;
|
||||
int saveerr;
|
||||
saveerr = errno;
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
char timestring[20];
|
||||
time_t timestamp;
|
||||
timestamp = time(NULL);
|
||||
strftime(timestring, sizeof(timestring), "%H:%M:%S", localtime(×tamp));
|
||||
#if defined(__ANDROID__)
|
||||
pid_t tid = gettid();
|
||||
#elif defined(__linux__)
|
||||
pid_t tid = 5;//syscall(SYS_gettid);
|
||||
#elif defined(__APPLE__)
|
||||
pid_t tid = pthread_mach_thread_np(pthread_self());
|
||||
#endif
|
||||
|
||||
if(DEBUG_TO_FILE > 0) {
|
||||
FILE *file = fopen("/Users/Joseph/code/ztnc_logfile.txt","a");
|
||||
fprintf(file, "%s [tid=%7d] ", timestring, tid);
|
||||
}
|
||||
|
||||
fprintf(stderr, "%s [tid=%7d] ", timestring, tid);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fflush(stderr);
|
||||
errno = saveerr;
|
||||
va_end(ap);
|
||||
#endif // NETCON_DEBUG
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#if __ANDROID__
|
||||
#include <jni.h>
|
||||
#include <android/log.h>
|
||||
#define LOG_TAG "ZTSDK"
|
||||
#define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
|
||||
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
|
||||
#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
|
||||
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
|
||||
#else
|
||||
#define LOGV(...) fprintf(stdout, __VA_ARGS__)
|
||||
#define LOGI(...) fprintf(stdout, __VA_ARGS__)
|
||||
#define LOGD(...) fprintf(stdout, __VA_ARGS__)
|
||||
#define LOGE(...) fprintf(stdout, __VA_ARGS__)
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
56
src/SDK_Debug.h
Normal file
56
src/SDK_Debug.h
Normal file
@@ -0,0 +1,56 @@
|
||||
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* 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/
|
||||
*/
|
||||
|
||||
#define DEBUG_LEVEL 5 // Set this to adjust what you'd like to see in the debug traces
|
||||
|
||||
#define MSG_TRANSFER 1 // RX/TX specific statements
|
||||
#define MSG_ERROR 2 // Errors
|
||||
#define MSG_INFO 3 // Information which is generally useful to any user
|
||||
#define MSG_DEBUG 4 // Information which is only useful to someone debugging
|
||||
#define MSG_DEBUG_EXTRA 5 // If nothing in your world makes sense
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
#if __ANDROID__
|
||||
#include <jni.h>
|
||||
#include <android/log.h>
|
||||
#define LOG_TAG "ZTSDK"
|
||||
#define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
|
||||
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
|
||||
#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
|
||||
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
|
||||
#else
|
||||
#define LOGV(...) fprintf(stdout, __VA_ARGS__)
|
||||
#define LOGI(...) fprintf(stdout, __VA_ARGS__)
|
||||
#define LOGD(...) fprintf(stdout, __VA_ARGS__)
|
||||
#define LOGE(...) fprintf(stdout, __VA_ARGS__)
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
1383
src/SDK_EthernetTap.cpp
Normal file
1383
src/SDK_EthernetTap.cpp
Normal file
File diff suppressed because it is too large
Load Diff
507
src/SDK_EthernetTap.hpp
Normal file
507
src/SDK_EthernetTap.hpp
Normal file
@@ -0,0 +1,507 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* 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/
|
||||
*/
|
||||
|
||||
#ifndef ZT_NETCONETHERNETTAP_HPP
|
||||
#define ZT_NETCONETHERNETTAP_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <stdexcept>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "node/Constants.hpp"
|
||||
#include "node/MulticastGroup.hpp"
|
||||
#include "node/Mutex.hpp"
|
||||
#include "node/InetAddress.hpp"
|
||||
#include "osdep/Thread.hpp"
|
||||
#include "osdep/Phy.hpp"
|
||||
|
||||
#include "netif/etharp.h"
|
||||
|
||||
#include "SDK_RPC.h"
|
||||
|
||||
struct tcp_pcb;
|
||||
struct udp_pcb;
|
||||
|
||||
struct socket_st;
|
||||
struct listen_st;
|
||||
struct bind_st;
|
||||
struct connect_st;
|
||||
struct getsockname_st;
|
||||
struct accept_st;
|
||||
|
||||
#define APPLICATION_POLL_FREQ 2
|
||||
#define ZT_LWIP_TCP_TIMER_INTERVAL 50
|
||||
#define STATUS_TMR_INTERVAL 500 // How often we check connection statuses (in ms)
|
||||
|
||||
// TCP Buffer sizes
|
||||
#define DEFAULT_TCP_TX_BUF_SZ 1024 * 1024
|
||||
#define DEFAULT_TCP_RX_BUF_SZ 1024 * 1024
|
||||
|
||||
// TCP RX/TX buffer soft boundaries
|
||||
#define DEFAULT_TCP_TX_BUF_SOFTMAX DEFAULT_TCP_TX_BUF_SZ * 0.80
|
||||
#define DEFAULT_TCP_TX_BUF_SOFTMIN DEFAULT_TCP_TX_BUF_SZ * 0.20
|
||||
#define DEFAULT_TCP_RX_BUF_SOFTMAX DEFAULT_TCP_RX_BUF_SZ * 0.80
|
||||
#define DEFAULT_TCP_RX_BUF_SOFTMIN DEFAULT_TCP_RX_BUF_SZ * 0.20
|
||||
|
||||
// UDP Buffer sizes (should be about the size of your MTU)
|
||||
#define DEFAULT_UDP_TX_BUF_SZ 1500
|
||||
#define DEFAULT_UDP_RX_BUF_SZ 1500
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class NetconEthernetTap;
|
||||
class LWIPStack;
|
||||
|
||||
/*
|
||||
* TCP connection
|
||||
*/
|
||||
struct Connection
|
||||
{
|
||||
bool listening, probation, disabled;
|
||||
int pid, txsz, rxsz, type;
|
||||
PhySocket *rpcSock, *sock;
|
||||
struct tcp_pcb *TCP_pcb;
|
||||
struct udp_pcb *UDP_pcb;
|
||||
struct sockaddr_storage *addr;
|
||||
unsigned short port;
|
||||
unsigned char txbuf[DEFAULT_TCP_TX_BUF_SZ];
|
||||
unsigned char rxbuf[DEFAULT_TCP_RX_BUF_SZ];
|
||||
|
||||
// TODO: necessary still?
|
||||
bool unread_udp_packet;
|
||||
int proxy_conn_state;
|
||||
};
|
||||
|
||||
/*
|
||||
* A helper for passing a reference to _phy to LWIP callbacks as a "state"
|
||||
*/
|
||||
struct Larg
|
||||
{
|
||||
NetconEthernetTap *tap;
|
||||
Connection *conn;
|
||||
Larg(NetconEthernetTap *_tap, Connection *conn) : tap(_tap), conn(conn) {}
|
||||
};
|
||||
|
||||
/*
|
||||
* Network Containers instance -- emulates an Ethernet tap device as far as OneService knows
|
||||
*/
|
||||
class NetconEthernetTap
|
||||
{
|
||||
friend class Phy<NetconEthernetTap *>;
|
||||
|
||||
public:
|
||||
NetconEthernetTap(
|
||||
const char *homePath,
|
||||
const MAC &mac,
|
||||
unsigned int mtu,
|
||||
unsigned int metric,
|
||||
uint64_t nwid,
|
||||
const char *friendlyName,
|
||||
void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
|
||||
void *arg);
|
||||
|
||||
~NetconEthernetTap();
|
||||
|
||||
void setEnabled(bool en);
|
||||
bool enabled() const;
|
||||
bool addIp(const InetAddress &ip);
|
||||
bool removeIp(const InetAddress &ip);
|
||||
std::vector<InetAddress> ips() const;
|
||||
void put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len);
|
||||
std::string deviceName() const;
|
||||
void setFriendlyName(const char *friendlyName);
|
||||
void scanMulticastGroups(std::vector<MulticastGroup> &added,std::vector<MulticastGroup> &removed);
|
||||
|
||||
void threadMain()
|
||||
throw();
|
||||
|
||||
LWIPStack *lwipstack;
|
||||
uint64_t _nwid;
|
||||
void (*_handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
|
||||
void *_arg;
|
||||
|
||||
private:
|
||||
// LWIP callbacks
|
||||
// NOTE: these are called from within LWIP, meaning that lwipstack->_lock is ALREADY
|
||||
// locked in this case!
|
||||
|
||||
/*
|
||||
* Callback from LWIP for when a connection has been accepted and the PCB has been
|
||||
* put into an ACCEPT state.
|
||||
*
|
||||
* A socketpair is created, one end is kept and wrapped into a PhySocket object
|
||||
* for use in the main ZT I/O loop, and one end is sent to the client. The client
|
||||
* is then required to tell the service what new file descriptor it has allocated
|
||||
* for this connection. After the mapping is complete, the accepted socket can be
|
||||
* used.
|
||||
*
|
||||
* @param associated service state object
|
||||
* @param newly allocated PCB
|
||||
* @param error code
|
||||
* @return ERR_OK if everything is ok, -1 otherwise
|
||||
*
|
||||
* i := should be implemented in intercept lib
|
||||
* I := is implemented in intercept lib
|
||||
* X := is implemented in service
|
||||
* ? := required treatment Unknown
|
||||
* - := Not needed
|
||||
*
|
||||
* [ ] 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.
|
||||
* [I] EBADF - The descriptor is invalid.
|
||||
* [I] ECONNABORTED - A connection has been aborted.
|
||||
* [i] 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).
|
||||
* [I] EINVAL - Socket is not listening for connections, or addrlen is invalid (e.g., is negative).
|
||||
* [I] EINVAL - (accept4()) invalid value in flags.
|
||||
* [I] 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.
|
||||
* [I] ENOTSOCK - The descriptor references a file, not a socket.
|
||||
* [I] EOPNOTSUPP - The referenced socket is not of type SOCK_STREAM.
|
||||
* [ ] EPROTO - Protocol error.
|
||||
*
|
||||
*/
|
||||
static err_t nc_accept(void *arg, struct tcp_pcb *newPCB, err_t err);
|
||||
|
||||
/*
|
||||
* Callback from LWIP for when data is available to be read from the network.
|
||||
*
|
||||
* Data is in the form of a linked list of struct pbufs, it is then recombined and
|
||||
* send to the client over the associated unix socket.
|
||||
*
|
||||
* @param associated service state object
|
||||
* @param allocated PCB
|
||||
* @param chain of pbufs
|
||||
* @param error code
|
||||
* @return ERR_OK if everything is ok, -1 otherwise
|
||||
*
|
||||
*/
|
||||
static err_t nc_recved(void *arg, struct tcp_pcb *PCB, struct pbuf *p, err_t err);
|
||||
static err_t nc_recved_proxy(void *arg, struct tcp_pcb *PCB, struct pbuf *p, err_t err);
|
||||
static void nc_udp_recved(void * arg, struct udp_pcb * upcb, struct pbuf * p, struct ip_addr * addr, u16_t port);
|
||||
|
||||
|
||||
/*
|
||||
* Callback from LWIP when an internal error is associtated with the given (arg)
|
||||
*
|
||||
* Since the PCB related to this error might no longer exist, only its perviously
|
||||
* associated (arg) is provided to us.
|
||||
*
|
||||
* @param associated service state object
|
||||
* @param error code
|
||||
*
|
||||
*/
|
||||
static void nc_err(void *arg, err_t err);
|
||||
|
||||
/*
|
||||
* Callback from LWIP to do whatever work we might need to do.
|
||||
*
|
||||
* @param associated service state object
|
||||
* @param PCB we're polling on
|
||||
* @return ERR_OK if everything is ok, -1 otherwise
|
||||
*
|
||||
*/
|
||||
static err_t nc_poll(void* arg, struct tcp_pcb *PCB);
|
||||
|
||||
/*
|
||||
* Callback from LWIP to signal that 'len' bytes have successfully been sent.
|
||||
* As a result, we should put our socket back into a notify-on-readability state
|
||||
* since there is now room on the PCB buffer to write to.
|
||||
*
|
||||
* NOTE: This could be used to track the amount of data sent by a connection.
|
||||
*
|
||||
* @param associated service state object
|
||||
* @param relevant PCB
|
||||
* @param length of data sent
|
||||
* @return ERR_OK if everything is ok, -1 otherwise
|
||||
*
|
||||
*/
|
||||
static err_t nc_sent(void *arg, struct tcp_pcb *PCB, u16_t len);
|
||||
|
||||
/*
|
||||
* Callback from LWIP which sends a return value to the client to signal that
|
||||
* a connection was established for this PCB
|
||||
*
|
||||
* @param associated service state object
|
||||
* @param relevant PCB
|
||||
* @param error code
|
||||
* @return ERR_OK if everything is ok, -1 otherwise
|
||||
*
|
||||
*/
|
||||
static err_t nc_connected(void *arg, struct tcp_pcb *PCB, err_t err);
|
||||
static err_t nc_connected_proxy(void *arg, struct tcp_pcb *PCB, err_t err);
|
||||
|
||||
|
||||
//static void nc_close(struct tcp_pcb *PCB);
|
||||
//static err_t nc_send(struct tcp_pcb *PCB);
|
||||
|
||||
/*
|
||||
* Handles an RPC to bind an LWIP PCB to a given address and port
|
||||
*
|
||||
* @param PhySocket associated with this RPC connection
|
||||
* @param structure containing the data and parameters for this client's RPC
|
||||
*
|
||||
|
||||
i := should be implemented in intercept lib
|
||||
I := is implemented in intercept lib
|
||||
X := is implemented in service
|
||||
? := required treatment Unknown
|
||||
- := Not needed
|
||||
|
||||
[ ] EACCES - The address is protected, and the user is not the superuser.
|
||||
[X] EADDRINUSE - The given address is already in use.
|
||||
[I] EBADF - sockfd is not a valid descriptor.
|
||||
[X] EINVAL - The socket is already bound to an address.
|
||||
[I] ENOTSOCK - sockfd is a descriptor for a file, not a socket.
|
||||
|
||||
[X] ENOMEM - Insufficient kernel memory was available.
|
||||
|
||||
- The following errors are specific to UNIX domain (AF_UNIX) sockets:
|
||||
|
||||
[-] EACCES - Search permission is denied on a component of the path prefix. (See also path_resolution(7).)
|
||||
[-] EADDRNOTAVAIL - A nonexistent interface was requested or the requested address was not local.
|
||||
[-] EFAULT - addr points outside the user's accessible address space.
|
||||
[-] EINVAL - The addrlen is wrong, or the socket was not in the AF_UNIX family.
|
||||
[-] ELOOP - Too many symbolic links were encountered in resolving addr.
|
||||
[-] ENAMETOOLONG - s addr is too long.
|
||||
[-] ENOENT - The file does not exist.
|
||||
[-] ENOTDIR - A component of the path prefix is not a directory.
|
||||
[-] EROFS - The socket inode would reside on a read-only file system.
|
||||
*/
|
||||
void handleBind(PhySocket *sock, PhySocket *rpcsock, void **uptr, struct bind_st *bind_rpc);
|
||||
|
||||
/*
|
||||
* Handles an RPC to put an LWIP PCB into LISTEN mode
|
||||
*
|
||||
* @param PhySocket associated with this RPC connection
|
||||
* @param structure containing the data and parameters for this client's RPC
|
||||
*
|
||||
|
||||
i := should be implemented in intercept lib
|
||||
I := is implemented in intercept lib
|
||||
X := is implemented in service
|
||||
? := required treatment Unknown
|
||||
- := Not needed
|
||||
|
||||
[?] EADDRINUSE - Another socket is already listening on the same port.
|
||||
[IX] EBADF - The argument sockfd is not a valid descriptor.
|
||||
[I] ENOTSOCK - The argument sockfd is not a socket.
|
||||
[I] EOPNOTSUPP - The socket is not of a type that supports the listen() operation.
|
||||
*/
|
||||
void handleListen(PhySocket *sock, PhySocket *rpcsock, void **uptr, struct listen_st *listen_rpc);
|
||||
|
||||
/*
|
||||
* Handles an RPC to create a socket (LWIP PCB and associated socketpair)
|
||||
*
|
||||
* A socketpair is created, one end is kept and wrapped into a PhySocket object
|
||||
* for use in the main ZT I/O loop, and one end is sent to the client. The client
|
||||
* is then required to tell the service what new file descriptor it has allocated
|
||||
* for this connection. After the mapping is complete, the socket can be used.
|
||||
*
|
||||
* @param PhySocket associated with this RPC connection
|
||||
* @param structure containing the data and parameters for this client's RPC
|
||||
*
|
||||
|
||||
i := should be implemented in intercept lib
|
||||
I := is implemented in intercept lib
|
||||
X := is implemented in service
|
||||
? := required treatment Unknown
|
||||
- := Not needed
|
||||
|
||||
[-] EACCES - Permission to create a socket of the specified type and/or protocol is denied.
|
||||
[I] EAFNOSUPPORT - The implementation does not support the specified address family.
|
||||
[I] EINVAL - Unknown protocol, or protocol family not available.
|
||||
[I] EINVAL - Invalid flags in type.
|
||||
[I] EMFILE - Process file table overflow.
|
||||
[?] ENFILE - The system limit on the total number of open files has been reached.
|
||||
[X] ENOBUFS or ENOMEM - Insufficient memory is available. The socket cannot be created until sufficient resources are freed.
|
||||
[?] EPROTONOSUPPORT - The protocol type or the specified protocol is not supported within this domain.
|
||||
*/
|
||||
Connection * handleSocket(PhySocket *sock, void **uptr, struct socket_st* socket_rpc);
|
||||
Connection * handleSocketProxy(PhySocket *sock, int socket_type);
|
||||
|
||||
/*
|
||||
* Handles an RPC to connect to a given address and port
|
||||
*
|
||||
* @param PhySocket associated with this RPC connection
|
||||
* @param structure containing the data and parameters for this client's RPC
|
||||
|
||||
--- Error handling in this method will only catch problems which are immedately
|
||||
apprent. Some errors will need to be caught in the nc_connected(0 callback
|
||||
|
||||
i := should be implemented in intercept lib
|
||||
I := is implemented in intercept lib
|
||||
X := is implemented in service
|
||||
? := required treatment Unknown
|
||||
- := Not needed
|
||||
|
||||
[-] EACCES - For UNIX domain sockets, which are identified by pathname: Write permission is denied ...
|
||||
[?] EACCES, EPERM - The user tried to connect to a broadcast address without having the socket broadcast flag enabled ...
|
||||
[X] EADDRINUSE - Local address is already in use.
|
||||
[I] EAFNOSUPPORT - The passed address didn't have the correct address family in its sa_family field.
|
||||
[X] EAGAIN - No more free local ports or insufficient entries in the routing cache.
|
||||
[ ] EALREADY - The socket is nonblocking and a previous connection attempt has not yet been completed.
|
||||
[IX] EBADF - The file descriptor is not a valid index in the descriptor table.
|
||||
[ ] ECONNREFUSED - No-one listening on the remote address.
|
||||
[i] EFAULT - The socket structure address is outside the user's address space.
|
||||
[ ] EINPROGRESS - The socket is nonblocking and the connection cannot be completed immediately.
|
||||
[-] EINTR - The system call was interrupted by a signal that was caught.
|
||||
[X] EISCONN - The socket is already connected.
|
||||
[X] ENETUNREACH - Network is unreachable.
|
||||
[I] ENOTSOCK - The file descriptor is not associated with a socket.
|
||||
[X] ETIMEDOUT - Timeout while attempting connection.
|
||||
|
||||
[X] EINVAL - Invalid argument, SVr4, generally makes sense to set this
|
||||
*/
|
||||
void handleConnect(PhySocket *sock, PhySocket *rpcsock, Connection *conn, struct connect_st* connect_rpc);
|
||||
int handleConnectProxy(PhySocket *sock, struct sockaddr_in *rawAddr);
|
||||
|
||||
// void handleIsConnected();
|
||||
|
||||
/*
|
||||
* Return the address that the socket is bound to
|
||||
*/
|
||||
void handleGetsockname(PhySocket *sock, PhySocket *rpcsock, void **uptr, struct getsockname_st *getsockname_rpc);
|
||||
|
||||
/*
|
||||
* Writes data from the application's socket to the LWIP connection
|
||||
*/
|
||||
void handleWrite(Connection *conn);
|
||||
|
||||
/*
|
||||
* Sends a return value to the intercepted application
|
||||
*/
|
||||
int sendReturnValue(PhySocket *sock, int retval, int _errno);
|
||||
int sendReturnValue(int fd, int retval, int _errno);
|
||||
|
||||
/*
|
||||
* Unpacks the buffer from an RPC command
|
||||
*/
|
||||
void unloadRPC(void *data, pid_t &pid, pid_t &tid,
|
||||
int &rpc_count, char (timestamp[RPC_TIMESTAMP_SZ]), char (magic[sizeof(uint64_t)]), char &cmd, void* &payload);
|
||||
|
||||
// Unused -- no UDP or TCP from this thread/Phy<>
|
||||
void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *local_address, const struct sockaddr *from,void *data,unsigned long len);
|
||||
void phyOnTcpConnect(PhySocket *sock,void **uptr,bool success);
|
||||
void phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from);
|
||||
void phyOnTcpClose(PhySocket *sock,void **uptr);
|
||||
void phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len);
|
||||
|
||||
void processReceivedData(PhySocket *sock,void **uptr,bool lwip_invoked);
|
||||
void phyOnTcpWritable(PhySocket *sock,void **uptr,bool lwip_invoked);
|
||||
|
||||
/*
|
||||
* Signals us to close the TcpConnection associated with this PhySocket
|
||||
*/
|
||||
void phyOnUnixClose(PhySocket *sock,void **uptr);
|
||||
|
||||
/*
|
||||
* Notifies us that there is data to be read from an application's socket
|
||||
*/
|
||||
void phyOnUnixData(PhySocket *sock,void **uptr,void *data,unsigned long len);
|
||||
|
||||
/*
|
||||
* Notifies us that we can write to an application's socket
|
||||
*/
|
||||
void phyOnUnixWritable(PhySocket *sock,void **uptr,bool lwip_invoked);
|
||||
|
||||
/*
|
||||
* Returns a pointer to a TcpConnection associated with a given PhySocket
|
||||
*/
|
||||
Connection *getConnection(PhySocket *sock);
|
||||
|
||||
/*
|
||||
* Closes a TcpConnection, associated LWIP PCB strcuture,
|
||||
* PhySocket, and underlying file descriptor
|
||||
*/
|
||||
void closeConnection(PhySocket *sock);
|
||||
|
||||
// --- Proxy stubs
|
||||
int proxyListenPort;
|
||||
|
||||
//std::map<int,PhySocket> proxySockStates;
|
||||
|
||||
int sockstate;
|
||||
int proxyListenSocket;
|
||||
PhySocket *proxyListenPhySocket;
|
||||
|
||||
void StartProxy(const char *nwid);
|
||||
|
||||
void phyOnFileDescriptorActivity(PhySocket *sock,void **uptr,bool readable,bool writable);
|
||||
|
||||
// --- end Proxy stubs
|
||||
|
||||
ip_addr_t convert_ip(struct sockaddr_in * addr)
|
||||
{
|
||||
ip_addr_t conn_addr;
|
||||
struct sockaddr_in *ipv4 = addr;
|
||||
short a = ip4_addr1(&(ipv4->sin_addr));
|
||||
short b = ip4_addr2(&(ipv4->sin_addr));
|
||||
short c = ip4_addr3(&(ipv4->sin_addr));
|
||||
short d = ip4_addr4(&(ipv4->sin_addr));
|
||||
IP4_ADDR(&conn_addr, a,b,c,d);
|
||||
return conn_addr;
|
||||
}
|
||||
|
||||
Phy<NetconEthernetTap *> _phy;
|
||||
PhySocket *_unixListenSocket;
|
||||
|
||||
std::vector<Connection*> _Connections;
|
||||
|
||||
std::map<uint64_t, std::pair<PhySocket*, void*> > jobmap;
|
||||
|
||||
pid_t rpcCounter;
|
||||
netif interface;
|
||||
|
||||
MAC _mac;
|
||||
Thread _thread;
|
||||
std::string _homePath;
|
||||
std::string _dev; // path to Unix domain socket
|
||||
|
||||
std::vector<MulticastGroup> _multicastGroups;
|
||||
Mutex _multicastGroups_m;
|
||||
|
||||
std::vector<InetAddress> _ips;
|
||||
Mutex _ips_m, _tcpconns_m, _rx_buf_m, _close_m;
|
||||
|
||||
unsigned int _mtu;
|
||||
volatile bool _enabled;
|
||||
volatile bool _run;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
571
src/SDK_Intercept.c
Normal file
571
src/SDK_Intercept.c
Normal file
@@ -0,0 +1,571 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* 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/
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <dlfcn.h>
|
||||
#include <strings.h>
|
||||
#include <pwd.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <netdb.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/resource.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <linux/errno.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <linux/net.h>
|
||||
#endif
|
||||
|
||||
#include "SDK.h"
|
||||
#include "SDK_Debug.h"
|
||||
#include "SDK_RPC.h"
|
||||
|
||||
int set_netpath(char * path);
|
||||
char *netpath = (char *)0;
|
||||
void dwr(int level, const char *fmt, ... );
|
||||
const char *get_netpath();
|
||||
|
||||
pthread_key_t thr_id_key;
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// --------------------- Get Original socket API pointers -----------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
void load_symbols()
|
||||
{
|
||||
dwr(MSG_DEBUG,"load_symbols\n");
|
||||
|
||||
#if defined(__linux__)
|
||||
realaccept4 = dlsym(RTLD_NEXT, "accept4");
|
||||
#if !defined(__ANDROID__)
|
||||
realsyscall = dlsym(RTLD_NEXT, "syscall");
|
||||
#endif
|
||||
#endif
|
||||
realsetsockopt = (int(*)(SETSOCKOPT_SIG))dlsym(RTLD_NEXT, "setsockopt");
|
||||
realgetsockopt = (int(*)(GETSOCKOPT_SIG))dlsym(RTLD_NEXT, "getsockopt");
|
||||
realsocket = (int(*)(SOCKET_SIG))dlsym(RTLD_NEXT, "socket");
|
||||
realconnect = (int(*)(CONNECT_SIG))dlsym(RTLD_NEXT, "connect");
|
||||
realaccept = (int(*)(ACCEPT_SIG))dlsym(RTLD_NEXT, "accept");
|
||||
reallisten = (int(*)(LISTEN_SIG))dlsym(RTLD_NEXT, "listen");
|
||||
realclose = (int(*)(CLOSE_SIG))dlsym(RTLD_NEXT, "close");
|
||||
realgetsockname = (int(*)(GETSOCKNAME_SIG))dlsym(RTLD_NEXT, "getsockname");
|
||||
#if !defined(__ANDROID__)
|
||||
realbind = (int(*)(BIND_SIG))dlsym(RTLD_NEXT, "bind");
|
||||
realsendto = (ssize_t(*)(int, const void *, size_t, int, const struct sockaddr *, socklen_t))dlsym(RTLD_NEXT, "sendto");
|
||||
realrecvfrom = (int(*)(RECVFROM_SIG))dlsym(RTLD_NEXT, "recvfrom");
|
||||
realrecvmsg = (int(*)(RECVMSG_SIG))dlsym(RTLD_NEXT, "recvmsg");
|
||||
#endif
|
||||
dwr(MSG_DEBUG,"complete\n");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ------------------------------- Intercept Setup ------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// Return whether 'intercept' shim is enabled for this thread
|
||||
|
||||
bool check_intercept_enabled_for_thread() {
|
||||
dwr(MSG_DEBUG_EXTRA, "check_intercept_enabled_for_thread()\n");
|
||||
if(!realconnect){
|
||||
load_symbols();
|
||||
}
|
||||
void *spec = pthread_getspecific(thr_id_key);
|
||||
int thr_id = spec != NULL ? *((int*)spec) : -1;
|
||||
return thr_id == INTERCEPT_ENABLED;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ------------------------- connected_to_service() -----------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// Check whether or not the socket is mapped to the service. We
|
||||
// need to know if this is a regular AF_LOCAL socket or an end of a socketpair
|
||||
// that the service uses. We don't want to keep state in the intercept, so
|
||||
// we simply ask the service via an RPC
|
||||
|
||||
int connected_to_service(int sockfd)
|
||||
{
|
||||
socklen_t len;
|
||||
struct sockaddr_storage addr;
|
||||
len = sizeof addr;
|
||||
struct sockaddr_un * addr_un;
|
||||
getpeername(sockfd, (struct sockaddr*)&addr, &len);
|
||||
if (addr.ss_family == AF_LOCAL || addr.ss_family == AF_LOCAL) {
|
||||
addr_un = (struct sockaddr_un*)&addr;
|
||||
return strcmp(addr_un->sun_path, netpath) == 0;
|
||||
}
|
||||
dwr(MSG_DEBUG,"connected_to_service(): Not connected to service\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ------------------------------------ sendto() --------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int sockfd, const void *buf, size_t len, int flags,
|
||||
// const struct sockaddr *addr, socklen_t addr_len
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
ssize_t sendto(SENDTO_SIG)
|
||||
{
|
||||
dwr(MSG_DEBUG, "sendto(%d, %d)\n", sockfd, len);
|
||||
//if (!check_intercept_enabled_for_thread())
|
||||
return realsendto(sockfd, buf, len, flags, addr, addr_len);
|
||||
return zt_sendto(sockfd, buf, len, flags, addr, addr_len);
|
||||
}
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ----------------------------------- sendmsg() --------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int socket, const struct msghdr *message, int flags
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
ssize_t sendmsg(SENDMSG_SIG)
|
||||
{
|
||||
dwr(MSG_DEBUG, "sendmsg()\n");
|
||||
//if(!check_intercept_enabled_for_thread())
|
||||
return realsendmsg(socket, message, flags);
|
||||
zt_sendmsg(socket, message, flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ---------------------------------- recvfrom() --------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int socket, void *restrict buffer, size_t length, int flags, struct sockaddr
|
||||
// *restrict address, socklen_t *restrict address_len
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
ssize_t recvfrom(RECVFROM_SIG)
|
||||
{
|
||||
dwr(MSG_DEBUG, "recvfrom(%d)\n", socket);
|
||||
//if(!check_intercept_enabled_for_thread())
|
||||
return realrecvfrom(socket, buffer, length, flags, address, address_len);
|
||||
return zt_recvfrom(socket, buffer, length, flags, address, address_len);
|
||||
}
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ----------------------------------- recvmsg() --------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int socket, struct msghdr *message, int flags
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
ssize_t recvmsg(RECVMSG_SIG)
|
||||
{
|
||||
dwr(MSG_DEBUG, "recvmsg(%d)\n", socket);
|
||||
//if(!check_intercept_enabled_for_thread())
|
||||
return realrecvmsg(socket, message, flags);
|
||||
return zt_recvmsg(socket, message, flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// --------------------------------- setsockopt() -------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int socket, int level, int option_name, const void *option_value,
|
||||
// socklen_t option_len
|
||||
|
||||
int setsockopt(SETSOCKOPT_SIG)
|
||||
{
|
||||
dwr(MSG_DEBUG, "setsockopt(%d)\n", socket);
|
||||
if (!check_intercept_enabled_for_thread())
|
||||
return realsetsockopt(socket, level, option_name, option_value, option_len);
|
||||
#if defined(__linux__)
|
||||
if(level == SOL_IPV6 && option_name == IPV6_V6ONLY)
|
||||
return 0;
|
||||
if(level == SOL_IP && (option_name == IP_TTL || option_name == IP_TOS))
|
||||
return 0;
|
||||
#endif
|
||||
if(level == IPPROTO_TCP || (level == SOL_SOCKET && option_name == SO_KEEPALIVE))
|
||||
return 0;
|
||||
if(realsetsockopt(socket, level, option_name, option_value, option_len) < 0)
|
||||
perror("setsockopt():\n");
|
||||
return zt_setsockopt(socket, level, option_name, option_value, option_len);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// --------------------------------- getsockopt() -------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int sockfd, int level, int optname, void *optval,
|
||||
// socklen_t *optlen
|
||||
|
||||
int getsockopt(GETSOCKOPT_SIG)
|
||||
{
|
||||
dwr(MSG_DEBUG, "getsockopt(%d)\n", sockfd);
|
||||
if (!check_intercept_enabled_for_thread() || !connected_to_service(sockfd))
|
||||
return realgetsockopt(sockfd, level, optname, optval, optlen);
|
||||
return zt_getsockopt(sockfd, level, optname, optval, optlen);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ----------------------------------- socket() ---------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int socket_family, int socket_type, int protocol
|
||||
|
||||
int socket(SOCKET_SIG)
|
||||
{
|
||||
dwr(MSG_DEBUG, "socket()\n");
|
||||
if (!check_intercept_enabled_for_thread() && socket_type) {
|
||||
int err = realsocket(socket_family, socket_type, protocol);
|
||||
if(err < 0) {
|
||||
perror("socket:\n");
|
||||
}
|
||||
else {
|
||||
dwr(MSG_DEBUG, " socket() = %d\n", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
// Check if local
|
||||
if(socket_family == AF_LOCAL
|
||||
#if defined(__linux__)
|
||||
|| socket_family == AF_NETLINK
|
||||
#endif
|
||||
|| socket_family == AF_UNIX) {
|
||||
int err = realsocket(socket_family, socket_type, protocol);
|
||||
dwr(MSG_DEBUG,"realsocket() = %d\n", err);
|
||||
return err;
|
||||
}
|
||||
return zt_socket(socket_family, socket_type, protocol);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ---------------------------------- connect() ---------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int __fd, const struct sockaddr * __addr, socklen_t __len
|
||||
|
||||
int connect(CONNECT_SIG)
|
||||
{
|
||||
dwr(MSG_DEBUG, "connect(%d)\n", __fd);
|
||||
struct sockaddr_in *connaddr;
|
||||
connaddr = (struct sockaddr_in *)__addr;
|
||||
if(__addr->sa_family == AF_LOCAL || __addr->sa_family == AF_UNIX) {
|
||||
struct sockaddr_storage storage;
|
||||
memcpy(&storage, __addr, __len);
|
||||
struct sockaddr_un *s_un = (struct sockaddr_un*)&storage;
|
||||
dwr(MSG_DEBUG, "connect(): address = %s\n", s_un->sun_path);
|
||||
}
|
||||
|
||||
int port = connaddr->sin_port;
|
||||
int ip = connaddr->sin_addr.s_addr;
|
||||
unsigned char d[4];
|
||||
d[0] = ip & 0xFF;
|
||||
d[1] = (ip >> 8) & 0xFF;
|
||||
d[2] = (ip >> 16) & 0xFF;
|
||||
d[3] = (ip >> 24) & 0xFF;
|
||||
dwr(MSG_DEBUG,"connect(): %d.%d.%d.%d: %d\n", d[0],d[1],d[2],d[3], ntohs(port));
|
||||
|
||||
if(!check_intercept_enabled_for_thread())
|
||||
return realconnect(__fd, __addr, __len);
|
||||
|
||||
/* Check that this is a valid fd */
|
||||
if(fcntl(__fd, F_GETFD) < 0) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
/* Check that it is a socket */
|
||||
int sock_type;
|
||||
socklen_t sock_type_len = sizeof(sock_type);
|
||||
if(getsockopt(__fd, SOL_SOCKET, SO_TYPE, (void *) &sock_type, &sock_type_len) < 0) {
|
||||
errno = ENOTSOCK;
|
||||
return -1;
|
||||
}
|
||||
#if defined(__linux__)
|
||||
/* Check family */
|
||||
if (connaddr->sin_family < 0 || connaddr->sin_family >= NPROTO){
|
||||
errno = EAFNOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
/* make sure we don't touch any standard outputs */
|
||||
if(__fd == 0 || __fd == 1 || __fd == 2)
|
||||
return(realconnect(__fd, __addr, __len));
|
||||
|
||||
if(__addr != NULL && (connaddr->sin_family == AF_LOCAL
|
||||
#if defined(__linux__)
|
||||
|| connaddr->sin_family == PF_NETLINK
|
||||
|| connaddr->sin_family == AF_NETLINK
|
||||
#endif
|
||||
|| connaddr->sin_family == AF_UNIX)) {
|
||||
return realconnect(__fd, __addr, __len);
|
||||
}
|
||||
return zt_connect(__fd, __addr, __len);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ------------------------------------ bind() ----------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int sockfd, const struct sockaddr *addr, socklen_t addrlen
|
||||
|
||||
int bind(BIND_SIG)
|
||||
{
|
||||
dwr(MSG_DEBUG,"bind(%d)\n", sockfd);
|
||||
// make sure we don't touch any standard outputs
|
||||
if(sockfd == 0 || sockfd == 1 || sockfd == 2)
|
||||
return(realbind(sockfd, addr, addrlen));
|
||||
struct sockaddr_in *connaddr;
|
||||
connaddr = (struct sockaddr_in *)addr;
|
||||
|
||||
if(connaddr->sin_family == AF_LOCAL
|
||||
#if defined(__linux__)
|
||||
|| connaddr->sin_family == AF_NETLINK
|
||||
#endif
|
||||
|| connaddr->sin_family == AF_UNIX) {
|
||||
int err = realbind(sockfd, addr, addrlen);
|
||||
dwr(MSG_DEBUG,"realbind, err = %d\n", err);
|
||||
return err;
|
||||
}
|
||||
int port = connaddr->sin_port;
|
||||
int ip = connaddr->sin_addr.s_addr;
|
||||
unsigned char d[4];
|
||||
d[0] = ip & 0xFF;
|
||||
d[1] = (ip >> 8) & 0xFF;
|
||||
d[2] = (ip >> 16) & 0xFF;
|
||||
d[3] = (ip >> 24) & 0xFF;
|
||||
dwr(MSG_DEBUG,"bind(): %d.%d.%d.%d: %d\n", d[0],d[1],d[2],d[3], ntohs(port));
|
||||
|
||||
int sock_type;
|
||||
socklen_t sock_type_len = sizeof(sock_type);
|
||||
if(getsockopt(sockfd, SOL_SOCKET, SO_TYPE, (void *) &sock_type, &sock_type_len) < 0) {
|
||||
errno = ENOTSOCK;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Otherwise, perform usual intercept logic
|
||||
if (!check_intercept_enabled_for_thread())
|
||||
return realbind(sockfd, addr, addrlen);
|
||||
|
||||
// Check that this is a valid fd
|
||||
if(fcntl(sockfd, F_GETFD) < 0) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
// Check that it is a socket
|
||||
int opt = -1;
|
||||
socklen_t opt_len;
|
||||
if(getsockopt(sockfd, SOL_SOCKET, SO_TYPE, (void *) &opt, &opt_len) < 0) {
|
||||
errno = ENOTSOCK;
|
||||
return -1;
|
||||
}
|
||||
return zt_bind(sockfd, addr, addrlen);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ----------------------------------- accept4() --------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags
|
||||
|
||||
#if defined(__linux__)
|
||||
int accept4(ACCEPT4_SIG) {
|
||||
dwr(MSG_DEBUG,"accept4(%d):\n", sockfd);
|
||||
return zt_accept4(sockfd, addr, addrlen, flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ----------------------------------- accept() ---------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int sockfd struct sockaddr *addr, socklen_t *addrlen
|
||||
|
||||
int accept(ACCEPT_SIG) {
|
||||
dwr(MSG_DEBUG,"accept(%d):\n", sockfd);
|
||||
if (!check_intercept_enabled_for_thread())
|
||||
return realaccept(sockfd, addr, addrlen);
|
||||
|
||||
/* Check that this is a valid fd */
|
||||
if(fcntl(sockfd, F_GETFD) < 0) {
|
||||
return -1;
|
||||
errno = EBADF;
|
||||
dwr(MSG_DEBUG,"EBADF\n");
|
||||
return -1;
|
||||
}
|
||||
/* Check that it is a socket */
|
||||
int opt;
|
||||
socklen_t opt_len;
|
||||
if(getsockopt(sockfd, SOL_SOCKET, SO_TYPE, (void *) &opt, &opt_len) < 0) {
|
||||
errno = ENOTSOCK;
|
||||
dwr(MSG_DEBUG,"ENOTSOCK\n");
|
||||
return -1;
|
||||
}
|
||||
/* Check that this socket supports accept() */
|
||||
if(!(opt && (SOCK_STREAM | SOCK_SEQPACKET))) {
|
||||
errno = EOPNOTSUPP;
|
||||
dwr(MSG_DEBUG,"EOPNOTSUPP\n");
|
||||
return -1;
|
||||
}
|
||||
/* Check that we haven't hit the soft-limit file descriptors allowed */
|
||||
struct rlimit rl;
|
||||
getrlimit(RLIMIT_NOFILE, &rl);
|
||||
if(sockfd >= rl.rlim_cur){
|
||||
errno = EMFILE;
|
||||
dwr(MSG_DEBUG,"EMFILE\n");
|
||||
return -1;
|
||||
}
|
||||
/* Check address length */
|
||||
if(addrlen < 0) {
|
||||
errno = EINVAL;
|
||||
dwr(MSG_DEBUG,"EINVAL\n");
|
||||
return -1;
|
||||
}
|
||||
/* redirect calls for standard I/O descriptors to kernel */
|
||||
if(sockfd == 0 || sockfd == 1 || sockfd == 2){
|
||||
dwr(MSG_DEBUG,"realaccept():\n");
|
||||
return(realaccept(sockfd, addr, addrlen));
|
||||
}
|
||||
|
||||
return zt_accept(sockfd, addr, addrlen);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ------------------------------------- listen()--------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int sockfd, int backlog
|
||||
|
||||
int listen(LISTEN_SIG)
|
||||
{
|
||||
dwr(MSG_DEBUG,"listen(%d):\n", sockfd);
|
||||
if (!check_intercept_enabled_for_thread())
|
||||
return reallisten(sockfd, backlog);
|
||||
|
||||
int sock_type;
|
||||
socklen_t sock_type_len = sizeof(sock_type);
|
||||
/* Check that this is a valid fd */
|
||||
if(fcntl(sockfd, F_GETFD) < 0) {
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
/* Check that it is a socket */
|
||||
if(getsockopt(sockfd, SOL_SOCKET, SO_TYPE, (void *) &sock_type, &sock_type_len) < 0) {
|
||||
errno = ENOTSOCK;
|
||||
return -1;
|
||||
}
|
||||
/* Check that this socket supports accept() */
|
||||
if(!(sock_type && (SOCK_STREAM | SOCK_SEQPACKET))) {
|
||||
errno = EOPNOTSUPP;
|
||||
return -1;
|
||||
}
|
||||
/* make sure we don't touch any standard outputs */
|
||||
if(sockfd == 0 || sockfd == 1 || sockfd == 2)
|
||||
return reallisten(sockfd, backlog);
|
||||
|
||||
if(!connected_to_service(sockfd)) {
|
||||
return reallisten(sockfd, backlog);
|
||||
}
|
||||
return zt_listen(sockfd, backlog);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ------------------------------------- close() --------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int fd
|
||||
|
||||
int close(CLOSE_SIG) {
|
||||
dwr(MSG_DEBUG, " close(%d)\n", fd);
|
||||
check_intercept_enabled_for_thread();
|
||||
return realclose(fd);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// -------------------------------- getsockname() -------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int sockfd, struct sockaddr *addr, socklen_t *addrlen
|
||||
|
||||
int getsockname(GETSOCKNAME_SIG)
|
||||
{
|
||||
dwr(MSG_DEBUG,"getsockname(%d):\n", sockfd);
|
||||
#if !defined(__IOS__)
|
||||
if (!check_intercept_enabled_for_thread())
|
||||
return realgetsockname(sockfd, addr, addrlen);
|
||||
#endif
|
||||
dwr(MSG_DEBUG,"getsockname(%d)\n", sockfd);
|
||||
if(!connected_to_service(sockfd)) {
|
||||
dwr(MSG_DEBUG,"getsockname(): not used by service\n");
|
||||
return realgetsockname(sockfd, addr, addrlen);
|
||||
}
|
||||
return zt_getsockname(sockfd, addr, addrlen);
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ------------------------------------ syscall() -------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
#if defined(__linux__)
|
||||
long syscall(SYSCALL_SIG)
|
||||
{
|
||||
va_list ap;
|
||||
uintptr_t a,b,c,d,e,f;
|
||||
va_start(ap, number);
|
||||
a=va_arg(ap, uintptr_t);
|
||||
b=va_arg(ap, uintptr_t);
|
||||
c=va_arg(ap, uintptr_t);
|
||||
d=va_arg(ap, uintptr_t);
|
||||
e=va_arg(ap, uintptr_t);
|
||||
f=va_arg(ap, uintptr_t);
|
||||
va_end(ap);
|
||||
|
||||
if (!check_intercept_enabled_for_thread())
|
||||
return realsyscall(number,a,b,c,d,e,f);
|
||||
dwr(MSG_DEBUG,"syscall(%u, ...)\n", number);
|
||||
|
||||
#if defined(__i386__)
|
||||
// TODO: Implement for 32-bit systems: syscall(__NR_socketcall, 18, args);
|
||||
// args[0] = (unsigned long) fd;
|
||||
// args[1] = (unsigned long) addr;
|
||||
// args[2] = (unsigned long) addrlen;
|
||||
// args[3] = (unsigned long) flags;
|
||||
#else
|
||||
if(number == __NR_accept4) {
|
||||
int sockfd = a;
|
||||
struct sockaddr * addr = (struct sockaddr*)b;
|
||||
socklen_t * addrlen = (socklen_t*)c;
|
||||
int flags = d;
|
||||
int old_errno = errno;
|
||||
int err = accept4(sockfd, addr, addrlen, flags);
|
||||
errno = old_errno;
|
||||
err = err == -EBADF ? -EAGAIN : err;
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
return realsyscall(number,a,b,c,d,e,f);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
334
src/SDK_LWIPStack.hpp
Normal file
334
src/SDK_LWIPStack.hpp
Normal file
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* 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/
|
||||
*/
|
||||
|
||||
#ifndef SDK_LWIPSTACK_H
|
||||
#define SDK_LWIPSTACK_H
|
||||
|
||||
#include "lwip/mem.h"
|
||||
#include "lwip/pbuf.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/init.h"
|
||||
#include "lwip/tcp_impl.h"
|
||||
#include "lwip/udp.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#ifdef D_GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
typedef ip_addr ip_addr_t;
|
||||
struct tcp_pcb;
|
||||
|
||||
// lwip General Stack API
|
||||
#define PBUF_FREE_SIG struct pbuf *p
|
||||
#define PBUF_ALLOC_SIG pbuf_layer layer, u16_t length, pbuf_type type
|
||||
#define LWIP_HTONS_SIG u16_t x
|
||||
#define LWIP_NTOHS_SIG u16_t x
|
||||
#define IPADDR_NTOA_SIG const ip_addr_t *addr
|
||||
#define ETHARP_OUTPUT_SIG struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr
|
||||
#define ETHERNET_INPUT_SIG struct pbuf *p, struct netif *netif
|
||||
#define IP_INPUT_SIG struct pbuf *p, struct netif *inp
|
||||
#define NETIF_SET_DEFAULT_SIG struct netif *netif
|
||||
#define NETIF_ADD_SIG struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask, ip_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input
|
||||
#define NETIF_SET_UP_SIG struct netif *netif
|
||||
#define NETIF_POLL_SIG struct netif *netif
|
||||
|
||||
// lwIP UDP API
|
||||
#define UDP_NEW_SIG void
|
||||
#define UDP_CONNECT_SIG struct udp_pcb * pcb, struct ip_addr * ipaddr, u16_t port
|
||||
#define UDP_SEND_SIG struct udp_pcb * pcb, struct pbuf * p
|
||||
#define UDP_SENDTO_SIG struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, u16_t dst_port
|
||||
#define UDP_RECV_SIG struct udp_pcb * pcb, void (* recv)(void * arg, struct udp_pcb * upcb, struct pbuf * p, struct ip_addr * addr, u16_t port), void * recv_arg
|
||||
#define UDP_RECVED_SIG struct udp_pcb * pcb, u16_t len
|
||||
#define UDP_BIND_SIG struct udp_pcb * pcb, struct ip_addr * ipaddr, u16_t port
|
||||
#define UDP_REMOVE_SIG struct udp_pcb *pcb
|
||||
|
||||
// lwIP TCP API
|
||||
#define TCP_WRITE_SIG struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags
|
||||
#define TCP_SENT_SIG struct tcp_pcb * pcb, err_t (* sent)(void * arg, struct tcp_pcb * tpcb, u16_t len)
|
||||
#define TCP_NEW_SIG void
|
||||
#define TCP_RECV_SIG struct tcp_pcb * pcb, err_t (* recv)(void * arg, struct tcp_pcb * tpcb, struct pbuf * p, err_t err)
|
||||
#define TCP_RECVED_SIG struct tcp_pcb * pcb, u16_t len
|
||||
#define TCP_SNDBUF_SIG struct tcp_pcb * pcb
|
||||
#define TCP_CONNECT_SIG struct tcp_pcb * pcb, struct ip_addr * ipaddr, u16_t port, err_t (* connected)(void * arg, struct tcp_pcb * tpcb, err_t err)
|
||||
#define TCP_RECV_SIG struct tcp_pcb * pcb, err_t (* recv)(void * arg, struct tcp_pcb * tpcb, struct pbuf * p, err_t err)
|
||||
#define TCP_ERR_SIG struct tcp_pcb * pcb, void (* err)(void * arg, err_t err)
|
||||
#define TCP_POLL_SIG struct tcp_pcb * pcb, err_t (* poll)(void * arg, struct tcp_pcb * tpcb), u8_t interval
|
||||
#define TCP_ARG_SIG struct tcp_pcb * pcb, void * arg
|
||||
#define TCP_CLOSE_SIG struct tcp_pcb * pcb
|
||||
#define TCP_ABORT_SIG struct tcp_pcb * pcb
|
||||
#define TCP_OUTPUT_SIG struct tcp_pcb * pcb
|
||||
#define TCP_ACCEPT_SIG struct tcp_pcb * pcb, err_t (* accept)(void * arg, struct tcp_pcb * newpcb, err_t err)
|
||||
#define TCP_LISTEN_SIG struct tcp_pcb * pcb
|
||||
#define TCP_LISTEN_WITH_BACKLOG_SIG struct tcp_pcb * pcb, u8_t backlog
|
||||
#define TCP_BIND_SIG struct tcp_pcb * pcb, struct ip_addr * ipaddr, u16_t port
|
||||
#define TCP_INPUT_SIG struct pbuf *p, struct netif *inp
|
||||
|
||||
void dwr(int level, const char *fmt, ... );
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
* Loads an instance of liblwip.so in a private memory arena
|
||||
*
|
||||
* This uses dlmopen() to load an instance of the LWIP stack into its
|
||||
* own private memory space. This is done to get around the stack's
|
||||
* lack of thread-safety or multi-instance support. The alternative
|
||||
* would be to massively refactor the stack so everything lives in a
|
||||
* state object instead of static memory space.
|
||||
*/
|
||||
class LWIPStack
|
||||
{
|
||||
public:
|
||||
|
||||
void *_libref;
|
||||
|
||||
void close() {
|
||||
#if defined(__STATIC__LWIP__)
|
||||
return;
|
||||
#elif defined(__DYNAMIC_LWIP__)
|
||||
dlclose(_libref);
|
||||
#endif
|
||||
}
|
||||
|
||||
void (*_lwip_init)();
|
||||
err_t (*_tcp_write)(TCP_WRITE_SIG);
|
||||
void (*_tcp_sent)(TCP_SENT_SIG);
|
||||
struct tcp_pcb * (*_tcp_new)(TCP_NEW_SIG);
|
||||
u16_t (*_tcp_sndbuf)(TCP_SNDBUF_SIG);
|
||||
err_t (*_tcp_connect)(TCP_CONNECT_SIG);
|
||||
|
||||
struct udp_pcb * (*_udp_new)(UDP_NEW_SIG);
|
||||
err_t (*_udp_connect)(UDP_CONNECT_SIG);
|
||||
err_t (*_udp_send)(UDP_SEND_SIG);
|
||||
err_t (*_udp_sendto)(UDP_SENDTO_SIG);
|
||||
void (*_udp_recv)(UDP_RECV_SIG);
|
||||
void (*_udp_recved)(UDP_RECVED_SIG);
|
||||
err_t (*_udp_bind)(UDP_BIND_SIG);
|
||||
void (*_udp_remove)(UDP_REMOVE_SIG);
|
||||
|
||||
void (*_tcp_recv)(TCP_RECV_SIG);
|
||||
void (*_tcp_recved)(TCP_RECVED_SIG);
|
||||
void (*_tcp_err)(TCP_ERR_SIG);
|
||||
void (*_tcp_poll)(TCP_POLL_SIG);
|
||||
void (*_tcp_arg)(TCP_ARG_SIG);
|
||||
err_t (*_tcp_close)(TCP_CLOSE_SIG);
|
||||
void (*_tcp_abort)(TCP_ABORT_SIG);
|
||||
err_t (*_tcp_output)(TCP_OUTPUT_SIG);
|
||||
void (*_tcp_accept)(TCP_ACCEPT_SIG);
|
||||
struct tcp_pcb * (*_tcp_listen)(TCP_LISTEN_SIG);
|
||||
struct tcp_pcb * (*_tcp_listen_with_backlog)(TCP_LISTEN_WITH_BACKLOG_SIG);
|
||||
err_t (*_tcp_bind)(TCP_BIND_SIG);
|
||||
void (*_etharp_tmr)(void);
|
||||
void (*_tcp_tmr)(void);
|
||||
u8_t (*_pbuf_free)(PBUF_FREE_SIG);
|
||||
struct pbuf * (*_pbuf_alloc)(PBUF_ALLOC_SIG);
|
||||
u16_t (*_lwip_htons)(LWIP_HTONS_SIG);
|
||||
u16_t (*_lwip_ntohs)(LWIP_NTOHS_SIG);
|
||||
char* (*_ipaddr_ntoa)(IPADDR_NTOA_SIG);
|
||||
err_t (*_etharp_output)(ETHARP_OUTPUT_SIG);
|
||||
err_t (*_ethernet_input)(ETHERNET_INPUT_SIG);
|
||||
void (*_tcp_input)(TCP_INPUT_SIG);
|
||||
err_t (*_ip_input)(IP_INPUT_SIG);
|
||||
void (*_netif_set_default)(NETIF_SET_DEFAULT_SIG);
|
||||
struct netif * (*_netif_add)(NETIF_ADD_SIG);
|
||||
void (*_netif_set_up)(NETIF_SET_UP_SIG);
|
||||
void (*_netif_poll)(NETIF_POLL_SIG);
|
||||
|
||||
Mutex _lock;
|
||||
|
||||
LWIPStack(const char* path) :
|
||||
_libref(NULL)
|
||||
{
|
||||
#if defined(__ANDROID__) || defined(__UNITY_3D__)
|
||||
#define __STATIC_LWIP__
|
||||
#elif defined(__linux__)
|
||||
#define __DYNAMIC_LWIP__
|
||||
// Dynamically load liblwip.so
|
||||
_libref = dlmopen(LM_ID_NEWLM, path, RTLD_NOW);
|
||||
#elif defined(__APPLE__)
|
||||
#include "TargetConditionals.h"
|
||||
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
|
||||
#include "node/Mutex.hpp"
|
||||
#define __STATIC_LWIP__
|
||||
// iOS Simulator or iOS device
|
||||
// Do nothing, symbols are statically-linked
|
||||
#elif TARGET_OS_MAC
|
||||
#define __DYNAMIC_LWIP__
|
||||
// Dynamically load liblwip.so
|
||||
_libref = dlopen(path, RTLD_NOW);
|
||||
#else
|
||||
error "Unknown Apple platform"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __STATIC_LWIP__ // Set static references (for use in iOS)
|
||||
_ethernet_input = (err_t(*)(ETHERNET_INPUT_SIG))ðernet_input;
|
||||
_etharp_output = (err_t(*)(ETHARP_OUTPUT_SIG))ðarp_output;
|
||||
_lwip_init = (void(*)(void))&lwip_init;
|
||||
_tcp_write = (err_t(*)(TCP_WRITE_SIG))&tcp_write;
|
||||
_tcp_sent = (void(*)(TCP_SENT_SIG))&tcp_sent;
|
||||
_tcp_new = (struct tcp_pcb*(*)(TCP_NEW_SIG))&tcp_new;
|
||||
|
||||
_udp_new = (struct udp_pcb*(*)(UDP_NEW_SIG))&udp_new;
|
||||
_udp_connect = (err_t(*)(UDP_CONNECT_SIG))&udp_connect;
|
||||
_udp_send = (err_t(*)(UDP_SEND_SIG))&udp_send;
|
||||
_udp_sendto = (err_t(*)(UDP_SENDTO_SIG))&udp_sendto;
|
||||
_udp_recv = (void(*)(UDP_RECV_SIG))&udp_recv;
|
||||
_udp_bind = (err_t(*)(UDP_BIND_SIG))&udp_bind;
|
||||
_udp_remove = (void(*)(UDP_REMOVE_SIG))&udp_remove;
|
||||
|
||||
_tcp_connect = (err_t(*)(TCP_CONNECT_SIG))&tcp_connect;
|
||||
_tcp_recv = (void(*)(TCP_RECV_SIG))&tcp_recv;
|
||||
_tcp_recved = (void(*)(TCP_RECVED_SIG))&tcp_recved;
|
||||
_tcp_err = (void(*)(TCP_ERR_SIG))&tcp_err;
|
||||
_tcp_poll = (void(*)(TCP_POLL_SIG))&tcp_poll;
|
||||
_tcp_arg = (void(*)(TCP_ARG_SIG))&tcp_arg;
|
||||
_tcp_close = (err_t(*)(TCP_CLOSE_SIG))&tcp_close;
|
||||
_tcp_abort = (void(*)(TCP_ABORT_SIG))&tcp_abort;
|
||||
_tcp_output = (err_t(*)(TCP_OUTPUT_SIG))&tcp_output;
|
||||
_tcp_accept = (void(*)(TCP_ACCEPT_SIG))&tcp_accept;
|
||||
_tcp_listen_with_backlog = (struct tcp_pcb*(*)(TCP_LISTEN_WITH_BACKLOG_SIG))&tcp_listen_with_backlog;
|
||||
_tcp_bind = (err_t(*)(TCP_BIND_SIG))&tcp_bind;
|
||||
_etharp_tmr = (void(*)(void))ðarp_tmr;
|
||||
_tcp_tmr = (void(*)(void))&tcp_tmr;
|
||||
_pbuf_free = (u8_t(*)(PBUF_FREE_SIG))&pbuf_free;
|
||||
_pbuf_alloc = (struct pbuf*(*)(PBUF_ALLOC_SIG))&pbuf_alloc;
|
||||
_lwip_htons = (u16_t(*)(LWIP_HTONS_SIG))&lwip_htons;
|
||||
_lwip_ntohs = (u16_t(*)(LWIP_NTOHS_SIG))&lwip_ntohs;
|
||||
_ipaddr_ntoa = (char*(*)(IPADDR_NTOA_SIG))&ipaddr_ntoa;
|
||||
_tcp_input = (void(*)(TCP_INPUT_SIG))&tcp_input;
|
||||
_ip_input = (err_t(*)(IP_INPUT_SIG))&ip_input;
|
||||
_netif_set_default = (void(*)(NETIF_SET_DEFAULT_SIG))&netif_set_default;
|
||||
_netif_add = (struct netif*(*)(NETIF_ADD_SIG))&netif_add;
|
||||
_netif_set_up = (void(*)(NETIF_SET_UP_SIG))&netif_set_up;
|
||||
#endif
|
||||
|
||||
#ifdef __DYNAMIC_LWIP__ // Use dynamically-loaded symbols (for use in normal desktop applications)
|
||||
|
||||
if(_libref == NULL)
|
||||
printf("dlerror(): %s\n", dlerror());
|
||||
|
||||
_ethernet_input = (err_t(*)(ETHERNET_INPUT_SIG))dlsym(_libref, "ethernet_input");
|
||||
_etharp_output = (err_t(*)(ETHARP_OUTPUT_SIG))dlsym(_libref, "etharp_output");
|
||||
_lwip_init = (void(*)(void))dlsym(_libref, "lwip_init");
|
||||
_tcp_write = (err_t(*)(TCP_WRITE_SIG))dlsym(_libref, "tcp_write");
|
||||
_tcp_sent = (void(*)(TCP_SENT_SIG))dlsym(_libref, "tcp_sent");
|
||||
_tcp_new = (struct tcp_pcb*(*)(TCP_NEW_SIG))dlsym(_libref, "tcp_new");
|
||||
|
||||
_udp_new = (struct udp_pcb*(*)(UDP_NEW_SIG))dlsym(_libref, "udp_new");
|
||||
_udp_connect = (err_t(*)(UDP_CONNECT_SIG))dlsym(_libref, "udp_connect");
|
||||
_udp_send = (err_t(*)(UDP_SEND_SIG))dlsym(_libref, "udp_send");
|
||||
_udp_sendto = (err_t(*)(UDP_SENDTO_SIG))dlsym(_libref, "udp_sendto");
|
||||
_udp_recv = (void(*)(UDP_RECV_SIG))dlsym(_libref, "udp_recv");
|
||||
_udp_bind = (err_t(*)(UDP_BIND_SIG))dlsym(_libref, "udp_bind");
|
||||
_udp_remove = (void(*)(UDP_REMOVE_SIG))dlsym(_libref, "udp_remove");
|
||||
|
||||
_tcp_sndbuf = (u16_t(*)(TCP_SNDBUF_SIG))dlsym(_libref, "tcp_sndbuf");
|
||||
_tcp_connect = (err_t(*)(TCP_CONNECT_SIG))dlsym(_libref, "tcp_connect");
|
||||
_tcp_recv = (void(*)(TCP_RECV_SIG))dlsym(_libref, "tcp_recv");
|
||||
_tcp_recved = (void(*)(TCP_RECVED_SIG))dlsym(_libref, "tcp_recved");
|
||||
_tcp_err = (void(*)(TCP_ERR_SIG))dlsym(_libref, "tcp_err");
|
||||
_tcp_poll = (void(*)(TCP_POLL_SIG))dlsym(_libref, "tcp_poll");
|
||||
_tcp_arg = (void(*)(TCP_ARG_SIG))dlsym(_libref, "tcp_arg");
|
||||
_tcp_close = (err_t(*)(TCP_CLOSE_SIG))dlsym(_libref, "tcp_close");
|
||||
_tcp_abort = (void(*)(TCP_ABORT_SIG))dlsym(_libref, "tcp_abort");
|
||||
_tcp_output = (err_t(*)(TCP_OUTPUT_SIG))dlsym(_libref, "tcp_output");
|
||||
_tcp_accept = (void(*)(TCP_ACCEPT_SIG))dlsym(_libref, "tcp_accept");
|
||||
_tcp_listen = (struct tcp_pcb*(*)(TCP_LISTEN_SIG))dlsym(_libref, "tcp_listen");
|
||||
_tcp_listen_with_backlog = (struct tcp_pcb*(*)(TCP_LISTEN_WITH_BACKLOG_SIG))dlsym(_libref, "tcp_listen_with_backlog");
|
||||
_tcp_bind = (err_t(*)(TCP_BIND_SIG))dlsym(_libref, "tcp_bind");
|
||||
_etharp_tmr = (void(*)(void))dlsym(_libref, "etharp_tmr");
|
||||
_tcp_tmr = (void(*)(void))dlsym(_libref, "tcp_tmr");
|
||||
_pbuf_free = (u8_t(*)(PBUF_FREE_SIG))dlsym(_libref, "pbuf_free");
|
||||
_pbuf_alloc = (struct pbuf*(*)(PBUF_ALLOC_SIG))dlsym(_libref, "pbuf_alloc");
|
||||
_lwip_htons = (u16_t(*)(LWIP_HTONS_SIG))dlsym(_libref, "lwip_htons");
|
||||
_lwip_ntohs = (u16_t(*)(LWIP_NTOHS_SIG))dlsym(_libref, "lwip_ntohs");
|
||||
_ipaddr_ntoa = (char*(*)(IPADDR_NTOA_SIG))dlsym(_libref, "ipaddr_ntoa");
|
||||
_tcp_input = (void(*)(TCP_INPUT_SIG))dlsym(_libref, "tcp_input");
|
||||
_ip_input = (err_t(*)(IP_INPUT_SIG))dlsym(_libref, "ip_input");
|
||||
_netif_set_default = (void(*)(NETIF_SET_DEFAULT_SIG))dlsym(_libref, "netif_set_default");
|
||||
_netif_add = (struct netif*(*)(NETIF_ADD_SIG))dlsym(_libref, "netif_add");
|
||||
_netif_set_up = (void(*)(NETIF_SET_UP_SIG))dlsym(_libref, "netif_set_up");
|
||||
#endif
|
||||
}
|
||||
|
||||
~LWIPStack()
|
||||
{
|
||||
if (_libref)
|
||||
dlclose(_libref);
|
||||
}
|
||||
|
||||
inline void __lwip_init() throw() { Mutex::Lock _l(_lock); return _lwip_init(); }
|
||||
inline err_t __tcp_write(TCP_WRITE_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_write(pcb,arg,len,apiflags); }
|
||||
inline void __tcp_sent(TCP_SENT_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_sent(pcb,sent); }
|
||||
inline struct tcp_pcb * __tcp_new(TCP_NEW_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_new(); }
|
||||
|
||||
inline struct udp_pcb * __udp_new(UDP_NEW_SIG) throw() { Mutex::Lock _l(_lock); return _udp_new(); }
|
||||
inline err_t __udp_connect(UDP_CONNECT_SIG) throw() { Mutex::Lock _l(_lock); return _udp_connect(pcb,ipaddr,port); }
|
||||
inline err_t __udp_send(UDP_SEND_SIG) throw() { Mutex::Lock _l(_lock); return _udp_send(pcb,p); }
|
||||
inline err_t __udp_sendto(UDP_SENDTO_SIG) throw() { Mutex::Lock _l(_lock); return _udp_sendto(pcb,p,dst_ip,dst_port); }
|
||||
inline void __udp_recv(UDP_RECV_SIG) throw() { Mutex::Lock _l(_lock); return _udp_recv(pcb,recv,recv_arg); }
|
||||
inline err_t __udp_bind(UDP_BIND_SIG) throw() { Mutex::Lock _l(_lock); return _udp_bind(pcb,ipaddr,port); }
|
||||
inline void __udp_remove(UDP_REMOVE_SIG) throw() { Mutex::Lock _l(_lock); return _udp_remove(pcb); }
|
||||
|
||||
inline u16_t __tcp_sndbuf(TCP_SNDBUF_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_sndbuf(pcb); }
|
||||
inline err_t __tcp_connect(TCP_CONNECT_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_connect(pcb,ipaddr,port,connected); }
|
||||
inline void __tcp_recv(TCP_RECV_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_recv(pcb,recv); }
|
||||
inline void __tcp_recved(TCP_RECVED_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_recved(pcb,len); }
|
||||
inline void __tcp_err(TCP_ERR_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_err(pcb,err); }
|
||||
inline void __tcp_poll(TCP_POLL_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_poll(pcb,poll,interval); }
|
||||
inline void __tcp_arg(TCP_ARG_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_arg(pcb,arg); }
|
||||
inline err_t __tcp_close(TCP_CLOSE_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_close(pcb); }
|
||||
inline void __tcp_abort(TCP_ABORT_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_abort(pcb); }
|
||||
inline err_t __tcp_output(TCP_OUTPUT_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_output(pcb); }
|
||||
inline void __tcp_accept(TCP_ACCEPT_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_accept(pcb,accept); }
|
||||
inline struct tcp_pcb * __tcp_listen(TCP_LISTEN_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_listen(pcb); }
|
||||
inline struct tcp_pcb * __tcp_listen_with_backlog(TCP_LISTEN_WITH_BACKLOG_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_listen_with_backlog(pcb,backlog); }
|
||||
inline err_t __tcp_bind(TCP_BIND_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_bind(pcb,ipaddr,port); }
|
||||
inline void __etharp_tmr(void) throw() { Mutex::Lock _l(_lock); return _etharp_tmr(); }
|
||||
inline void __tcp_tmr(void) throw() { Mutex::Lock _l(_lock); return _tcp_tmr(); }
|
||||
inline u8_t __pbuf_free(PBUF_FREE_SIG) throw() { Mutex::Lock _l(_lock); return _pbuf_free(p); }
|
||||
inline struct pbuf * __pbuf_alloc(PBUF_ALLOC_SIG) throw() { Mutex::Lock _l(_lock); return _pbuf_alloc(layer,length,type); }
|
||||
inline u16_t __lwip_htons(LWIP_HTONS_SIG) throw() { Mutex::Lock _l(_lock); return _lwip_htons(x); }
|
||||
inline u16_t __lwip_ntohs(LWIP_NTOHS_SIG) throw() { Mutex::Lock _l(_lock); return _lwip_ntohs(x); }
|
||||
inline char* __ipaddr_ntoa(IPADDR_NTOA_SIG) throw() { Mutex::Lock _l(_lock); return _ipaddr_ntoa(addr); }
|
||||
inline err_t __etharp_output(ETHARP_OUTPUT_SIG) throw() { Mutex::Lock _l(_lock); return _etharp_output(netif,q,ipaddr); }
|
||||
inline err_t __ethernet_input(ETHERNET_INPUT_SIG) throw() { Mutex::Lock _l(_lock); return _ethernet_input(p,netif); }
|
||||
inline void __tcp_input(TCP_INPUT_SIG) throw() { Mutex::Lock _l(_lock); return _tcp_input(p,inp); }
|
||||
inline err_t __ip_input(IP_INPUT_SIG) throw() { Mutex::Lock _l(_lock); return _ip_input(p,inp); }
|
||||
inline void __netif_set_default(NETIF_SET_DEFAULT_SIG) throw() { Mutex::Lock _l(_lock); return _netif_set_default(netif); }
|
||||
inline struct netif * __netif_add(NETIF_ADD_SIG) throw() { Mutex::Lock _l(_lock); return _netif_add(netif,ipaddr,netmask,gw,state,init,input); }
|
||||
inline void __netif_set_up(NETIF_SET_UP_SIG) throw() { Mutex::Lock _l(_lock); return _netif_set_up(netif); }
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
||||
411
src/SDK_Proxy.cpp
Normal file
411
src/SDK_Proxy.cpp
Normal file
@@ -0,0 +1,411 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* 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/
|
||||
*/
|
||||
|
||||
#include "SDK_Debug.h"
|
||||
#include "SDK_EthernetTap.hpp"
|
||||
#include "osdep/Phy.hpp"
|
||||
#include "node/Utils.hpp"
|
||||
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#define SOCKS_OPEN 0
|
||||
#define SOCKS_CONNECT_INIT 1
|
||||
#define SOCKS_CONNECT_IPV4 2
|
||||
#define SOCKS_UDP 3 // ?
|
||||
#define SOCKS_COMPLETE 4
|
||||
|
||||
#define CONNECTION_TIMEOUT 8
|
||||
|
||||
#define IDX_VERSION 0
|
||||
#define IDX_COMMAND 1
|
||||
#define IDX_METHOD 1
|
||||
#define IDX_FRAG 1
|
||||
#define IDX_ERROR_CODE 1
|
||||
#define IDX_NMETHODS 1
|
||||
#define IDX_METHODS 2 // Supported methods
|
||||
#define IDX_ATYP 3
|
||||
#define IDX_DST_ADDR 4 // L:D where L = addrlen, D = addr
|
||||
#define IDX_
|
||||
|
||||
#define THIS_PROXY_VERSION 5
|
||||
#define MAX_ADDR_LEN 32
|
||||
#define PORT_LEN 2
|
||||
|
||||
namespace ZeroTier
|
||||
{
|
||||
void NetconEthernetTap::StartProxy(const char *nwid)
|
||||
{
|
||||
#if defined (__ANDROID__)
|
||||
LOGV("StartProxy()\n");
|
||||
#else
|
||||
printf("StartProxy()\n");
|
||||
#endif
|
||||
|
||||
unsigned int randp = 0;
|
||||
Utils::getSecureRandom(&randp,sizeof(randp));
|
||||
proxyListenPort = 1000 + (randp % 1000);
|
||||
struct sockaddr_in in4;
|
||||
memset(&in4,0,sizeof(in4));
|
||||
in4.sin_family = AF_INET;
|
||||
in4.sin_addr.s_addr = Utils::hton((uint32_t)0x00000000); // right now we just listen for TCP @0.0.0.0
|
||||
in4.sin_port = Utils::hton((uint16_t)proxyListenPort);
|
||||
proxyListenPhySocket = _phy.tcpListen((const struct sockaddr*)&in4,(void *)this);
|
||||
sockstate = SOCKS_OPEN;
|
||||
printf("SOCKS5 proxy server address for <%s> is: <0.0.0.0:%d> (sock=%p)\n", nwid, proxyListenPort, (void*)&proxyListenPhySocket);
|
||||
}
|
||||
|
||||
void ExtractAddress(int addr_type, unsigned char *buf, struct sockaddr_in * addr)
|
||||
{
|
||||
// TODO: Generalize extraction logic
|
||||
if(addr_type == 3)
|
||||
{
|
||||
// Extract address from buffer
|
||||
int domain_len = buf[IDX_DST_ADDR]; // (L):D
|
||||
char addr_[MAX_ADDR_LEN];
|
||||
int port_ = 0;
|
||||
memset(addr_, 0, MAX_ADDR_LEN);
|
||||
memcpy(addr_, &buf[IDX_DST_ADDR+1], domain_len); // L:(D)
|
||||
memcpy(&port_, &buf[IDX_DST_ADDR+1]+(domain_len), PORT_LEN);
|
||||
port_ = Utils::hton((uint16_t)port_);
|
||||
std::string addr_str(addr_);
|
||||
// Format address for Netcon/lwIP
|
||||
addr->sin_family = AF_INET;
|
||||
addr->sin_port = port_;
|
||||
addr->sin_addr.s_addr = inet_addr(addr_str.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void NetconEthernetTap::phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len)
|
||||
{
|
||||
printf("phyOnTcpData(): sock=%p, len=%lu\n", (void*)&sock, len);
|
||||
unsigned char *buf;
|
||||
buf = (unsigned char *)data;
|
||||
|
||||
// Get connection for this PhySocket
|
||||
Connection *conn = getConnection(sock);
|
||||
if(!conn) {
|
||||
printf("phyOnTcpData(): Unable to locate Connection for sock=%p\n", (void*)&sock);
|
||||
return;
|
||||
}
|
||||
|
||||
// Write data to lwIP PCB (outgoing)
|
||||
if(conn->proxy_conn_state == SOCKS_COMPLETE)
|
||||
{
|
||||
if(len) {
|
||||
printf("len=%lu\n", len);
|
||||
memcpy((&conn->txbuf)+(conn->txsz), buf, len);
|
||||
conn->txsz += len;
|
||||
handleWrite(conn);
|
||||
}
|
||||
}
|
||||
|
||||
if(conn->proxy_conn_state==SOCKS_UDP)
|
||||
{
|
||||
printf("SOCKS_UDP from client\n");
|
||||
// +----+------+------+----------+----------+----------+
|
||||
// |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA |
|
||||
// +----+------+------+----------+----------+----------+
|
||||
// | 2 | 1 | 1 | Variable | 2 | Variable |
|
||||
// +----+------+------+----------+----------+----------+
|
||||
|
||||
//int fragment_num = buf[2];
|
||||
//int addr_type = buf[3];
|
||||
}
|
||||
|
||||
// SOCKS_OPEN
|
||||
// +----+----------+----------+
|
||||
// |VER | NMETHODS | METHODS |
|
||||
// +----+----------+----------+
|
||||
// | 1 | 1 | 1 to 255 |
|
||||
// +----+----------+----------+
|
||||
if(conn->proxy_conn_state==SOCKS_OPEN)
|
||||
{
|
||||
if(len >= 3)
|
||||
{
|
||||
int version = buf[IDX_VERSION];
|
||||
int methodsLength = buf[IDX_NMETHODS];
|
||||
int firstSupportedMethod = buf[IDX_METHODS];
|
||||
int supportedMethod = 0;
|
||||
|
||||
// Password auth
|
||||
if(firstSupportedMethod == 2) {
|
||||
supportedMethod = firstSupportedMethod;
|
||||
}
|
||||
printf(" INFO <ver=%d, meth_len=%d, supp_meth=%d>\n", version, methodsLength, supportedMethod);
|
||||
|
||||
// Send METHOD selection msg
|
||||
// +----+--------+
|
||||
// |VER | METHOD |
|
||||
// +----+--------+
|
||||
// | 1 | 1 |
|
||||
// +----+--------+
|
||||
char reply[2];
|
||||
reply[IDX_VERSION] = THIS_PROXY_VERSION; // version
|
||||
reply[IDX_METHOD] = supportedMethod;
|
||||
_phy.streamSend(sock, reply, sizeof(reply));
|
||||
|
||||
// Set state for next message
|
||||
conn->proxy_conn_state = SOCKS_CONNECT_INIT;
|
||||
}
|
||||
}
|
||||
|
||||
// SOCKS_CONNECT
|
||||
// +----+-----+-------+------+----------+----------+
|
||||
// |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
|
||||
// +----+-----+-------+------+----------+----------+
|
||||
// | 1 | 1 | X'00' | 1 | Variable | 2 |
|
||||
// +----+-----+-------+------+----------+----------+
|
||||
if(conn->proxy_conn_state==SOCKS_CONNECT_INIT)
|
||||
{
|
||||
// Ex. 4(meta) + 4(ipv4) + 2(port) = 10
|
||||
if(len >= 10)
|
||||
{
|
||||
int version = buf[IDX_VERSION];
|
||||
int cmd = buf[IDX_COMMAND];
|
||||
int addr_type = buf[IDX_ATYP];
|
||||
|
||||
printf("SOCKS REQUEST = <ver=%d, cmd=%d, typ=%d>\n", version, cmd, addr_type);
|
||||
|
||||
// CONNECT request
|
||||
if(cmd == 1) {
|
||||
// Ipv4
|
||||
if(addr_type == 1)
|
||||
{
|
||||
//printf("IPv4\n");
|
||||
int raw_addr;
|
||||
memcpy(&raw_addr, &buf[4], 4);
|
||||
char newaddr[16];
|
||||
inet_ntop(AF_INET, &raw_addr, (char*)newaddr, INET_ADDRSTRLEN);
|
||||
printf("new addr = %s\n", newaddr);
|
||||
|
||||
int rawport, port;
|
||||
memcpy(&rawport, &buf[5], 2);
|
||||
port = Utils::ntoh(rawport);
|
||||
printf("new port = %d\n", port);
|
||||
|
||||
// Assemble new address
|
||||
struct sockaddr_in addr;
|
||||
addr.sin_addr.s_addr = IPADDR_ANY;
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = Utils::hton(proxyListenPort);
|
||||
|
||||
int fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if(fd < 0)
|
||||
perror("socket");
|
||||
|
||||
int err = connect(fd, (struct sockaddr*)&addr, sizeof(addr));
|
||||
if(err < 0)
|
||||
perror("connect");
|
||||
}
|
||||
|
||||
// Fully-qualified domain name
|
||||
if(addr_type == 3)
|
||||
{
|
||||
int domain_len = buf[IDX_DST_ADDR]; // (L):D
|
||||
struct sockaddr_in addr;
|
||||
ExtractAddress(addr_type,buf,&addr);
|
||||
PhySocket * new_sock = handleSocketProxy(sock, SOCK_STREAM);
|
||||
if(!new_sock)
|
||||
printf("Error while creating proxied-socket\n");
|
||||
handleConnectProxy(sock, &addr);
|
||||
|
||||
// Convert connection err code into SOCKS-err-code
|
||||
// X'00' succeeded
|
||||
// X'01' general SOCKS server failure
|
||||
// X'02' connection not allowed by ruleset
|
||||
// X'03' Network unreachable
|
||||
// X'04' Host unreachable
|
||||
// X'05' Connection refused
|
||||
// X'06' TTL expired
|
||||
// X'07' Command not supported
|
||||
// X'08' Address type not supported
|
||||
// X'09' to X'FF' unassigned
|
||||
|
||||
// SOCKS_CONNECT_REPLY
|
||||
// +----+-----+-------+------+----------+----------+
|
||||
// |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
|
||||
// +----+-----+-------+------+----------+----------+
|
||||
// | 1 | 1 | X'00' | 1 | Variable | 2 |
|
||||
// +----+-----+-------+------+----------+----------+
|
||||
|
||||
printf("REPLY = %d\n", addr.sin_port);
|
||||
char reply[len]; // TODO: determine proper length
|
||||
int addr_len = domain_len;
|
||||
memset(reply, 0, len); // Create reply buffer at least as big as incoming SOCKS request data
|
||||
memcpy(&reply[IDX_DST_ADDR],&buf[IDX_DST_ADDR],domain_len);
|
||||
reply[IDX_VERSION] = THIS_PROXY_VERSION; // version
|
||||
reply[IDX_ERROR_CODE] = 0; // success/err code
|
||||
reply[2] = 0; // RSV
|
||||
reply[IDX_ATYP] = addr_type; // ATYP (1, 3, 4)
|
||||
reply[IDX_DST_ADDR] = addr_len;
|
||||
memcpy(&reply[IDX_DST_ADDR+domain_len], &addr.sin_port, PORT_LEN); // PORT
|
||||
_phy.streamSend(sock, reply, sizeof(reply));
|
||||
|
||||
// Any further data activity on this PhySocket will be considered data to send
|
||||
conn->proxy_conn_state = SOCKS_COMPLETE;
|
||||
}
|
||||
// END CONNECT
|
||||
}
|
||||
|
||||
// BIND Request
|
||||
if(cmd == 2)
|
||||
{
|
||||
printf("BIND request\n");
|
||||
//char raw_addr[15];
|
||||
//int bind_port;
|
||||
}
|
||||
|
||||
// UDP ASSOCIATION Request
|
||||
if(cmd == 3)
|
||||
{
|
||||
// PORT supplied should be port assigned by server in previous msg
|
||||
printf("UDP association request\n");
|
||||
|
||||
// SOCKS_CONNECT (Cont.)
|
||||
// +----+-----+-------+------+----------+----------+
|
||||
// |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
|
||||
// +----+-----+-------+------+----------+----------+
|
||||
// | 1 | 1 | X'00' | 1 | Variable | 2 |
|
||||
// +----+-----+-------+------+----------+----------+
|
||||
|
||||
// NOTE: Similar to cmd==1, should consolidate logic
|
||||
|
||||
// NOTE: Can't separate out port with method used in IPv4 block
|
||||
int domain_len = buf[4];
|
||||
// Grab Addr:Port
|
||||
char raw_addr[domain_len];
|
||||
memset(raw_addr, 0, domain_len);
|
||||
memcpy(raw_addr, &buf[5], domain_len);
|
||||
|
||||
std::string ip, port, addrstr(raw_addr);
|
||||
int del = addrstr.find(":");
|
||||
ip = addrstr.substr(0, del);
|
||||
port = addrstr.substr(del+1, domain_len);
|
||||
|
||||
// Create new lwIP PCB
|
||||
PhySocket * new_sock = handleSocketProxy(sock, SOCK_DGRAM);
|
||||
|
||||
printf("new_sock = %p\n", (void*)&sock);
|
||||
printf("new_sock = %p\n", (void*)&new_sock);
|
||||
if(!new_sock)
|
||||
printf("Error while creating proxied-socket\n");
|
||||
|
||||
// Form address
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, '0', sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = Utils::hton((uint16_t)atoi(port.c_str()));
|
||||
addr.sin_addr.s_addr = inet_addr(ip.c_str());
|
||||
//addr.sin_addr.s_addr = inet_addr("10.5.5.2");
|
||||
handleConnectProxy(sock, &addr);
|
||||
conn->proxy_conn_state = SOCKS_UDP;
|
||||
}
|
||||
|
||||
//if(addr_type == 1337)
|
||||
//{
|
||||
// // IPv6
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetconEthernetTap::phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from)
|
||||
{
|
||||
printf("phyOnTcpAccept(): sock=%p\n", (void*)&sockN);
|
||||
Connection *newConn = new Connection();
|
||||
newConn->sock = sockN;
|
||||
_phy.setNotifyWritable(sockN, false);
|
||||
_Connections.push_back(newConn);
|
||||
}
|
||||
|
||||
void NetconEthernetTap::phyOnTcpConnect(PhySocket *sock,void **uptr,bool success)
|
||||
{
|
||||
printf("phyOnTcpConnect(): sock=%p\n", (void*)&sock);
|
||||
}
|
||||
|
||||
// Unused -- no UDP or TCP from this thread/Phy<>
|
||||
void NetconEthernetTap::phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *local_address, const struct sockaddr *from,void *data,unsigned long len)
|
||||
{
|
||||
printf("phyOnDatagram(): len = %lu\n", len);
|
||||
if(len) {
|
||||
Connection *conn = getConnection(sock);
|
||||
if(!conn){
|
||||
printf("unable to locate Connection: sock=%p\n", (void*)sock);
|
||||
return;
|
||||
}
|
||||
unsigned char *buf = (unsigned char*)data;
|
||||
memcpy((&conn->txbuf)+(conn->txsz), buf, len);
|
||||
conn->txsz += len;
|
||||
handleWrite(conn);
|
||||
}
|
||||
}
|
||||
|
||||
void NetconEthernetTap::phyOnTcpClose(PhySocket *sock,void **uptr)
|
||||
{
|
||||
printf("phyOnTcpClose(): sock=%p\n", (void*)&sock);
|
||||
Mutex::Lock _l(_tcpconns_m);
|
||||
closeConnection(sock);
|
||||
}
|
||||
|
||||
void NetconEthernetTap::phyOnTcpWritable(PhySocket *sock,void **uptr, bool lwip_invoked)
|
||||
{
|
||||
printf(" phyOnTcpWritable(): sock=%p\n", (void*)&sock);
|
||||
processReceivedData(sock,uptr,lwip_invoked);
|
||||
}
|
||||
|
||||
// RX data on stream socks and send back over client sock's underlying fd
|
||||
void NetconEthernetTap::phyOnFileDescriptorActivity(PhySocket *sock,void **uptr,bool readable,bool writable)
|
||||
{
|
||||
printf("phyOnFileDescriptorActivity(): sock=%p\n", (void*&)sock);
|
||||
/*
|
||||
if(readable)
|
||||
{
|
||||
//ProxyConn *conn = (ProxyConn*)*uptr;
|
||||
//if(!conn){
|
||||
// printf("\t!conn");
|
||||
// return;
|
||||
//}
|
||||
//char buf[50];
|
||||
//memset(buf, 0, sizeof(buf));
|
||||
//printf("Activity(R)->socket() = %d\n", _phy.getDescriptor(sock));
|
||||
//printf("Activity(W)->socket() = %d\n", conn->fd);
|
||||
//int n_read = read(_phy.getDescriptor(sock), buf, sizeof(buf));
|
||||
//printf(" read = %d\n", n_read);
|
||||
//int n_sent = write(conn->fd, buf, n_read);
|
||||
//printf("buf = %s\n", buf);
|
||||
//printf(" sent = %d\n", n_sent);
|
||||
}
|
||||
if(writable)
|
||||
{
|
||||
printf(" writable\n");
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
324
src/SDK_RPC.c
Normal file
324
src/SDK_RPC.c
Normal file
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* 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/
|
||||
*/
|
||||
|
||||
#ifdef USE_GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/syscall.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/un.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <dlfcn.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/socket.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include "SDK.h"
|
||||
#include "SDK_RPC.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define SERVICE_CONNECT_ATTEMPTS 30
|
||||
|
||||
static int rpc_count;
|
||||
|
||||
static pthread_mutex_t lock;
|
||||
void rpc_mutex_init() {
|
||||
if(pthread_mutex_init(&lock, NULL) != 0) {
|
||||
fprintf(stderr, "error while initializing service call mutex\n");
|
||||
}
|
||||
}
|
||||
void rpc_mutex_destroy() {
|
||||
pthread_mutex_destroy(&lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads a new file descriptor from the service
|
||||
*/
|
||||
int get_new_fd(int sock)
|
||||
{
|
||||
char buf[BUF_SZ];
|
||||
int newfd;
|
||||
ssize_t size = sock_fd_read(sock, buf, sizeof(buf), &newfd);
|
||||
if(size > 0)
|
||||
return newfd;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reads a return value from the service and sets errno (if applicable)
|
||||
*/
|
||||
int get_retval(int rpc_sock)
|
||||
{
|
||||
if(rpc_sock >= 0) {
|
||||
int retval;
|
||||
int sz = sizeof(char) + sizeof(retval) + sizeof(errno);
|
||||
char retbuf[BUF_SZ];
|
||||
memset(&retbuf, 0, sz);
|
||||
long n_read = read(rpc_sock, &retbuf, sz);
|
||||
if(n_read > 0) {
|
||||
memcpy(&retval, &retbuf[1], sizeof(retval));
|
||||
memcpy(&errno, &retbuf[1+sizeof(retval)], sizeof(errno));
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int load_symbols_rpc()
|
||||
{
|
||||
#if defined(NETCON_INTERCEPT) || defined(__IOS__) || defined(__UNITY_3D__)
|
||||
realsocket = dlsym(RTLD_NEXT, "socket");
|
||||
realconnect = dlsym(RTLD_NEXT, "connect");
|
||||
if(!realconnect || !realsocket)
|
||||
return -1;
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
int rpc_join(char * sockname)
|
||||
{
|
||||
|
||||
FILE *f = fopen("/Users/Joseph/utest2/_log.txt","a");
|
||||
fprintf(f, sockname);
|
||||
|
||||
if(sockname == NULL) {
|
||||
printf("Warning, rpc netpath is NULL\n");
|
||||
}
|
||||
if(!load_symbols_rpc())
|
||||
return -1;
|
||||
struct sockaddr_un addr;
|
||||
int conn_err = -1, attempts = 0;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, sockname, sizeof(addr.sun_path)-1);
|
||||
int sock;
|
||||
if((sock = realsocket(AF_UNIX, SOCK_STREAM, 0)) < 0){
|
||||
fprintf(stderr, "Error while creating RPC socket\n");
|
||||
return -1;
|
||||
}
|
||||
while((conn_err != 0) && (attempts < SERVICE_CONNECT_ATTEMPTS)){
|
||||
if((conn_err = realconnect(sock, (struct sockaddr*)&addr, sizeof(addr))) != 0) {
|
||||
fprintf(stderr, "Error while connecting to RPC socket. Re-attempting...\n");
|
||||
sleep(1);
|
||||
}
|
||||
else
|
||||
return sock;
|
||||
attempts++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send a command to the service
|
||||
*/
|
||||
int rpc_send_command(char *path, int cmd, int forfd, void *data, int len)
|
||||
{
|
||||
pthread_mutex_lock(&lock);
|
||||
char c, padding[] = {PADDING};
|
||||
char cmdbuf[BUF_SZ], CANARY[CANARY_SZ+PADDING_SZ], metabuf[BUF_SZ];
|
||||
|
||||
memcpy(CANARY+CANARY_SZ, padding, sizeof(padding));
|
||||
uint64_t canary_num;
|
||||
// ephemeral RPC socket used only for this command
|
||||
int rpc_sock = rpc_join(path);
|
||||
// Generate token
|
||||
int fdrand = open("/dev/urandom", O_RDONLY);
|
||||
if(read(fdrand, &CANARY, CANARY_SZ) < 0) {
|
||||
fprintf(stderr,"unable to read from /dev/urandom for RPC canary data\n");
|
||||
return -1;
|
||||
}
|
||||
memcpy(&canary_num, CANARY, CANARY_SZ);
|
||||
cmdbuf[CMD_ID_IDX] = cmd;
|
||||
memcpy(&cmdbuf[CANARY_IDX], &canary_num, CANARY_SZ);
|
||||
memcpy(&cmdbuf[STRUCT_IDX], data, len);
|
||||
|
||||
rpc_count++;
|
||||
memset(metabuf, 0, BUF_SZ);
|
||||
#if defined(__linux__)
|
||||
#if !defined(__ANDROID__)
|
||||
pid_t pid = 5; //syscall(SYS_getpid);
|
||||
pid_t tid = 4;//syscall(SYS_gettid);
|
||||
#else
|
||||
// Dummy values
|
||||
pid_t pid = 5;
|
||||
pid_t tid = gettid();
|
||||
#endif
|
||||
#endif
|
||||
char timestring[20];
|
||||
time_t timestamp;
|
||||
timestamp = time(NULL);
|
||||
strftime(timestring, sizeof(timestring), "%H:%M:%S", localtime(×tamp));
|
||||
#if defined(__linux__)
|
||||
memcpy(&metabuf[IDX_PID], &pid, sizeof(pid_t) ); /* pid */
|
||||
memcpy(&metabuf[IDX_TID], &tid, sizeof(pid_t) ); /* tid */
|
||||
#endif
|
||||
memcpy(&metabuf[IDX_COUNT], &rpc_count, sizeof(rpc_count) ); /* rpc_count */
|
||||
memcpy(&metabuf[IDX_TIME], ×tring, 20 ); /* timestamp */
|
||||
|
||||
/* Combine command flag+payload with RPC metadata */
|
||||
memcpy(metabuf, RPC_PHRASE, RPC_PHRASE_SZ); // Write signal phrase
|
||||
memcpy(&metabuf[IDX_PAYLOAD], cmdbuf, len + 1 + CANARY_SZ);
|
||||
// Write RPC
|
||||
long n_write = write(rpc_sock, &metabuf, BUF_SZ);
|
||||
if(n_write < 0) {
|
||||
fprintf(stderr, "Error writing command to service (CMD = %d)\n", cmdbuf[CMD_ID_IDX]);
|
||||
errno = 0;
|
||||
}
|
||||
// Write token to corresponding data stream
|
||||
if(read(rpc_sock, &c, 1) < 0) {
|
||||
fprintf(stderr, "unable to read RPC ACK byte from service.\n");
|
||||
return -1;
|
||||
}
|
||||
if(c == 'z' && n_write > 0 && forfd > -1){
|
||||
if(send(forfd, &CANARY, CANARY_SZ+PADDING_SZ, 0) < 0) {
|
||||
perror("send: \n");
|
||||
fprintf(stderr,"unable to write canary to stream (fd=%d)\n", forfd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
// Process response from service
|
||||
int ret = ERR_OK;
|
||||
if(n_write > 0) {
|
||||
if(cmdbuf[CMD_ID_IDX]==RPC_SOCKET) {
|
||||
pthread_mutex_unlock(&lock);
|
||||
return rpc_sock; // Used as new socket
|
||||
}
|
||||
if(cmdbuf[CMD_ID_IDX]==RPC_CONNECT
|
||||
|| cmdbuf[CMD_ID_IDX]==RPC_BIND
|
||||
|| cmdbuf[CMD_ID_IDX]==RPC_LISTEN) {
|
||||
ret = get_retval(rpc_sock);
|
||||
}
|
||||
if(cmdbuf[CMD_ID_IDX]==RPC_GETSOCKNAME) {
|
||||
pthread_mutex_unlock(&lock);
|
||||
return rpc_sock; // Don't close rpc here, we'll use it to read getsockopt_st
|
||||
}
|
||||
}
|
||||
else
|
||||
ret = -1;
|
||||
close(rpc_sock); // We're done with this RPC socket, close it (if type-R)
|
||||
pthread_mutex_unlock(&lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send file descriptor
|
||||
*/
|
||||
ssize_t sock_fd_write(int sock, int fd)
|
||||
{
|
||||
ssize_t size;
|
||||
struct msghdr msg;
|
||||
struct iovec iov;
|
||||
char buf = '\0';
|
||||
int buflen = 1;
|
||||
union {
|
||||
struct cmsghdr cmsghdr;
|
||||
char control[CMSG_SPACE(sizeof (int))];
|
||||
} cmsgu;
|
||||
struct cmsghdr *cmsg;
|
||||
iov.iov_base = &buf;
|
||||
iov.iov_len = buflen;
|
||||
msg.msg_name = NULL;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
if (fd != -1) {
|
||||
msg.msg_control = cmsgu.control;
|
||||
msg.msg_controllen = sizeof(cmsgu.control);
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof (int));
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
*((int *) CMSG_DATA(cmsg)) = fd;
|
||||
} else {
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
}
|
||||
size = sendmsg(sock, &msg, 0);
|
||||
if (size < 0)
|
||||
perror ("sendmsg");
|
||||
return size;
|
||||
}
|
||||
/*
|
||||
* Read a file descriptor
|
||||
*/
|
||||
ssize_t sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd)
|
||||
{
|
||||
ssize_t size;
|
||||
if (fd) {
|
||||
struct msghdr msg;
|
||||
struct iovec iov;
|
||||
union {
|
||||
struct cmsghdr cmsghdr;
|
||||
char control[CMSG_SPACE(sizeof (int))];
|
||||
} cmsgu;
|
||||
struct cmsghdr *cmsg;
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = bufsize;
|
||||
msg.msg_name = NULL;
|
||||
msg.msg_namelen = 0;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = cmsgu.control;
|
||||
msg.msg_controllen = sizeof(cmsgu.control);
|
||||
size = recvmsg (sock, &msg, 0);
|
||||
if (size < 0)
|
||||
return -1;
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
|
||||
if (cmsg->cmsg_level != SOL_SOCKET) {
|
||||
fprintf (stderr, "invalid cmsg_level %d\n",cmsg->cmsg_level);
|
||||
return -1;
|
||||
}
|
||||
if (cmsg->cmsg_type != SCM_RIGHTS) {
|
||||
fprintf (stderr, "invalid cmsg_type %d\n",cmsg->cmsg_type);
|
||||
return -1;
|
||||
}
|
||||
*fd = *((int *) CMSG_DATA(cmsg));
|
||||
} else *fd = -1;
|
||||
} else {
|
||||
size = read (sock, buf, bufsize);
|
||||
if (size < 0) {
|
||||
fprintf(stderr, "sock_fd_read(): read: Error\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
152
src/SDK_RPC.h
Normal file
152
src/SDK_RPC.h
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* 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/
|
||||
*/
|
||||
|
||||
#ifndef __RPCLIB_H_
|
||||
#define __RPCLIB_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/un.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <dlfcn.h>
|
||||
#include <sys/socket.h>
|
||||
#include <strings.h>
|
||||
|
||||
#define CANARY_SZ sizeof(uint64_t)
|
||||
#define PADDING_SZ 12
|
||||
#define PADDING 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89
|
||||
|
||||
#define RPC_PHRASE "zerotier\0"
|
||||
#define RPC_PHRASE_SZ 9
|
||||
#define RPC_TIMESTAMP_SZ 20
|
||||
// 1st RPC section (metdata)
|
||||
#define IDX_SIGNAL_PHRASE 0
|
||||
#define IDX_PID IDX_SIGNAL_PHRASE + RPC_PHRASE_SZ
|
||||
#define IDX_TID sizeof(pid_t) + IDX_PID
|
||||
#define IDX_COUNT IDX_TID + sizeof(pid_t)
|
||||
#define IDX_TIME IDX_COUNT + sizeof(int)
|
||||
#define IDX_PAYLOAD IDX_TIME + RPC_TIMESTAMP_SZ
|
||||
// 2nd RPC section (payload and canary)
|
||||
#define CMD_ID_IDX 0
|
||||
#define CANARY_IDX 1
|
||||
#define STRUCT_IDX CANARY_IDX+CANARY_SZ
|
||||
|
||||
#define BUF_SZ 512
|
||||
|
||||
#define ERR_OK 0
|
||||
|
||||
/* RPC codes */
|
||||
#define RPC_UNDEFINED 0
|
||||
#define RPC_CONNECT 1
|
||||
#define RPC_CONNECT_SOCKARG 2
|
||||
#define RPC_CLOSE 3
|
||||
#define RPC_READ 4
|
||||
#define RPC_WRITE 5
|
||||
#define RPC_BIND 6
|
||||
#define RPC_ACCEPT 7
|
||||
#define RPC_LISTEN 8
|
||||
#define RPC_SOCKET 9
|
||||
#define RPC_SHUTDOWN 10
|
||||
#define RPC_GETSOCKNAME 11
|
||||
#define RPC_RETVAL 12
|
||||
#define RPC_IS_CONNECTED 13
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int get_retval(int);
|
||||
int rpc_join( char * sockname);
|
||||
int rpc_send_command(char *path, int cmd, int forfd, void *data, int len);
|
||||
|
||||
int get_new_fd(int sock);
|
||||
ssize_t sock_fd_write(int sock, int fd);
|
||||
ssize_t sock_fd_read(int sock, void *buf, ssize_t bufsize, int *fd);
|
||||
|
||||
void rpc_mutex_destroy();
|
||||
void rpc_mutex_init();
|
||||
|
||||
|
||||
/* Structures used for sending commands via RPC mechanism */
|
||||
|
||||
struct bind_st {
|
||||
int sockfd;
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrlen;
|
||||
int __tid;
|
||||
};
|
||||
|
||||
struct connect_st {
|
||||
int __fd;
|
||||
struct sockaddr_storage __addr;
|
||||
socklen_t __len;
|
||||
int __tid;
|
||||
};
|
||||
|
||||
struct close_st {
|
||||
int fd;
|
||||
};
|
||||
|
||||
struct listen_st {
|
||||
int sockfd;
|
||||
int backlog;
|
||||
int __tid;
|
||||
};
|
||||
|
||||
struct socket_st {
|
||||
int socket_family;
|
||||
int socket_type;
|
||||
int protocol;
|
||||
int __tid;
|
||||
};
|
||||
|
||||
struct accept_st {
|
||||
int sockfd;
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrlen;
|
||||
int __tid;
|
||||
};
|
||||
|
||||
struct shutdown_st {
|
||||
int socket;
|
||||
int how;
|
||||
};
|
||||
|
||||
struct getsockname_st {
|
||||
int sockfd;
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrlen;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
250
src/SDK_ServiceSetup.cpp
Normal file
250
src/SDK_ServiceSetup.cpp
Normal file
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* 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/
|
||||
*/
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <jni.h>
|
||||
#endif
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <sys/socket.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "OneService.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "OSUtils.hpp"
|
||||
|
||||
#include "SDK.h"
|
||||
#include "SDK_Debug.h"
|
||||
#include "SDK_ServiceSetup.hpp"
|
||||
|
||||
std::string service_path;
|
||||
pthread_t intercept_thread;
|
||||
int * intercept_thread_id;
|
||||
pthread_key_t thr_id_key;
|
||||
static ZeroTier::OneService *volatile zt1Service;
|
||||
static std::string homeDir;
|
||||
std::string netDir;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void join_network(const char * nwid)
|
||||
{
|
||||
std::string confFile = netDir + "/" + nwid + ".conf";
|
||||
if(!ZeroTier::OSUtils::mkdir(netDir)) {
|
||||
LOGV("unable to create %s\n", netDir.c_str());
|
||||
}
|
||||
if(!ZeroTier::OSUtils::writeFile(confFile.c_str(), "")) {
|
||||
LOGV("unable to write network conf file: %s\n", nwid);
|
||||
}
|
||||
zt1Service->join(nwid);
|
||||
}
|
||||
|
||||
void leave_network(const char *nwid)
|
||||
{
|
||||
zt1Service->leave(nwid);
|
||||
}
|
||||
|
||||
void zt_join_network(std::string nwid) {
|
||||
join_network(nwid.c_str());
|
||||
}
|
||||
void zt_leave_network(std::string nwid) {
|
||||
leave_network(nwid.c_str());
|
||||
}
|
||||
|
||||
bool zt_is_running() {
|
||||
return zt1Service->isRunning();
|
||||
}
|
||||
void zt_terminate() {
|
||||
zt1Service->terminate();
|
||||
}
|
||||
|
||||
#if defined(__UNITY_3D__)
|
||||
// .NET Interop-friendly debug mechanism
|
||||
typedef void (*FuncPtr)( const char * );
|
||||
FuncPtr Debug;
|
||||
|
||||
void SetDebugFunction( FuncPtr fp ) { Debug = fp; }
|
||||
|
||||
// Starts a service at the specified path
|
||||
void unity_start_service(char * path, int len) {
|
||||
Debug(path);
|
||||
init_service(INTERCEPT_DISABLED, path);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
/*
|
||||
* Starts a service thread and performs basic setup tasks
|
||||
*/
|
||||
void init_service(int key, const char * path)
|
||||
{
|
||||
service_path = path;
|
||||
pthread_key_create(&thr_id_key, NULL);
|
||||
intercept_thread_id = (int*)malloc(sizeof(int));
|
||||
*intercept_thread_id = key;
|
||||
pthread_create(&intercept_thread, NULL, startOneService, (void *)(intercept_thread_id));
|
||||
}
|
||||
|
||||
/*
|
||||
* Enables or disables intercept for current thread using key in thread-local storage
|
||||
*/
|
||||
void set_intercept_status(int mode)
|
||||
{
|
||||
fprintf(stderr, "set_intercept_status(mode=%d): tid = %d\n", mode, pthread_mach_thread_np(pthread_self()));
|
||||
pthread_key_create(&thr_id_key, NULL);
|
||||
intercept_thread_id = (int*)malloc(sizeof(int));
|
||||
*intercept_thread_id = mode;
|
||||
pthread_setspecific(thr_id_key, intercept_thread_id);
|
||||
#if !defined(__UNITY_3D__)
|
||||
check_intercept_enabled_for_thread();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Starts a new service instance
|
||||
*/
|
||||
#if defined(__ANDROID__)
|
||||
JNIEXPORT void JNICALL Java_Netcon_NetconWrapper_startOneService(JNIEnv *env, jobject thisObj)
|
||||
{
|
||||
#else
|
||||
void *startOneService(void *thread_id)
|
||||
{
|
||||
set_intercept_status(INTERCEPT_DISABLED);
|
||||
#endif
|
||||
int MAX_DIR_SZ = 256;
|
||||
char current_dir[MAX_DIR_SZ];
|
||||
getcwd(current_dir, MAX_DIR_SZ);
|
||||
chdir(service_path.c_str());
|
||||
zt1Service = (ZeroTier::OneService *)0;
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
homeDir = "/sdcard/zerotier";
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include "TargetConditionals.h"
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
// homeDir = "dont/run/this/in/the/simulator";
|
||||
#elif TARGET_OS_IPHONE
|
||||
homeDir = "ZeroTier/One";
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__UNITY_3D__)
|
||||
homeDir = "/Users/Joseph/utest2/";
|
||||
#endif
|
||||
|
||||
netDir = homeDir + "/networks.d";
|
||||
LOGV("Starting ZT service...\n");
|
||||
//Debug("Starting ZT service...");
|
||||
|
||||
if (!homeDir.length()) {
|
||||
#if defined(__ANDROID__)
|
||||
return;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
} else {
|
||||
std::vector<std::string> hpsp(ZeroTier::Utils::split(homeDir.c_str(),ZT_PATH_SEPARATOR_S,"",""));
|
||||
std::string ptmp;
|
||||
if (homeDir[0] == ZT_PATH_SEPARATOR)
|
||||
ptmp.push_back(ZT_PATH_SEPARATOR);
|
||||
for(std::vector<std::string>::iterator pi(hpsp.begin());pi!=hpsp.end();++pi) {
|
||||
if (ptmp.length() > 0)
|
||||
ptmp.push_back(ZT_PATH_SEPARATOR);
|
||||
ptmp.append(*pi);
|
||||
if ((*pi != ".")&&(*pi != "..")) {
|
||||
if (!ZeroTier::OSUtils::mkdir(ptmp)) {
|
||||
std::string homePathErrStr = "home path does not exist, and could not create";
|
||||
//Debug(homePathErrStr.c_str());
|
||||
throw std::runtime_error(homePathErrStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chdir(current_dir); // Return to previous current working directory (at the request of Unity3D)
|
||||
|
||||
LOGV("homeDir = %s", homeDir.c_str());
|
||||
//Debug(homeDir.c_str());
|
||||
|
||||
// Generate random port for new service instance
|
||||
unsigned int randp = 0;
|
||||
ZeroTier::Utils::getSecureRandom(&randp,sizeof(randp));
|
||||
int servicePort = 9000 + (randp % 1000);
|
||||
|
||||
for(;;) {
|
||||
zt1Service = ZeroTier::OneService::newInstance(homeDir.c_str(),servicePort);
|
||||
switch(zt1Service->run()) {
|
||||
case ZeroTier::OneService::ONE_STILL_RUNNING: // shouldn't happen, run() won't return until done
|
||||
case ZeroTier::OneService::ONE_NORMAL_TERMINATION:
|
||||
break;
|
||||
case ZeroTier::OneService::ONE_UNRECOVERABLE_ERROR:
|
||||
//fprintf(stderr,"%s: fatal error: %s" ZT_EOL_S,argv[0],zt1Service->fatalErrorMessage().c_str());
|
||||
//returnValue = 1;
|
||||
break;
|
||||
case ZeroTier::OneService::ONE_IDENTITY_COLLISION: {
|
||||
delete zt1Service;
|
||||
zt1Service = (ZeroTier::OneService *)0;
|
||||
std::string oldid;
|
||||
//OSUtils::readFile((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret").c_str(),oldid);
|
||||
if (oldid.length()) {
|
||||
//OSUtils::writeFile((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret.saved_after_collision").c_str(),oldid);
|
||||
//OSUtils::rm((homeDir + ZT_PATH_SEPARATOR_S + "identity.secret").c_str());
|
||||
//OSUtils::rm((homeDir + ZT_PATH_SEPARATOR_S + "identity.public").c_str());
|
||||
}
|
||||
} continue; // restart!
|
||||
}
|
||||
break; // terminate loop -- normally we don't keep restarting
|
||||
}
|
||||
#if defined(__ANDROID__)
|
||||
return;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
59
src/SDK_ServiceSetup.hpp
Normal file
59
src/SDK_ServiceSetup.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* 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/
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <jni.h>
|
||||
#endif
|
||||
|
||||
#ifndef ONE_SERVICE_SETUP_HPP
|
||||
#define ONE_SERVICE_SETUP_HPP
|
||||
|
||||
#define INTERCEPT_ENABLED 111
|
||||
#define INTERCEPT_DISABLED 222
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
JNIEXPORT void JNICALL Java_SDK_SDKWrapper_startOneService(JNIEnv *env, jobject thisObj);
|
||||
#else
|
||||
void *startOneService(void *thread_id);
|
||||
void init_service(int key, const char * path);
|
||||
void init_intercept(int key);
|
||||
#endif
|
||||
void set_intercept_status(int mode);
|
||||
void join_network(const char * nwid);
|
||||
void leave_network(const char * nwid);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
57
src/SDK_Signatures.h
Normal file
57
src/SDK_Signatures.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* 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/
|
||||
*/
|
||||
|
||||
#ifndef _SIGNATURES_H
|
||||
#define _SIGNATURES_H 1
|
||||
|
||||
#include <sys/socket.h>
|
||||
|
||||
#define SETSOCKOPT_SIG int socket, int level, int option_name, const void *option_value, socklen_t option_len
|
||||
#define GETSOCKOPT_SIG int sockfd, int level, int optname, void *optval, socklen_t *optlen
|
||||
|
||||
#define SENDMSG_SIG int socket, const struct msghdr *message, int flags
|
||||
#define SENDTO_SIG int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addr_len
|
||||
#define RECV_SIG int socket, void *buffer, size_t length, int flags
|
||||
#define RECVFROM_SIG int socket, void * buffer, size_t length, int flags, struct sockaddr * __restrict address, socklen_t * __restrict address_len
|
||||
#define RECVMSG_SIG int socket, struct msghdr *message,int flags
|
||||
|
||||
#define SEND_SIG int socket, const void *buffer, size_t length, int flags
|
||||
#define WRITE_SIG int __fd, const void *__buf, size_t __n
|
||||
#define RECV_SIG int socket, void *buffer, size_t length, int flags
|
||||
#define READ_SIG int __fd, void *__buf, size_t __nbytes
|
||||
|
||||
#define SOCKET_SIG int socket_family, int socket_type, int protocol
|
||||
#define CONNECT_SIG int __fd, const struct sockaddr * __addr, socklen_t __len
|
||||
#define BIND_SIG int sockfd, const struct sockaddr *addr, socklen_t addrlen
|
||||
#define LISTEN_SIG int sockfd, int backlog
|
||||
#define ACCEPT4_SIG int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags
|
||||
#define ACCEPT_SIG int sockfd, struct sockaddr *addr, socklen_t *addrlen
|
||||
#define CLOSE_SIG int fd
|
||||
#define GETSOCKNAME_SIG int sockfd, struct sockaddr *addr, socklen_t *addrlen
|
||||
#define SYSCALL_SIG long number, ...
|
||||
|
||||
#endif // _SIGNATURES_H
|
||||
540
src/SDK_Sockets.c
Normal file
540
src/SDK_Sockets.c
Normal file
@@ -0,0 +1,540 @@
|
||||
/*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* --
|
||||
*
|
||||
* 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/
|
||||
*/
|
||||
|
||||
#ifdef USE_GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <dlfcn.h>
|
||||
#include <strings.h>
|
||||
#include <pwd.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <netdb.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/resource.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <linux/errno.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <linux/net.h>
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
#define SOCK_MAX (SOCK_PACKET + 1)
|
||||
#endif
|
||||
#define SOCK_TYPE_MASK 0xf
|
||||
|
||||
#include "SDK.h"
|
||||
#include "SDK_Signatures.h"
|
||||
#include "SDK_Debug.h"
|
||||
#include "SDK_RPC.h"
|
||||
#include "node/Constants.hpp" // For Tap's MTU
|
||||
|
||||
void print_addr(struct sockaddr *addr);
|
||||
void dwr(int level, const char *fmt, ... );
|
||||
|
||||
static char *api_netpath = (char *)0;
|
||||
|
||||
|
||||
// TODO: Remove before production
|
||||
void set_netpath(char * path)
|
||||
{
|
||||
dwr(MSG_DEBUG,"set_netpath(%s)", path);
|
||||
api_netpath = path;
|
||||
rpc_mutex_init(); // TODO: double-check this
|
||||
}
|
||||
|
||||
const char *get_netpath()
|
||||
{
|
||||
return api_netpath;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ---------------------------------- zt_init_rpc -------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
|
||||
void zt_init_rpc(const char *nwid)
|
||||
{
|
||||
|
||||
// TODO: Remove before production
|
||||
//#if defined(__UNITY_3D__)
|
||||
api_netpath = "/Users/Joseph/utest2/nc_565799d8f6e1c11a";
|
||||
//Debug("Set unix RPC path");
|
||||
//#endif
|
||||
|
||||
//#if defined(__IOS__)
|
||||
//api_netpath = "ZeroTier/One/nc_" + nwid;
|
||||
/*
|
||||
void *spec = pthread_getspecific(thr_id_key);
|
||||
int thr_id = spec != NULL ? *((int*)spec) : -1;
|
||||
// dwr(MSG_DEBUG_EXTRA, "set_up_intercept(thr_id=%d)\n", thr_id);
|
||||
if(thr_id == INTERCEPT_ENABLED) {
|
||||
if (!api_netpath) {
|
||||
api_netpath = "ZeroTier/One/nc_e5cd7a9e1c3511dd"; // Path allowed on iOS devices
|
||||
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
*/
|
||||
|
||||
/*
|
||||
#elif defined(__ANDROID__)
|
||||
netpath = "ZeroTier/One/nc_" + nwid;
|
||||
return 1;
|
||||
#else
|
||||
if (!netpath) {
|
||||
netpath = getenv("ZT_NC_NETWORK");
|
||||
set_netpath(netpath);
|
||||
if(!netpath) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
#endif
|
||||
*/
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ------------------------------------ sendto() --------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int sockfd, const void *buf, size_t len, int flags,
|
||||
// const struct sockaddr *addr, socklen_t addr_len
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
ssize_t zt_sendto(SENDTO_SIG)
|
||||
{
|
||||
dwr(MSG_DEBUG, "zt_sendto()\n");
|
||||
if(len > ZT_UDP_DEFAULT_PAYLOAD_MTU) {
|
||||
errno = EMSGSIZE; // Msg is too large
|
||||
return -1;
|
||||
}
|
||||
int socktype = 0;
|
||||
socklen_t socktype_len;
|
||||
getsockopt(sockfd,SOL_SOCKET, SO_TYPE, (void*)&socktype, &socktype_len);
|
||||
|
||||
if((socktype & SOCK_STREAM) || (socktype & SOCK_SEQPACKET)) {
|
||||
if(addr == NULL || flags != 0) {
|
||||
errno = EISCONN;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// the socket isn't connected
|
||||
//int err = rpc_send_command(api_netpath, RPC_IS_CONNECTED, -1, &fd, sizeof(struct fd));
|
||||
//if(err == -1) {
|
||||
// errno = ENOTCONN;
|
||||
// return -1;
|
||||
//}
|
||||
|
||||
// EMSGSIZE should be returned if the message is too long to be passed atomically through
|
||||
// the underlying protocol, in our case MTU?
|
||||
// TODO: More efficient solution
|
||||
// This connect call is used to get the address info to the stack for sending the packet
|
||||
int err;
|
||||
if((err = connect(sockfd, addr, addr_len)) < 0) {
|
||||
dwr(MSG_DEBUG, "sendto(): unknown problem passing address info to stack\n");
|
||||
errno = EISCONN; // double-check this is correct
|
||||
return -1;
|
||||
}
|
||||
return send(sockfd, buf, len, flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ----------------------------------- sendmsg() --------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int socket, const struct msghdr *message, int flags
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
ssize_t zt_sendmsg(SENDMSG_SIG)
|
||||
{
|
||||
dwr(MSG_DEBUG, "zt_sendmsg()\n");
|
||||
char * p, * buf;
|
||||
size_t tot_len = 0;
|
||||
size_t err;
|
||||
struct iovec * iov = message->msg_iov;
|
||||
for(int i=0; i<message->msg_iovlen; ++i)
|
||||
tot_len += iov[i].iov_len;
|
||||
if(tot_len > ZT_UDP_DEFAULT_PAYLOAD_MTU) {
|
||||
errno = EMSGSIZE; // Message too large to send atomically via underlying protocol, don't send
|
||||
return -1;
|
||||
}
|
||||
buf = malloc(tot_len);
|
||||
if(tot_len != 0 && buf == NULL) {
|
||||
errno = ENOMEM; // Unable to allocate space for message
|
||||
return -1;
|
||||
}
|
||||
p = buf;
|
||||
for(int i=0; i < message->msg_iovlen; ++i) {
|
||||
memcpy(p, iov[i].iov_base, iov[i].iov_len);
|
||||
p += iov[i].iov_len;
|
||||
}
|
||||
err = sendto(socket, buf, tot_len, flags, message->msg_name, message->msg_namelen);
|
||||
free(buf);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ---------------------------------- recvfrom() --------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int socket, void *restrict buffer, size_t length, int flags, struct sockaddr
|
||||
// *restrict address, socklen_t *restrict address_len
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
ssize_t zt_recvfrom(RECVFROM_SIG)
|
||||
{
|
||||
dwr(MSG_DEBUG,"zt_recvfrom(%d)\n", socket);
|
||||
// TODO: Remove for production
|
||||
|
||||
ssize_t err;
|
||||
//int sock_type;
|
||||
//socklen_t type_len;
|
||||
//realgetsockopt(socket, SOL_SOCKET, SO_TYPE, (void *) &sock_type, &type_len);
|
||||
unsigned int addr;
|
||||
unsigned short port;
|
||||
char addr_buf[sizeof(addr) + sizeof(port)];
|
||||
// Since this can be called for connection-oriented sockets,
|
||||
// we need to check the type before we try to read the address info
|
||||
//if(sock_type == SOCK_DGRAM && address != NULL && address_len != NULL) {
|
||||
err = read(socket, &addr_buf, sizeof(addr_buf)); // Read prepended address info
|
||||
memcpy(&addr, addr_buf, sizeof(addr));
|
||||
memcpy(&port, addr_buf+sizeof(addr), sizeof(port));
|
||||
*address_len=sizeof(addr_buf);
|
||||
//}
|
||||
err = read(socket, buffer, length); // Read what was placed on buffer from service
|
||||
if(err < 0)
|
||||
perror("read:\n");
|
||||
|
||||
port = htons(port);
|
||||
memcpy(address->sa_data, &port, sizeof(port));
|
||||
memcpy(address->sa_data+2, &addr, sizeof(addr));
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ----------------------------------- recvmsg() --------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int socket, struct msghdr *message, int flags
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
ssize_t zt_recvmsg(RECVMSG_SIG)
|
||||
{
|
||||
dwr(MSG_DEBUG, "zt_recvmsg(%d)\n", socket);
|
||||
ssize_t err, n, tot_len = 0;
|
||||
char *buf, *p;
|
||||
struct iovec *iov = message->msg_iov;
|
||||
|
||||
for(int i = 0; i < message->msg_iovlen; ++i)
|
||||
tot_len += iov[i].iov_len;
|
||||
buf = malloc(tot_len);
|
||||
if(tot_len != 0 && buf == NULL) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
n = err = recvfrom(socket, buf, tot_len, flags, message->msg_name, &message->msg_namelen);
|
||||
p = buf;
|
||||
|
||||
// According to: http://pubs.opengroup.org/onlinepubs/009695399/functions/recvmsg.html
|
||||
if(err > message->msg_controllen && !( message->msg_flags & MSG_PEEK)) {
|
||||
// excess data should be disgarded
|
||||
message->msg_flags |= MSG_TRUNC; // Indicate that the buffer has been truncated
|
||||
}
|
||||
|
||||
while (n > 0) {
|
||||
ssize_t count = n < iov->iov_len ? n : iov->iov_len;
|
||||
memcpy (iov->iov_base, p, count);
|
||||
p += count;
|
||||
n -= count;
|
||||
++iov;
|
||||
}
|
||||
free(buf);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(__UNITY_3D__)
|
||||
|
||||
ssize_t zt_send(int fd, struct UnityArrayInput *buf, int len)
|
||||
{
|
||||
return write(fd, buf->array, len);
|
||||
}
|
||||
|
||||
ssize_t zt_recv(int fd, struct UnityArrayInput *buf, int len)
|
||||
{
|
||||
return read(fd, buf->array, len);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// --------------------------------- setsockopt() -------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int socket, int level, int option_name, const void *option_value,
|
||||
// socklen_t option_len
|
||||
|
||||
int zt_setsockopt(SETSOCKOPT_SIG)
|
||||
{
|
||||
dwr(MSG_DEBUG, "zt_setsockopt()\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// --------------------------------- getsockopt() -------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int sockfd, int level, int optname, void *optval,
|
||||
// socklen_t *optlen
|
||||
|
||||
int zt_getsockopt(GETSOCKOPT_SIG)
|
||||
{
|
||||
dwr(MSG_DEBUG,"zt_getsockopt(%d)\n", sockfd);
|
||||
if(optname == SO_TYPE) {
|
||||
int* val = (int*)optval;
|
||||
*val = 2;
|
||||
optval = (void*)val;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ----------------------------------- socket() ---------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int socket_family, int socket_type, int protocol
|
||||
|
||||
int zt_socket(SOCKET_SIG) {
|
||||
zt_init_rpc("");
|
||||
dwr(MSG_DEBUG, "zt_socket()\n");
|
||||
/* Check that type makes sense */
|
||||
#if defined(__linux__)
|
||||
int flags = socket_type & ~SOCK_TYPE_MASK;
|
||||
#if !defined(__ANDROID__)
|
||||
if (flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
socket_type &= SOCK_TYPE_MASK;
|
||||
/* Check protocol is in range */
|
||||
#if defined(__linux__)
|
||||
if (socket_family < 0 || socket_family >= NPROTO){
|
||||
errno = EAFNOSUPPORT;
|
||||
return -1;
|
||||
}
|
||||
if (socket_type < 0 || socket_type >= SOCK_MAX) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
/* Assemble and send RPC */
|
||||
struct socket_st rpc_st;
|
||||
rpc_st.socket_family = socket_family;
|
||||
rpc_st.socket_type = socket_type;
|
||||
rpc_st.protocol = protocol;
|
||||
#if defined(__linux__)
|
||||
#if !defined(__ANDROID__)
|
||||
rpc_st.__tid = 5; //syscall(SYS_gettid);
|
||||
#else
|
||||
rpc_st.__tid = gettid(); // dummy value
|
||||
#endif
|
||||
#endif
|
||||
/* -1 is passed since we we're generating the new socket in this call */
|
||||
int err = rpc_send_command(api_netpath, RPC_SOCKET, -1, &rpc_st, sizeof(struct socket_st));
|
||||
dwr(MSG_DEBUG," socket() = %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ---------------------------------- connect() ---------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int __fd, const struct sockaddr * __addr, socklen_t __len
|
||||
|
||||
int zt_connect(CONNECT_SIG)
|
||||
{
|
||||
dwr(MSG_DEBUG,"zt_connect(%d)\n", __fd);
|
||||
struct connect_st rpc_st;
|
||||
#if defined(__linux__)
|
||||
#if !defined(__ANDROID__)
|
||||
rpc_st.__tid = syscall(SYS_gettid);
|
||||
#else
|
||||
rpc_st.__tid = gettid(); // dummy value
|
||||
#endif
|
||||
#endif
|
||||
rpc_st.__fd = __fd;
|
||||
memcpy(&rpc_st.__addr, __addr, sizeof(struct sockaddr_storage));
|
||||
memcpy(&rpc_st.__len, &__len, sizeof(socklen_t));
|
||||
return rpc_send_command(api_netpath, RPC_CONNECT, __fd, &rpc_st, sizeof(struct connect_st));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ------------------------------------ bind() ----------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int sockfd, const struct sockaddr *addr, socklen_t addrlen
|
||||
|
||||
#if !defined(__ANDROID__)
|
||||
int zt_bind(BIND_SIG)
|
||||
{
|
||||
dwr(MSG_DEBUG,"zt_bind(%d)\n", sockfd);
|
||||
struct bind_st rpc_st;
|
||||
rpc_st.sockfd = sockfd;
|
||||
#if defined(__linux__)
|
||||
#if !defined(__ANDROID__)
|
||||
rpc_st.__tid = 5;//syscall(SYS_gettid);
|
||||
#else
|
||||
rpc_st.__tid = gettid(); // dummy value
|
||||
#endif
|
||||
#endif
|
||||
memcpy(&rpc_st.addr, addr, sizeof(struct sockaddr_storage));
|
||||
memcpy(&rpc_st.addrlen, &addrlen, sizeof(socklen_t));
|
||||
return rpc_send_command(api_netpath, RPC_BIND, sockfd, &rpc_st, sizeof(struct bind_st));
|
||||
}
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ----------------------------------- accept4() --------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags
|
||||
|
||||
#if defined(__linux)
|
||||
int zt_accept4(ACCEPT4_SIG)
|
||||
{
|
||||
dwr(MSG_DEBUG,"zt_accept4(%d):\n", sockfd);
|
||||
#if !defined(__ANDROID__)
|
||||
if ((flags & SOCK_CLOEXEC))
|
||||
fcntl(sockfd, F_SETFL, FD_CLOEXEC);
|
||||
if ((flags & SOCK_NONBLOCK))
|
||||
fcntl(sockfd, F_SETFL, O_NONBLOCK);
|
||||
#endif
|
||||
return accept(sockfd, addr, addrlen);
|
||||
}
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ----------------------------------- accept() ---------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int sockfd struct sockaddr *addr, socklen_t *addrlen
|
||||
|
||||
int zt_accept(ACCEPT_SIG)
|
||||
{
|
||||
dwr(MSG_DEBUG,"zt_accept(%d):\n", sockfd);
|
||||
if(addr)
|
||||
addr->sa_family = AF_INET;
|
||||
|
||||
int new_fd = get_new_fd(sockfd);
|
||||
if(new_fd > 0) {
|
||||
errno = ERR_OK;
|
||||
return new_fd;
|
||||
}
|
||||
errno = EAGAIN;
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ------------------------------------- listen()--------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int sockfd, int backlog
|
||||
|
||||
int zt_listen(LISTEN_SIG)
|
||||
{
|
||||
dwr(MSG_DEBUG,"zt_listen(%d):\n", sockfd);
|
||||
struct listen_st rpc_st;
|
||||
rpc_st.sockfd = sockfd;
|
||||
rpc_st.backlog = backlog;
|
||||
#if defined(__linux__)
|
||||
#if !defined(__ANDROID__)
|
||||
rpc_st.__tid = syscall(SYS_gettid);
|
||||
#else
|
||||
rpc_st.__tid = gettid(); // dummy value
|
||||
#endif
|
||||
#endif
|
||||
return rpc_send_command(api_netpath, RPC_LISTEN, sockfd, &rpc_st, sizeof(struct listen_st));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// ------------------------------------- close() --------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int fd
|
||||
|
||||
int zt_close(CLOSE_SIG) {
|
||||
dwr(MSG_DEBUG, "zt_close(%d)", fd);
|
||||
return close(fd);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// -------------------------------- getsockname() -------------------------------
|
||||
// ------------------------------------------------------------------------------
|
||||
// int sockfd, struct sockaddr *addr, socklen_t *addrlen
|
||||
|
||||
int zt_getsockname(GETSOCKNAME_SIG)
|
||||
{
|
||||
dwr(MSG_DEBUG,"zt_getsockname(%d):\n", sockfd);
|
||||
/* TODO: This is kind of a hack as it stands -- assumes sockaddr is sockaddr_in
|
||||
* and is an IPv4 address. */
|
||||
struct getsockname_st rpc_st;
|
||||
rpc_st.sockfd = sockfd;
|
||||
memcpy(&rpc_st.addrlen, &addrlen, sizeof(socklen_t));
|
||||
int rpcfd = rpc_send_command(api_netpath, RPC_GETSOCKNAME, sockfd, &rpc_st, sizeof(struct getsockname_st));
|
||||
/* read address info from service */
|
||||
char addrbuf[sizeof(struct sockaddr_storage)];
|
||||
memset(&addrbuf, 0, sizeof(struct sockaddr_storage));
|
||||
|
||||
if(rpcfd > -1)
|
||||
if(read(rpcfd, &addrbuf, sizeof(struct sockaddr_storage)) > 0)
|
||||
close(rpcfd);
|
||||
|
||||
struct sockaddr_storage sock_storage;
|
||||
memcpy(&sock_storage, addrbuf, sizeof(struct sockaddr_storage));
|
||||
*addrlen = sizeof(struct sockaddr_in);
|
||||
memcpy(addr, &sock_storage, (*addrlen > sizeof(sock_storage)) ? sizeof(sock_storage) : *addrlen);
|
||||
addr->sa_family = AF_INET;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user