/*
* ZeroTier One - Network Virtualization Everywhere
* Copyright (C) 2011-2015 ZeroTier, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
* --
*
* ZeroTier may be used and distributed under the terms of the GPLv3, which
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
*
* If you would like to embed ZeroTier into a commercial application or
* redistribute it in a modified binary form, please contact ZeroTier Networks
* LLC. Start here: http://www.zerotier.com/
*/
#include "SDK_Debug.h"
#include "SDK_EthernetTap.hpp"
#include "Phy.hpp"
#include "Utils.hpp"
#include "OSUtils.hpp"
#include
#include
#include
#include
#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
void dwr(int level, const char *fmt, ... );
namespace ZeroTier
{
int NetconEthernetTap::getProxyServerAddress(struct sockaddr_storage *addr) {
if(sockstate >= 0) {
addr = &proxyServerAddress;
return 0;
}
return -1;
}
int NetconEthernetTap::getProxyServerPort() {
struct sockaddr_in *in4;
in4 = (struct sockaddr_in *)&proxyServerAddress;
return in4->sin_port;
}
int NetconEthernetTap::stopProxyServer()
{
DEBUG_INFO("stopProxyServer()");
if(proxyListenPhySocket) {
_phy.close(proxyListenPhySocket);
return 0;
}
DEBUG_ERROR("stopProxyServer(): Invalid proxyListenPhySocket");
return -1;
}
int NetconEthernetTap::startProxyServer(const char *homepath, uint64_t nwid, struct sockaddr_storage *addr)
{
// Address of proxy server is determined in the following order:
// - Provided address in param: addr
// - If no address, assume 127.0.0.1:
// - If no port assignment file, 127.0.0.1:RANDOM_PORT
DEBUG_INFO("startProxyServer()\n");
int portno = -1;
if(addr) {
DEBUG_INFO("startProxyServer(): Using provided address");
// This address pointer may come from a different memory space and might be de-allocated, so we keep a copy
memcpy(&proxyServerAddress, addr, sizeof(struct sockaddr_storage));
struct sockaddr_in *in4 = (struct sockaddr_in *)&addr;
proxyListenPhySocket = _phy.tcpListen((const struct sockaddr*)&in4,(void *)this);
sockstate = SOCKS_OPEN;
DEBUG_INFO("SOCKS5 proxy server address for <%.16lx> is: <%s> (sock=%p)\n", nwid, inet_ntoa(in4->sin_addr), /*ntohs(in4->sin_port), */(void*)&proxyListenPhySocket);
return 0;
}
else {
DEBUG_INFO("startProxyServer(): No address provided. Checking port file.");
// Look for a port file for this network's proxy server instance
char portFile[4096];
Utils::snprintf(portFile,sizeof(portFile),"%s/networks.d/%.16llx.port",homepath,nwid);
std::string portStr;
DEBUG_INFO("Proxy(): Reading port from: %s\n", portFile);
if(ZeroTier::OSUtils::fileExists(portFile,true))
{
if(ZeroTier::OSUtils::readFile(portFile, portStr)) {
portno = atoi(portStr.c_str());
}
}
else {
unsigned int randp = 0;
Utils::getSecureRandom(&randp,sizeof(randp));
portno = 1000 + (randp % 1000);
DEBUG_INFO("Proxy(): No port specified in networks.d/%.16lx.port, randomly picking port\n", nwid);
std::stringstream ss;
ss << portno;
portStr = ss.str();
if(!ZeroTier::OSUtils::writeFile(portFile, portStr)) {
DEBUG_ERROR("unable to write proxy port file: %s\n", portFile);
}
}
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)portno);
proxyListenPhySocket = _phy.tcpListen((const struct sockaddr*)&in4,(void *)this);
sockstate = SOCKS_OPEN;
//DEBUG_INFO("SOCKS5 proxy server address for <%.16llx> is: <%s:%d> (sock=%p)\n", nwid, , portno, (void*)&proxyListenPhySocket);
}
return 0;
}
void ExtractAddress(int addr_type, unsigned char *buf, struct sockaddr_in * addr)
{
// TODO: Generalize extraction logic
if(addr_type == 144)
{
// 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)
{
DEBUG_INFO("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) {
DEBUG_INFO("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) {
DEBUG_INFO("len=%lu\n", len);
memcpy((&conn->txbuf)+(conn->txsz), buf, len);
conn->txsz += len;
handleWrite(conn);
}
}
if(conn->proxy_conn_state==SOCKS_UDP)
{
DEBUG_INFO("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;
}
DEBUG_INFO(" INFO \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];
DEBUG_INFO("SOCKS REQUEST = \n", version, cmd, addr_type);
// CONNECT request
if(cmd == 1) {
DEBUG_INFO("CONNECT request\n");
// Ipv4
/*
if(addr_type == 144)
{
//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);
DEBUG_INFO("new addr = %s\n", newaddr);
int rawport, port;
memcpy(&rawport, &buf[5], 2);
port = Utils::ntoh(rawport);
DEBUG_INFO("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(8080);
int fd = socket(AF_INET, SOCK_STREAM, 0);
DEBUG_INFO("fd = %d\n", fd);
if(fd < 0)
perror("socket");
int err = connect(fd, (struct sockaddr*)&addr, sizeof(addr));
DEBUG_INFO("connect_err = %d\n", err);
if(err < 0)
perror("connect");
}
*/
// Fully-qualified domain name
if(addr_type == 144)
{
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)
DEBUG_ERROR("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 |
// +----+-----+-------+------+----------+----------+
DEBUG_INFO("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)
{
DEBUG_INFO("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
DEBUG_INFO("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);
ssize_t 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);
DEBUG_INFO("new_sock = %p\n", (void*)&sock);
DEBUG_INFO("new_sock = %p\n", (void*)&new_sock);
if(!new_sock)
DEBUG_ERROR("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)
{
DEBUG_INFO("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)
{
DEBUG_INFO("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)
{
DEBUG_INFO("phyOnDatagram(): len = %lu\n", len);
if(len) {
Connection *conn = getConnection(sock);
if(!conn){
DEBUG_ERROR("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)
{
DEBUG_INFO("phyOnTcpClose(): sock=%p\n", (void*)&sock);
Mutex::Lock _l(_tcpconns_m);
closeConnection(sock);
}
void NetconEthernetTap::phyOnTcpWritable(PhySocket *sock,void **uptr, bool lwip_invoked)
{
DEBUG_INFO(" 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)
{
DEBUG_INFO("phyOnFileDescriptorActivity(): sock=%p\n", (void*&)sock);
}
}