mirror of
https://github.com/norohind/hans.git
synced 2025-04-23 17:30:27 +03:00
Compare commits
74 Commits
Author | SHA1 | Date | |
---|---|---|---|
266a5f7d7a | |||
|
7659a5e173 | ||
|
41f9ed5ef4 | ||
|
c9b07d96bc | ||
|
295f6b4051 | ||
|
7991b11a15 | ||
|
6ae989fdee | ||
|
4c10ac711a | ||
|
0801a67257 | ||
|
f0eb5a46ac | ||
|
3cd2bb95dd | ||
|
535ddc75c7 | ||
|
ed3d7de6cc | ||
|
5e1004038f | ||
|
032089ce7a | ||
|
74bad3b702 | ||
|
da404dac65 | ||
|
e6d7f4c043 | ||
|
a4b81ef64d | ||
|
3a39facc56 | ||
|
1be922591e | ||
|
b805963b3a | ||
|
ca42f469b4 | ||
|
308e87270a | ||
|
893ac91eaf | ||
|
aee7af9cc1 | ||
|
c70575ee96 | ||
|
7d6c1dc290 | ||
|
7fa3579c7e | ||
|
da01402651 | ||
|
01a65b13e3 | ||
|
1b2370143d | ||
|
d79187028a | ||
|
0d03ebbefe | ||
|
6f534b31e9 | ||
|
b58ad1bee8 | ||
|
3459887cf7 | ||
|
736099944d | ||
|
363a9d765e | ||
|
31dba9e57b | ||
|
65429e8a49 | ||
|
02b6626b3c | ||
|
7261752c5c | ||
|
455da8b0da | ||
|
34acb3f6d5 | ||
|
8014a9641f | ||
|
832e2ee3ae | ||
|
d2679783af | ||
|
5b5a2f8198 | ||
|
cfdbe646af | ||
|
821e275d88 | ||
|
2df93b82a6 | ||
|
7e5bb37d1a | ||
|
e4e08ca7e5 | ||
|
542e510520 | ||
|
98327991f1 | ||
|
6f211f8ee9 | ||
|
05064b327e | ||
|
085e76e34b | ||
|
de03a9b272 | ||
|
207f93a795 | ||
|
9147b7f015 | ||
|
55cc53c858 | ||
|
3f13e25b93 | ||
|
fd8ef174db | ||
|
96eb6f501a | ||
|
c760898ec6 | ||
|
6ac7f04876 | ||
|
8041ab2a39 | ||
|
40b291d735 | ||
|
25d3e03e49 | ||
|
3d06d62adc | ||
|
fcde2ad1b1 | ||
|
dadd460972 |
53
CHANGES
Normal file
53
CHANGES
Normal file
@ -0,0 +1,53 @@
|
||||
Release 1.0 (November 2016)
|
||||
---------------------------
|
||||
* Added cygwin support
|
||||
* Remove supplementary groups when dropping privileges
|
||||
* Fixed compile error on FreeBSD
|
||||
|
||||
Release 0.4.4 (February 2014)
|
||||
-----------------------------
|
||||
* Fixed writing beyond array bounds
|
||||
* Fixed some smaller bugs
|
||||
* Clean exit on signal
|
||||
* Improved error reporting
|
||||
|
||||
Release 0.4.3 (Mai 2013)
|
||||
------------------------
|
||||
* Fixed tunemu compiling issue
|
||||
|
||||
Release 0.4.2 (Mai 2013)
|
||||
------------------------
|
||||
* Fixed compilation issue on Linux
|
||||
* Changed the license of tunemu from GPL to BSD
|
||||
* Minor changes
|
||||
|
||||
Release 0.4.1 (February 2010)
|
||||
-----------------------------
|
||||
* Complies on GCC 4.4.1
|
||||
|
||||
Release 0.4 (December 2009)
|
||||
---------------------------
|
||||
* Improved IP assignment
|
||||
|
||||
Release 0.3.1 (August 2009)
|
||||
---------------------------
|
||||
* Fixed IP assignment on Linux
|
||||
* Corrected usage text
|
||||
|
||||
Release 0.3 (August 2009)
|
||||
-------------------------
|
||||
* Fixed specifying the tun device with the "-d" option
|
||||
* Added the debug output option "-v"
|
||||
* Ignore broadcasts
|
||||
* Added tun device emulation for Darwin (tunemu)
|
||||
* Improved IP and route assignment
|
||||
* Improved error handling
|
||||
|
||||
Release 0.2 (July 2009)
|
||||
-----------------------
|
||||
* Compiles on FreeBSD and OpenBSD
|
||||
* Minor enhancements
|
||||
|
||||
Release 0.1 (July 2009)
|
||||
-----------------------
|
||||
* First release
|
90
Makefile
90
Makefile
@ -1,68 +1,62 @@
|
||||
OS = $(shell uname | tr "a-z" "A-Z")
|
||||
LDFLAGS = `sh osflags ld $(MODE)`
|
||||
CFLAGS = -c -g `sh osflags c $(MODE)`
|
||||
CPPFLAGS = -c -g -std=c++98 -pedantic -Wall -Wextra -Wno-sign-compare -Wno-missing-field-initializers `sh osflags c $(MODE)`
|
||||
TUN_DEV_FILE = `sh osflags dev $(MODE)`
|
||||
GCC = gcc
|
||||
GPP = g++
|
||||
|
||||
LDFLAGS =
|
||||
CFLAGS = -c -g -D$(OS) $(OS_CFLAGS)
|
||||
.PHONY: directories
|
||||
|
||||
ifeq ($(OS), LINUX)
|
||||
TUN_DEV_FILE = tun_dev_linux.c
|
||||
OS_CFLAGS = -DHAVE_LINUX_IF_TUN_H
|
||||
else
|
||||
all: directories hans
|
||||
|
||||
ifeq ($(OS), OPENBSD)
|
||||
TUN_DEV_FILE = tun_dev_openbsd.c
|
||||
else
|
||||
directories: build_dir
|
||||
|
||||
ifeq ($(OS), FREEBSD)
|
||||
TUN_DEV_FILE = tun_dev_freebsd.c
|
||||
else
|
||||
build_dir:
|
||||
mkdir -p build
|
||||
|
||||
TUN_DEV_FILE = tun_dev_generic.c
|
||||
tunemu.o: directories build/tunemu.o
|
||||
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
hans: build/tun.o build/sha1.o build/main.o build/client.o build/server.o build/auth.o build/worker.o build/time.o build/tun_dev.o build/echo.o build/exception.o build/utility.o
|
||||
$(GPP) -o hans build/tun.o build/sha1.o build/main.o build/client.o build/server.o build/auth.o build/worker.o build/time.o build/tun_dev.o build/echo.o build/exception.o build/utility.o $(LDFLAGS)
|
||||
|
||||
build/utility.o: src/utility.cpp src/utility.h
|
||||
$(GPP) -c src/utility.cpp -o $@ -o $@ $(CPPFLAGS)
|
||||
|
||||
all: hans
|
||||
build/exception.o: src/exception.cpp src/exception.h
|
||||
$(GPP) -c src/exception.cpp -o $@ $(CPPFLAGS)
|
||||
|
||||
hans: tun.o sha1.o main.o client.o server.o auth.o worker.o time.o tun_dev.o echo.o exception.o utility.o
|
||||
g++ -o hans tun.o sha1.o main.o client.o server.o auth.o worker.o time.o tun_dev.o echo.o exception.o utility.o $(LDFLAGS)
|
||||
build/echo.o: src/echo.cpp src/echo.h src/exception.h
|
||||
$(GPP) -c src/echo.cpp -o $@ $(CPPFLAGS)
|
||||
|
||||
utility.o: utility.cpp utility.h
|
||||
g++ -c utility.cpp $(CFLAGS)
|
||||
build/tun.o: src/tun.cpp src/tun.h src/exception.h src/utility.h src/tun_dev.h
|
||||
$(GPP) -c src/tun.cpp -o $@ $(CPPFLAGS)
|
||||
|
||||
exception.o: exception.cpp exception.h
|
||||
g++ -c exception.cpp $(CFLAGS)
|
||||
build/tun_dev.o:
|
||||
$(GCC) -c $(TUN_DEV_FILE) -o build/tun_dev.o -o $@ $(CFLAGS)
|
||||
|
||||
echo.o: echo.cpp echo.h exception.h
|
||||
g++ -c echo.cpp $(CFLAGS)
|
||||
build/sha1.o: src/sha1.cpp src/sha1.h
|
||||
$(GPP) -c src/sha1.cpp -o $@ $(CPPFLAGS)
|
||||
|
||||
tun.o: tun.cpp tun.h exception.h utility.h tun_dev.h
|
||||
g++ -c tun.cpp $(CFLAGS)
|
||||
build/main.o: src/main.cpp src/client.h src/server.h src/exception.h src/worker.h src/auth.h src/time.h src/echo.h src/tun.h src/tun_dev.h
|
||||
$(GPP) -c src/main.cpp -o $@ $(CPPFLAGS)
|
||||
|
||||
tun_dev.o: $(TUN_DEV_FILE)
|
||||
gcc -c $(TUN_DEV_FILE) -o tun_dev.o $(CFLAGS)
|
||||
build/client.o: src/client.cpp src/client.h src/server.h src/exception.h src/config.h src/worker.h src/auth.h src/time.h src/echo.h src/tun.h src/tun_dev.h
|
||||
$(GPP) -c src/client.cpp -o $@ $(CPPFLAGS)
|
||||
|
||||
sha1.o: sha1.cpp sha1.h
|
||||
g++ -c sha1.cpp $(CFLAGS)
|
||||
build/server.o: src/server.cpp src/server.h src/client.h src/utility.h src/config.h src/worker.h src/auth.h src/time.h src/echo.h src/tun.h src/tun_dev.h
|
||||
$(GPP) -c src/server.cpp -o $@ $(CPPFLAGS)
|
||||
|
||||
main.o: main.cpp client.h server.h exception.h worker.h auth.h time.h echo.h tun.h tun_dev.h
|
||||
g++ -c main.cpp $(CFLAGS)
|
||||
build/auth.o: src/auth.cpp src/auth.h src/sha1.h src/utility.h
|
||||
$(GPP) -c src/auth.cpp -o $@ $(CPPFLAGS)
|
||||
|
||||
client.o: client.cpp client.h server.h exception.h config.h worker.h auth.h time.h echo.h tun.h tun_dev.h
|
||||
g++ -c client.cpp $(CFLAGS)
|
||||
build/worker.o: src/worker.cpp src/worker.h src/tun.h src/exception.h src/time.h src/echo.h src/tun_dev.h src/config.h
|
||||
$(GPP) -c src/worker.cpp -o $@ $(CPPFLAGS)
|
||||
|
||||
server.o: server.cpp server.h client.h utility.h config.h worker.h auth.h time.h echo.h tun.h tun_dev.h
|
||||
g++ -c server.cpp $(CFLAGS)
|
||||
|
||||
auth.o: auth.cpp auth.h sha1.h utility.h
|
||||
g++ -c auth.cpp $(CFLAGS)
|
||||
|
||||
worker.o: worker.cpp worker.h tun.h exception.h time.h echo.h tun_dev.h config.h
|
||||
g++ -c worker.cpp $(CFLAGS)
|
||||
|
||||
time.o: time.cpp time.h
|
||||
g++ -c time.cpp $(CFLAGS)
|
||||
build/time.o: src/time.cpp src/time.h
|
||||
$(GPP) -c src/time.cpp -o $@ $(CPPFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f tun.o sha1.o main.o client.o server.o auth.o worker.o time.o tun_dev.o echo.o exception.o utility.o hans
|
||||
rm -rf build hans
|
||||
|
||||
build/tunemu.o: src/tunemu.h src/tunemu.c
|
||||
$(GCC) -c src/tunemu.c -o build/tunemu.o
|
||||
|
16
README
16
README
@ -1,16 +0,0 @@
|
||||
### Compiling ###
|
||||
|
||||
make
|
||||
|
||||
### Running as a server ###
|
||||
|
||||
./hans -s 10.1.2.0 -r -p password -u nobody
|
||||
echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
|
||||
|
||||
### Running as a client ###
|
||||
|
||||
./hans -c server_address -p password -u nobody
|
||||
|
||||
### Help ###
|
||||
|
||||
./hans
|
6
README.md
Normal file
6
README.md
Normal file
@ -0,0 +1,6 @@
|
||||
Hans - IP over ICMP
|
||||
===================
|
||||
|
||||
Hans makes it possible to tunnel IPv4 through ICMP echo packets, so you could call it a ping tunnel. This can be useful when you find yourself in the situation that your Internet access is firewalled, but pings are allowed.
|
||||
|
||||
http://code.gerade.org/hans/
|
233
client.cpp
233
client.cpp
@ -1,233 +0,0 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "client.h"
|
||||
#include "server.h"
|
||||
#include "exception.h"
|
||||
#include "config.h"
|
||||
#include "utility.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <syslog.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
const Worker::TunnelHeader::Magic Client::magic("hanc");
|
||||
|
||||
Client::Client(int tunnelMtu, const char *deviceName, uint32_t serverIp, int maxPolls,
|
||||
const char *passphrase, uid_t uid, gid_t gid, bool changeEchoId, bool changeEchoSeq)
|
||||
: Worker(tunnelMtu, deviceName, false, uid, gid), auth(passphrase)
|
||||
{
|
||||
this->serverIp = serverIp;
|
||||
this->maxPolls = maxPolls;
|
||||
this->nextEchoId = Utility::rand();
|
||||
this->changeEchoId = changeEchoId;
|
||||
this->changeEchoSeq = changeEchoSeq;
|
||||
this->nextEchoSequence = Utility::rand();
|
||||
|
||||
state = STATE_CLOSED;
|
||||
}
|
||||
|
||||
Client::~Client()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Client::sendConnectionRequest()
|
||||
{
|
||||
Server::ClientConnectData *connectData = (Server::ClientConnectData *)echoSendPayloadBuffer();
|
||||
connectData->maxPolls = maxPolls;
|
||||
|
||||
syslog(LOG_DEBUG, "sending connection request");
|
||||
|
||||
sendEchoToServer(TunnelHeader::TYPE_CONNECTION_REQUEST, sizeof(Server::ClientConnectData));
|
||||
|
||||
state = STATE_CONNECTION_REQUEST_SENT;
|
||||
setTimeout(5000);
|
||||
}
|
||||
|
||||
void Client::sendChallengeResponse(int dataLength)
|
||||
{
|
||||
if (dataLength != CHALLENGE_SIZE)
|
||||
throw Exception("invalid challenge received");
|
||||
|
||||
state = STATE_CHALLENGE_RESPONSE_SENT;
|
||||
|
||||
syslog(LOG_DEBUG, "sending challenge response");
|
||||
|
||||
vector<char> challenge;
|
||||
challenge.resize(dataLength);
|
||||
memcpy(&challenge[0], echoReceivePayloadBuffer(), dataLength);
|
||||
|
||||
Auth::Response response = auth.getResponse(challenge);
|
||||
|
||||
memcpy(echoSendPayloadBuffer(), (char *)&response, sizeof(Auth::Response));
|
||||
sendEchoToServer(TunnelHeader::TYPE_CHALLENGE_RESPONSE, sizeof(Auth::Response));
|
||||
|
||||
setTimeout(5000);
|
||||
}
|
||||
|
||||
bool Client::handleEchoData(const TunnelHeader &header, int dataLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq)
|
||||
{
|
||||
if (realIp != serverIp || !reply)
|
||||
return false;
|
||||
|
||||
if (header.magic != Server::magic)
|
||||
return false;
|
||||
|
||||
switch (header.type)
|
||||
{
|
||||
case TunnelHeader::TYPE_RESET_CONNECTION:
|
||||
syslog(LOG_DEBUG, "reset reveiced");
|
||||
|
||||
if (privilegesDropped)
|
||||
throw Exception("cannot reconnect without root privileges");
|
||||
|
||||
sendConnectionRequest();
|
||||
return true;
|
||||
case TunnelHeader::TYPE_SERVER_FULL:
|
||||
if (state == STATE_CONNECTION_REQUEST_SENT)
|
||||
{
|
||||
throw Exception("server full");
|
||||
}
|
||||
break;
|
||||
case TunnelHeader::TYPE_CHALLENGE:
|
||||
if (state == STATE_CONNECTION_REQUEST_SENT)
|
||||
{
|
||||
syslog(LOG_DEBUG, "challenge received");
|
||||
sendChallengeResponse(dataLength);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case TunnelHeader::TYPE_CONNECTION_ACCEPT:
|
||||
if (state == STATE_CHALLENGE_RESPONSE_SENT)
|
||||
{
|
||||
if (dataLength != sizeof(uint32_t))
|
||||
{
|
||||
throw Exception("invalid ip received");
|
||||
return true;
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, "connection established");
|
||||
|
||||
tun->setIp(ntohl(*(uint32_t *)echoReceivePayloadBuffer()));
|
||||
state = STATE_ESTABLISHED;
|
||||
|
||||
dropPrivileges();
|
||||
startPolling();
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case TunnelHeader::TYPE_CHALLENGE_ERROR:
|
||||
if (state == STATE_CHALLENGE_RESPONSE_SENT)
|
||||
{
|
||||
throw Exception("password error");
|
||||
}
|
||||
break;
|
||||
case TunnelHeader::TYPE_DATA:
|
||||
if (state == STATE_ESTABLISHED)
|
||||
{
|
||||
handleDataFromServer(dataLength);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
syslog(LOG_DEBUG, "invalid packet type: %d, state:\n", header.type, state);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Client::sendEchoToServer(int type, int dataLength)
|
||||
{
|
||||
if (maxPolls == 0 && state == STATE_ESTABLISHED)
|
||||
setTimeout(KEEP_ALIVE_INTERVAL);
|
||||
|
||||
sendEcho(magic, type, dataLength, serverIp, false, nextEchoId, nextEchoSequence);
|
||||
|
||||
if (changeEchoId)
|
||||
nextEchoId = nextEchoId + 38543; // some random prime
|
||||
if (changeEchoSeq)
|
||||
nextEchoSequence = nextEchoSequence + 38543; // some random prime
|
||||
}
|
||||
|
||||
void Client::startPolling()
|
||||
{
|
||||
if (maxPolls == 0)
|
||||
{
|
||||
setTimeout(KEEP_ALIVE_INTERVAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < maxPolls; i++)
|
||||
sendEchoToServer(TunnelHeader::TYPE_POLL, 0);
|
||||
setTimeout(POLL_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::handleDataFromServer(int dataLength)
|
||||
{
|
||||
if (dataLength == 0)
|
||||
{
|
||||
syslog(LOG_WARNING, "received empty data packet");
|
||||
return;
|
||||
}
|
||||
|
||||
sendToTun(dataLength);
|
||||
|
||||
if (maxPolls != 0)
|
||||
sendEchoToServer(TunnelHeader::TYPE_POLL, 0);
|
||||
}
|
||||
|
||||
void Client::handleTunData(int dataLength, uint32_t sourceIp, uint32_t destIp)
|
||||
{
|
||||
if (state != STATE_ESTABLISHED)
|
||||
return;
|
||||
|
||||
sendEchoToServer(TunnelHeader::TYPE_DATA, dataLength);
|
||||
}
|
||||
|
||||
void Client::handleTimeout()
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case STATE_CONNECTION_REQUEST_SENT:
|
||||
case STATE_CHALLENGE_RESPONSE_SENT:
|
||||
sendConnectionRequest();
|
||||
break;
|
||||
|
||||
case STATE_ESTABLISHED:
|
||||
sendEchoToServer(TunnelHeader::TYPE_POLL, 0);
|
||||
setTimeout(maxPolls == 0 ? KEEP_ALIVE_INTERVAL : POLL_INTERVAL);
|
||||
break;
|
||||
case STATE_CLOSED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Client::run()
|
||||
{
|
||||
now = Time::now();
|
||||
|
||||
sendConnectionRequest();
|
||||
|
||||
Worker::run();
|
||||
}
|
74
client.h
74
client.h
@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CLIENT_H
|
||||
#define CLIENT_H
|
||||
|
||||
#include "worker.h"
|
||||
#include "auth.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class Client : public Worker
|
||||
{
|
||||
public:
|
||||
Client(int tunnelMtu, const char *deviceName, uint32_t serverIp, int maxPolls,
|
||||
const char *passphrase, uid_t uid, gid_t gid, bool changeEchoId, bool changeEchoSeq);
|
||||
virtual ~Client();
|
||||
|
||||
virtual void run();
|
||||
|
||||
static const Worker::TunnelHeader::Magic magic;
|
||||
protected:
|
||||
enum State
|
||||
{
|
||||
STATE_CLOSED,
|
||||
STATE_CONNECTION_REQUEST_SENT,
|
||||
STATE_CHALLENGE_RESPONSE_SENT,
|
||||
STATE_ESTABLISHED
|
||||
};
|
||||
|
||||
virtual bool handleEchoData(const TunnelHeader &header, int dataLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq);
|
||||
virtual void handleTunData(int dataLength, uint32_t sourceIp, uint32_t destIp);
|
||||
virtual void handleTimeout();
|
||||
|
||||
void handleDataFromServer(int length);
|
||||
|
||||
void startPolling();
|
||||
|
||||
void sendEchoToServer(int type, int dataLength);
|
||||
void sendChallengeResponse(int dataLength);
|
||||
void sendConnectionRequest();
|
||||
|
||||
Auth auth;
|
||||
|
||||
uint32_t serverIp;
|
||||
|
||||
int maxPolls;
|
||||
int pollTimeoutNr;
|
||||
|
||||
bool changeEchoId, changeEchoSeq;
|
||||
|
||||
uint16_t nextEchoId;
|
||||
uint16_t nextEchoSequence;
|
||||
|
||||
State state;
|
||||
};
|
||||
|
||||
#endif
|
110
echo.cpp
110
echo.cpp
@ -1,110 +0,0 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "echo.h"
|
||||
#include "exception.h"
|
||||
|
||||
#include <netinet/ip.h>
|
||||
|
||||
typedef ip IpHeader;
|
||||
|
||||
Echo::Echo(int maxPayloadSize)
|
||||
{
|
||||
fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
|
||||
if (fd == -1)
|
||||
throw Exception("creating icmp socket", true);
|
||||
|
||||
bufferSize = maxPayloadSize + headerSize();
|
||||
sendBuffer = new char[bufferSize];
|
||||
receiveBuffer = new char[bufferSize];
|
||||
}
|
||||
|
||||
Echo::~Echo()
|
||||
{
|
||||
close(fd);
|
||||
|
||||
delete[] sendBuffer;
|
||||
delete[] receiveBuffer;
|
||||
}
|
||||
|
||||
int Echo::headerSize()
|
||||
{
|
||||
return sizeof(IpHeader) + sizeof(EchoHeader);
|
||||
}
|
||||
|
||||
void Echo::send(int payloadLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq)
|
||||
{
|
||||
struct sockaddr_in target;
|
||||
target.sin_family = AF_INET;
|
||||
target.sin_addr.s_addr = htonl(realIp);
|
||||
|
||||
if (payloadLength + sizeof(IpHeader) + sizeof(EchoHeader) > bufferSize)
|
||||
throw Exception("packet too big");
|
||||
|
||||
EchoHeader *header = (EchoHeader *)(sendBuffer + sizeof(IpHeader));
|
||||
header->type = reply ? 0: 8;
|
||||
header->code = 0;
|
||||
header->id = htons(id);
|
||||
header->seq = htons(seq);
|
||||
header->chksum = 0;
|
||||
header->chksum = icmpChecksum(sendBuffer + sizeof(IpHeader), payloadLength + sizeof(EchoHeader));
|
||||
|
||||
int result = sendto(fd, sendBuffer + sizeof(IpHeader), payloadLength + sizeof(EchoHeader), 0, (struct sockaddr *)&target, sizeof(struct sockaddr_in));
|
||||
if (result == -1)
|
||||
throw Exception("sendto", true);
|
||||
}
|
||||
|
||||
int Echo::receive(uint32_t &realIp, bool &reply, uint16_t &id, uint16_t &seq)
|
||||
{
|
||||
struct sockaddr_in source;
|
||||
int source_addr_len = sizeof(struct sockaddr_in);
|
||||
|
||||
int dataLength = recvfrom(fd, receiveBuffer, bufferSize, 0, (struct sockaddr *)&source, (socklen_t *)&source_addr_len);
|
||||
if (dataLength == -1)
|
||||
throw Exception("recvfrom", true);
|
||||
|
||||
if (dataLength < sizeof(IpHeader) + sizeof(EchoHeader))
|
||||
return -1;
|
||||
|
||||
EchoHeader *header = (EchoHeader *)(receiveBuffer + sizeof(IpHeader));
|
||||
if ((header->type != 0 && header->type != 8) || header->code != 0)
|
||||
return -1;
|
||||
|
||||
realIp = ntohl(source.sin_addr.s_addr);
|
||||
reply = header->type == 0;
|
||||
id = ntohs(header->id);
|
||||
seq = ntohs(header->seq);
|
||||
|
||||
return dataLength - sizeof(IpHeader) - sizeof(EchoHeader);
|
||||
}
|
||||
|
||||
uint16_t Echo::icmpChecksum(const char *data, int length)
|
||||
{
|
||||
uint16_t *data16 = (uint16_t *)data;
|
||||
uint32_t sum = 0;
|
||||
|
||||
for (sum = 0; length > 1; length -= 2)
|
||||
sum += *data16++;
|
||||
if (length == 1)
|
||||
sum += *(unsigned char *)data16;
|
||||
|
||||
sum = (sum >> 16) + (sum & 0xffff);
|
||||
sum += (sum >> 16);
|
||||
return ~sum;
|
||||
}
|
202
main.cpp
202
main.cpp
@ -1,202 +0,0 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "client.h"
|
||||
#include "server.h"
|
||||
#include "exception.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <pwd.h>
|
||||
#include <netdb.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <syslog.h>
|
||||
|
||||
void usage()
|
||||
{
|
||||
printf(
|
||||
"Hans - IP over ICMP version 0.1\n\n"
|
||||
"RUN AS SERVER\n"
|
||||
" hans -s network [-fr] [-p password] [-u unprivileged_user] [-d tun_device] [-m reference_mtu]\n\n"
|
||||
"RUN AS CLIENT\n"
|
||||
" hans -c server [-f] [-p password] [-u unprivileged_user] [-d tun_device] [-m reference_mtu] [-w polls]\n\n"
|
||||
"ARGUMENTS\n"
|
||||
" -s network Run as a server with the given network address for the virtual interface.\n"
|
||||
" -c server Connect to a server.\n"
|
||||
" -f Run in foreground.\n"
|
||||
" -r Respond to ordinary pings. Only in server mode.\n"
|
||||
" -p password Use a password.\n"
|
||||
" -u username Set the user under which the program should run.\n"
|
||||
" -d device Use the given tun device.\n"
|
||||
" -m mtu Use this mtu to calculate the tunnel mtu.\n"
|
||||
" The generated echo packets will not be bigger than this value.\n"
|
||||
" Has to be the same on client and server. Defaults to 1500.\n"
|
||||
" -w polls Number of echo requests the client sends to the server for polling.\n"
|
||||
" 0 disables polling. Defaults to 10.\n"
|
||||
" -i Change the echo id for every echo request.\n"
|
||||
" -q Change the echo sequence number for every echo request.\n"
|
||||
);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char *serverName;
|
||||
const char *userName = NULL;
|
||||
const char *password = "";
|
||||
const char *device = NULL;
|
||||
bool isServer = false;
|
||||
bool isClient = false;
|
||||
bool foreground = false;
|
||||
int mtu = 1500;
|
||||
int maxPolls = 10;
|
||||
uint32_t network = INADDR_NONE;
|
||||
bool answerPing = false;
|
||||
uid_t uid = 0;
|
||||
gid_t gid = 0;
|
||||
bool changeEchoId = false;
|
||||
bool changeEchoSeq = false;
|
||||
|
||||
openlog(argv[0], LOG_PERROR, LOG_DAEMON);
|
||||
|
||||
int c;
|
||||
while ((c = getopt(argc, argv, "fru:d:p:s:c:m:w:qi")) != -1)
|
||||
{
|
||||
switch(c) {
|
||||
case 'f':
|
||||
foreground = true;
|
||||
break;
|
||||
case 'u':
|
||||
userName = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
device = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
password = strdup(optarg);
|
||||
memset(optarg, 0, strlen(optarg));
|
||||
break;
|
||||
case 'c':
|
||||
isClient = true;
|
||||
serverName = optarg;
|
||||
break;
|
||||
case 's':
|
||||
isServer = true;
|
||||
network = ntohl(inet_addr(optarg));
|
||||
if (network == INADDR_NONE)
|
||||
printf("invalid network\n");
|
||||
break;
|
||||
case 'm':
|
||||
mtu = atoi(optarg);
|
||||
break;
|
||||
case 'w':
|
||||
maxPolls = atoi(optarg);
|
||||
break;
|
||||
case 'r':
|
||||
answerPing = true;
|
||||
break;
|
||||
case 'q':
|
||||
changeEchoSeq = true;
|
||||
break;
|
||||
case 'i':
|
||||
changeEchoId = true;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
mtu -= Echo::headerSize() + Worker::headerSize();
|
||||
|
||||
if (mtu < 68)
|
||||
{
|
||||
// RFC 791: Every internet module must be able to forward a datagram of 68 octets without further fragmentation.
|
||||
printf("mtu too small\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((isClient == isServer) ||
|
||||
(isServer && network == INADDR_NONE) ||
|
||||
(maxPolls < 0 || maxPolls > 255) ||
|
||||
(isServer && (changeEchoSeq || changeEchoId)))
|
||||
{
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (userName != NULL)
|
||||
{
|
||||
passwd *pw = getpwnam(userName);
|
||||
|
||||
if (pw != NULL)
|
||||
{
|
||||
uid = pw->pw_uid;
|
||||
gid = pw->pw_gid;
|
||||
}
|
||||
else
|
||||
{
|
||||
syslog(LOG_ERR, "user not found");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Worker *worker;
|
||||
|
||||
if (isServer)
|
||||
{
|
||||
worker = new Server(mtu, device, password, network, answerPing, uid, gid, 5000);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t serverIp = inet_addr(serverName);
|
||||
if (serverIp == INADDR_NONE)
|
||||
{
|
||||
struct hostent* he = gethostbyname(serverName);
|
||||
if (!he)
|
||||
{
|
||||
syslog(LOG_ERR, "gethostbyname: %s", hstrerror(h_errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
serverIp = *(uint32_t *)he->h_addr;
|
||||
}
|
||||
|
||||
worker = new Client(mtu, device, ntohl(serverIp), maxPolls, password, uid, gid, changeEchoId, changeEchoSeq);
|
||||
}
|
||||
|
||||
if (!foreground)
|
||||
{
|
||||
syslog(LOG_INFO, "detaching from terminal");
|
||||
daemon(0, 0);
|
||||
}
|
||||
|
||||
worker->run();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
syslog(LOG_ERR, "%s", e.errorMessage());
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
55
osflags
Normal file
55
osflags
Normal file
@ -0,0 +1,55 @@
|
||||
#!/bin/sh
|
||||
|
||||
OS=`uname | tr "a-z" "A-Z"`
|
||||
MODE=`echo "$2" | tr "a-z" "A-Z"`
|
||||
|
||||
case $1 in
|
||||
ld)
|
||||
case $OS in
|
||||
DARWIN)
|
||||
if [ "$MODE" == TUNEMU ]; then
|
||||
echo build/tunemu.o -lpcap
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
c)
|
||||
case $OS in
|
||||
LINUX)
|
||||
echo $FLAGS -DHAVE_LINUX_IF_TUN_H -DLINUX
|
||||
;;
|
||||
CYGWIN*)
|
||||
echo $FLAGS -DWIN32
|
||||
;;
|
||||
*)
|
||||
echo $FLAGS
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
dev)
|
||||
case $OS in
|
||||
LINUX)
|
||||
echo src/tun_dev_linux.c
|
||||
;;
|
||||
FREEBSD)
|
||||
echo src/tun_dev_freebsd.c
|
||||
;;
|
||||
OPENBSD)
|
||||
echo src/tun_dev_openbsd.c
|
||||
;;
|
||||
DARWIN)
|
||||
if [ "$MODE" == TUNEMU ]; then
|
||||
echo src/tun_dev_darwin_emu.c
|
||||
else
|
||||
echo src/tun_dev_generic.c
|
||||
fi
|
||||
;;
|
||||
CYGWIN*)
|
||||
echo src/tun_dev_cygwin.c
|
||||
;;
|
||||
*)
|
||||
echo src/tun_dev_generic.c
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
338
server.cpp
338
server.cpp
@ -1,338 +0,0 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "server.h"
|
||||
#include "client.h"
|
||||
#include "config.h"
|
||||
#include "utility.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <syslog.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
const Worker::TunnelHeader::Magic Server::magic("hans");
|
||||
|
||||
Server::Server(int tunnelMtu, const char *deviceName, const char *passphrase, uint32_t network, bool answerEcho, uid_t uid, gid_t gid, int pollTimeout)
|
||||
: Worker(tunnelMtu, deviceName, answerEcho, uid, gid), auth(passphrase)
|
||||
{
|
||||
this->network = network & 0xffffff00;
|
||||
this->pollTimeout = pollTimeout;
|
||||
|
||||
tun->setIp(this->network + 1);
|
||||
|
||||
dropPrivileges();
|
||||
}
|
||||
|
||||
Server::~Server()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Server::handleUnknownClient(const TunnelHeader &header, int dataLength, uint32_t realIp, uint16_t echoId, uint16_t echoSeq)
|
||||
{
|
||||
ClientData client;
|
||||
client.realIp = realIp;
|
||||
client.maxPolls = 1;
|
||||
|
||||
pollReceived(&client, echoId, echoSeq);
|
||||
|
||||
if (header.type != TunnelHeader::TYPE_CONNECTION_REQUEST || dataLength != sizeof(ClientConnectData))
|
||||
{
|
||||
syslog(LOG_DEBUG, "invalid request %s", Utility::formatIp(realIp).c_str());
|
||||
sendReset(&client);
|
||||
return;
|
||||
}
|
||||
|
||||
ClientConnectData *connectData = (ClientConnectData *)echoReceivePayloadBuffer();
|
||||
|
||||
client.maxPolls = connectData->maxPolls;
|
||||
client.state = ClientData::STATE_NEW;
|
||||
client.tunnelIp = reserveTunnelIp();
|
||||
|
||||
syslog(LOG_DEBUG, "new client: %s (%s)\n", Utility::formatIp(client.realIp).c_str(), Utility::formatIp(client.tunnelIp).c_str());
|
||||
|
||||
if (client.tunnelIp != 0)
|
||||
{
|
||||
client.challenge = auth.generateChallenge(CHALLENGE_SIZE);
|
||||
sendChallenge(&client);
|
||||
|
||||
// add client to list
|
||||
clientList.push_back(client);
|
||||
clientRealIpMap[realIp] = clientList.size() - 1;
|
||||
clientTunnelIpMap[client.tunnelIp] = clientList.size() - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
syslog(LOG_WARNING, "server full");
|
||||
sendEchoToClient(&client, TunnelHeader::TYPE_SERVER_FULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Server::sendChallenge(ClientData *client)
|
||||
{
|
||||
syslog(LOG_DEBUG, "sending challenge to: %s\n", Utility::formatIp(client->realIp).c_str());
|
||||
|
||||
memcpy(echoSendPayloadBuffer(), &client->challenge[0], client->challenge.size());
|
||||
sendEchoToClient(client, TunnelHeader::TYPE_CHALLENGE, client->challenge.size());
|
||||
|
||||
client->state = ClientData::STATE_CHALLENGE_SENT;
|
||||
}
|
||||
|
||||
void Server::removeClient(ClientData *client)
|
||||
{
|
||||
syslog(LOG_DEBUG, "removing client: %s (%s)\n", Utility::formatIp(client->realIp).c_str(), Utility::formatIp(client->tunnelIp).c_str());
|
||||
|
||||
releaseTunnelIp(client->tunnelIp);
|
||||
|
||||
int nr = clientRealIpMap[client->realIp];
|
||||
|
||||
clientRealIpMap.erase(client->realIp);
|
||||
clientTunnelIpMap.erase(client->tunnelIp);
|
||||
|
||||
clientList.erase(clientList.begin() + nr);
|
||||
}
|
||||
|
||||
void Server::checkChallenge(ClientData *client, int length)
|
||||
{
|
||||
Auth::Response rightResponse = auth.getResponse(client->challenge);
|
||||
|
||||
if (length != sizeof(Auth::Response) || memcmp(&rightResponse, echoReceivePayloadBuffer(), length) != 0)
|
||||
{
|
||||
syslog(LOG_DEBUG, "wrong challenge response\n");
|
||||
|
||||
sendEchoToClient(client, TunnelHeader::TYPE_CHALLENGE_ERROR, 0);
|
||||
|
||||
removeClient(client);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t *ip = (uint32_t *)echoSendPayloadBuffer();
|
||||
*ip = htonl(client->tunnelIp);
|
||||
|
||||
sendEchoToClient(client, TunnelHeader::TYPE_CONNECTION_ACCEPT, sizeof(uint32_t));
|
||||
|
||||
client->state = ClientData::STATE_ESTABLISHED;
|
||||
|
||||
syslog(LOG_INFO, "connection established: %s", Utility::formatIp(client->realIp).c_str());
|
||||
}
|
||||
|
||||
void Server::sendReset(ClientData *client)
|
||||
{
|
||||
syslog(LOG_DEBUG, "sending reset: %s", Utility::formatIp(client->realIp).c_str());
|
||||
sendEchoToClient(client, TunnelHeader::TYPE_RESET_CONNECTION, 0);
|
||||
}
|
||||
|
||||
bool Server::handleEchoData(const TunnelHeader &header, int dataLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq)
|
||||
{
|
||||
if (reply)
|
||||
return false;
|
||||
|
||||
if (header.magic != Client::magic)
|
||||
return false;
|
||||
|
||||
ClientData *client = getClientByRealIp(realIp);
|
||||
if (client == NULL)
|
||||
{
|
||||
handleUnknownClient(header, dataLength, realIp, id, seq);
|
||||
return true;
|
||||
}
|
||||
|
||||
pollReceived(client, id, seq);
|
||||
|
||||
switch (header.type)
|
||||
{
|
||||
case TunnelHeader::TYPE_CONNECTION_REQUEST:
|
||||
if (client->state == ClientData::STATE_CHALLENGE_SENT)
|
||||
{
|
||||
sendChallenge(client);
|
||||
return true;
|
||||
}
|
||||
|
||||
while (client->pollIds.size() > 1)
|
||||
client->pollIds.pop();
|
||||
|
||||
syslog(LOG_DEBUG, "reconnecting %s", Utility::formatIp(realIp).c_str());
|
||||
sendReset(client);
|
||||
removeClient(client);
|
||||
return true;
|
||||
case TunnelHeader::TYPE_CHALLENGE_RESPONSE:
|
||||
if (client->state == ClientData::STATE_CHALLENGE_SENT)
|
||||
{
|
||||
checkChallenge(client, dataLength);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case TunnelHeader::TYPE_DATA:
|
||||
if (client->state == ClientData::STATE_ESTABLISHED)
|
||||
{
|
||||
if (dataLength == 0)
|
||||
{
|
||||
syslog(LOG_WARNING, "received empty data packet");
|
||||
throw 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
sendToTun(dataLength);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case TunnelHeader::TYPE_POLL:
|
||||
return true;
|
||||
}
|
||||
|
||||
syslog(LOG_DEBUG, "invalid packet from: %s, type: %d, state:\n", Utility::formatIp(realIp).c_str(), header.type, client->state);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Server::ClientData *Server::getClientByTunnelIp(uint32_t ip)
|
||||
{
|
||||
ClientIpMap::iterator clientMapIterator = clientTunnelIpMap.find(ip);
|
||||
if (clientMapIterator == clientTunnelIpMap.end())
|
||||
return NULL;
|
||||
|
||||
return &clientList[clientMapIterator->second];
|
||||
}
|
||||
|
||||
Server::ClientData *Server::getClientByRealIp(uint32_t ip)
|
||||
{
|
||||
ClientIpMap::iterator clientMapIterator = clientRealIpMap.find(ip);
|
||||
if (clientMapIterator == clientRealIpMap.end())
|
||||
return NULL;
|
||||
|
||||
return &clientList[clientMapIterator->second];
|
||||
}
|
||||
|
||||
void Server::handleTunData(int dataLength, uint32_t sourceIp, uint32_t destIp)
|
||||
{
|
||||
ClientData *client = getClientByTunnelIp(destIp);
|
||||
|
||||
if (client == NULL)
|
||||
{
|
||||
syslog(LOG_DEBUG, "unknown client: %s\n", Utility::formatIp(destIp).c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
sendEchoToClient(client, TunnelHeader::TYPE_DATA, dataLength);
|
||||
}
|
||||
|
||||
void Server::pollReceived(ClientData *client, uint16_t echoId, uint16_t echoSeq)
|
||||
{
|
||||
unsigned int maxSavedPolls = client->maxPolls != 0 ? client->maxPolls : 1;
|
||||
|
||||
client->pollIds.push(ClientData::EchoId(echoId, echoSeq));
|
||||
if (client->pollIds.size() > maxSavedPolls)
|
||||
client->pollIds.pop();
|
||||
DEBUG_ONLY(printf("poll -> %d\n", client->pollIds.size()));
|
||||
|
||||
if (client->pendingPackets.size() > 0)
|
||||
{
|
||||
Packet &packet = client->pendingPackets.front();
|
||||
memcpy(echoSendPayloadBuffer(), &packet.data[0], packet.data.size());
|
||||
client->pendingPackets.pop();
|
||||
|
||||
DEBUG_ONLY(printf("pending packet: %d bytes\n", packet.data.size()));
|
||||
sendEchoToClient(client, packet.type, packet.data.size());
|
||||
}
|
||||
|
||||
client->lastActivity = now;
|
||||
}
|
||||
|
||||
void Server::sendEchoToClient(ClientData *client, int type, int dataLength)
|
||||
{
|
||||
if (client->maxPolls == 0)
|
||||
{
|
||||
sendEcho(magic, type, dataLength, client->realIp, true, client->pollIds.front().id, client->pollIds.front().seq);
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->pollIds.size() != 0)
|
||||
{
|
||||
ClientData::EchoId echoId = client->pollIds.front();
|
||||
client->pollIds.pop();
|
||||
|
||||
DEBUG_ONLY(printf("sending -> %d\n", client->pollIds.size()));
|
||||
sendEcho(magic, type, dataLength, client->realIp, true, echoId.id, echoId.seq);
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->pendingPackets.size() == MAX_BUFFERED_PACKETS)
|
||||
{
|
||||
client->pendingPackets.pop();
|
||||
syslog(LOG_WARNING, "packet dropped to %s", Utility::formatIp(client->tunnelIp).c_str());
|
||||
}
|
||||
|
||||
DEBUG_ONLY(printf("packet queued: %d bytes\n", dataLength));
|
||||
|
||||
client->pendingPackets.push(Packet());
|
||||
Packet &packet = client->pendingPackets.back();
|
||||
packet.type = type;
|
||||
packet.data.resize(dataLength);
|
||||
memcpy(&packet.data[0], echoReceivePayloadBuffer(), dataLength);
|
||||
}
|
||||
|
||||
void Server::releaseTunnelIp(uint32_t tunnelIp)
|
||||
{
|
||||
usedIps.remove(tunnelIp);
|
||||
}
|
||||
|
||||
void Server::handleTimeout()
|
||||
{
|
||||
for (int i = 0; i < clientList.size(); i++)
|
||||
{
|
||||
ClientData *client = &clientList[i];
|
||||
|
||||
if (client->lastActivity + KEEP_ALIVE_INTERVAL * 2 < now)
|
||||
{
|
||||
syslog(LOG_DEBUG, "client timeout: %s\n", Utility::formatIp(client->realIp).c_str());
|
||||
removeClient(client);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(KEEP_ALIVE_INTERVAL);
|
||||
}
|
||||
|
||||
uint32_t Server::reserveTunnelIp()
|
||||
{
|
||||
uint32_t ip = network + 2;
|
||||
|
||||
list<uint32_t>::iterator i;
|
||||
for (i = usedIps.begin(); i != usedIps.end(); ++i)
|
||||
{
|
||||
if (*i > ip)
|
||||
break;
|
||||
ip = ip + 1;
|
||||
}
|
||||
|
||||
if (ip - network >= 255)
|
||||
return 0;
|
||||
|
||||
usedIps.insert(i, ip);
|
||||
return ip;
|
||||
}
|
||||
|
||||
void Server::run()
|
||||
{
|
||||
setTimeout(KEEP_ALIVE_INTERVAL);
|
||||
|
||||
Worker::run();
|
||||
}
|
122
server.h
122
server.h
@ -1,122 +0,0 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SERVER_H
|
||||
#define SERVER_H
|
||||
|
||||
#include "worker.h"
|
||||
#include "auth.h"
|
||||
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
class Server : public Worker
|
||||
{
|
||||
public:
|
||||
Server(int tunnelMtu, const char *deviceName, const char *passphrase, uint32_t network, bool answerEcho, uid_t uid, gid_t gid, int pollTimeout);
|
||||
virtual ~Server();
|
||||
|
||||
struct ClientConnectData
|
||||
{
|
||||
uint8_t maxPolls;
|
||||
};
|
||||
|
||||
static const Worker::TunnelHeader::Magic magic;
|
||||
|
||||
protected:
|
||||
struct Packet
|
||||
{
|
||||
int type;
|
||||
std::vector<char> data;
|
||||
};
|
||||
|
||||
struct ClientData
|
||||
{
|
||||
enum State
|
||||
{
|
||||
STATE_NEW,
|
||||
STATE_CHALLENGE_SENT,
|
||||
STATE_ESTABLISHED
|
||||
};
|
||||
|
||||
struct EchoId
|
||||
{
|
||||
EchoId(uint16_t id, uint16_t seq) { this->id = id; this->seq = seq; }
|
||||
|
||||
uint16_t id;
|
||||
uint16_t seq;
|
||||
};
|
||||
|
||||
uint32_t realIp;
|
||||
uint32_t tunnelIp;
|
||||
|
||||
std::queue<Packet> pendingPackets;
|
||||
|
||||
int maxPolls;
|
||||
std::queue<EchoId> pollIds;
|
||||
Time lastActivity;
|
||||
|
||||
State state;
|
||||
|
||||
Auth::Challenge challenge;
|
||||
};
|
||||
|
||||
typedef std::vector<ClientData> ClientList;
|
||||
typedef std::map<uint32_t, int> ClientIpMap;
|
||||
|
||||
virtual bool handleEchoData(const TunnelHeader &header, int dataLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq);
|
||||
virtual void handleTunData(int dataLength, uint32_t sourceIp, uint32_t destIp);
|
||||
virtual void handleTimeout();
|
||||
|
||||
virtual void run();
|
||||
|
||||
void serveTun(ClientData *client);
|
||||
|
||||
void handleUnknownClient(const TunnelHeader &header, int dataLength, uint32_t realIp, uint16_t echoId, uint16_t echoSeq);
|
||||
void removeClient(ClientData *client);
|
||||
|
||||
void sendChallenge(ClientData *client);
|
||||
void checkChallenge(ClientData *client, int dataLength);
|
||||
void sendReset(ClientData *client);
|
||||
|
||||
void sendEchoToClient(ClientData *client, int type, int dataLength);
|
||||
|
||||
void pollReceived(ClientData *client, uint16_t echoId, uint16_t echoSeq);
|
||||
|
||||
uint32_t reserveTunnelIp();
|
||||
void releaseTunnelIp(uint32_t tunnelIp);
|
||||
|
||||
ClientData *getClientByTunnelIp(uint32_t ip);
|
||||
ClientData *getClientByRealIp(uint32_t ip);
|
||||
|
||||
Auth auth;
|
||||
|
||||
uint32_t network;
|
||||
std::list<uint32_t> usedIps;
|
||||
|
||||
Time pollTimeout;
|
||||
|
||||
ClientList clientList;
|
||||
ClientIpMap clientRealIpMap;
|
||||
ClientIpMap clientTunnelIpMap;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,20 +1,20 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
|
||||
*
|
||||
* Copyright (C) 2009 Friedrich Schöller <hans@schoeller.se>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "auth.h"
|
||||
@ -23,35 +23,34 @@
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
Auth::Auth(const char *passphrase)
|
||||
{
|
||||
this->passphrase = passphrase;
|
||||
}
|
||||
Auth::Auth(const std::string &passphrase)
|
||||
: passphrase(passphrase)
|
||||
{ }
|
||||
|
||||
Auth::Response Auth::getResponse(const Challenge &challenge) const
|
||||
{
|
||||
SHA1 hasher;
|
||||
SHA1 hasher;
|
||||
|
||||
Response response;
|
||||
Response response;
|
||||
|
||||
hasher << passphrase.c_str();
|
||||
hasher.Input(&challenge[0], challenge.size());
|
||||
hasher << passphrase.data();
|
||||
hasher.Input(&challenge[0], challenge.size());
|
||||
|
||||
hasher.Result((unsigned int *)response.data);
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
response.data[i] = htonl(response.data[i]);
|
||||
hasher.Result((unsigned int *)response.data);
|
||||
|
||||
return response;
|
||||
for (int i = 0; i < 5; i++)
|
||||
response.data[i] = htonl(response.data[i]);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
Auth::Challenge Auth::generateChallenge(int length) const
|
||||
{
|
||||
Challenge challenge;
|
||||
challenge.resize(length);
|
||||
Challenge challenge;
|
||||
challenge.resize(length);
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
challenge[i] = Utility::rand();
|
||||
for (int i = 0; i < length; i++)
|
||||
challenge[i] = Utility::rand();
|
||||
|
||||
return challenge;
|
||||
return challenge;
|
||||
}
|
@ -1,20 +1,20 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
|
||||
*
|
||||
* Copyright (C) 2009 Friedrich Schöller <hans@schoeller.se>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AUTH_H
|
||||
@ -23,26 +23,27 @@
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
class Auth
|
||||
{
|
||||
public:
|
||||
typedef std::vector<char> Challenge;
|
||||
typedef std::vector<char> Challenge;
|
||||
|
||||
struct Response
|
||||
{
|
||||
uint32_t data[5];
|
||||
bool operator==(const Response &other) const { return memcmp(this, &other, sizeof(Response)) == 0; }
|
||||
};
|
||||
struct Response
|
||||
{
|
||||
uint32_t data[5];
|
||||
bool operator==(const Response &other) const { return memcmp(this, &other, sizeof(Response)) == 0; }
|
||||
};
|
||||
|
||||
Auth(const char *passphrase);
|
||||
Auth(const std::string &passphrase);
|
||||
|
||||
Challenge generateChallenge(int length) const;
|
||||
Response getResponse(const Challenge &challenge) const;
|
||||
Challenge generateChallenge(int length) const;
|
||||
Response getResponse(const Challenge &challenge) const;
|
||||
|
||||
protected:
|
||||
std::string passphrase;
|
||||
std::string challenge;
|
||||
std::string passphrase;
|
||||
std::string challenge;
|
||||
};
|
||||
|
||||
#endif
|
247
src/client.cpp
Normal file
247
src/client.cpp
Normal file
@ -0,0 +1,247 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <hans@schoeller.se>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "client.h"
|
||||
#include "server.h"
|
||||
#include "exception.h"
|
||||
#include "config.h"
|
||||
#include "utility.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <syslog.h>
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
|
||||
const Worker::TunnelHeader::Magic Client::magic("hanc");
|
||||
|
||||
Client::Client(int tunnelMtu, const string *deviceName, uint32_t serverIp,
|
||||
int maxPolls, const string &passphrase, uid_t uid, gid_t gid,
|
||||
bool changeEchoId, bool changeEchoSeq, uint32_t desiredIp)
|
||||
: Worker(tunnelMtu, deviceName, false, uid, gid), auth(passphrase)
|
||||
{
|
||||
this->serverIp = serverIp;
|
||||
this->clientIp = INADDR_NONE;
|
||||
this->desiredIp = desiredIp;
|
||||
this->maxPolls = maxPolls;
|
||||
this->nextEchoId = Utility::rand();
|
||||
this->changeEchoId = changeEchoId;
|
||||
this->changeEchoSeq = changeEchoSeq;
|
||||
this->nextEchoSequence = Utility::rand();
|
||||
|
||||
state = STATE_CLOSED;
|
||||
}
|
||||
|
||||
Client::~Client()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Client::sendConnectionRequest()
|
||||
{
|
||||
Server::ClientConnectData *connectData = (Server::ClientConnectData *)echoSendPayloadBuffer();
|
||||
connectData->maxPolls = maxPolls;
|
||||
connectData->desiredIp = desiredIp;
|
||||
|
||||
syslog(LOG_DEBUG, "sending connection request");
|
||||
|
||||
sendEchoToServer(TunnelHeader::TYPE_CONNECTION_REQUEST, sizeof(Server::ClientConnectData));
|
||||
|
||||
state = STATE_CONNECTION_REQUEST_SENT;
|
||||
setTimeout(5000);
|
||||
}
|
||||
|
||||
void Client::sendChallengeResponse(int dataLength)
|
||||
{
|
||||
if (dataLength != CHALLENGE_SIZE)
|
||||
throw Exception("invalid challenge received");
|
||||
|
||||
state = STATE_CHALLENGE_RESPONSE_SENT;
|
||||
|
||||
syslog(LOG_DEBUG, "sending challenge response");
|
||||
|
||||
vector<char> challenge;
|
||||
challenge.resize(dataLength);
|
||||
memcpy(&challenge[0], echoReceivePayloadBuffer(), dataLength);
|
||||
|
||||
Auth::Response response = auth.getResponse(challenge);
|
||||
|
||||
memcpy(echoSendPayloadBuffer(), (char *)&response, sizeof(Auth::Response));
|
||||
sendEchoToServer(TunnelHeader::TYPE_CHALLENGE_RESPONSE, sizeof(Auth::Response));
|
||||
|
||||
setTimeout(5000);
|
||||
}
|
||||
|
||||
bool Client::handleEchoData(const TunnelHeader &header, int dataLength, uint32_t realIp, bool reply, uint16_t, uint16_t)
|
||||
{
|
||||
if (realIp != serverIp || !reply)
|
||||
return false;
|
||||
|
||||
if (header.magic != Server::magic)
|
||||
return false;
|
||||
|
||||
switch (header.type)
|
||||
{
|
||||
case TunnelHeader::TYPE_RESET_CONNECTION:
|
||||
syslog(LOG_DEBUG, "reset received");
|
||||
|
||||
sendConnectionRequest();
|
||||
return true;
|
||||
case TunnelHeader::TYPE_SERVER_FULL:
|
||||
if (state == STATE_CONNECTION_REQUEST_SENT)
|
||||
{
|
||||
throw Exception("server full");
|
||||
}
|
||||
break;
|
||||
case TunnelHeader::TYPE_CHALLENGE:
|
||||
if (state == STATE_CONNECTION_REQUEST_SENT)
|
||||
{
|
||||
syslog(LOG_DEBUG, "authentication request received");
|
||||
sendChallengeResponse(dataLength);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case TunnelHeader::TYPE_CONNECTION_ACCEPT:
|
||||
if (state == STATE_CHALLENGE_RESPONSE_SENT)
|
||||
{
|
||||
if (dataLength != sizeof(uint32_t))
|
||||
{
|
||||
throw Exception("invalid ip received");
|
||||
return true;
|
||||
}
|
||||
|
||||
syslog(LOG_INFO, "connection established");
|
||||
|
||||
uint32_t ip = ntohl(*(uint32_t *)echoReceivePayloadBuffer());
|
||||
if (ip != clientIp)
|
||||
{
|
||||
if (privilegesDropped)
|
||||
throw Exception("could not get the same ip address, so root privileges are required to change it");
|
||||
|
||||
clientIp = ip;
|
||||
desiredIp = ip;
|
||||
tun.setIp(ip, (ip & 0xffffff00) + 1);
|
||||
}
|
||||
state = STATE_ESTABLISHED;
|
||||
|
||||
dropPrivileges();
|
||||
startPolling();
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case TunnelHeader::TYPE_CHALLENGE_ERROR:
|
||||
if (state == STATE_CHALLENGE_RESPONSE_SENT)
|
||||
{
|
||||
throw Exception("password error");
|
||||
}
|
||||
break;
|
||||
case TunnelHeader::TYPE_DATA:
|
||||
if (state == STATE_ESTABLISHED)
|
||||
{
|
||||
handleDataFromServer(dataLength);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
syslog(LOG_DEBUG, "invalid packet type: %d, state: %d", header.type, state);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Client::sendEchoToServer(Worker::TunnelHeader::Type type, int dataLength)
|
||||
{
|
||||
if (maxPolls == 0 && state == STATE_ESTABLISHED)
|
||||
setTimeout(KEEP_ALIVE_INTERVAL);
|
||||
|
||||
sendEcho(magic, type, dataLength, serverIp, false, nextEchoId, nextEchoSequence);
|
||||
|
||||
if (changeEchoId)
|
||||
nextEchoId = nextEchoId + 38543; // some random prime
|
||||
if (changeEchoSeq)
|
||||
nextEchoSequence = nextEchoSequence + 38543; // some random prime
|
||||
}
|
||||
|
||||
void Client::startPolling()
|
||||
{
|
||||
if (maxPolls == 0)
|
||||
{
|
||||
setTimeout(KEEP_ALIVE_INTERVAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < maxPolls; i++)
|
||||
sendEchoToServer(TunnelHeader::TYPE_POLL, 0);
|
||||
setTimeout(POLL_INTERVAL);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::handleDataFromServer(int dataLength)
|
||||
{
|
||||
if (dataLength == 0)
|
||||
{
|
||||
syslog(LOG_WARNING, "received empty data packet");
|
||||
return;
|
||||
}
|
||||
|
||||
sendToTun(dataLength);
|
||||
|
||||
if (maxPolls != 0)
|
||||
sendEchoToServer(TunnelHeader::TYPE_POLL, 0);
|
||||
}
|
||||
|
||||
void Client::handleTunData(int dataLength, uint32_t, uint32_t)
|
||||
{
|
||||
if (state != STATE_ESTABLISHED)
|
||||
return;
|
||||
|
||||
sendEchoToServer(TunnelHeader::TYPE_DATA, dataLength);
|
||||
}
|
||||
|
||||
void Client::handleTimeout()
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case STATE_CONNECTION_REQUEST_SENT:
|
||||
case STATE_CHALLENGE_RESPONSE_SENT:
|
||||
sendConnectionRequest();
|
||||
break;
|
||||
|
||||
case STATE_ESTABLISHED:
|
||||
sendEchoToServer(TunnelHeader::TYPE_POLL, 0);
|
||||
setTimeout(maxPolls == 0 ? KEEP_ALIVE_INTERVAL : POLL_INTERVAL);
|
||||
break;
|
||||
case STATE_CLOSED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Client::run()
|
||||
{
|
||||
now = Time::now();
|
||||
|
||||
sendConnectionRequest();
|
||||
|
||||
Worker::run();
|
||||
}
|
78
src/client.h
Normal file
78
src/client.h
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <hans@schoeller.se>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CLIENT_H
|
||||
#define CLIENT_H
|
||||
|
||||
#include "worker.h"
|
||||
#include "auth.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class Client : public Worker
|
||||
{
|
||||
|
||||
public:
|
||||
Client(int tunnelMtu, const std::string *deviceName, uint32_t serverIp,
|
||||
int maxPolls, const std::string &passphrase, uid_t uid, gid_t gid,
|
||||
bool changeEchoId, bool changeEchoSeq, uint32_t desiredIp);
|
||||
virtual ~Client();
|
||||
|
||||
virtual void run();
|
||||
|
||||
static const Worker::TunnelHeader::Magic magic;
|
||||
protected:
|
||||
enum State
|
||||
{
|
||||
STATE_CLOSED,
|
||||
STATE_CONNECTION_REQUEST_SENT,
|
||||
STATE_CHALLENGE_RESPONSE_SENT,
|
||||
STATE_ESTABLISHED
|
||||
};
|
||||
|
||||
virtual bool handleEchoData(const TunnelHeader &header, int dataLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq);
|
||||
virtual void handleTunData(int dataLength, uint32_t sourceIp, uint32_t destIp);
|
||||
virtual void handleTimeout();
|
||||
|
||||
void handleDataFromServer(int length);
|
||||
|
||||
void startPolling();
|
||||
|
||||
void sendEchoToServer(Worker::TunnelHeader::Type type, int dataLength);
|
||||
void sendChallengeResponse(int dataLength);
|
||||
void sendConnectionRequest();
|
||||
|
||||
Auth auth;
|
||||
|
||||
uint32_t serverIp;
|
||||
uint32_t clientIp;
|
||||
uint32_t desiredIp;
|
||||
|
||||
int maxPolls;
|
||||
int pollTimeoutNr;
|
||||
|
||||
bool changeEchoId, changeEchoSeq;
|
||||
|
||||
uint16_t nextEchoId;
|
||||
uint16_t nextEchoSequence;
|
||||
|
||||
State state;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,20 +1,20 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
|
||||
*
|
||||
* Copyright (C) 2009 Friedrich Schöller <hans@schoeller.se>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#define MAX_BUFFERED_PACKETS 20
|
||||
@ -24,5 +24,5 @@
|
||||
|
||||
#define CHALLENGE_SIZE 20
|
||||
|
||||
//#define DEBUG_ONLY(a) a
|
||||
// #define DEBUG_ONLY(a) a
|
||||
#define DEBUG_ONLY(a)
|
131
src/echo.cpp
Normal file
131
src/echo.cpp
Normal file
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <hans@schoeller.se>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "echo.h"
|
||||
#include "exception.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in_systm.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <syslog.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
typedef ip IpHeader;
|
||||
|
||||
Echo::Echo(int maxPayloadSize)
|
||||
{
|
||||
fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
|
||||
if (fd == -1)
|
||||
throw Exception("creating icmp socket", true);
|
||||
|
||||
bufferSize = maxPayloadSize + headerSize();
|
||||
sendBuffer.resize(bufferSize);
|
||||
receiveBuffer.resize(bufferSize);
|
||||
}
|
||||
|
||||
Echo::~Echo()
|
||||
{
|
||||
close(fd);
|
||||
}
|
||||
|
||||
int Echo::headerSize()
|
||||
{
|
||||
return sizeof(IpHeader) + sizeof(EchoHeader);
|
||||
}
|
||||
|
||||
void Echo::send(int payloadLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq)
|
||||
{
|
||||
struct sockaddr_in target;
|
||||
target.sin_family = AF_INET;
|
||||
target.sin_addr.s_addr = htonl(realIp);
|
||||
|
||||
if (payloadLength + sizeof(IpHeader) + sizeof(EchoHeader) > bufferSize)
|
||||
throw Exception("packet too big");
|
||||
|
||||
EchoHeader *header = (EchoHeader *)(sendBuffer.data() + sizeof(IpHeader));
|
||||
header->type = reply ? 0: 8;
|
||||
header->code = 0;
|
||||
header->id = htons(id);
|
||||
header->seq = htons(seq);
|
||||
header->chksum = 0;
|
||||
header->chksum = icmpChecksum(sendBuffer.data() + sizeof(IpHeader), payloadLength + sizeof(EchoHeader));
|
||||
|
||||
int result = sendto(fd, sendBuffer.data() + sizeof(IpHeader), payloadLength + sizeof(EchoHeader), 0, (struct sockaddr *)&target, sizeof(struct sockaddr_in));
|
||||
if (result == -1)
|
||||
syslog(LOG_ERR, "error sending icmp packet: %s", strerror(errno));
|
||||
}
|
||||
|
||||
int Echo::receive(uint32_t &realIp, bool &reply, uint16_t &id, uint16_t &seq)
|
||||
{
|
||||
struct sockaddr_in source;
|
||||
int source_addr_len = sizeof(struct sockaddr_in);
|
||||
|
||||
int dataLength = recvfrom(fd, receiveBuffer.data(), bufferSize, 0, (struct sockaddr *)&source, (socklen_t *)&source_addr_len);
|
||||
if (dataLength == -1)
|
||||
{
|
||||
syslog(LOG_ERR, "error receiving icmp packet: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dataLength < sizeof(IpHeader) + sizeof(EchoHeader))
|
||||
return -1;
|
||||
|
||||
EchoHeader *header = (EchoHeader *)(receiveBuffer.data() + sizeof(IpHeader));
|
||||
if ((header->type != 0 && header->type != 8) || header->code != 0)
|
||||
return -1;
|
||||
|
||||
realIp = ntohl(source.sin_addr.s_addr);
|
||||
reply = header->type == 0;
|
||||
id = ntohs(header->id);
|
||||
seq = ntohs(header->seq);
|
||||
|
||||
return dataLength - sizeof(IpHeader) - sizeof(EchoHeader);
|
||||
}
|
||||
|
||||
uint16_t Echo::icmpChecksum(const char *data, int length)
|
||||
{
|
||||
uint16_t *data16 = (uint16_t *)data;
|
||||
uint32_t sum = 0;
|
||||
|
||||
for (sum = 0; length > 1; length -= 2)
|
||||
sum += *data16++;
|
||||
if (length == 1)
|
||||
sum += *(unsigned char *)data16;
|
||||
|
||||
sum = (sum >> 16) + (sum & 0xffff);
|
||||
sum += (sum >> 16);
|
||||
return ~sum;
|
||||
}
|
||||
|
||||
char *Echo::sendPayloadBuffer()
|
||||
{
|
||||
return sendBuffer.data() + headerSize();
|
||||
}
|
||||
|
||||
char *Echo::receivePayloadBuffer()
|
||||
{
|
||||
return receiveBuffer.data() + headerSize();
|
||||
}
|
@ -1,58 +1,60 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
|
||||
*
|
||||
* Copyright (C) 2009 Friedrich Schöller <hans@schoeller.se>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ECHO_H
|
||||
#define ECHO_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdint.h>
|
||||
|
||||
class Echo
|
||||
{
|
||||
public:
|
||||
Echo(int maxPayloadSize);
|
||||
~Echo();
|
||||
Echo(int maxPayloadSize);
|
||||
~Echo();
|
||||
|
||||
int getFd() { return fd; }
|
||||
int getFd() { return fd; }
|
||||
|
||||
void send(int payloadLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq);
|
||||
int receive(uint32_t &realIp, bool &reply, uint16_t &id, uint16_t &seq);
|
||||
void send(int payloadLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq);
|
||||
int receive(uint32_t &realIp, bool &reply, uint16_t &id, uint16_t &seq);
|
||||
|
||||
char *sendPayloadBuffer() { return sendBuffer + headerSize(); }
|
||||
char *receivePayloadBuffer() { return receiveBuffer + headerSize(); }
|
||||
char *sendPayloadBuffer();
|
||||
char *receivePayloadBuffer();
|
||||
|
||||
static int headerSize();
|
||||
static int headerSize();
|
||||
protected:
|
||||
struct EchoHeader
|
||||
{
|
||||
uint8_t type;
|
||||
uint8_t code;
|
||||
uint16_t chksum;
|
||||
uint16_t id;
|
||||
uint16_t seq;
|
||||
}; // size = 8
|
||||
struct EchoHeader
|
||||
{
|
||||
uint8_t type;
|
||||
uint8_t code;
|
||||
uint16_t chksum;
|
||||
uint16_t id;
|
||||
uint16_t seq;
|
||||
}; // size = 8
|
||||
|
||||
uint16_t icmpChecksum(const char *data, int length);
|
||||
uint16_t icmpChecksum(const char *data, int length);
|
||||
|
||||
int fd;
|
||||
int bufferSize;
|
||||
char *sendBuffer, *receiveBuffer;
|
||||
int fd;
|
||||
int bufferSize;
|
||||
std::vector<char> sendBuffer;
|
||||
std::vector<char> receiveBuffer;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,38 +1,39 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
|
||||
*
|
||||
* Copyright (C) 2009 Friedrich Schöller <hans@schoeller.se>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "exception.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
using namespace std;
|
||||
using std::string;
|
||||
|
||||
Exception::Exception(const char *msg)
|
||||
Exception::Exception(const string &msg)
|
||||
{
|
||||
this->msg = msg;
|
||||
this->msg = msg;
|
||||
}
|
||||
|
||||
Exception::Exception(const char *msg, bool appendSystemError)
|
||||
Exception::Exception(const string &msg, bool appendSystemError)
|
||||
{
|
||||
if (appendSystemError)
|
||||
this->msg = string(msg) + ": " + strerror(errno);
|
||||
else
|
||||
this->msg = msg;
|
||||
if (appendSystemError)
|
||||
this->msg = msg + ": " + strerror(errno);
|
||||
else
|
||||
this->msg = msg;
|
||||
}
|
@ -1,20 +1,20 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
|
||||
*
|
||||
* Copyright (C) 2009 Friedrich Schöller <hans@schoeller.se>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
@ -22,10 +22,10 @@
|
||||
class Exception
|
||||
{
|
||||
public:
|
||||
Exception(const char *msg);
|
||||
Exception(const char *msg, bool appendSystemError);
|
||||
|
||||
const char *errorMessage() const { return msg.c_str(); }
|
||||
Exception(const std::string &msg);
|
||||
Exception(const std::string &msg, bool appendSystemError);
|
||||
|
||||
const std::string &errorMessage() const { return msg; }
|
||||
protected:
|
||||
std::string msg;
|
||||
std::string msg;
|
||||
};
|
264
src/main.cpp
Normal file
264
src/main.cpp
Normal file
@ -0,0 +1,264 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <hans@schoeller.se>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "client.h"
|
||||
#include "server.h"
|
||||
#include "exception.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
#include <pwd.h>
|
||||
#include <netdb.h>
|
||||
// #include <uuid/uuid.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <signal.h>
|
||||
#include <memory>
|
||||
|
||||
#ifndef AI_V4MAPPED // Not supported on OpenBSD 6.0
|
||||
#define AI_V4MAPPED 0
|
||||
#endif
|
||||
|
||||
using std::string;
|
||||
|
||||
static Worker *worker = NULL;
|
||||
|
||||
static void sig_term_handler(int)
|
||||
{
|
||||
syslog(LOG_INFO, "SIGTERM received");
|
||||
if (worker)
|
||||
worker->stop();
|
||||
}
|
||||
|
||||
static void sig_int_handler(int)
|
||||
{
|
||||
syslog(LOG_INFO, "SIGINT received");
|
||||
if (worker)
|
||||
worker->stop();
|
||||
}
|
||||
|
||||
static void usage()
|
||||
{
|
||||
std::cerr <<
|
||||
"Hans - IP over ICMP version 1.0\n\n"
|
||||
"RUN AS CLIENT\n"
|
||||
" hans -c server [-fv] [-p passphrase] [-u user] [-d tun_device]\n"
|
||||
" [-m reference_mtu] [-w polls]\n\n"
|
||||
"RUN AS SERVER (linux only)\n"
|
||||
" hans -s network [-fvr] [-p passphrase] [-u user] [-d tun_device]\n"
|
||||
" [-m reference_mtu] [-a ip]\n\n"
|
||||
"ARGUMENTS\n"
|
||||
" -c server Run as client. Connect to given server address.\n"
|
||||
" -s network Run as server. Use given network address on virtual interfaces.\n"
|
||||
" -p passphrase Set passphrase.\n"
|
||||
" -u username Change user under which the program runs.\n"
|
||||
" -a ip Request assignment of given tunnel ip address from the server.\n"
|
||||
" -r Respond to ordinary pings in server mode.\n"
|
||||
" -d device Use given tun device.\n"
|
||||
" -m mtu Set maximum echo packet size. This should correspond to the MTU\n"
|
||||
" of the network between client and server, which is usually 1500\n"
|
||||
" over Ethernet. Has to be the same on client and server. Defaults\n"
|
||||
" to 1500.\n"
|
||||
" -w polls Number of echo requests the client sends in advance for the\n"
|
||||
" server to reply to. 0 disables polling, which is the best choice\n"
|
||||
" if the network allows unlimited echo replies. Defaults to 10.\n"
|
||||
" -i Change echo id on every echo request. May help with buggy\n"
|
||||
" routers. May impact performance with others.\n"
|
||||
" -q Change echo sequence number on every echo request. May help with\n"
|
||||
" buggy routers. May impact performance with others.\n"
|
||||
" -f Run in foreground.\n"
|
||||
" -v Print debug information.\n";
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
string serverName;
|
||||
string userName;
|
||||
string passphrase;
|
||||
string device;
|
||||
bool isServer = false;
|
||||
bool isClient = false;
|
||||
bool foreground = false;
|
||||
int mtu = 1500;
|
||||
int maxPolls = 10;
|
||||
uint32_t network = INADDR_NONE;
|
||||
uint32_t clientIp = INADDR_NONE;
|
||||
bool answerPing = false;
|
||||
uid_t uid = 0;
|
||||
gid_t gid = 0;
|
||||
bool changeEchoId = false;
|
||||
bool changeEchoSeq = false;
|
||||
bool verbose = false;
|
||||
|
||||
openlog(argv[0], LOG_PERROR, LOG_DAEMON);
|
||||
|
||||
int c;
|
||||
while ((c = getopt(argc, argv, "fru:d:p:s:c:m:w:qiva:")) != -1)
|
||||
{
|
||||
switch(c) {
|
||||
case 'f':
|
||||
foreground = true;
|
||||
break;
|
||||
case 'u':
|
||||
userName = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
device = optarg;
|
||||
break;
|
||||
case 'p':
|
||||
passphrase = optarg;
|
||||
memset(optarg, 0, strlen(optarg));
|
||||
break;
|
||||
case 'c':
|
||||
isClient = true;
|
||||
serverName = optarg;
|
||||
break;
|
||||
case 's':
|
||||
isServer = true;
|
||||
network = ntohl(inet_addr(optarg));
|
||||
if (network == INADDR_NONE)
|
||||
std::cerr << "invalid network\n";
|
||||
break;
|
||||
case 'm':
|
||||
mtu = atoi(optarg);
|
||||
break;
|
||||
case 'w':
|
||||
maxPolls = atoi(optarg);
|
||||
break;
|
||||
case 'r':
|
||||
answerPing = true;
|
||||
break;
|
||||
case 'q':
|
||||
changeEchoSeq = true;
|
||||
break;
|
||||
case 'i':
|
||||
changeEchoId = true;
|
||||
break;
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
case 'a':
|
||||
clientIp = ntohl(inet_addr(optarg));
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
mtu -= Echo::headerSize() + Worker::headerSize();
|
||||
|
||||
if (mtu < 68)
|
||||
{
|
||||
// RFC 791: Every internet module must be able to forward a datagram of
|
||||
// 68 octets without further fragmentation.
|
||||
std::cerr << "mtu too small\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ((isClient == isServer) ||
|
||||
(isServer && network == INADDR_NONE) ||
|
||||
(maxPolls < 0 || maxPolls > 255) ||
|
||||
(isServer && (changeEchoSeq || changeEchoId)))
|
||||
{
|
||||
usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!userName.empty())
|
||||
{
|
||||
#ifdef WIN32
|
||||
syslog(LOG_ERR, "dropping privileges is not supported on Windows");
|
||||
return 1;
|
||||
#endif
|
||||
passwd *pw = getpwnam(userName.data());
|
||||
|
||||
if (pw != NULL)
|
||||
{
|
||||
uid = pw->pw_uid;
|
||||
gid = pw->pw_gid;
|
||||
}
|
||||
else
|
||||
{
|
||||
syslog(LOG_ERR, "user not found");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!verbose)
|
||||
setlogmask(LOG_UPTO(LOG_INFO));
|
||||
|
||||
signal(SIGTERM, sig_term_handler);
|
||||
signal(SIGINT, sig_int_handler);
|
||||
|
||||
try
|
||||
{
|
||||
if (isServer)
|
||||
{
|
||||
worker = new Server(mtu, device.empty() ? NULL : &device, passphrase,
|
||||
network, answerPing, uid, gid, 5000);
|
||||
}
|
||||
else
|
||||
{
|
||||
struct addrinfo hints = {0};
|
||||
struct addrinfo *res = NULL;
|
||||
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG;
|
||||
|
||||
int err = getaddrinfo(serverName.data(), NULL, &hints, &res);
|
||||
if (err)
|
||||
{
|
||||
syslog(LOG_ERR, "getaddrinfo: %s", gai_strerror(err));
|
||||
return 1;
|
||||
}
|
||||
|
||||
sockaddr_in *sockaddr = reinterpret_cast<sockaddr_in *>(res->ai_addr);
|
||||
uint32_t serverIp = sockaddr->sin_addr.s_addr;
|
||||
|
||||
worker = new Client(mtu, device.empty() ? NULL : &device,
|
||||
ntohl(serverIp), maxPolls, passphrase, uid, gid,
|
||||
changeEchoId, changeEchoSeq, clientIp);
|
||||
|
||||
freeaddrinfo(res);
|
||||
}
|
||||
|
||||
if (!foreground)
|
||||
{
|
||||
syslog(LOG_INFO, "detaching from terminal");
|
||||
daemon(0, 0);
|
||||
}
|
||||
|
||||
worker->run();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
syslog(LOG_ERR, "%s", e.errorMessage().data());
|
||||
delete worker;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
373
src/server.cpp
Normal file
373
src/server.cpp
Normal file
@ -0,0 +1,373 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <hans@schoeller.se>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "server.h"
|
||||
#include "client.h"
|
||||
#include "config.h"
|
||||
#include "utility.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <syslog.h>
|
||||
#include <iostream>
|
||||
|
||||
using std::string;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
#define FIRST_ASSIGNED_IP_OFFSET 100
|
||||
|
||||
const Worker::TunnelHeader::Magic Server::magic("hans");
|
||||
|
||||
Server::Server(int tunnelMtu, const string *deviceName, const string &passphrase,
|
||||
uint32_t network, bool answerEcho, uid_t uid, gid_t gid, int pollTimeout)
|
||||
: Worker(tunnelMtu, deviceName, answerEcho, uid, gid), auth(passphrase)
|
||||
{
|
||||
this->network = network & 0xffffff00;
|
||||
this->pollTimeout = pollTimeout;
|
||||
this->latestAssignedIpOffset = FIRST_ASSIGNED_IP_OFFSET - 1;
|
||||
|
||||
tun.setIp(this->network + 1, this->network + 2);
|
||||
|
||||
dropPrivileges();
|
||||
}
|
||||
|
||||
Server::~Server()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void Server::handleUnknownClient(const TunnelHeader &header, int dataLength, uint32_t realIp, uint16_t echoId, uint16_t echoSeq)
|
||||
{
|
||||
ClientData client;
|
||||
client.realIp = realIp;
|
||||
client.maxPolls = 1;
|
||||
|
||||
pollReceived(&client, echoId, echoSeq);
|
||||
|
||||
if (header.type != TunnelHeader::TYPE_CONNECTION_REQUEST || dataLength != sizeof(ClientConnectData))
|
||||
{
|
||||
syslog(LOG_DEBUG, "invalid request (type %d) from %s", header.type,
|
||||
Utility::formatIp(realIp).c_str());
|
||||
sendReset(&client);
|
||||
return;
|
||||
}
|
||||
|
||||
ClientConnectData *connectData = (ClientConnectData *)echoReceivePayloadBuffer();
|
||||
|
||||
client.maxPolls = connectData->maxPolls;
|
||||
client.state = ClientData::STATE_NEW;
|
||||
client.tunnelIp = reserveTunnelIp(connectData->desiredIp);
|
||||
|
||||
syslog(LOG_DEBUG, "new client %s with tunnel address %s\n",
|
||||
Utility::formatIp(client.realIp).data(),
|
||||
Utility::formatIp(client.tunnelIp).data());
|
||||
|
||||
if (client.tunnelIp != 0)
|
||||
{
|
||||
client.challenge = auth.generateChallenge(CHALLENGE_SIZE);
|
||||
sendChallenge(&client);
|
||||
|
||||
// add client to list
|
||||
clientList.push_front(client);
|
||||
clientRealIpMap[realIp] = clientList.begin();
|
||||
clientTunnelIpMap[client.tunnelIp] = clientList.begin();
|
||||
}
|
||||
else
|
||||
{
|
||||
syslog(LOG_WARNING, "server full");
|
||||
sendEchoToClient(&client, TunnelHeader::TYPE_SERVER_FULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Server::sendChallenge(ClientData *client)
|
||||
{
|
||||
syslog(LOG_DEBUG, "sending authentication request to %s\n",
|
||||
Utility::formatIp(client->realIp).data());
|
||||
|
||||
memcpy(echoSendPayloadBuffer(), &client->challenge[0], client->challenge.size());
|
||||
sendEchoToClient(client, TunnelHeader::TYPE_CHALLENGE, client->challenge.size());
|
||||
|
||||
client->state = ClientData::STATE_CHALLENGE_SENT;
|
||||
}
|
||||
|
||||
void Server::removeClient(ClientData *client)
|
||||
{
|
||||
syslog(LOG_DEBUG, "removing client %s with tunnel ip %s\n",
|
||||
Utility::formatIp(client->realIp).data(),
|
||||
Utility::formatIp(client->tunnelIp).data());
|
||||
|
||||
releaseTunnelIp(client->tunnelIp);
|
||||
|
||||
ClientList::iterator it = clientRealIpMap[client->realIp];
|
||||
|
||||
clientRealIpMap.erase(client->realIp);
|
||||
clientTunnelIpMap.erase(client->tunnelIp);
|
||||
|
||||
clientList.erase(it);
|
||||
}
|
||||
|
||||
void Server::checkChallenge(ClientData *client, int length)
|
||||
{
|
||||
Auth::Response rightResponse = auth.getResponse(client->challenge);
|
||||
|
||||
if (length != sizeof(Auth::Response) || memcmp(&rightResponse, echoReceivePayloadBuffer(), length) != 0)
|
||||
{
|
||||
syslog(LOG_DEBUG, "wrong challenge response from %s\n",
|
||||
Utility::formatIp(client->realIp).data());
|
||||
|
||||
sendEchoToClient(client, TunnelHeader::TYPE_CHALLENGE_ERROR, 0);
|
||||
|
||||
removeClient(client);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t *ip = (uint32_t *)echoSendPayloadBuffer();
|
||||
*ip = htonl(client->tunnelIp);
|
||||
|
||||
sendEchoToClient(client, TunnelHeader::TYPE_CONNECTION_ACCEPT, sizeof(uint32_t));
|
||||
|
||||
client->state = ClientData::STATE_ESTABLISHED;
|
||||
|
||||
syslog(LOG_INFO, "connection established to %s",
|
||||
Utility::formatIp(client->realIp).data());
|
||||
}
|
||||
|
||||
void Server::sendReset(ClientData *client)
|
||||
{
|
||||
syslog(LOG_DEBUG, "sending reset to %s",
|
||||
Utility::formatIp(client->realIp).data());
|
||||
sendEchoToClient(client, TunnelHeader::TYPE_RESET_CONNECTION, 0);
|
||||
}
|
||||
|
||||
bool Server::handleEchoData(const TunnelHeader &header, int dataLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq)
|
||||
{
|
||||
if (reply)
|
||||
return false;
|
||||
|
||||
if (header.magic != Client::magic)
|
||||
return false;
|
||||
|
||||
ClientData *client = getClientByRealIp(realIp);
|
||||
if (client == NULL)
|
||||
{
|
||||
handleUnknownClient(header, dataLength, realIp, id, seq);
|
||||
return true;
|
||||
}
|
||||
|
||||
pollReceived(client, id, seq);
|
||||
|
||||
switch (header.type)
|
||||
{
|
||||
case TunnelHeader::TYPE_CONNECTION_REQUEST:
|
||||
if (client->state == ClientData::STATE_CHALLENGE_SENT)
|
||||
{
|
||||
sendChallenge(client);
|
||||
return true;
|
||||
}
|
||||
|
||||
while (client->pollIds.size() > 1)
|
||||
client->pollIds.pop();
|
||||
|
||||
syslog(LOG_DEBUG, "reconnecting %s", Utility::formatIp(realIp).data());
|
||||
sendReset(client);
|
||||
removeClient(client);
|
||||
return true;
|
||||
case TunnelHeader::TYPE_CHALLENGE_RESPONSE:
|
||||
if (client->state == ClientData::STATE_CHALLENGE_SENT)
|
||||
{
|
||||
checkChallenge(client, dataLength);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case TunnelHeader::TYPE_DATA:
|
||||
if (client->state == ClientData::STATE_ESTABLISHED)
|
||||
{
|
||||
if (dataLength == 0)
|
||||
{
|
||||
syslog(LOG_WARNING, "received empty data packet");
|
||||
return true;
|
||||
}
|
||||
|
||||
sendToTun(dataLength);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case TunnelHeader::TYPE_POLL:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
syslog(LOG_DEBUG, "invalid packet from: %s, type: %d, state: %d",
|
||||
Utility::formatIp(realIp).data(), header.type, client->state);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Server::ClientData *Server::getClientByTunnelIp(uint32_t ip)
|
||||
{
|
||||
ClientIpMap::iterator it = clientTunnelIpMap.find(ip);
|
||||
if (it == clientTunnelIpMap.end())
|
||||
return NULL;
|
||||
|
||||
return &*it->second;
|
||||
}
|
||||
|
||||
Server::ClientData *Server::getClientByRealIp(uint32_t ip)
|
||||
{
|
||||
ClientIpMap::iterator it = clientRealIpMap.find(ip);
|
||||
if (it == clientRealIpMap.end())
|
||||
return NULL;
|
||||
|
||||
return &*it->second;
|
||||
}
|
||||
|
||||
void Server::handleTunData(int dataLength, uint32_t, uint32_t destIp)
|
||||
{
|
||||
if (destIp == network + 255) // ignore broadcasts
|
||||
return;
|
||||
|
||||
ClientData *client = getClientByTunnelIp(destIp);
|
||||
|
||||
if (client == NULL)
|
||||
{
|
||||
syslog(LOG_DEBUG, "data received for unknown client %s\n",
|
||||
Utility::formatIp(destIp).data());
|
||||
return;
|
||||
}
|
||||
|
||||
sendEchoToClient(client, TunnelHeader::TYPE_DATA, dataLength);
|
||||
}
|
||||
|
||||
void Server::pollReceived(ClientData *client, uint16_t echoId, uint16_t echoSeq)
|
||||
{
|
||||
unsigned int maxSavedPolls = client->maxPolls != 0 ? client->maxPolls : 1;
|
||||
|
||||
client->pollIds.push(ClientData::EchoId(echoId, echoSeq));
|
||||
if (client->pollIds.size() > maxSavedPolls)
|
||||
client->pollIds.pop();
|
||||
DEBUG_ONLY(cout << "poll -> " << client->pollIds.size() << endl);
|
||||
|
||||
if (client->pendingPackets.size() > 0)
|
||||
{
|
||||
Packet &packet = client->pendingPackets.front();
|
||||
memcpy(echoSendPayloadBuffer(), &packet.data[0], packet.data.size());
|
||||
client->pendingPackets.pop();
|
||||
|
||||
DEBUG_ONLY(cout << "pending packet: " << packet.data.size() << " bytes\n");
|
||||
sendEchoToClient(client, packet.type, packet.data.size());
|
||||
}
|
||||
|
||||
client->lastActivity = now;
|
||||
}
|
||||
|
||||
void Server::sendEchoToClient(ClientData *client, TunnelHeader::Type type, int dataLength)
|
||||
{
|
||||
if (client->maxPolls == 0)
|
||||
{
|
||||
sendEcho(magic, type, dataLength, client->realIp, true, client->pollIds.front().id, client->pollIds.front().seq);
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->pollIds.size() != 0)
|
||||
{
|
||||
ClientData::EchoId echoId = client->pollIds.front();
|
||||
client->pollIds.pop();
|
||||
|
||||
DEBUG_ONLY(cout << "sending -> " << client->pollIds.size() << endl);
|
||||
sendEcho(magic, type, dataLength, client->realIp, true, echoId.id, echoId.seq);
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->pendingPackets.size() == MAX_BUFFERED_PACKETS)
|
||||
{
|
||||
client->pendingPackets.pop();
|
||||
syslog(LOG_WARNING, "packet to %s dropped",
|
||||
Utility::formatIp(client->tunnelIp).data());
|
||||
}
|
||||
|
||||
DEBUG_ONLY(cout << "packet queued: " << dataLength << " bytes\n");
|
||||
|
||||
client->pendingPackets.push(Packet());
|
||||
Packet &packet = client->pendingPackets.back();
|
||||
packet.type = type;
|
||||
packet.data.resize(dataLength);
|
||||
memcpy(&packet.data[0], echoReceivePayloadBuffer(), dataLength);
|
||||
}
|
||||
|
||||
void Server::releaseTunnelIp(uint32_t tunnelIp)
|
||||
{
|
||||
usedIps.erase(tunnelIp);
|
||||
}
|
||||
|
||||
void Server::handleTimeout()
|
||||
{
|
||||
ClientList::iterator it = clientList.begin();
|
||||
while (it != clientList.end())
|
||||
{
|
||||
ClientData &client = *it++;
|
||||
|
||||
if (client.lastActivity + KEEP_ALIVE_INTERVAL * 2 < now)
|
||||
{
|
||||
syslog(LOG_DEBUG, "client %s timed out\n",
|
||||
Utility::formatIp(client.realIp).data());
|
||||
removeClient(&client);
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(KEEP_ALIVE_INTERVAL);
|
||||
}
|
||||
|
||||
uint32_t Server::reserveTunnelIp(uint32_t desiredIp)
|
||||
{
|
||||
if (desiredIp > network + 1 && desiredIp < network + 255 && !usedIps.count(desiredIp))
|
||||
{
|
||||
usedIps.insert(desiredIp);
|
||||
return desiredIp;
|
||||
}
|
||||
|
||||
bool ipAvailable = false;
|
||||
|
||||
for (int i = 0; i < 255 - FIRST_ASSIGNED_IP_OFFSET; i++)
|
||||
{
|
||||
latestAssignedIpOffset++;
|
||||
if (latestAssignedIpOffset == 255)
|
||||
latestAssignedIpOffset = FIRST_ASSIGNED_IP_OFFSET;
|
||||
|
||||
if (!usedIps.count(network + latestAssignedIpOffset))
|
||||
{
|
||||
ipAvailable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ipAvailable)
|
||||
return 0;
|
||||
|
||||
usedIps.insert(network + latestAssignedIpOffset);
|
||||
return network + latestAssignedIpOffset;
|
||||
}
|
||||
|
||||
void Server::run()
|
||||
{
|
||||
setTimeout(KEEP_ALIVE_INTERVAL);
|
||||
|
||||
Worker::run();
|
||||
}
|
127
src/server.h
Normal file
127
src/server.h
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <hans@schoeller.se>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SERVER_H
|
||||
#define SERVER_H
|
||||
|
||||
#include "worker.h"
|
||||
#include "auth.h"
|
||||
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
class Server : public Worker
|
||||
{
|
||||
public:
|
||||
Server(int tunnelMtu, const std::string *deviceName, const std::string &passphrase,
|
||||
uint32_t network, bool answerEcho, uid_t uid, gid_t gid, int pollTimeout);
|
||||
virtual ~Server();
|
||||
|
||||
struct ClientConnectData
|
||||
{
|
||||
uint8_t maxPolls;
|
||||
uint32_t desiredIp;
|
||||
};
|
||||
|
||||
static const TunnelHeader::Magic magic;
|
||||
|
||||
protected:
|
||||
struct Packet
|
||||
{
|
||||
TunnelHeader::Type type;
|
||||
std::vector<char> data;
|
||||
};
|
||||
|
||||
struct ClientData
|
||||
{
|
||||
enum State
|
||||
{
|
||||
STATE_NEW,
|
||||
STATE_CHALLENGE_SENT,
|
||||
STATE_ESTABLISHED
|
||||
};
|
||||
|
||||
struct EchoId
|
||||
{
|
||||
EchoId(uint16_t id, uint16_t seq) { this->id = id; this->seq = seq; }
|
||||
|
||||
uint16_t id;
|
||||
uint16_t seq;
|
||||
};
|
||||
|
||||
uint32_t realIp;
|
||||
uint32_t tunnelIp;
|
||||
|
||||
std::queue<Packet> pendingPackets;
|
||||
|
||||
int maxPolls;
|
||||
std::queue<EchoId> pollIds;
|
||||
Time lastActivity;
|
||||
|
||||
State state;
|
||||
|
||||
Auth::Challenge challenge;
|
||||
};
|
||||
|
||||
typedef std::list<ClientData> ClientList;
|
||||
typedef std::map<uint32_t, ClientList::iterator> ClientIpMap;
|
||||
|
||||
virtual bool handleEchoData(const TunnelHeader &header, int dataLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq);
|
||||
virtual void handleTunData(int dataLength, uint32_t sourceIp, uint32_t destIp);
|
||||
virtual void handleTimeout();
|
||||
|
||||
virtual void run();
|
||||
|
||||
void serveTun(ClientData *client);
|
||||
|
||||
void handleUnknownClient(const TunnelHeader &header, int dataLength, uint32_t realIp, uint16_t echoId, uint16_t echoSeq);
|
||||
void removeClient(ClientData *client);
|
||||
|
||||
void sendChallenge(ClientData *client);
|
||||
void checkChallenge(ClientData *client, int dataLength);
|
||||
void sendReset(ClientData *client);
|
||||
|
||||
void sendEchoToClient(ClientData *client, TunnelHeader::Type type, int dataLength);
|
||||
|
||||
void pollReceived(ClientData *client, uint16_t echoId, uint16_t echoSeq);
|
||||
|
||||
uint32_t reserveTunnelIp(uint32_t desiredIp);
|
||||
void releaseTunnelIp(uint32_t tunnelIp);
|
||||
|
||||
ClientData *getClientByTunnelIp(uint32_t ip);
|
||||
ClientData *getClientByRealIp(uint32_t ip);
|
||||
|
||||
Auth auth;
|
||||
|
||||
uint32_t network;
|
||||
std::set<uint32_t> usedIps;
|
||||
uint32_t latestAssignedIpOffset;
|
||||
|
||||
Time pollTimeout;
|
||||
|
||||
ClientList clientList;
|
||||
ClientIpMap clientRealIpMap;
|
||||
ClientIpMap clientTunnelIpMap;
|
||||
};
|
||||
|
||||
#endif
|
85
src/time.cpp
Normal file
85
src/time.cpp
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <hans@schoeller.se>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "time.h"
|
||||
|
||||
const Time Time::ZERO = Time(0);
|
||||
|
||||
Time::Time(int ms)
|
||||
{
|
||||
tv.tv_sec = ms / 1000;
|
||||
tv.tv_usec = (ms % 1000) * 1000;
|
||||
}
|
||||
|
||||
Time Time::operator-(const Time &other) const
|
||||
{
|
||||
Time result;
|
||||
result.tv.tv_sec = tv.tv_sec - other.tv.tv_sec;
|
||||
result.tv.tv_usec = tv.tv_usec - other.tv.tv_usec;
|
||||
|
||||
if (result.tv.tv_usec < 0)
|
||||
{
|
||||
result.tv.tv_usec += 1000000;
|
||||
result.tv.tv_sec -= 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Time Time::operator+(const Time &other) const
|
||||
{
|
||||
Time result;
|
||||
result.tv.tv_sec = tv.tv_sec + other.tv.tv_sec;
|
||||
result.tv.tv_usec = tv.tv_usec + other.tv.tv_usec;
|
||||
|
||||
if (result.tv.tv_usec >= 1000000)
|
||||
{
|
||||
result.tv.tv_usec -= 1000000;
|
||||
result.tv.tv_sec += 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Time::operator==(const Time &other) const
|
||||
{
|
||||
return tv.tv_sec != other.tv.tv_sec ? false : tv.tv_usec == other.tv.tv_usec;
|
||||
}
|
||||
|
||||
bool Time::operator!=(const Time &other) const
|
||||
{
|
||||
return tv.tv_sec != other.tv.tv_sec ? true : tv.tv_usec != other.tv.tv_usec;
|
||||
}
|
||||
|
||||
bool Time::operator<(const Time &other) const
|
||||
{
|
||||
return tv.tv_sec != other.tv.tv_sec ? tv.tv_sec < other.tv.tv_sec : tv.tv_usec < other.tv.tv_usec;
|
||||
}
|
||||
|
||||
bool Time::operator>(const Time &other) const
|
||||
{
|
||||
return tv.tv_sec != other.tv.tv_sec ? tv.tv_sec > other.tv.tv_sec : tv.tv_usec > other.tv.tv_usec;
|
||||
}
|
||||
|
||||
Time Time::now()
|
||||
{
|
||||
Time result;
|
||||
gettimeofday(&result.tv, 0);
|
||||
return result;
|
||||
}
|
@ -1,20 +1,20 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
|
||||
*
|
||||
* Copyright (C) 2009 Friedrich Schöller <hans@schoeller.se>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TIME_H
|
||||
@ -25,24 +25,24 @@
|
||||
class Time
|
||||
{
|
||||
public:
|
||||
Time() { tv.tv_sec = 0; tv.tv_usec = 0; };
|
||||
Time(int ms);
|
||||
Time() { tv.tv_sec = 0; tv.tv_usec = 0; };
|
||||
Time(int ms);
|
||||
|
||||
timeval &getTimeval() { return tv; }
|
||||
timeval &getTimeval() { return tv; }
|
||||
|
||||
Time operator+(const Time &other) const;
|
||||
Time operator-(const Time &other) const;
|
||||
Time operator+(const Time &other) const;
|
||||
Time operator-(const Time &other) const;
|
||||
|
||||
bool operator!=(const Time &other) const;
|
||||
bool operator==(const Time &other) const;
|
||||
bool operator<(const Time &other) const;
|
||||
bool operator>(const Time &other) const;
|
||||
bool operator!=(const Time &other) const;
|
||||
bool operator==(const Time &other) const;
|
||||
bool operator<(const Time &other) const;
|
||||
bool operator>(const Time &other) const;
|
||||
|
||||
static Time now();
|
||||
static Time now();
|
||||
|
||||
static const Time ZERO;
|
||||
static const Time ZERO;
|
||||
protected:
|
||||
timeval tv;
|
||||
timeval tv;
|
||||
};
|
||||
|
||||
#endif
|
145
src/tun.cpp
Normal file
145
src/tun.cpp
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <hans@schoeller.se>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tun.h"
|
||||
#include "exception.h"
|
||||
#include "utility.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/types.h>
|
||||
#include <netinet/in_systm.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <syslog.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <sstream>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <w32api/windows.h>
|
||||
#endif
|
||||
|
||||
typedef ip IpHeader;
|
||||
|
||||
using std::string;
|
||||
|
||||
#ifdef WIN32
|
||||
static void winsystem(char *cmd)
|
||||
{
|
||||
STARTUPINFO info = { sizeof(info) };
|
||||
PROCESS_INFORMATION processInfo;
|
||||
if (CreateProcess(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &info, &processInfo))
|
||||
{
|
||||
WaitForSingleObject(processInfo.hProcess, INFINITE);
|
||||
CloseHandle(processInfo.hProcess);
|
||||
CloseHandle(processInfo.hThread);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#define ip_path "/sbin/ip "
|
||||
|
||||
Tun::Tun(const string *device, int mtu)
|
||||
{
|
||||
this->mtu = mtu;
|
||||
|
||||
if (device)
|
||||
this->device = *device;
|
||||
|
||||
this->device.resize(VTUN_DEV_LEN);
|
||||
fd = tun_open(&this->device[0]);
|
||||
this->device.resize(strlen(this->device.data()));
|
||||
|
||||
if (fd == -1)
|
||||
throw Exception(string("could not create tunnel device: ") + tun_last_error());
|
||||
|
||||
syslog(LOG_INFO, "opened tunnel device: %s", this->device.data());
|
||||
|
||||
std::stringstream cmdline;
|
||||
|
||||
#ifdef WIN32
|
||||
cmdline << "netsh interface ipv4 set subinterface \"" << this->device
|
||||
<< "\" mtu=" << mtu;
|
||||
winsystem(cmdline.str().data());
|
||||
#else
|
||||
// ip link set dev <device> mtu <mtu>
|
||||
cmdline << ip_path << "link set dev " << this->device << " mtu " << mtu;
|
||||
// cmdline << "/sbin/ifconfig " << this->device << " mtu " << mtu;
|
||||
if (system(cmdline.str().data()) != 0)
|
||||
syslog(LOG_ERR, "could not set tun device mtu");
|
||||
#endif
|
||||
}
|
||||
|
||||
Tun::~Tun()
|
||||
{
|
||||
tun_close(fd, &device[0]);
|
||||
}
|
||||
|
||||
void Tun::setIp(uint32_t ip, uint32_t destIp)
|
||||
{
|
||||
std::stringstream cmdline;
|
||||
string ips = Utility::formatIp(ip);
|
||||
string destIps = Utility::formatIp(destIp);
|
||||
|
||||
#ifdef WIN32
|
||||
cmdline << "netsh interface ip set address name=\"" << device << "\" "
|
||||
<< "static " << ips << " 255.255.255.0";
|
||||
winsystem(cmdline.str().data());
|
||||
|
||||
if (!tun_set_ip(fd, ip, ip & 0xffffff00, 0xffffff00))
|
||||
syslog(LOG_ERR, "could not set tun device driver ip address: %s", tun_last_error());
|
||||
#elif LINUX
|
||||
// ip addr add <ip>/24 dev <device>
|
||||
cmdline << ip_path << "addr add " << ips << "/24 dev " << device;
|
||||
// cmdline << "/sbin/ifconfig " << device << " " << ips << " netmask 255.255.255.0";
|
||||
if (system(cmdline.str().data()) != 0)
|
||||
syslog(LOG_ERR, "could not set tun device ip address");
|
||||
#else
|
||||
cmdline << "/sbin/ifconfig " << device << " " << ips << " " << destIps
|
||||
<< " netmask 255.255.255.255";
|
||||
if (system(cmdline.str().data()) != 0)
|
||||
syslog(LOG_ERR, "could not set tun device ip address");
|
||||
#endif
|
||||
}
|
||||
|
||||
void Tun::write(const char *buffer, int length)
|
||||
{
|
||||
if (tun_write(fd, (char *)buffer, length) == -1)
|
||||
syslog(LOG_ERR, "error writing %d bytes to tun: %s", length, tun_last_error());
|
||||
}
|
||||
|
||||
int Tun::read(char *buffer)
|
||||
{
|
||||
int length = tun_read(fd, buffer, mtu);
|
||||
if (length == -1)
|
||||
syslog(LOG_ERR, "error reading from tun: %s", tun_last_error());
|
||||
return length;
|
||||
}
|
||||
|
||||
int Tun::read(char *buffer, uint32_t &sourceIp, uint32_t &destIp)
|
||||
{
|
||||
int length = read(buffer);
|
||||
|
||||
IpHeader *header = (IpHeader *)buffer;
|
||||
sourceIp = ntohl(header->ip_src.s_addr);
|
||||
destIp = ntohl(header->ip_dst.s_addr);
|
||||
|
||||
return length;
|
||||
}
|
@ -1,20 +1,20 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
|
||||
*
|
||||
* Copyright (C) 2009 Friedrich Schöller <hans@schoeller.se>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TUN_H
|
||||
@ -28,22 +28,22 @@
|
||||
class Tun
|
||||
{
|
||||
public:
|
||||
Tun(const char *device, int mtu);
|
||||
~Tun();
|
||||
Tun(const std::string *device, int mtu);
|
||||
~Tun();
|
||||
|
||||
int getFd() { return fd; }
|
||||
int getFd() { return fd; }
|
||||
|
||||
int read(char *buffer);
|
||||
int read(char *buffer, uint32_t &sourceIp, uint32_t &destIp);
|
||||
int read(char *buffer);
|
||||
int read(char *buffer, uint32_t &sourceIp, uint32_t &destIp);
|
||||
|
||||
void write(const char *buffer, int length);
|
||||
void write(const char *buffer, int length);
|
||||
|
||||
void setIp(uint32_t ip);
|
||||
void setIp(uint32_t ip, uint32_t destIp);
|
||||
protected:
|
||||
char device[VTUN_DEV_LEN];
|
||||
std::string device;
|
||||
|
||||
int mtu;
|
||||
int fd;
|
||||
int mtu;
|
||||
int fd;
|
||||
};
|
||||
|
||||
#endif
|
50
src/tun_dev.h
Normal file
50
src/tun_dev.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2013 Friedrich Schöller <hans@schoeller.se>
|
||||
* 1998-2000 Maxim Krasnyansky <max_mk@yahoo.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define VTUN_DEV_LEN 100
|
||||
|
||||
#else
|
||||
|
||||
#define VTUN_DEV_LEN 20
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int tun_open(char *dev);
|
||||
int tun_close(int fd, char *dev);
|
||||
int tun_write(int fd, char *buf, int len);
|
||||
int tun_read(int fd, char *buf, int len);
|
||||
const char *tun_last_error();
|
||||
|
||||
#ifdef WIN32
|
||||
bool tun_set_ip(int fd, uint32_t local, uint32_t network, uint32_t netmask);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
318
src/tun_dev_cygwin.c
Normal file
318
src/tun_dev_cygwin.c
Normal file
@ -0,0 +1,318 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2013 Friedrich Schöller <hans@schoeller.se>
|
||||
* 2002-2005 Ivo Timmermans,
|
||||
* 2002-2011 Guus Sliepen <guus@tinc-vpn.org>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tun_dev.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/socket.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <w32api/windows.h>
|
||||
#include <w32api/winioctl.h>
|
||||
|
||||
#define TAP_WIN_CONTROL_CODE(request,method) CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
|
||||
#define TAP_WIN_IOCTL_GET_MAC TAP_WIN_CONTROL_CODE (1, METHOD_BUFFERED)
|
||||
#define TAP_WIN_IOCTL_GET_VERSION TAP_WIN_CONTROL_CODE (2, METHOD_BUFFERED)
|
||||
#define TAP_WIN_IOCTL_GET_MTU TAP_WIN_CONTROL_CODE (3, METHOD_BUFFERED)
|
||||
#define TAP_WIN_IOCTL_GET_INFO TAP_WIN_CONTROL_CODE (4, METHOD_BUFFERED)
|
||||
#define TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT TAP_WIN_CONTROL_CODE (5, METHOD_BUFFERED)
|
||||
#define TAP_WIN_IOCTL_SET_MEDIA_STATUS TAP_WIN_CONTROL_CODE (6, METHOD_BUFFERED)
|
||||
#define TAP_WIN_IOCTL_CONFIG_DHCP_MASQ TAP_WIN_CONTROL_CODE (7, METHOD_BUFFERED)
|
||||
#define TAP_WIN_IOCTL_GET_LOG_LINE TAP_WIN_CONTROL_CODE (8, METHOD_BUFFERED)
|
||||
#define TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT TAP_WIN_CONTROL_CODE (9, METHOD_BUFFERED)
|
||||
#define TAP_WIN_IOCTL_CONFIG_TUN TAP_WIN_CONTROL_CODE (10, METHOD_BUFFERED)
|
||||
#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
|
||||
#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
|
||||
#define USERMODEDEVICEDIR "\\\\.\\Global\\"
|
||||
#define SYSDEVICEDIR "\\Device\\"
|
||||
#define USERDEVICEDIR "\\DosDevices\\Global\\"
|
||||
#define TAP_WIN_SUFFIX ".tap"
|
||||
|
||||
struct adapter_info
|
||||
{
|
||||
int reader_read_fd, reader_write_fd;
|
||||
HANDLE reader_thread;
|
||||
HANDLE adapter_handle;
|
||||
};
|
||||
|
||||
#define ERROR_BUFFER_SIZE 1024
|
||||
|
||||
char error_buffer[ERROR_BUFFER_SIZE];
|
||||
|
||||
static void error(char *format, ...)
|
||||
{
|
||||
va_list vl;
|
||||
va_start(vl, format);
|
||||
vsnprintf(error_buffer, ERROR_BUFFER_SIZE, format, vl);
|
||||
va_end(vl);
|
||||
}
|
||||
|
||||
static void noerror(void)
|
||||
{
|
||||
*error_buffer = 0;
|
||||
}
|
||||
|
||||
static const char *winerror(int err)
|
||||
{
|
||||
static char buf[1024], *ptr;
|
||||
|
||||
ptr = buf + sprintf(buf, "(%d) ", err);
|
||||
|
||||
if (!FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), ptr, sizeof(buf) - (ptr - buf), NULL)) {
|
||||
strcpy(ptr, "(unable to format errormessage)");
|
||||
};
|
||||
|
||||
if((ptr = strchr(buf, '\r')))
|
||||
*ptr = '\0';
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static struct adapter_info *get_adapter_info_from_fd(int fd)
|
||||
{
|
||||
static struct adapter_info single_adapter_info = {
|
||||
.reader_read_fd = -1,
|
||||
.reader_write_fd = -1,
|
||||
.reader_thread = INVALID_HANDLE_VALUE,
|
||||
.adapter_handle = INVALID_HANDLE_VALUE
|
||||
};
|
||||
return &single_adapter_info;
|
||||
}
|
||||
|
||||
static HANDLE open_tap_adapter(char *name)
|
||||
{
|
||||
HKEY connections_key, adapter_key;
|
||||
int adapter_index, error_code;
|
||||
char regpath[1024];
|
||||
char adapter_id[VTUN_DEV_LEN];
|
||||
char adapter_path[1024];
|
||||
char adapter_name[1024];
|
||||
HANDLE adapter_handle = INVALID_HANDLE_VALUE;
|
||||
DWORD len;
|
||||
|
||||
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, NETWORK_CONNECTIONS_KEY, 0, KEY_READ, &connections_key) != ERROR_SUCCESS)
|
||||
{
|
||||
error("opening registry: %s", winerror(GetLastError()));
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
for (adapter_index = 0; ; adapter_index++)
|
||||
{
|
||||
len = sizeof(adapter_id);
|
||||
if (RegEnumKeyEx(connections_key, adapter_index, adapter_id, &len, 0, 0, 0, NULL) != ERROR_SUCCESS)
|
||||
break;
|
||||
|
||||
snprintf(regpath, sizeof(regpath), "%s\\%s\\Connection", NETWORK_CONNECTIONS_KEY, adapter_id);
|
||||
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_READ, &adapter_key) != ERROR_SUCCESS)
|
||||
continue;
|
||||
len = sizeof(adapter_name);
|
||||
if (RegQueryValueEx(adapter_key, "Name", 0, 0, adapter_name, &len) != ERROR_SUCCESS)
|
||||
{
|
||||
RegCloseKey(adapter_key);
|
||||
continue;
|
||||
}
|
||||
RegCloseKey(adapter_key);
|
||||
|
||||
if (name && name[0] && strcmp(name, adapter_name) && strcmp(name, adapter_id))
|
||||
continue;
|
||||
|
||||
snprintf(adapter_path, sizeof(adapter_path), USERMODEDEVICEDIR "%s" TAP_WIN_SUFFIX, adapter_id);
|
||||
|
||||
adapter_handle = CreateFile(adapter_path, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, 0);
|
||||
if (adapter_handle != INVALID_HANDLE_VALUE)
|
||||
break;
|
||||
}
|
||||
|
||||
RegCloseKey(connections_key);
|
||||
|
||||
if (adapter_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
error("could not open tap adapter");
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
strncpy(name, adapter_name, VTUN_DEV_LEN);
|
||||
|
||||
noerror();
|
||||
return adapter_handle;
|
||||
}
|
||||
|
||||
static __stdcall DWORD reader_thread(LPVOID ptr)
|
||||
{
|
||||
struct adapter_info *adapter_info = ptr;
|
||||
char buf[0xffff]; // maximum IPv4 packet size
|
||||
OVERLAPPED overlapped;
|
||||
DWORD len;
|
||||
int wait_result;
|
||||
|
||||
memset(&overlapped, 0, sizeof(overlapped));
|
||||
overlapped.hEvent = CreateEvent(NULL, true, false, NULL);
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!ReadFile(adapter_info->adapter_handle, buf, sizeof(buf), &len, &overlapped))
|
||||
{
|
||||
if (GetLastError() != ERROR_IO_PENDING)
|
||||
{
|
||||
syslog(LOG_ERR, "error reading from tap adapter: %s", winerror(GetLastError()));
|
||||
return 1;
|
||||
}
|
||||
|
||||
wait_result = WaitForSingleObjectEx(overlapped.hEvent, INFINITE, false);
|
||||
|
||||
if (wait_result != WAIT_OBJECT_0)
|
||||
{
|
||||
syslog(LOG_ERR, "error waiting for tap adapter: %s", winerror(GetLastError()));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!GetOverlappedResult(adapter_info->adapter_handle, &overlapped, &len, true))
|
||||
{
|
||||
syslog(LOG_ERR, "error getting tap adapter reading result: %s", winerror(GetLastError()));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
write(adapter_info->reader_write_fd, buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
int tun_open(char *dev)
|
||||
{
|
||||
struct adapter_info *adapter_info;
|
||||
int socket_pair[2];
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_DGRAM, PF_UNIX, socket_pair))
|
||||
{
|
||||
error("creating socket pair: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
adapter_info = get_adapter_info_from_fd(socket_pair[0]);
|
||||
adapter_info->reader_read_fd = socket_pair[0];
|
||||
adapter_info->reader_write_fd = socket_pair[1];
|
||||
|
||||
adapter_info->adapter_handle = open_tap_adapter(dev);
|
||||
if (adapter_info->adapter_handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
tun_close(adapter_info->reader_read_fd, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
adapter_info->reader_thread = CreateThread(NULL, 0, reader_thread, adapter_info, 0, NULL);
|
||||
if (adapter_info->reader_thread == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
error("reader thread creation: %s", winerror(GetLastError()));
|
||||
tun_close(adapter_info->reader_read_fd, NULL);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return adapter_info->reader_read_fd;
|
||||
}
|
||||
|
||||
int tun_close(int fd, char *dev)
|
||||
{
|
||||
struct adapter_info *adapter_info = get_adapter_info_from_fd(fd);
|
||||
|
||||
if (adapter_info->reader_thread != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
TerminateThread(adapter_info->reader_thread, 0);
|
||||
adapter_info->reader_thread = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
close(adapter_info->reader_read_fd);
|
||||
adapter_info->reader_read_fd = -1;
|
||||
|
||||
close(adapter_info->reader_write_fd);
|
||||
adapter_info->reader_write_fd = -1;
|
||||
|
||||
if (adapter_info->adapter_handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
CloseHandle(adapter_info->adapter_handle);
|
||||
adapter_info->adapter_handle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tun_write(int fd, char *buf, int len)
|
||||
{
|
||||
struct adapter_info *adapter_info = get_adapter_info_from_fd(fd);
|
||||
OVERLAPPED overlapped;
|
||||
DWORD written;
|
||||
|
||||
memset(&overlapped, 0, sizeof(overlapped));
|
||||
|
||||
if (!WriteFile(adapter_info->adapter_handle, buf, len, &written, &overlapped))
|
||||
{
|
||||
error("tap write: %s", winerror(GetLastError()));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
int tun_read(int fd, char *buf, int len)
|
||||
{
|
||||
len = read(fd, buf, len);
|
||||
if (len == -1)
|
||||
error("reader read: %s", strerror(errno));
|
||||
return len;
|
||||
}
|
||||
|
||||
const char *tun_last_error()
|
||||
{
|
||||
return error_buffer;
|
||||
}
|
||||
|
||||
bool tun_set_ip(int fd, uint32_t local, uint32_t network, uint32_t netmask)
|
||||
{
|
||||
struct adapter_info *adapter_info = get_adapter_info_from_fd(fd);
|
||||
uint32_t addresses[3];
|
||||
DWORD status;
|
||||
DWORD len;
|
||||
|
||||
addresses[0] = htonl(local);
|
||||
addresses[1] = htonl(network);
|
||||
addresses[2] = htonl(netmask);
|
||||
|
||||
if (!DeviceIoControl(adapter_info->adapter_handle, TAP_WIN_IOCTL_CONFIG_TUN,
|
||||
&addresses, sizeof(addresses), &addresses, sizeof(addresses), &len, NULL))
|
||||
{
|
||||
error("configuring tap addresses: %s", winerror(GetLastError()));
|
||||
return false;
|
||||
}
|
||||
|
||||
status = true;
|
||||
if (!DeviceIoControl(adapter_info->adapter_handle, TAP_WIN_IOCTL_SET_MEDIA_STATUS,
|
||||
&status, sizeof(status), &status, sizeof(status), &len, NULL))
|
||||
{
|
||||
error("enabling tap device: %s", winerror(GetLastError()));
|
||||
return false;
|
||||
}
|
||||
|
||||
noerror();
|
||||
return true;
|
||||
}
|
47
src/tun_dev_darwin_emu.c
Normal file
47
src/tun_dev_darwin_emu.c
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <hans@schoeller.se>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tunemu.h"
|
||||
|
||||
#include <syslog.h>
|
||||
|
||||
int tun_open(char *dev)
|
||||
{
|
||||
return tunemu_open(dev);
|
||||
}
|
||||
|
||||
int tun_close(int fd, char *dev)
|
||||
{
|
||||
return tunemu_close(fd);
|
||||
}
|
||||
|
||||
int tun_write(int fd, char *buf, int len)
|
||||
{
|
||||
return tunemu_write(fd, buf, len);
|
||||
}
|
||||
|
||||
int tun_read(int fd, char *buf, int len)
|
||||
{
|
||||
return tunemu_read(fd, buf, len);
|
||||
}
|
||||
|
||||
const char *tun_last_error()
|
||||
{
|
||||
return tunemu_error;
|
||||
}
|
@ -28,6 +28,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/if_tun.h>
|
||||
@ -60,9 +61,9 @@ int tun_open(char *dev)
|
||||
if( fd > -1 ){
|
||||
i=0;
|
||||
/* Disable extended modes */
|
||||
ioctl(fd, TUNSLMODE, &i);
|
||||
ioctl(fd, TUNSLMODE, &i);
|
||||
ioctl(fd, TUNSIFHEAD, &i);
|
||||
}
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
@ -81,3 +82,8 @@ int tun_read(int fd, char *buf, int len)
|
||||
{
|
||||
return read(fd, buf, len);
|
||||
}
|
||||
|
||||
const char *tun_last_error()
|
||||
{
|
||||
return strerror(errno);
|
||||
}
|
@ -28,6 +28,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* #include "vtun.h"
|
||||
#include "lib.h" */
|
||||
@ -72,3 +73,8 @@ int tun_read(int fd, char *buf, int len)
|
||||
{
|
||||
return read(fd, buf, len);
|
||||
}
|
||||
|
||||
const char *tun_last_error()
|
||||
{
|
||||
return strerror(errno);
|
||||
}
|
@ -62,11 +62,11 @@ static int tun_open_common0(char *dev, int istun)
|
||||
}
|
||||
else if (errno != ENOENT)
|
||||
err = errno;
|
||||
else if (i) /* don't try all 256 devices */
|
||||
else if (i) /* don't try all 256 devices */
|
||||
break;
|
||||
}
|
||||
if (err)
|
||||
errno = err;
|
||||
errno = err;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -97,9 +97,9 @@ static int tun_open_common(char *dev, int istun)
|
||||
|
||||
if (ioctl(fd, TUNSETIFF, (void *) &ifr) < 0) {
|
||||
if (errno == EBADFD) {
|
||||
/* Try old ioctl */
|
||||
if (ioctl(fd, OTUNSETIFF, (void *) &ifr) < 0)
|
||||
goto failed;
|
||||
/* Try old ioctl */
|
||||
if (ioctl(fd, OTUNSETIFF, (void *) &ifr) < 0)
|
||||
goto failed;
|
||||
} else
|
||||
goto failed;
|
||||
}
|
||||
@ -130,3 +130,8 @@ int tap_write(int fd, char *buf, int len) { return write(fd, buf, len); }
|
||||
|
||||
int tun_read(int fd, char *buf, int len) { return read(fd, buf, len); }
|
||||
int tap_read(int fd, char *buf, int len) { return read(fd, buf, len); }
|
||||
|
||||
const char *tun_last_error()
|
||||
{
|
||||
return strerror(errno);
|
||||
}
|
@ -28,6 +28,7 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/uio.h>
|
||||
@ -98,3 +99,8 @@ int tun_read(int fd, char *buf, int len)
|
||||
else
|
||||
return rlen;
|
||||
}
|
||||
|
||||
const char *tun_last_error()
|
||||
{
|
||||
return strerror(errno);
|
||||
}
|
@ -74,7 +74,7 @@ int tun_open(char *dev)
|
||||
char *ptr;
|
||||
|
||||
if( *dev ){
|
||||
ptr = dev;
|
||||
ptr = dev;
|
||||
while( *ptr && !isdigit((int)*ptr) ) ptr++;
|
||||
ppa = atoi(ptr);
|
||||
}
|
||||
@ -175,3 +175,8 @@ int tun_read(int fd, char *buf, int len)
|
||||
sbuf.buf = buf;
|
||||
return getmsg(fd, NULL, &sbuf, &f) >=0 ? sbuf.len : -1;
|
||||
}
|
||||
|
||||
const char *tun_last_error()
|
||||
{
|
||||
return strerror(errno);
|
||||
}
|
396
src/tunemu.c
Normal file
396
src/tunemu.c
Normal file
@ -0,0 +1,396 @@
|
||||
/*
|
||||
* tunemu - Tun device emulation for Darwin
|
||||
*
|
||||
* Copyright (c) 2009-2013 Friedrich Schöller <hans@schoeller.se>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tunemu.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <memory.h>
|
||||
#include <util.h>
|
||||
#include <pcap.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#define PPPPROTO_CTL 1
|
||||
|
||||
#define PPP_IP 0x21
|
||||
#define PPP_IPV6 0x57
|
||||
|
||||
#define SC_LOOP_TRAFFIC 0x00000200
|
||||
|
||||
#define PPPIOCNEWUNIT _IOWR('t', 62, int)
|
||||
#define PPPIOCSFLAGS _IOW('t', 89, int)
|
||||
#define PPPIOCSNPMODE _IOW('t', 75, struct npioctl)
|
||||
#define PPPIOCATTCHAN _IOW('t', 56, int)
|
||||
#define PPPIOCGCHAN _IOR('t', 55, int)
|
||||
#define PPPIOCCONNECT _IOW('t', 58, int)
|
||||
#define PPPIOCGUNIT _IOR('t', 86, int)
|
||||
|
||||
struct sockaddr_ppp
|
||||
{
|
||||
u_int8_t ppp_len;
|
||||
u_int8_t ppp_family;
|
||||
u_int16_t ppp_proto;
|
||||
u_int32_t ppp_cookie;
|
||||
};
|
||||
|
||||
enum NPmode
|
||||
{
|
||||
NPMODE_PASS,
|
||||
NPMODE_DROP,
|
||||
NPMODE_ERROR,
|
||||
NPMODE_QUEUE
|
||||
};
|
||||
|
||||
struct npioctl
|
||||
{
|
||||
int protocol;
|
||||
enum NPmode mode;
|
||||
};
|
||||
|
||||
#define PPP_KEXT_PATH "/System/Library/Extensions/PPP.kext"
|
||||
|
||||
#define ERROR_BUFFER_SIZE 1024
|
||||
|
||||
char tunemu_error[ERROR_BUFFER_SIZE];
|
||||
|
||||
static int pcap_use_count = 0;
|
||||
static pcap_t *pcap = NULL;
|
||||
|
||||
static int data_buffer_length = 0;
|
||||
static char *data_buffer = NULL;
|
||||
|
||||
static void tun_error(char *format, ...)
|
||||
{
|
||||
va_list vl;
|
||||
va_start(vl, format);
|
||||
vsnprintf(tunemu_error, ERROR_BUFFER_SIZE, format, vl);
|
||||
va_end(vl);
|
||||
}
|
||||
|
||||
static void tun_noerror(void)
|
||||
{
|
||||
*tunemu_error = 0;
|
||||
}
|
||||
|
||||
static void closeall(void)
|
||||
{
|
||||
int fd = getdtablesize();
|
||||
while (fd--)
|
||||
close(fd);
|
||||
|
||||
open("/dev/null", O_RDWR, 0);
|
||||
dup(0);
|
||||
dup(0);
|
||||
}
|
||||
|
||||
static int ppp_load_kext(void)
|
||||
{
|
||||
int pid = fork();
|
||||
if (pid < 0)
|
||||
{
|
||||
tun_error("fork for ppp kext: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pid == 0)
|
||||
{
|
||||
closeall();
|
||||
execle("/sbin/kextload", "kextload", PPP_KEXT_PATH, NULL, NULL);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int status;
|
||||
while (waitpid(pid, &status, 0) < 0)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
tun_error("waitpid for ppp kext: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (WEXITSTATUS(status) != 0)
|
||||
{
|
||||
tun_error("could not load ppp kext \"%s\"", PPP_KEXT_PATH);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tun_noerror();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ppp_new_instance(void)
|
||||
{
|
||||
// create ppp socket
|
||||
int ppp_sockfd = socket(PF_PPP, SOCK_RAW, PPPPROTO_CTL);
|
||||
if (ppp_sockfd < 0)
|
||||
{
|
||||
if (ppp_load_kext() < 0)
|
||||
return -1;
|
||||
|
||||
ppp_sockfd = socket(PF_PPP, SOCK_RAW, PPPPROTO_CTL);
|
||||
if (ppp_sockfd < 0)
|
||||
{
|
||||
tun_error("creating ppp socket: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// connect to ppp procotol
|
||||
struct sockaddr_ppp pppaddr;
|
||||
pppaddr.ppp_len = sizeof(struct sockaddr_ppp);
|
||||
pppaddr.ppp_family = AF_PPP;
|
||||
pppaddr.ppp_proto = PPPPROTO_CTL;
|
||||
pppaddr.ppp_cookie = 0;
|
||||
if (connect(ppp_sockfd, (struct sockaddr *)&pppaddr, sizeof(struct sockaddr_ppp)) < 0)
|
||||
{
|
||||
tun_error("connecting ppp socket: %s", strerror(errno));
|
||||
close(ppp_sockfd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tun_noerror();
|
||||
return ppp_sockfd;
|
||||
}
|
||||
|
||||
static int ppp_new_unit(int *unit_number)
|
||||
{
|
||||
int fd = ppp_new_instance();
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
// create ppp unit
|
||||
if (ioctl(fd, PPPIOCNEWUNIT, unit_number) < 0)
|
||||
{
|
||||
tun_error("creating ppp unit: %s", strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tun_noerror();
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int ppp_setup_unit(int unit_fd)
|
||||
{
|
||||
// send traffic to program
|
||||
int flags = SC_LOOP_TRAFFIC;
|
||||
if (ioctl(unit_fd, PPPIOCSFLAGS, &flags) < 0)
|
||||
{
|
||||
tun_error("setting ppp loopback mode: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// allow packets
|
||||
struct npioctl npi;
|
||||
npi.protocol = PPP_IP;
|
||||
npi.mode = NPMODE_PASS;
|
||||
if (ioctl(unit_fd, PPPIOCSNPMODE, &npi) < 0)
|
||||
{
|
||||
tun_error("starting ppp unit: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
tun_noerror();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int open_pcap(void)
|
||||
{
|
||||
if (pcap != NULL)
|
||||
{
|
||||
pcap_use_count++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char errbuf[PCAP_ERRBUF_SIZE];
|
||||
pcap = pcap_open_live("lo0", BUFSIZ, 0, 1, errbuf);
|
||||
pcap_use_count = 1;
|
||||
|
||||
if (pcap == NULL)
|
||||
{
|
||||
tun_error("opening pcap: %s", errbuf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
tun_noerror();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void close_pcap(void)
|
||||
{
|
||||
if (pcap == NULL)
|
||||
return;
|
||||
|
||||
pcap_use_count--;
|
||||
if (pcap_use_count == 0)
|
||||
{
|
||||
pcap_close(pcap);
|
||||
pcap = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void allocate_data_buffer(int size)
|
||||
{
|
||||
if (data_buffer_length < size)
|
||||
{
|
||||
free(data_buffer);
|
||||
data_buffer_length = size;
|
||||
data_buffer = malloc(data_buffer_length);
|
||||
}
|
||||
}
|
||||
|
||||
static void make_device_name(tunemu_device device, int unit_number)
|
||||
{
|
||||
snprintf(device, sizeof(tunemu_device), "ppp%d", unit_number);
|
||||
}
|
||||
|
||||
static int check_device_name(tunemu_device device)
|
||||
{
|
||||
if (strlen(device) < 4)
|
||||
return -1;
|
||||
|
||||
int unit_number = atoi(device + 3);
|
||||
if (unit_number < 0 || unit_number > 999)
|
||||
return -1;
|
||||
|
||||
tunemu_device compare;
|
||||
make_device_name(compare, unit_number);
|
||||
|
||||
if (strcmp(device, compare) != 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tunemu_open(tunemu_device device)
|
||||
{
|
||||
int ppp_unit_number = -1;
|
||||
if (device[0] != 0)
|
||||
{
|
||||
if (check_device_name(device) < 0)
|
||||
{
|
||||
tun_error("invalid device name \"%s\"", device);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ppp_unit_number = atoi(device + 3);
|
||||
}
|
||||
|
||||
int ppp_unit_fd = ppp_new_unit(&ppp_unit_number);
|
||||
if (ppp_unit_fd < 0)
|
||||
return -1;
|
||||
|
||||
if (ppp_setup_unit(ppp_unit_fd) < 0)
|
||||
{
|
||||
close(ppp_unit_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (open_pcap() < 0)
|
||||
{
|
||||
close(ppp_unit_fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
make_device_name(device, ppp_unit_number);
|
||||
|
||||
return ppp_unit_fd;
|
||||
}
|
||||
|
||||
int tunemu_close(int ppp_sockfd)
|
||||
{
|
||||
int ret = close(ppp_sockfd);
|
||||
|
||||
if (ret == 0)
|
||||
close_pcap();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int tunemu_read(int ppp_sockfd, char *buffer, int length)
|
||||
{
|
||||
allocate_data_buffer(length + 2);
|
||||
|
||||
length = read(ppp_sockfd, data_buffer, length + 2);
|
||||
if (length < 0)
|
||||
{
|
||||
tun_error("reading packet: %s", strerror(errno));
|
||||
return length;
|
||||
}
|
||||
tun_noerror();
|
||||
|
||||
length -= 2;
|
||||
if (length < 0)
|
||||
return 0;
|
||||
|
||||
memcpy(buffer, data_buffer + 2, length);
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
int tunemu_write(int ppp_sockfd, char *buffer, int length)
|
||||
{
|
||||
allocate_data_buffer(length + 4);
|
||||
|
||||
data_buffer[0] = 0x02;
|
||||
data_buffer[1] = 0x00;
|
||||
data_buffer[2] = 0x00;
|
||||
data_buffer[3] = 0x00;
|
||||
|
||||
memcpy(data_buffer + 4, buffer, length);
|
||||
|
||||
if (pcap == NULL)
|
||||
{
|
||||
tun_error("pcap not open");
|
||||
return -1;
|
||||
}
|
||||
|
||||
length = pcap_inject(pcap, data_buffer, length + 4);
|
||||
if (length < 0)
|
||||
{
|
||||
tun_error("injecting packet: %s", pcap_geterr(pcap));
|
||||
return length;
|
||||
}
|
||||
tun_noerror();
|
||||
|
||||
length -= 4;
|
||||
if (length < 0)
|
||||
return 0;
|
||||
|
||||
return length;
|
||||
}
|
41
src/tunemu.h
Normal file
41
src/tunemu.h
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* tunemu - Tun device emulation for Darwin
|
||||
*
|
||||
* Copyright (c) 2009-2013 Friedrich Schöller <hans@schoeller.se>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TUNEMU_H
|
||||
#define TUNEMU_H
|
||||
|
||||
typedef char tunemu_device[7];
|
||||
|
||||
extern char tunemu_error[];
|
||||
|
||||
int tunemu_open(tunemu_device dev);
|
||||
int tunemu_close(int fd);
|
||||
int tunemu_read(int fd, char *buffer, int length);
|
||||
int tunemu_write(int fd, char *buffer, int length);
|
||||
|
||||
#endif
|
@ -1,42 +1,46 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
|
||||
*
|
||||
* Copyright (C) 2009 Friedrich Schöller <hans@schoeller.se>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "utility.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
string Utility::formatIp(uint32_t ip)
|
||||
std::string Utility::formatIp(uint32_t ip)
|
||||
{
|
||||
char buffer[16];
|
||||
sprintf(buffer, "%d.%d.%d.%d", (ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
|
||||
return buffer;
|
||||
std::stringstream s;
|
||||
s << ((ip >> 24) & 0xff) << '.'
|
||||
<< ((ip >> 16) & 0xff) << '.'
|
||||
<< ((ip >> 8) & 0xff) << '.'
|
||||
<< ((ip >> 0) & 0xff);
|
||||
return s.str();
|
||||
}
|
||||
|
||||
int Utility::rand()
|
||||
{
|
||||
static bool init = false;
|
||||
if (!init)
|
||||
{
|
||||
init = true;
|
||||
srand(time(NULL));
|
||||
}
|
||||
return ::rand();
|
||||
static bool init = false;
|
||||
if (!init)
|
||||
{
|
||||
init = true;
|
||||
srand(time(NULL));
|
||||
}
|
||||
return ::rand();
|
||||
}
|
@ -1,32 +1,33 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
|
||||
*
|
||||
* Copyright (C) 2009 Friedrich Schöller <hans@schoeller.se>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef UTILITY_H
|
||||
#define UTILITY_H
|
||||
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
|
||||
class Utility
|
||||
{
|
||||
public:
|
||||
static std::string formatIp(uint32_t ip);
|
||||
static int rand();
|
||||
static std::string formatIp(uint32_t ip);
|
||||
static int rand();
|
||||
};
|
||||
|
||||
#endif
|
226
src/worker.cpp
Normal file
226
src/worker.cpp
Normal file
@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <hans@schoeller.se>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "worker.h"
|
||||
#include "tun.h"
|
||||
#include "exception.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/select.h>
|
||||
#include <grp.h>
|
||||
#include <iostream>
|
||||
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
Worker::TunnelHeader::Magic::Magic(const char *magic)
|
||||
{
|
||||
memset(data, 0, sizeof(data));
|
||||
strncpy(data, magic, sizeof(data));
|
||||
}
|
||||
|
||||
bool Worker::TunnelHeader::Magic::operator==(const Magic &other) const
|
||||
{
|
||||
return memcmp(data, other.data, sizeof(data)) == 0;
|
||||
}
|
||||
|
||||
bool Worker::TunnelHeader::Magic::operator!=(const Magic &other) const
|
||||
{
|
||||
return memcmp(data, other.data, sizeof(data)) != 0;
|
||||
}
|
||||
|
||||
Worker::Worker(int tunnelMtu, const std::string *deviceName, bool answerEcho,
|
||||
uid_t uid, gid_t gid)
|
||||
: echo(tunnelMtu + sizeof(TunnelHeader)), tun(deviceName, tunnelMtu)
|
||||
{
|
||||
this->tunnelMtu = tunnelMtu;
|
||||
this->answerEcho = answerEcho;
|
||||
this->uid = uid;
|
||||
this->gid = gid;
|
||||
this->privilegesDropped = false;
|
||||
}
|
||||
|
||||
void Worker::sendEcho(const TunnelHeader::Magic &magic, TunnelHeader::Type type,
|
||||
int length, uint32_t realIp, bool reply, uint16_t id, uint16_t seq)
|
||||
{
|
||||
if (length > payloadBufferSize())
|
||||
throw Exception("packet too big");
|
||||
|
||||
TunnelHeader *header = (TunnelHeader *)echo.sendPayloadBuffer();
|
||||
header->magic = magic;
|
||||
header->type = type;
|
||||
|
||||
DEBUG_ONLY(
|
||||
cout << "sending: type " << type << ", length " << length
|
||||
<< ", id " << id << ", seq " << seq << endl);
|
||||
|
||||
echo.send(length + sizeof(TunnelHeader), realIp, reply, id, seq);
|
||||
}
|
||||
|
||||
void Worker::sendToTun(int length)
|
||||
{
|
||||
tun.write(echoReceivePayloadBuffer(), length);
|
||||
}
|
||||
|
||||
void Worker::setTimeout(Time delta)
|
||||
{
|
||||
nextTimeout = now + delta;
|
||||
}
|
||||
|
||||
void Worker::run()
|
||||
{
|
||||
now = Time::now();
|
||||
alive = true;
|
||||
|
||||
int maxFd = echo.getFd() > tun.getFd() ? echo.getFd() : tun.getFd();
|
||||
|
||||
while (alive)
|
||||
{
|
||||
fd_set fs;
|
||||
Time timeout;
|
||||
|
||||
FD_ZERO(&fs);
|
||||
FD_SET(tun.getFd(), &fs);
|
||||
FD_SET(echo.getFd(), &fs);
|
||||
|
||||
if (nextTimeout != Time::ZERO)
|
||||
{
|
||||
timeout = nextTimeout - now;
|
||||
if (timeout < Time::ZERO)
|
||||
timeout = Time::ZERO;
|
||||
}
|
||||
|
||||
// wait for data or timeout
|
||||
timeval *timeval = nextTimeout != Time::ZERO ? &timeout.getTimeval() : NULL;
|
||||
int result = select(maxFd + 1 , &fs, NULL, NULL, timeval);
|
||||
if (result == -1)
|
||||
{
|
||||
if (alive)
|
||||
throw Exception("select", true);
|
||||
else
|
||||
return;
|
||||
}
|
||||
now = Time::now();
|
||||
|
||||
// timeout
|
||||
if (result == 0)
|
||||
{
|
||||
nextTimeout = Time::ZERO;
|
||||
handleTimeout();
|
||||
continue;
|
||||
}
|
||||
|
||||
// icmp data
|
||||
if (FD_ISSET(echo.getFd(), &fs))
|
||||
{
|
||||
bool reply;
|
||||
uint16_t id, seq;
|
||||
uint32_t ip;
|
||||
|
||||
int dataLength = echo.receive(ip, reply, id, seq);
|
||||
if (dataLength != -1)
|
||||
{
|
||||
bool valid = dataLength >= sizeof(TunnelHeader);
|
||||
|
||||
if (valid)
|
||||
{
|
||||
TunnelHeader *header = (TunnelHeader *)echo.receivePayloadBuffer();
|
||||
|
||||
DEBUG_ONLY(
|
||||
cout << "received: type " << header->type
|
||||
<< ", length " << dataLength - sizeof(TunnelHeader)
|
||||
<< ", id " << id << ", seq " << seq << endl);
|
||||
|
||||
valid = handleEchoData(*header, dataLength - sizeof(TunnelHeader), ip, reply, id, seq);
|
||||
}
|
||||
|
||||
if (!valid && !reply && answerEcho)
|
||||
{
|
||||
memcpy(echo.sendPayloadBuffer(), echo.receivePayloadBuffer(), dataLength);
|
||||
echo.send(dataLength, ip, true, id, seq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// data from tun
|
||||
if (FD_ISSET(tun.getFd(), &fs))
|
||||
{
|
||||
uint32_t sourceIp, destIp;
|
||||
|
||||
int dataLength = tun.read(echoSendPayloadBuffer(), sourceIp, destIp);
|
||||
|
||||
if (dataLength == 0)
|
||||
throw Exception("tunnel closed");
|
||||
|
||||
if (dataLength != -1)
|
||||
handleTunData(dataLength, sourceIp, destIp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Worker::stop()
|
||||
{
|
||||
alive = false;
|
||||
}
|
||||
|
||||
void Worker::dropPrivileges()
|
||||
{
|
||||
if (uid <= 0 || privilegesDropped)
|
||||
return;
|
||||
|
||||
#ifdef WIN32
|
||||
throw Exception("dropping privileges not supported");
|
||||
#else
|
||||
syslog(LOG_INFO, "dropping privileges");
|
||||
|
||||
if (setgroups(0, NULL) == -1)
|
||||
throw Exception("setgroups", true);
|
||||
|
||||
if (setgid(gid) == -1)
|
||||
throw Exception("setgid", true);
|
||||
|
||||
if (setuid(uid) == -1)
|
||||
throw Exception("setuid", true);
|
||||
|
||||
privilegesDropped = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Worker::handleEchoData(const TunnelHeader &, int, uint32_t, bool, uint16_t, uint16_t)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void Worker::handleTunData(int, uint32_t, uint32_t) { }
|
||||
|
||||
void Worker::handleTimeout() { }
|
||||
|
||||
char *Worker::echoSendPayloadBuffer()
|
||||
{
|
||||
return echo.sendPayloadBuffer() + sizeof(TunnelHeader);
|
||||
}
|
||||
|
||||
char *Worker::echoReceivePayloadBuffer()
|
||||
{
|
||||
return echo.receivePayloadBuffer() + sizeof(TunnelHeader);
|
||||
}
|
110
src/worker.h
Normal file
110
src/worker.h
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <hans@schoeller.se>
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WORKER_H
|
||||
#define WORKER_H
|
||||
|
||||
#include "time.h"
|
||||
#include "echo.h"
|
||||
#include "tun.h"
|
||||
|
||||
#include <string>
|
||||
#include <sys/types.h>
|
||||
|
||||
class Worker
|
||||
{
|
||||
public:
|
||||
Worker(int tunnelMtu, const std::string *deviceName, bool answerEcho,
|
||||
uid_t uid, gid_t gid);
|
||||
virtual ~Worker() { }
|
||||
|
||||
virtual void run();
|
||||
virtual void stop();
|
||||
|
||||
static int headerSize() { return sizeof(TunnelHeader); }
|
||||
|
||||
protected:
|
||||
struct TunnelHeader
|
||||
{
|
||||
struct Magic
|
||||
{
|
||||
Magic() { }
|
||||
Magic(const char *magic);
|
||||
|
||||
bool operator==(const Magic &other) const;
|
||||
bool operator!=(const Magic &other) const;
|
||||
|
||||
char data[4];
|
||||
};
|
||||
|
||||
enum Type
|
||||
{
|
||||
TYPE_RESET_CONNECTION = 1,
|
||||
TYPE_CONNECTION_REQUEST = 2,
|
||||
TYPE_CHALLENGE = 3,
|
||||
TYPE_CHALLENGE_RESPONSE = 4,
|
||||
TYPE_CONNECTION_ACCEPT = 5,
|
||||
TYPE_CHALLENGE_ERROR = 6,
|
||||
TYPE_DATA = 7,
|
||||
TYPE_POLL = 8,
|
||||
TYPE_SERVER_FULL = 9
|
||||
};
|
||||
|
||||
Magic magic;
|
||||
uint8_t type;
|
||||
}; // size = 5
|
||||
|
||||
virtual bool handleEchoData(const TunnelHeader &header, int dataLength,
|
||||
uint32_t realIp, bool reply, uint16_t id, uint16_t seq);
|
||||
virtual void handleTunData(int dataLength, uint32_t sourceIp,
|
||||
uint32_t destIp); // to echoSendPayloadBuffer
|
||||
virtual void handleTimeout();
|
||||
|
||||
void sendEcho(const TunnelHeader::Magic &magic, TunnelHeader::Type type,
|
||||
int length, uint32_t realIp, bool reply, uint16_t id, uint16_t seq);
|
||||
void sendToTun(int length); // from echoReceivePayloadBuffer
|
||||
|
||||
void setTimeout(Time delta);
|
||||
|
||||
char *echoSendPayloadBuffer();
|
||||
char *echoReceivePayloadBuffer();
|
||||
|
||||
int payloadBufferSize() { return tunnelMtu; }
|
||||
|
||||
void dropPrivileges();
|
||||
|
||||
Echo echo;
|
||||
Tun tun;
|
||||
bool alive;
|
||||
bool answerEcho;
|
||||
int tunnelMtu;
|
||||
int maxTunnelHeaderSize;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
|
||||
bool privilegesDropped;
|
||||
|
||||
Time now;
|
||||
private:
|
||||
int readIcmpData(int *realIp, int *id, int *seq);
|
||||
|
||||
Time nextTimeout;
|
||||
};
|
||||
|
||||
#endif
|
85
time.cpp
85
time.cpp
@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "time.h"
|
||||
|
||||
const Time Time::ZERO = Time(0);
|
||||
|
||||
Time::Time(int ms)
|
||||
{
|
||||
tv.tv_sec = ms / 1000;
|
||||
tv.tv_usec = (ms % 1000) * 1000;
|
||||
}
|
||||
|
||||
Time Time::operator-(const Time &other) const
|
||||
{
|
||||
Time result;
|
||||
result.tv.tv_sec = tv.tv_sec - other.tv.tv_sec;
|
||||
result.tv.tv_usec = tv.tv_usec - other.tv.tv_usec;
|
||||
|
||||
if (result.tv.tv_usec < 0)
|
||||
{
|
||||
result.tv.tv_usec += 1000000;
|
||||
result.tv.tv_sec -= 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Time Time::operator+(const Time &other) const
|
||||
{
|
||||
Time result;
|
||||
result.tv.tv_sec = tv.tv_sec + other.tv.tv_sec;
|
||||
result.tv.tv_usec = tv.tv_usec + other.tv.tv_usec;
|
||||
|
||||
if (result.tv.tv_usec >= 1000000)
|
||||
{
|
||||
result.tv.tv_usec -= 1000000;
|
||||
result.tv.tv_sec += 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Time::operator==(const Time &other) const
|
||||
{
|
||||
return tv.tv_sec != other.tv.tv_sec ? false : tv.tv_usec == other.tv.tv_usec;
|
||||
}
|
||||
|
||||
bool Time::operator!=(const Time &other) const
|
||||
{
|
||||
return tv.tv_sec != other.tv.tv_sec ? true : tv.tv_usec != other.tv.tv_usec;
|
||||
}
|
||||
|
||||
bool Time::operator<(const Time &other) const
|
||||
{
|
||||
return tv.tv_sec != other.tv.tv_sec ? tv.tv_sec < other.tv.tv_sec : tv.tv_usec < other.tv.tv_usec;
|
||||
}
|
||||
|
||||
bool Time::operator>(const Time &other) const
|
||||
{
|
||||
return tv.tv_sec != other.tv.tv_sec ? tv.tv_sec > other.tv.tv_sec : tv.tv_usec > other.tv.tv_usec;
|
||||
}
|
||||
|
||||
Time Time::now()
|
||||
{
|
||||
Time result;
|
||||
gettimeofday(&result.tv, 0);
|
||||
return result;
|
||||
}
|
103
tun.cpp
103
tun.cpp
@ -1,103 +0,0 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "tun.h"
|
||||
#include "exception.h"
|
||||
#include "utility.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <syslog.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
typedef ip IpHeader;
|
||||
|
||||
using namespace std;
|
||||
|
||||
Tun::Tun(const char *device, int mtu)
|
||||
{
|
||||
this->mtu = mtu;
|
||||
|
||||
if (device != NULL)
|
||||
{
|
||||
strncmp(this->device, device, VTUN_DEV_LEN);
|
||||
this->device[VTUN_DEV_LEN] = 0;
|
||||
}
|
||||
else
|
||||
this->device[0] = 0;
|
||||
|
||||
fd = tun_open(this->device);
|
||||
if (fd == -1)
|
||||
throw Exception("could not create tunnel device");
|
||||
|
||||
char cmdline[512];
|
||||
snprintf(cmdline, sizeof(cmdline), "/sbin/ifconfig %s mtu %u", this->device, mtu);
|
||||
if (system(cmdline) != 0)
|
||||
syslog(LOG_ERR, "could not set tun device mtu");
|
||||
}
|
||||
|
||||
Tun::~Tun()
|
||||
{
|
||||
tun_close(fd, device);
|
||||
}
|
||||
|
||||
void Tun::setIp(uint32_t ip)
|
||||
{
|
||||
char cmdline[512];
|
||||
string ips = Utility::formatIp(ip);
|
||||
|
||||
snprintf(cmdline, sizeof(cmdline), "/sbin/ifconfig %s %s %s netmask 255.255.255.0", device, ips.c_str(), ips.c_str());
|
||||
if (system(cmdline) != 0)
|
||||
syslog(LOG_ERR, "could not set tun device ip address");
|
||||
|
||||
#ifndef LINUX
|
||||
snprintf(cmdline, sizeof(cmdline), "/sbin/route add %s/24 %s", ips.c_str(), ips.c_str());
|
||||
if (system(cmdline) != 0)
|
||||
syslog(LOG_ERR, "could not add route");
|
||||
#endif
|
||||
}
|
||||
|
||||
void Tun::write(const char *buffer, int length)
|
||||
{
|
||||
if (tun_write(fd, (char *)buffer, length) == -1)
|
||||
{
|
||||
syslog(LOG_ERR, "error writing %d bytes to tun", length);
|
||||
throw Exception("writing to tun", true);
|
||||
}
|
||||
}
|
||||
|
||||
int Tun::read(char *buffer)
|
||||
{
|
||||
int length = tun_read(fd, buffer, mtu);
|
||||
if (length == -1)
|
||||
throw Exception("reading from tun", true);
|
||||
return length;
|
||||
}
|
||||
|
||||
int Tun::read(char *buffer, uint32_t &sourceIp, uint32_t &destIp)
|
||||
{
|
||||
int length = read(buffer);
|
||||
|
||||
IpHeader *header = (IpHeader *)buffer;
|
||||
sourceIp = ntohl(header->ip_src.s_addr);
|
||||
destIp = ntohl(header->ip_dst.s_addr);
|
||||
|
||||
return length;
|
||||
}
|
27
tun_dev.h
27
tun_dev.h
@ -1,27 +0,0 @@
|
||||
/*
|
||||
VTun - Virtual Tunnel over TCP/IP network.
|
||||
|
||||
Copyright (C) 1998-2000 Maxim Krasnyansky <max_mk@yahoo.com>
|
||||
|
||||
VTun has been derived from VPPP package by Maxim Krasnyansky.
|
||||
|
||||
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 2 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.
|
||||
*/
|
||||
|
||||
#define VTUN_DEV_LEN 20
|
||||
|
||||
extern "C"
|
||||
{
|
||||
int tun_open(char *dev);
|
||||
int tun_close(int fd, char *dev);
|
||||
int tun_write(int fd, char *buf, int len);
|
||||
int tun_read(int fd, char *buf, int len);
|
||||
}
|
196
worker.cpp
196
worker.cpp
@ -1,196 +0,0 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "worker.h"
|
||||
#include "tun.h"
|
||||
#include "exception.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
Worker::TunnelHeader::Magic::Magic(const char *magic)
|
||||
{
|
||||
memset(data, 0, sizeof(data));
|
||||
strncpy(data, magic, sizeof(data));
|
||||
}
|
||||
|
||||
bool Worker::TunnelHeader::Magic::operator==(const Magic &other) const
|
||||
{
|
||||
return memcmp(data, other.data, sizeof(data)) == 0;
|
||||
}
|
||||
|
||||
bool Worker::TunnelHeader::Magic::operator!=(const Magic &other) const
|
||||
{
|
||||
return memcmp(data, other.data, sizeof(data)) != 0;
|
||||
}
|
||||
|
||||
Worker::Worker(int tunnelMtu, const char *deviceName, bool answerEcho, uid_t uid, gid_t gid)
|
||||
{
|
||||
this->tunnelMtu = tunnelMtu;
|
||||
this->answerEcho = answerEcho;
|
||||
this->uid = uid;
|
||||
this->gid = gid;
|
||||
this->privilegesDropped = false;
|
||||
|
||||
echo = NULL;
|
||||
tun = NULL;
|
||||
|
||||
try
|
||||
{
|
||||
echo = new Echo(tunnelMtu + sizeof(TunnelHeader));
|
||||
tun = new Tun(deviceName, tunnelMtu);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
delete echo;
|
||||
delete tun;
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
Worker::~Worker()
|
||||
{
|
||||
delete echo;
|
||||
delete tun;
|
||||
}
|
||||
|
||||
void Worker::sendEcho(const TunnelHeader::Magic &magic, int type, int length, uint32_t realIp, bool reply, uint16_t id, uint16_t seq)
|
||||
{
|
||||
if (length > payloadBufferSize())
|
||||
throw Exception("packet too big");
|
||||
|
||||
TunnelHeader *header = (TunnelHeader *)echo->sendPayloadBuffer();
|
||||
header->magic = magic;
|
||||
header->type = type;
|
||||
|
||||
DEBUG_ONLY(printf("sending: type %d, length %d, id %d, seq %d\n", type, length, id, seq));
|
||||
|
||||
echo->send(length + sizeof(TunnelHeader), realIp, reply, id, seq);
|
||||
}
|
||||
|
||||
void Worker::sendToTun(int length)
|
||||
{
|
||||
tun->write(echoReceivePayloadBuffer(), length);
|
||||
}
|
||||
|
||||
void Worker::setTimeout(Time delta)
|
||||
{
|
||||
nextTimeout = now + delta;
|
||||
}
|
||||
|
||||
void Worker::run()
|
||||
{
|
||||
now = Time::now();
|
||||
alive = true;
|
||||
|
||||
int maxFd = echo->getFd() > tun->getFd() ? echo->getFd() : tun->getFd();
|
||||
|
||||
while (alive)
|
||||
{
|
||||
fd_set fs;
|
||||
Time timeout;
|
||||
|
||||
FD_ZERO(&fs);
|
||||
FD_SET(tun->getFd(), &fs);
|
||||
FD_SET(echo->getFd(), &fs);
|
||||
|
||||
if (nextTimeout != Time::ZERO)
|
||||
{
|
||||
timeout = nextTimeout - now;
|
||||
if (timeout < Time::ZERO)
|
||||
timeout = Time::ZERO;
|
||||
}
|
||||
|
||||
// wait for data or timeout
|
||||
int result = select(maxFd + 1 , &fs, NULL, NULL, nextTimeout != Time::ZERO ? &timeout.getTimeval() : NULL);
|
||||
if (result == -1)
|
||||
throw Exception("select", true);
|
||||
now = Time::now();
|
||||
|
||||
// timeout
|
||||
if (result == 0)
|
||||
{
|
||||
nextTimeout = Time::ZERO;
|
||||
handleTimeout();
|
||||
continue;
|
||||
}
|
||||
|
||||
// icmp data
|
||||
if (FD_ISSET(echo->getFd(), &fs))
|
||||
{
|
||||
bool reply;
|
||||
uint16_t id, seq;
|
||||
uint32_t ip;
|
||||
|
||||
int dataLength = echo->receive(ip, reply, id, seq);
|
||||
if (dataLength != -1)
|
||||
{
|
||||
bool valid = dataLength >= sizeof(TunnelHeader);
|
||||
|
||||
if (valid)
|
||||
{
|
||||
TunnelHeader header = *(TunnelHeader *)echo->receivePayloadBuffer(); // make a copy!
|
||||
|
||||
DEBUG_ONLY(printf("received: type %d, length %d, id %d, seq %d\n", header.type, dataLength - sizeof(TunnelHeader), id, seq));
|
||||
|
||||
valid = handleEchoData(header, dataLength - sizeof(TunnelHeader), ip, reply, id, seq);
|
||||
}
|
||||
|
||||
if (!valid && !reply && answerEcho)
|
||||
{
|
||||
memcpy(echo->sendPayloadBuffer(), echo->receivePayloadBuffer(), dataLength);
|
||||
echo->send(dataLength, ip, true, id, seq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// data from tun
|
||||
if (FD_ISSET(tun->getFd(), &fs))
|
||||
{
|
||||
uint32_t sourceIp, destIp;
|
||||
|
||||
int dataLength = tun->read(echoSendPayloadBuffer(), sourceIp, destIp);
|
||||
|
||||
if (dataLength == 0)
|
||||
throw Exception("tunnel closed");
|
||||
|
||||
handleTunData(dataLength, sourceIp, destIp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Worker::dropPrivileges()
|
||||
{
|
||||
if (uid <= 0 || privilegesDropped)
|
||||
return;
|
||||
|
||||
syslog(LOG_INFO, "dropping privileges");
|
||||
|
||||
if (setgid(gid) == -1)
|
||||
throw Exception("setgid", true);
|
||||
|
||||
if (setuid(uid) == -1)
|
||||
throw Exception("setuid", true);
|
||||
|
||||
privilegesDropped = true;
|
||||
}
|
104
worker.h
104
worker.h
@ -1,104 +0,0 @@
|
||||
/*
|
||||
* Hans - IP over ICMP
|
||||
* Copyright (C) 2009 Friedrich Schöller <friedrich.schoeller@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef WORKER_H
|
||||
#define WORKER_H
|
||||
|
||||
#include "time.h"
|
||||
#include "echo.h"
|
||||
#include "tun.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
class Worker
|
||||
{
|
||||
public:
|
||||
Worker(int tunnelMtu, const char *deviceName, bool answerEcho, uid_t uid, gid_t gid);
|
||||
virtual ~Worker();
|
||||
|
||||
virtual void run();
|
||||
|
||||
static int headerSize() { return sizeof(TunnelHeader); }
|
||||
|
||||
protected:
|
||||
struct TunnelHeader
|
||||
{
|
||||
struct Magic
|
||||
{
|
||||
Magic() { }
|
||||
Magic(const char *magic);
|
||||
|
||||
bool operator==(const Magic &other) const;
|
||||
bool operator!=(const Magic &other) const;
|
||||
|
||||
char data[4];
|
||||
};
|
||||
|
||||
Magic magic;
|
||||
uint8_t type;
|
||||
|
||||
enum Type
|
||||
{
|
||||
TYPE_RESET_CONNECTION = 1,
|
||||
TYPE_CONNECTION_REQUEST = 2,
|
||||
TYPE_CHALLENGE = 3,
|
||||
TYPE_CHALLENGE_RESPONSE = 4,
|
||||
TYPE_CONNECTION_ACCEPT = 5,
|
||||
TYPE_CHALLENGE_ERROR = 6,
|
||||
TYPE_DATA = 7,
|
||||
TYPE_POLL = 8,
|
||||
TYPE_SERVER_FULL = 9
|
||||
};
|
||||
}; // size = 5
|
||||
|
||||
virtual bool handleEchoData(const TunnelHeader &header, int dataLength, uint32_t realIp, bool reply, uint16_t id, uint16_t seq) { return true; }
|
||||
virtual void handleTunData(int dataLength, uint32_t sourceIp, uint32_t destIp) { } // to echoSendPayloadBuffer
|
||||
virtual void handleTimeout() { }
|
||||
|
||||
void sendEcho(const TunnelHeader::Magic &magic, int type, int length, uint32_t realIp, bool reply, uint16_t id, uint16_t seq);
|
||||
void sendToTun(int length); // from echoReceivePayloadBuffer
|
||||
|
||||
void setTimeout(Time delta);
|
||||
|
||||
char *echoSendPayloadBuffer() { return echo->sendPayloadBuffer() + sizeof(TunnelHeader); }
|
||||
char *echoReceivePayloadBuffer() { return echo->receivePayloadBuffer() + sizeof(TunnelHeader); }
|
||||
|
||||
int payloadBufferSize() { return tunnelMtu; }
|
||||
|
||||
void dropPrivileges();
|
||||
|
||||
Echo *echo;
|
||||
Tun *tun;
|
||||
bool alive;
|
||||
bool answerEcho;
|
||||
int tunnelMtu;
|
||||
int maxTunnelHeaderSize;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
|
||||
bool privilegesDropped;
|
||||
|
||||
Time now;
|
||||
private:
|
||||
int readIcmpData(int *realIp, int *id, int *seq);
|
||||
|
||||
Time nextTimeout;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user