Skip to content
Snippets Groups Projects
Commit 9582914d authored by Recolic Keghart's avatar Recolic Keghart
Browse files

archive

parent 8cf5fadd
No related branches found
No related tags found
No related merge requests found
.history 0 → 100644
commit 9450db11a189b4099ca8c5d4ccc4f303e2ee2320
Author: Recolic <root@recolic.net>
Date: Sun Jun 30 16:02:28 2019 +0800
Update README.md
commit abbf82bfeba26c28a470447d3311236817e96d42
Author: Recolic Keghart <root@recolic.net>
Date: Tue Jun 11 21:33:38 2019 +0800
remove packed struct
commit afed693fefd78a73dfd97e1b3929d619720b860a
Author: Recolic Keghart <root@recolic.net>
Date: Tue Jun 11 21:33:30 2019 +0800
remove packed struct
commit 65ee23e090436f761ceb30fbff53638207d150de
Author: Recolic Keghart <root@recolic.net>
Date: Tue Jun 11 21:30:19 2019 +0800
update cmake
commit 9f58b2ad47bddfdd338217f88a9491327a2d5f62
Author: Recolic Keghart <root@recolic.net>
Date: Tue Jun 11 21:27:26 2019 +0800
Done. Dynamic connection tested. (UDP lost control packet not tested!)
commit 0badf744588c7e19b3ffbc02b9d384fb3ca8c669
Author: Recolic Keghart <root@recolic.net>
Date: Tue Jun 11 02:52:51 2019 +0800
DO NOT BUILD: finished but not debugged
commit 13c1c79f50f28f365cc6938462047de9f9e37d3d
Author: Recolic Keghart <root@recolic.net>
Date: Mon Jun 10 18:57:58 2019 +0800
update readme
commit ae8ba24d6bdd5218bc3db1a9fe364b27b32b55ca
Author: Recolic <root@recolic.net>
Date: Mon Jun 10 18:52:33 2019 +0800
Create LICENSE
commit 35225ec5403aa1a3b69b42c994c41954da096be6
Author: Recolic <root@recolic.net>
Date: Mon Jun 10 18:49:57 2019 +0800
Update README.md
commit a51bf7957bdaaf8531c76f2750742eb40ba06002
Author: Recolic Keghart <root@recolic.net>
Date: Mon Jun 10 18:49:00 2019 +0800
finished
commit 3d48635f4dafba301574e8162296cb08dde36b0a
Author: Recolic Keghart <root@recolic.net>
Date: Mon Jun 10 18:05:26 2019 +0800
bug fix
commit 79da4564218eed98cecf2a9ec3866a7e0ca142e6
Author: Recolic Keghart <root@recolic.net>
Date: Mon Jun 10 14:40:12 2019 +0800
bug fix
commit 0675b0afedb596c141f49017ee7faf922419e068
Author: Recolic Keghart <root@recolic.net>
Date: Mon Jun 10 13:26:20 2019 +0800
DO NOT BUILD: A failing version for TCP
commit 58aa632ca4ddb281b92a3e1c311f7738471c805f
Author: Recolic Keghart <root@recolic.net>
Date: Mon Jun 10 12:57:38 2019 +0800
seems working. push to server before cleanup
commit ba09af0f49fcbb5abaa1b89cf7a536ae8cfc90e7
Author: Recolic Keghart <root@recolic.net>
Date: Mon Jun 10 11:55:23 2019 +0800
bugfix
commit a85df551ee45be323cad86109d41b022338b8b69
Author: Recolic Keghart <root@recolic.net>
Date: Mon Jun 10 02:14:35 2019 +0800
bugfix
commit 0c6943999f34bdf8f7fb6ae3b42c437b1c9aefac
Author: Recolic Keghart <root@recolic.net>
Date: Mon Jun 10 01:56:48 2019 +0800
bugfix
commit 956b8af2b912e6f3983a4c621ff74e27d2280a4b
Author: Recolic Keghart <root@recolic.net>
Date: Mon Jun 10 01:52:24 2019 +0800
init
commit 8cf5faddaa33926c98905dfd0184c2b39f61c5f6
Author: Recolic Keghart <root@recolic.net>
Date: Mon Jun 10 01:50:11 2019 +0800
init
cmake_minimum_required(VERSION 3.14)
cmake_minimum_required(VERSION 3.0)
project(udp_forwarder_ng)
find_package(rlib)
find_package(Threads)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_FLAGS_DEBUG "-g -DMALLOC_CHECK_=2")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
include_directories(./lib)
include_directories(.)
add_executable(udp_forwarder_ng main.cc Forwarder.hpp Crypto.hpp Dictionary.hpp)
add_executable(udp_forwarder_ng main.cc Forwarder.hpp Crypto.hpp Config.hpp Util.hpp)
target_link_libraries(udp_forwarder_ng r)
add_executable(crypto_test test/TestCrypto.cc Crypto.hpp Dictionary.hpp)
target_link_libraries(udp_forwarder_ng Threads::Threads)
add_executable(crypto_test test/TestCrypto.cc Crypto.hpp Config.hpp)
target_link_libraries(crypto_test r)
install(TARGETS udp_forwarder_ng DESTINATION /usr/bin)
......@@ -3,8 +3,19 @@
#include <string>
// DGRAM packet usually smaller than 1400B.
constexpr size_t DGRAM_BUFFER_SIZE = 20480; // 20KiB
// epoll buffer size.
constexpr size_t MAX_EVENTS = 16;
// Change a connection on every n seconds,
// to reset the GFW deep-packet-inspection process.
// ( Only if server side is encrypted, so nothing happens
// to the real openvpn server.
constexpr size_t SERVER_ENCRYPT_CONNECTION_TIMEOUT_SECONDS = 60;
// a random long string to fill the buffer.
const std::string crypto_dictionary = R"RSTR(
Optimizing C++/Code optimization/Faster operations
< Optimizing C++ | Code optimization
Jump to navigationJump to search
......
......@@ -8,7 +8,7 @@
#include <string>
#include <numeric>
#include <limits>
#include "Dictionary.hpp"
#include "Config.hpp"
#if defined(__linux__)
# include <endian.h>
......@@ -25,10 +25,12 @@
using std::string;
// WARNING: should be thread-safe.
class Crypto {
public:
Crypto() = default;
void convertL2R(string &data, const string &lKey, const string &rKey) {
// If data is empty, maybe it's a bare control msg: a exception maybe thrown.
// If lKey is not null, decrypt the data.
// If rKey is not null, encrypt the data.
if(!lKey.empty()) {
......@@ -85,6 +87,8 @@ private:
if(data.size() < 8)
throw std::runtime_error("Decrypt: Data length less than 8. ");
string nonce = data.substr(0, 8);
if(uint64FromBinStr(nonce) == 0)
throw std::runtime_error("Bad nonce: nonce is zero: ctl msg not fucked.");
string toDecrypt = data.substr(8);
block_decrypt(toDecrypt, key, nonce);
......@@ -106,6 +110,7 @@ private:
size_t dict_current_index = 0;
struct {
// This rand is just QUICK and good enough. Maybe still good enough for multi-threading.
uint64_t x=123456789, y=362436069, z=521288629;
uint64_t get() {
uint64_t t;
......@@ -118,7 +123,10 @@ private:
y = z;
z = t ^ x ^ y;
return z;
if(z != 0) // nonce can not be zero. zero nonce means control message.
return z;
else
return get();
}
} xorshf_rand;
......
......@@ -9,12 +9,38 @@
#include <picosha2.h>
#include <rlib/sys/sio.hpp>
#include <sys/epoll.h>
#include <rlib/stdio.hpp>
#include "Crypto.hpp"
#include <thread>
#include <Crypto.hpp>
#include <unordered_map>
#include "Config.hpp"
#include "Util.hpp"
using std::string;
using namespace std::literals;
inline void epoll_add_fd(fd_t epollFd, fd_t fd) {
epoll_event event {
.events = EPOLLIN,
.data = {
.fd = fd,
}
};
auto ret1 = epoll_ctl(epollFd, EPOLL_CTL_ADD, fd, &event);
if(ret1 == -1)
throw std::runtime_error("epoll_ctl failed.");
}
inline void epoll_del_fd(fd_t epollFd, fd_t fd) {
epoll_event event {
.events = EPOLLIN,
.data = {
.fd = fd,
}
};
auto ret1 = epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, &event); // Can be nullptr since linux 2.6.9
if(ret1 == -1)
throw std::runtime_error("epoll_ctl failed.");
}
class Forwarder {
public:
Forwarder(string listenAddr, uint16_t listenPort, string serverAddr, uint16_t serverPort, string lPassword,
......@@ -29,40 +55,50 @@ public:
rKey = "";
}
private:
auto setup_epoll(fd_t listenFd, fd_t serverFd) {
auto epollFd = epoll_create1(NULL);
if(epollFd == -1)
throw std::runtime_error("Failed to create epoll fd.");
// setup epoll.
epoll_event eventL {
.events = EPOLLIN,
.data.fd = listenFd,
}, eventS {
.events = EPOLLIN,
.data.fd = serverFd,
};
auto ret1 = epoll_ctl(epollFd, EPOLL_CTL_ADD, listenFd, &eventL);
auto ret2 = epoll_ctl(epollFd, EPOLL_CTL_ADD, serverFd, &eventS);
if(ret1 == -1 or ret2 == -1)
throw std::runtime_error("epoll_ctl failed.");
return epollFd;
}
public:
[[noreturn]] void run() {
// setup connections.
auto listenFd = rlib::quick_listen(listenAddr, listenPort, true);
auto serverFd = rlib::quick_connect(serverAddr, serverPort, true);
auto epollFd = setup_epoll(listenFd, serverFd);
rlib_defer([=]{close(listenFd);});
auto epollFd = epoll_create1(0);
if(epollFd == -1)
throw std::runtime_error("Failed to create epoll fd.");
epoll_add_fd(epollFd, listenFd);
constexpr size_t MAX_EVENTS = 16;
epoll_event events[MAX_EVENTS];
// DGRAM packet usually smaller than 1400B.
constexpr size_t DGRAM_BUFFER_SIZE = 20480; // 20KiB
char buffer[DGRAM_BUFFER_SIZE];
// WARN: If you want to modify this program to work for both TCP and UDP, PLEASE use rlib::sockIO::recv instead of fixed buffer.
// Map from serverSession to clientSession.
// If I see a packet from client, throw it to server.
// If I see a packet from server, I have to determine which client to throw it.
// So I have to record the map between client and server, one-to-one.
std::unordered_map<clientInfo, fd_t, clientInfoHash> client2server;
std::unordered_map<fd_t, clientInfo> server2client;
std::unordered_map<fd_t, size_t> server2wallTime;
// If connection creation time is less than walltime, the connection timed out.
auto connForNewClient = [&, this](const clientInfo &info) {
if(info.isNull()) throw std::runtime_error("Invalid client info");
auto serverFd = rlib::quick_connect(serverAddr, serverPort, true);
rlog.verbose("creating new connection... {}", serverFd);
client2server[info] = serverFd; // May overwrite existing element on server timing out.
server2client.insert(std::make_pair(serverFd, info));
server2wallTime.insert(std::make_pair(serverFd, getWallTime()));
epoll_add_fd(epollFd, serverFd);
return serverFd;
};
auto eraseServerConn = [&](fd_t fd) {
server2client.erase(fd);
server2wallTime.erase(fd);
epoll_del_fd(epollFd, fd);
close(fd);
};
rlog.info("Forwarding server working...");
rlog.info("Listening {}:{}, with upstream server {}:{}.", listenAddr, listenPort, serverAddr, serverPort);
// Main loop!
while(true) {
......@@ -71,31 +107,169 @@ public:
throw std::runtime_error("epoll_wait failed.");
for(auto cter = 0; cter < nfds; ++cter) {
auto recvFd = events[cter].data.fd;
auto recvSideIsListenSide = recvFd == listenFd;
auto anotherFd = recvSideIsListenSide ? serverFd : listenFd;
const auto &recvSideKey = recvSideIsListenSide ? lKey : rKey;
const auto &sendSideKey = recvSideIsListenSide ? rKey : lKey;
const auto recvFd = events[cter].data.fd;
const auto recvSideIsClientSide = server2client.find(recvFd) == server2client.end(); // is not server
const auto &recvSideKey = recvSideIsClientSide ? lKey : rKey;
const auto &sendSideKey = recvSideIsClientSide ? rKey : lKey;
rlog.debug("woke up fd {}, isCLient={}", recvFd, recvSideIsClientSide);
try {
auto size = recvfrom(recvFd, buffer, DGRAM_BUFFER_SIZE, NULL, NULL, NULL);
if(size == -1) {
throw std::runtime_error("ERR: recvfrom returns -1. "s + strerror(errno));
size_t size;
fd_t anotherFd;
sockaddr *sendtoAddr = nullptr;
socklen_t sendtoAddrLen = 0;
clientInfo clientSideInfo;
// Recv /////////////////////////////////////////////////////////////////////////////////////
if(recvSideIsClientSide) {
// Client to Server packet.
auto &info = clientSideInfo;
size = recvfrom(recvFd, buffer, DGRAM_BUFFER_SIZE, 0, (sockaddr *)&info.addr, &info.len);
if(size == -1)
throw std::runtime_error("ERR: recvfrom returns -1. "s + strerror(errno));
auto pos = client2server.find(info);
if(pos == client2server.end())
anotherFd = connForNewClient(info);
else
anotherFd = pos->second;
}
else {
// Server to Client packet.
size = recvfrom(recvFd, buffer, DGRAM_BUFFER_SIZE, 0, nullptr, nullptr);
if(size == -1)
throw std::runtime_error("ERR: recvfrom returns -1. "s + strerror(errno));
clientInfo &info = server2client.at(recvFd); // If server not found, drop the msg. (The server may just timed out)
sendtoAddr = (sockaddr *)&info.addr;
sendtoAddrLen = info.len;
anotherFd = listenFd;
clientSideInfo = info;
}
// received raw data.
string bufferStr (std::begin(buffer), std::begin(buffer) + size);
// Addon: ConnTimeout ///////////////////////////////////////////////////////////////////////////
// Recolic: The GFW use deep-packet-inspection to fuck my OpenVPN connection in about 10 minutes.
// What if I change a new connection in every 1 minute?
// Try it.
if(bufferStr.size() >= sizeof(uint64_t)) {
// Check control msg. Its nonce is zero.
if(*(uint64_t*)bufferStr.data() == 0) {
if(recvSideIsClientSide) {
// ctl msg from client. (conn change req)
uint16_t previous_port, new_port;
if(bufferStr.size() < sizeof(uint64_t) + 2*sizeof(previous_port))
throw std::runtime_error("ctl msg from client too short.");
std::memcpy(&previous_port, bufferStr.data()+sizeof(uint64_t), sizeof(previous_port));
std::memcpy(&new_port, bufferStr.data()+sizeof(uint64_t)+sizeof(previous_port), sizeof(previous_port));
previous_port = be16toh(previous_port);
new_port = be16toh(new_port);
// getsockname on UDP ONLY works for the local port. Ignore the addr!
auto iter = std::find_if(client2server.begin(), client2server.end(), [previous_port](const auto &kv){
// Known bug 1632: If there's two udp client with different addr and same port. it booms.
return kv.first.getPortNum() == previous_port;
});
if(iter == client2server.end())
throw std::runtime_error("ctl msg from client: change conn: prev conn not exist.");
auto clientSideInfoBackup = clientSideInfo;
clientSideInfo.setPortNum(new_port);
auto serverFd = iter->second;
rlog.debug("Client requested to change conn:", previous_port, new_port);
server2client[serverFd].setPortNum(new_port);
client2server[clientSideInfo] = serverFd; // Old record is not erased now.
// send ACK to client(recvFd). Should use previous port.
// The client will see the ACK from OLD connection and erase it.
string ackStr (sizeof(uint64_t), '\0');
auto ret = sendto(recvFd, ackStr.data(), ackStr.size(), 0, (sockaddr*)&clientSideInfoBackup.addr, clientSideInfoBackup.len);
if(ret == -1)
throw std::runtime_error("Failed to send CONN CHANGE ACK");
// remove the ctl prefix
bufferStr = bufferStr.substr(sizeof(uint64_t) + 2*sizeof(previous_port));
}
else {
// ACK ctl msg from server (conn change ack)
if(bufferStr.size() != sizeof(uint64_t))
throw std::runtime_error("wrong ack ctl from server");
rlog.verbose("REMOVEING FD", recvFd);
eraseServerConn(recvFd);
continue; // nothing todo with bare ACK.
}
}
}
// Encrypt/Decrypt ///////////////////////////////////////////////////////////////////////////////
crypto.convertL2R(bufferStr, recvSideKey, sendSideKey);
// Encrypt/Decrypt End. Continue ConnTimeout Addon ///////////////////////////////////////////////
auto prepareConnChangeReq = [&](fd_t prevFd, fd_t newFd) {
clientInfo previous, newOne;
newOne.len = previous.len = sizeof(previous.addr);
auto ret = getsockname(prevFd, (sockaddr*)&previous.addr, &previous.len) +
getsockname(newFd, (sockaddr*)&newOne.addr, &newOne.len);
if(ret != 0)
throw std::runtime_error("getsockname failed.");
auto previous_port = htobe16(previous.getPortNum()),
new_port = htobe16(newOne.getPortNum());
rlog.debug("COnnChangeReq (port num in BIG ENDIAN):", previous_port, new_port);
// Add control header.
bufferStr = string(sizeof(uint64_t) + sizeof(previous_port)*2, '\0') + bufferStr;
std::memcpy((char*)bufferStr.data()+sizeof(uint64_t), &previous_port, sizeof(previous_port));
std::memcpy((char*)bufferStr.data()+sizeof(uint64_t)+sizeof(previous_port), &new_port, sizeof(new_port));
};
if(recvSideIsClientSide && !sendSideKey.empty()) {
// Check server connection timeout.
// Only timeout the connection if server-side is encrypted. Or OpenVPN server will confuse.
// If the connection is timeout:
// 1. Create the new connection, reset timeout, update client2server and insert server2client.
// 2. Attach the control header onto the origin data packet, send it on the new connection.
// When received message from server, if server2client doesn't match client2server, meaning
// that this connection is already timed out.
// If the message is ACK control message, remove the old entry in server2client.
// Otherwise, resend the bare control header in previous step 2.
if(server2wallTime.at(anotherFd) < getWallTime()) {
// This connection timed out.
rlog.verbose("A Connection timed out, creating new conn...");
auto newConnFd = connForNewClient(clientSideInfo);
prepareConnChangeReq(anotherFd, newConnFd);
}
}
size = sendto(anotherFd, bufferStr.data(), bufferStr.size(), NULL, NULL, NULL);
if(!recvSideIsClientSide) {
// server to client: I said to change conn but server still using old conn.
// Maybe my req lost. resend.
auto clientOwnerFd = client2server.at(server2client.at(recvFd));
if(clientOwnerFd != recvFd) {
// Client2server already modified, but not receiving ACK.
prepareConnChangeReq(recvFd, clientOwnerFd);
}
}
// Send /////////////////////////////////////////////////////////////////////////////////////
rlog.debug("sending on fd", anotherFd);
if(recvSideIsClientSide) {
// Client to Server packet.
size = send(anotherFd, bufferStr.data(), bufferStr.size(), 0);
}
else {
// Server to Client packet.
size = sendto(anotherFd, bufferStr.data(), bufferStr.size(), 0, sendtoAddr, sendtoAddrLen);
}
if(size == -1) {
throw std::runtime_error("ERR: sendto returns -1. "s + strerror(errno));
throw std::runtime_error("sendto returns -1. "s + strerror(errno));
}
if(size != bufferStr.size()) {
rlib::println("ERR: sendto not sent all data.");
rlog.warning("sendto not sent all data.");
}
// Done /////////////////////////////////////////////////////////////////////////////////////
}
catch(std::exception &e) {
rlib::println(e.what());
rlog.error(e.what());
}
}
}
......
LICENSE 0 → 100644
This diff is collapsed.
# UDP forwarder
A simple **UNSAFE** tool to encrypt / hide your UDP packets. Trying to trick The Great Firewall deep learning VPN detection
model. The packet can be easily decrypted if attacker checks the source code.
This tool is usually used with OpenVPN, to avoid being `deep-learned` by the GFW. OpenVPN already encrypted the packets,
so I needn't do it again.
## NOTE
DO NOT USE MASTER BRANCH. It's still under debugging. (the connection migration is not stable)
## Design
![explain.png](https://raw.githubusercontent.com/recolic/udp_forwarder_ng/master/res/explain.png)
## Build
```
mkdir build && cd build
cmake .. && make
./udp_forwarder_ng [args ...]
```
## Common Deployment
![solu.png](https://raw.githubusercontent.com/recolic/udp_forwarder_ng/master/res/solu.png)
## Common mistake
If you run OpenVPN and udp_forwarder on the same PC, you won't access the Internet successfully.
If you did it, please think again and you'll realize how stupid you are.
## Naive performance test
Note: the connection setup procedure maybe a little slow, but it doesn't matter.
- Latency
with proxy: (encrypted OpenVPN + encrypted&obfs udp_forwarder_ng) 70.386ms
without proxy: 0.475ms + 68.578ms = 69.053ms
overhead (OpenVPN+Forwarder): 1.333ms
## Known bug
Every UDP Forwarder, will drop the first client2server packet. (Doesn't matter).
Every UDP Forwarder, will drop
I assume that every connection has a unique port. search for comment tag `1632`
Util.hpp 0 → 100644
//
// Created by recolic on 19-6-10.
//
#ifndef UDP_FORWARDER_NG_CONNECTIONTIMEOUTCTL_HPP
#define UDP_FORWARDER_NG_CONNECTIONTIMEOUTCTL_HPP
#include <cstddef>
#include <rlib/sys/fd.hpp>
#include <ctime>
#include <rlib/log.hpp>
extern rlib::logger rlog;
inline size_t getWallTime() {
auto seconds = static_cast<size_t>(std::time(nullptr));
return seconds / SERVER_ENCRYPT_CONNECTION_TIMEOUT_SECONDS;
}
inline char nibbleToHex(int nibble)
{
const int ascii_zero = 48;
const int ascii_a = 65;
if((nibble >= 0) && (nibble <= 9))
{
return (char) (nibble + ascii_zero);
}
if((nibble >= 10) && (nibble <= 15))
{
return (char) (nibble - 10 + ascii_a);
}
return '?';
}
inline string char2str(char byteVal)
{
int upp = (byteVal & 0xF0) >> 4;
int low = (byteVal & 0x0F);
return std::string() + nibbleToHex(upp) + nibbleToHex(low);
}
template <typename T>
inline string printBinaryObj(const T &obj) {
string res = "[";
const char *p = (const char *)&obj;
for(auto i = 0; i < sizeof(T); ++i) {
res += char2str(p[i]) + ' ';
}
return res + "]";
}
struct clientInfo {
sockaddr_storage addr; socklen_t len;
bool operator==(const clientInfo &another) const {
return do_compare(another, true);
}
bool matchWildcard(const clientInfo &another) const {
return do_compare(another, false);
}
bool do_compare(const clientInfo &another, bool need_same_addr = true) const {
const auto *me = (const sockaddr_in*)&this->addr;
const auto *an = (const sockaddr_in*)&another.addr;
if(me->sin_family != an->sin_family)
return false;
if(me->sin_family == AF_INET) {
auto same_addr = std::memcmp(&me->sin_addr, &an->sin_addr, sizeof(me->sin_addr)) == 0;
auto same_port = me->sin_port == an->sin_port;
return need_same_addr ? same_addr && same_port : same_port;
}
else if(me->sin_family == AF_INET6) {
const auto *me = (const sockaddr_in6*)&this->addr;
const auto *an = (const sockaddr_in6*)&another.addr;
auto same_addr = std::memcmp(&me->sin6_addr, &an->sin6_addr, sizeof(me->sin6_addr)) == 0;
auto same_port = me->sin6_port == an->sin6_port;
return need_same_addr ? same_addr && same_port : same_port;
}
else
throw std::runtime_error("Invalid client info: sin_family is not AF_INET or AF_INET6");
}
bool isNull() const {
for(auto cter = 0; cter < sizeof(addr); ++cter) {
if(cter[(char *)&addr] != 0)
return false;
}
return true;
}
uint16_t getPortNum() const {
const auto *me = (const sockaddr_in*)&this->addr;
if(me->sin_family == AF_INET) {
return me->sin_port;
}
else if(me->sin_family == AF_INET6) {
const auto *me = (const sockaddr_in6*)&this->addr;
return me->sin6_port;
}
else
throw std::runtime_error("Invalid client info: sin_family is not AF_INET or AF_INET6: got " + std::to_string(me->sin_family));
}
void setPortNum(uint16_t port) {
auto *me = (sockaddr_in*)&this->addr;
if(me->sin_family == AF_INET) {
me->sin_port = port;
}
else if(me->sin_family == AF_INET6) {
auto *me = (sockaddr_in6*)&this->addr;
me->sin6_port = port;
}
else
throw std::runtime_error("Invalid client info: sin_family is not AF_INET or AF_INET6: got " + std::to_string(me->sin_family));
}
};
struct clientInfoHash {std::size_t operator()(const clientInfo &info) const {return *(std::size_t*)&info.addr;}}; // hash basing on port number and part of ip (v4/v6) address.
//// Change connection to encrypted server in every 1 minute to avoid GFW deep-packet-inspect.
//class ConnectionTimeoutCtl {
//public:
// ConnectionTimeoutCtl(size_t timeoutSeconds, bool serverSideIsEncrypted)
// : timeoutSeconds(timeoutSeconds), serverSideIsEncrypted(serverSideIsEncrypted) {
// // If server side is unencrypted, set timeout to +inf.
// }
//
// bool encryptedMessageIsControlMessage() {
// // Client side operation.
// // 1. Return true if the message is control message (nonce=0)
// // 2. if the message is control msg, deal with it.
// }
//
// fd_t shouldChangeConnection() {
// // Server side operation.
// // 1. Return new fd ONLY if the connection should time out AND the new connection is ready.
// // else return -1.
// // 2. This function should send the control message, and update the client2server map.
// }
//
//
//private:
// size_t timeoutSeconds;
// bool serverSideIsEncrypted;
//};
#endif //UDP_FORWARDER_NG_CONNECTIONTIMEOUTCTL_HPP
#include <iostream>
#include <rlib/stdio.hpp>
#include <rlib/opt.hpp>
#include "Forwarder.hpp"
rlib::logger rlog(std::cerr);
using namespace rlib::literals;
int main(int argc, char **argv) {
rlib::opt_parser args(argc, argv);
if(args.getBoolArg("--help", "-h")) {
rlib::println("Usage: {} -l listenAddr -p listenPort -s serverAddr -P serverPort -lp LPassword -rp R(emote)Password"_rs.format(args.getSelf()));
rlib::println("Usage: {} -l listenAddr -p listenPort -s serverAddr -P serverPort [-lp LPassword] [-rp R(emote)Password] [--log=error/info/verbose/debug]"_rs.format(args.getSelf()));
rlib::println("Leave LPassword for empty if listenAddr is other UDP application.");
rlib::println("Leave RPassword for empty if serverAddr is other UDP application.");
return 0;
}
auto listenAddr = args.getValueArg("-l");
auto listenPort = args.getValueArg("-p").as<uint16_t>();
auto serverAddr = args.getValueArg("-s");
auto serverPort = args.getValueArg("-P").as<uint16_t>();
auto lPassword = args.getValueArg("-lp");
auto rPassword = args.getValueArg("-rp");
auto lPassword = args.getValueArg("-lp", false, "");
auto rPassword = args.getValueArg("-rp", false, "");
auto log_level = args.getValueArg("--log", false, "info");
if(log_level == "error")
rlog.set_log_level(rlib::log_level_t::ERROR);
else if(log_level == "info")
rlog.set_log_level(rlib::log_level_t::INFO);
else if(log_level == "verbose")
rlog.set_log_level(rlib::log_level_t::VERBOSE);
else if(log_level == "debug")
rlog.set_log_level(rlib::log_level_t::DEBUG);
else
throw std::runtime_error("Unknown log level: " + log_level);
Forwarder fwd(listenAddr, listenPort, serverAddr, serverPort, lPassword, rPassword);
fwd.run();
return 0;
}
\ No newline at end of file
res/explain.png

93.3 KiB

res/solu.png

252 KiB

0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment