/tor6tun/tor6tun.cpp
C++ | 942 lines | 776 code | 83 blank | 83 comment | 146 complexity | 530aec8690b557af27bd87d4c2ecf93b MD5 | raw file
- /****************************************************************************
- * Copyright (c) 2016 by Christoph Grenz *
- * christophg@grenz-bonn.de *
- * *
- * 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/>. *
- * *
- ****************************************************************************
- * Main application: *
- * Connection management, IPv6 packet rewriting, callbacks, etc. *
- ****************************************************************************/
- #include "tor6tun.h"
- #include "base32.h"
- #include "call.h"
- #include "getaddrinfo.h"
- #include "if_addrs.h"
- #include "ip_packets.h"
- #include "logging.h"
- #include "packetbuffer.h"
- #include "util.h"
- #include <sstream>
- #include <netinet/in.h>
- #include <netinet/icmp6.h>
- class socks_error: public std::runtime_error
- {
- int c;
- public:
- socks_error(const char *what, int code=-1): std::runtime_error(what), c(code) {}
- int code() const noexcept
- {
- return c;
- }
- };
- static Socket socks_open(const std::string &socks_server, sockaddr_in6 &socks_addr)
- {
- // Create and connect socket
- Socket sock(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
- try {
- sock.connect(socks_addr);
- } catch (std::system_error &exc) {
- // On recoverable errors, iterate through all addrinfo records
- // trying to connect to every address of the host until it
- // succedes.
- int code = exc.code().value();
- bool retry = false;
- switch (code) {
- case EACCES:
- case EPERM:
- case ECONNREFUSED:
- case ENETUNREACH:
- case ETIMEDOUT:
- retry = true;
- break;
- default:
- break;
- }
- if (retry) {
- for (auto &&item : getaddrinfo(socks_server, AI_V4MAPPED|AI_IDN, AF_INET6, SOCK_STREAM)) {
- std::memcpy(&socks_addr, item.ai_addr, sizeof(sockaddr_in6));
- try {
- sock.connect(socks_addr);
- } catch (std::system_error &exc) {
- if (item.ai_next == nullptr) {
- // If no address works, bail out
- throw;
- }
- continue;
- }
- break;
- }
- }
- else {
- throw;
- }
- }
- sock.set_cloexec(true);
-
- // Send SOCKS 5 anonymous auth command
- bytevector buffer({0x05, 1, 0});
- sock.send(buffer);
-
- return sock;
- }
- static std::string ipv6_to_onion(const IPv6Address &addr)
- {
- std::string onion = b32encode(&addr.s6_addr[6], 10);
- onion.append(".onion");
- return onion;
- }
- static void socks_connect_1(AsyncSocket& sock, const IPv6Address &addr, uint16_t port)
- {
- bytevector buffer;
- sock.recv(buffer, 2);
- if (buffer[0] != 5 or buffer[1] != 0)
- throw socks_error("SOCKS 5 auth failed");
- // Send SOCKS 5 connection request
- buffer = {0x05, 1, 0, 4};
- sock.send(buffer);
- buffer.resize(16);
- std::memcpy(&buffer[0], &addr, 16);
- sock.send(buffer);
- buffer = {static_cast<uint8_t>(port>>8), static_cast<uint8_t>(port&0xFF)};
- sock.send(buffer);
- sock.flush();
- }
- static void socks_connect_1(AsyncSocket& sock, const std::string &onion, uint16_t port)
- {
- bytevector buffer;
- sock.recv(buffer, 2);
- if (buffer[0] != 5 or buffer[1] != 0)
- throw socks_error("SOCKS 5 auth failed");
- // Send SOCKS 5 connection request
- buffer = {0x05, 1, 0, 3, static_cast<uint8_t>(onion.length())};
- sock.send(buffer);
- sock.send(onion);
- buffer = {static_cast<uint8_t>(port>>8), static_cast<uint8_t>(port&0xFF)};
- sock.send(buffer);
- sock.flush();
- }
- static void socks_connect_2(AsyncSocket &sock)
- {
- bytevector buffer;
- sock.recv(buffer, 4);
- int status = -1, tmp;
- if (buffer[0] != 5)
- throw socks_error("SOCKS 5 invalid connect response");
- status = buffer[1];
- switch (buffer[3]) {
- case 1:
- sock.recv(buffer, 6);
- break;
- case 3:
- sock.recv(buffer, 1);
- tmp = buffer[0];
- sock.recv(buffer, tmp+2);
- case 4:
- sock.recv(buffer, 18);
- break;
- default:
- throw socks_error("SOCKS 5 invalid connect response");
- }
- if (status != 0)
- throw socks_error("SOCKS 5 connect failed", status);
- }
- std::ostream& operator << (std::ostream &lhs, const ConnKey &rhs)
- {
- return lhs << "<[" << rhs.addr << "]:" << rhs.port << '>';
- }
- Tor6TunApplication::Tor6TunApplication(
- const std::string &tun_name,
- const std::string &socks_server,
- const IPv6Network &onion_net,
- const std::string &port
- )
- : socks_server(socks_server), onion_network(onion_net), tun(tun_name),
- max_connections(65535), max_connreqs(65535), last_router_adv(0),
- in_shutdown(false)
- {
- // Setup sockets
- tun.set_cloexec(true);
- tcp_sock.open(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
- tcp_sock.set_cloexec(true);
-
- // Setup TUN
- std::string addrstr { setup_tun() };
- log::debug("tun device ", tun.get_name(), " opened");
-
- // Bind tcp loopback socket
- struct sockaddr_in6 addr;
- for (auto &&item : getaddrinfo(addrstr, port, AI_PASSIVE, AF_INET6, SOCK_STREAM)) {
- addr = *reinterpret_cast<sockaddr_in6*>(item.ai_addr);
- break;
- }
- tcp_sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1);
- tcp_sock.setsockopt(SOL_SOCKET, SO_OOBINLINE, 1);
- tcp_sock.setsockopt(SOL_SOCKET, SO_DONTROUTE, 1);
- try {
- tcp_sock.setsockopt(SOL_SOCKET, SO_BINDTODEVICE, tun.get_name());
- } catch (std::system_error &exc) {
- if (exc.code().value() != EPERM)
- throw;
- log::debug("SO_BINDTODEVICE on internal TCP socket failed");
- }
- tcp_sock.setsockopt<struct linger>(SOL_SOCKET, SO_LINGER, {0, 2});
- tcp_sock.bind<struct sockaddr_in6>(addr);
- tcp_sock.listen(16);
-
- addr = tcp_sock.getsockname<sockaddr_in6>();
- tunnel_proxy_port = ntohs(addr.sin6_port);
-
- // Set transfer network
- transfer_network = "fe90::0/12";
-
- log::debug("using TCP port ", tunnel_proxy_port, " for internal purposes");
- log::debug(".onion urls are mapped to IPv6 network ", onion_network);
-
- // Set tor server address
- for (auto &&item : getaddrinfo(socks_server, AI_V4MAPPED|AI_IDN, AF_INET6, SOCK_STREAM)) {
- socks_addr = *reinterpret_cast<sockaddr_in6*>(item.ai_addr);
- break;
- }
-
- // Set event handlers
- selector.read.add(tun, std::bind(&Tor6TunApplication::on_tun_packet, this));
- selector.read.add(tcp_sock, std::bind(&Tor6TunApplication::on_new_connection, this));
- }
- std::string Tor6TunApplication::setup_tun()
- {
- // Discover IPv6 addresses on tun interface
- IPv6Address tun_addr;
- bool tun_is_up = false;
- std::string tun_name { tun.get_name() };
- IPv6Address mask {"ffc0::"};
- IPv6Network net {"fe80::/10"};
- for (auto &ifaddr: getifaddrs()) {
- if (!ifaddr.ifa_addr or ifaddr.ifa_addr->sa_family != AF_INET6)
- continue;
- if (tun_name != ifaddr.ifa_name)
- continue;
- sockaddr_in6 *saddr = reinterpret_cast<sockaddr_in6*>(ifaddr.ifa_addr);
- sockaddr_in6 *smask = reinterpret_cast<sockaddr_in6*>(ifaddr.ifa_netmask);
- IPv6Address addr {saddr->sin6_addr};
- log::info(addr);
- if (addr.is_in(net) and mask == smask->sin6_addr) {
- tun_addr = addr;
- tun_is_up = ifaddr.ifa_flags & IFF_UP;
- break;
- }
- }
- // Activate TUN network interface, if needed
- if (!tun_is_up) {
- log::debug("activating ", tun_name);
- check_call({"ip", "link", "set", tun_name, "up"});
- }
- // Set interface address, if needed
- if (!tun_addr) {
- log::debug("adding IPv6 address fe80::1/10 to ", tun_name);
- check_call({"ip", "-6", "addr", "add", "fe80::1/10", "scope", "link", "dev", tun_name});
- tun_addr = "fe80::1";
- }
-
- return tun_addr.to_string()+"%"+tun_name;
- }
- unsigned long Tor6TunApplication::get_max_connreqs()
- { return max_connreqs; }
- unsigned long Tor6TunApplication::get_max_connections()
- { return max_connections; }
- void Tor6TunApplication::set_max_connreqs(unsigned long v)
- { max_connreqs = v; }
- void Tor6TunApplication::set_max_connections(unsigned long v)
- { max_connections = v; }
- void Tor6TunApplication::process()
- {
- /* Send unsolicited router advertisements */
- time_t current_time = time(nullptr);
- if (last_router_adv < current_time-59) {
- send_router_adv();
- last_router_adv = current_time;
- }
- /* Handle socket events */
- selector.select(16);
-
- /* Cleanup */
- cleanup_closing_connections();
- }
- bool Tor6TunApplication::shutdown()
- {
- bool had_connections = connections.empty();
- time_t current_time = time(nullptr);
- in_shutdown = true;
- send_router_adv(false);
- for (auto &pair: closing) {
- if (pair.first > current_time+2)
- pair.first = current_time+2;
- }
- for (auto &pair: connections) {
- Connection& conn = pair.second;
- if (conn.socks_socket.fileno() != -1)
- conn.socks_socket.flush();
- else
- continue;
- if (conn.client_socket.fileno() != -1)
- conn.client_socket.flush();
- selector.read.remove(conn.client_socket);
- selector.read.remove(conn.socks_socket);
- selector.write.remove(conn.client_socket);
- selector.write.remove(conn.socks_socket);
- log::debug("connection [", conn.socks_socket.fileno(), "] closing");
- conn.client_socket.close();
- conn.socks_socket.close();
- // Append to closing list
- closing.push_back(
- std::make_pair(current_time+2, pair.first)
- );
- }
- while (!connections.empty()) {
- had_connections = true;
- selector.select(0.10);
- cleanup_closing_connections();
- }
- return had_connections;
- }
- void Tor6TunApplication::cleanup_closing_connections()
- {
- time_t current_time = time(nullptr);
- size_t closed_count = 0;
- auto end_it = --closing.begin();
- for (auto it = --closing.end(); it != end_it; --it) {
- if (it->first <= current_time) {
- connections.erase(it->second);
- closing.erase(it);
- closed_count++;
- }
- }
- if (closed_count)
- log::debug("closed ", closed_count, " lingering connection(s)");
- if (closing.capacity() > (closing.size()+1)*120/100)
- closing.shrink_to_fit();
- }
- void Tor6TunApplication::on_tun_packet()
- {
- static thread_local bytevector buffer;
- buffer.reserve(2052);
- if (!tun.recv(buffer))
- return;
-
- if (buffer.size() < 5) {
- log::warn("too small packet on TUN interface");
- return;
- }
-
- PacketBufferView packet(buffer);
- tun_frame_header &tun_header = packet.next<tun_frame_header>();
-
- // IPv6
- if (tun_header.proto == htons(ETH_P_IPV6)) {
- IPv6Header &ipv6_header = packet.peek<IPv6Header>();
- auto source = ipv6_header.source();
- auto destination = ipv6_header.destination();
- // Skip packets from ::
- if (!source) {
- return;
- }
- // Skip non-ICMP packets to multicast
- if (destination.s6_addr[0] == 0xFF && ipv6_header.next_header() != IPPROTO_ICMPV6) {
- return;
- }
- // Verify payload size and skip truncated packets
- if (ipv6_header.payload_length() != packet.size()-40) {
- return;
- }
-
- if (destination.is_in(transfer_network)) {
- // Packets from SOCKS tunnel to outside
- if (ipv6_header.next_header() == IPPROTO_TCP) {
- handle_tun_tcp_reply(packet, buffer);
- }
- } else if (!source.is_in(transfer_network)) {
- // Packets from outside to SOCKS tunnel
- switch (ipv6_header.next_header()) {
- case IPPROTO_TCP:
- handle_tun_tcp_packet(packet, buffer);
- break;
- case IPPROTO_UDP:
- send_icmpv6_error(packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOPORT);
- break;
- case IPPROTO_ICMPV6:
- handle_tun_icmpv6_packet(packet);
- break;
- default:
- send_icmpv6_error(packet, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_NEXTHEADER, 40);
- break;
- }
- }
- } else if (tun_header.proto == htons(ETH_P_IP)) {
- // For now ignore IPv4 packets
- }
- // Ignore all non-IP packets
- }
- void Tor6TunApplication::on_new_connection()
- {
- // accept and match to pending connections
- auto pair = tcp_sock.accept<struct sockaddr_in6>();
- IPv6Address addr(pair.second.sin6_addr);
- pair.first.set_cloexec(true);
- pair.first.set_blocking(false);
- auto it = connections.find({pair.second.sin6_addr, ntohs(pair.second.sin6_port)});
- if (it->second.socks_socket.fileno() == -1) {
- // Reuse of a closing address combination
- log::warn("address-port combination ", it->first," already in cooldown phase. TCP connection aborted.");
- pair.first.close();
- return;
- }
- Connection& conn = it->second;
- conn.client_socket = std::move(pair.first);
-
- log::debug("connection [", conn.socks_socket.fileno(),"] established");
-
- // Add callbacks
- selector.read.add(conn.client_socket, std::bind(
- &Tor6TunApplication::on_conn_recv,
- this,
- std::ref(it->first),
- std::ref(conn.client_socket),
- std::ref(conn.socks_socket)
- ));
- selector.read.add(conn.socks_socket, std::bind(
- &Tor6TunApplication::on_conn_recv,
- this,
- std::ref(it->first),
- std::ref(conn.socks_socket),
- std::ref(conn.client_socket)
- ));
- }
- void Tor6TunApplication::handle_tun_tcp_reply(PacketBufferView packet, bytevector &buffer)
- {
- IPv6Header &ipv6 = packet.next<IPv6Header>();
- TCPHeader &tcp = packet.next<TCPHeader>();
-
- if (tcp.source_port() != tunnel_proxy_port)
- return;
- auto it = connections.find({ipv6.destination(), tcp.destination_port()});
- if (it == connections.end())
- return;
- Connection &conn = it->second;
-
- ipv6.destination() = conn.source;
- ipv6.source() = conn.destination;
- tcp.source_port(conn.dport);
- tcp.set_checksum(ipv6);
- tun.send(buffer);
- }
- void Tor6TunApplication::handle_tun_tcp_packet(PacketBufferView packet, bytevector &buffer)
- {
- IPv6Header &ipv6 = packet.next<IPv6Header>();
- TCPHeader &tcp = packet.next<TCPHeader>();
-
- if ((tcp.flags() & 0x3F) == 2) { // SYN
- if (in_shutdown)
- return;
- if (connreqs.size() >= max_connreqs) {
- return;
- }
- if (connections.size() >= max_connections)
- return;
-
- IPv6Address key_addr = ipv6.source();
- key_addr.renumber(transfer_network);
- // Initiate connection to SOCKS proxy
- Socket socks_socket;
- try {
- socks_socket = socks_open(socks_server, socks_addr);
- } catch (std::system_error &exc) {
- log::warn("SOCKS connection error: ", exc.what());
- send_icmpv6_error(packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE);
- return;
- }
- // Create connection get_name
- std::ostringstream stream;
- stream << '[' << ipv6.source() << "]:" << tcp.source_port() << " => ";
- if (ipv6.destination().is_in(onion_network))
- stream << ipv6_to_onion(ipv6.destination()) << ':' << tcp.destination_port();
- else
- stream << '[' << ipv6.destination() << "]:" << tcp.destination_port();
- // Add connection to list
- auto result = connections.insert(std::make_pair<ConnKey, Connection>(
- {key_addr, tcp.source_port()},
- {
- ipv6.source(), ipv6.destination(),
- tcp.source_port(), tcp.destination_port(),
- {}, std::move(socks_socket)
- }
- ));
- if (result.second == false)
- return;
- if (ipv6.destination().is_in(onion_network))
- log::debug("new TCP connection [", result.first->second.socks_socket.fileno(),
- "]: from [", ipv6.source(), "]:", tcp.source_port(), " to ",
- ipv6_to_onion(ipv6.destination()), ':', tcp.destination_port()
- );
- else
- log::debug("new TCP connection [", result.first->second.socks_socket.fileno(),
- "]: from [", ipv6.source(), "]:", tcp.source_port(), " to [",
- ipv6.destination(), "]:", tcp.destination_port()
- );
-
- // Record SYN packet
- bytevector tmp;
- tmp.swap(buffer);
- tmp.shrink_to_fit();
- auto result2 = connreqs.insert(std::make_pair<ConnKey, bytevector>(
- {key_addr, tcp.source_port()},
- std::move(tmp)
- ));
- if (result2.second == false) {
- connections.erase(result.first);
- return;
- }
- // Set callback
- selector.read.add(
- result.first->second.socks_socket,
- std::bind(
- &Tor6TunApplication::on_socks_auth,
- this,
- std::ref(result.first->first),
- std::ref(result.first->second.socks_socket)
- )
- );
- return;
- } else if ((tcp.flags() & 0x04)) { // RST
- // Build connection key
- IPv6Address key_addr = ipv6.source();
- key_addr.renumber(transfer_network);
- // Check if matching connection
- ConnKey key {key_addr, tcp.source_port()};
- auto it = connections.find(key);
- if (it != connections.end()) {
- // If not an established connection
- if (it->second.client_socket.fileno() == -1) {
- // try to remove connection request
- auto it2 = connreqs.find(key);
- if (it2 != connreqs.end())
- connreqs.erase(it2);
- // Remove listeners
- selector.read.remove(it->second.socks_socket);
- selector.write.remove(it->second.socks_socket);
- // Log it
- log::debug("connection [", it->second.socks_socket.fileno(),
- "] reset"
- );
- // Erase connection
- connections.erase(it);
- return;
- }
- }
- // else fallthrough, let kernel handle RST
- }
- // Rewrite source and destination
- ipv6.source().renumber(transfer_network);
- ipv6.destination() = "fe80::1";
- tcp.destination_port(tunnel_proxy_port);
- tcp.set_checksum(ipv6);
- // Forward packet
- tun.send(buffer);
- }
- void Tor6TunApplication::on_socks_auth(const ConnKey &key, AsyncSocket &sock)
- {
- auto it = connections.find(key);
- auto it2 = connreqs.find(key);
- // Consistence checks
- if (it == connections.end()) {
- connreqs.erase(key);
- selector.read.remove(sock);
- return;
- }
- else if (sock.fileno() == -1) {
- connreqs.erase(key);
- connections.erase(it);
- selector.read.remove(sock);
- return;
- }
- if (it2 == connreqs.end()) {
- connections.erase(it);
- selector.read.remove(sock);
- return;
- }
-
- PacketBufferView packet(it2->second);
- packet.next<tun_frame_header>();
-
- // Continue SOCKS connection setup
- Connection &conn = it->second;
- try {
- if (conn.destination.is_in(onion_network)) {
- // To onion address
- std::string onion = ipv6_to_onion(conn.destination);
- socks_connect_1(sock, onion, conn.dport);
- } else {
- socks_connect_1(sock, conn.destination, conn.dport);
- }
- } catch (socks_error &exc) {
- int code = exc.code();
- log::warn("SOCKS connection failed with error code ", code);
- send_icmpv6_error(packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE);
- connreqs.erase(key);
- connections.erase(it);
- selector.read.remove(sock);
- return;
- } catch (std::system_error &exc) {
- log::warn("SOCKS connection error: ", exc.what());
- send_icmpv6_error(packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE);
- connreqs.erase(key);
- connections.erase(it);
- selector.read.remove(sock);
- return;
- }
- selector.read.replace(sock, std::bind(&Tor6TunApplication::on_socks_assoc, this, std::ref(key), std::ref(sock)));
- }
- void Tor6TunApplication::on_socks_assoc(const ConnKey &key, AsyncSocket &sock)
- {
- auto it = connections.find(key);
- auto it2 = connreqs.find(key);
- // Consistence checks
- if (it == connections.end()) {
- connreqs.erase(key);
- selector.read.remove(sock);
- return;
- }
- else if (sock.fileno() == -1) {
- connreqs.erase(key);
- connections.erase(it);
- selector.read.remove(sock);
- return;
- }
- if (it2 == connreqs.end()) {
- connections.erase(key);
- selector.read.remove(sock);
- return;
- }
-
- // Get saved SYN packet
- bytevector buffer;
- buffer.swap(it2->second);
- connreqs.erase(it2);
- PacketBufferView packet(buffer);
- packet.next<tun_frame_header>();
-
- // Complete SOCKS connection setup
- Connection &conn = it->second;
- try {
- socks_connect_2(sock);
- } catch (socks_error &exc) {
- int code = exc.code();
- if (code == 5 or code == 1) {
- log::debug("connection [", conn.socks_socket.fileno(),"] failed: connection refused");
- send_tcp_rst(packet);
- } else if (code == 2) {
- log::debug("connection [", conn.socks_socket.fileno(),"] failed: not allowed");
- send_icmpv6_error(packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADMIN);
- } else if (code == 3 or code == 8) {
- log::debug("connection [", conn.socks_socket.fileno(),"] failed: network unreachable");
- send_icmpv6_error(packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE);
- } else if (code == 4) {
- log::debug("connection [", conn.socks_socket.fileno(),"] failed: host unreachable");
- send_icmpv6_error(packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR);
- } else if (code == 6) {
- log::debug("connection [", conn.socks_socket.fileno(),"] failed: timeout");
- send_icmpv6_error(packet, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT);
- } else {
- log::warn("SOCKS connection [", conn.socks_socket.fileno(),"] failed with error code ", code);
- send_icmpv6_error(packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE);
- }
- selector.read.remove(sock);
- connections.erase(it);
- return;
- } catch (std::system_error &exc) {
- log::warn("SOCKS connection [", conn.socks_socket.fileno(),"] error: ", exc.what());
- send_icmpv6_error(packet, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE);
- connections.erase(it);
- selector.read.remove(sock);
- return;
- }
- sock.set_blocking(false);
-
- // Send SYN packet
- IPv6Header &ipv6 = packet.next<IPv6Header>();
- TCPHeader &tcp = packet.next<TCPHeader>();
- ipv6.source().renumber(transfer_network);
- ipv6.destination() = "fe80::1";
- tcp.destination_port(tunnel_proxy_port);
- tcp.set_checksum(ipv6);
- tun.send(buffer);
-
- log::debug("connection [", conn.socks_socket.fileno(),"] accepted");
-
- // Remove from set until client connection is accepted
- selector.read.remove(sock);
- }
- void Tor6TunApplication::on_conn_recv(const ConnKey &key, AsyncSocket &src, AsyncSocket &dst)
- {
- static thread_local bytevector buffer;
- if (src.fileno() == -1 || dst.fileno() == -1) {
- return;
- }
- buffer.reserve(4096);
- try {
- if (src.recv(buffer)) {
- if (dst.send(buffer)) {
- dst.flush();
- } else {
- selector.read.remove(src);
- selector.write.add(dst, std::bind(
- &Tor6TunApplication::on_conn_send,
- this,
- std::ref(key),
- std::ref(src),
- std::ref(dst)
- ));
- }
- }
- } catch (connection_closed &) {
- selector.read.remove(src);
- selector.read.remove(dst);
- selector.write.remove(src);
- selector.write.remove(dst);
- auto it = connections.find(key);
- if (it != connections.end())
- log::debug("connection [", it->second.socks_socket.fileno(), "] closing");
- else
- log::debug("connection [?] closing");
- src.close();
- dst.close();
- // Append to closing list
- closing.push_back(
- std::make_pair(time(nullptr)+10, key)
- );
- }
- }
- void Tor6TunApplication::on_conn_send(const ConnKey &key, AsyncSocket &src, AsyncSocket &dst)
- {
- try {
- if (dst.flush()) {
- selector.write.remove(dst);
- selector.read.add(src, std::bind(
- &Tor6TunApplication::on_conn_recv,
- this,
- std::ref(key),
- std::ref(src),
- std::ref(dst)
- ));
- }
- } catch (connection_closed &) {
- selector.read.remove(src);
- selector.read.remove(dst);
- selector.write.remove(src);
- selector.write.remove(dst);
- auto it = connections.find(key);
- if (it != connections.end())
- log::debug("connection [", it->second.socks_socket.fileno(), "] closing");
- else
- log::debug("connection [?] closing");
- src.close();
- dst.close();
- // Append to closing list
- closing.push_back(
- std::make_pair(time(nullptr)+10, key)
- );
- }
- }
- void Tor6TunApplication::handle_tun_icmpv6_packet(PacketBufferView packet)
- {
- PacketBufferView orig_view = packet;
- IPv6Header &header = packet.next<IPv6Header>();
- ICMPv6Header &icmpv6 = packet.peek<ICMPv6Header>();
- // Verify checksum and skip damaged packets
- if (!icmpv6.verify_checksum(header))
- return;
- // Skip error messages and redirects
- uint8_t type = icmpv6.type();
- if (type < 128 || type == 137)
- return;
- // Handle other packets
- if (type == 133) {
- // Send Router Advertisements on Solicitations
- time_t current_time = time(nullptr);
- if (last_router_adv < current_time) {
- send_router_adv();
- last_router_adv = current_time;
- }
- } else if (header.destination().s6_addr[0] != 0xFF) {
- // Send error response to non-multicast messages
- send_icmpv6_error(orig_view, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOPORT);
- }
- }
- void Tor6TunApplication::send_icmpv6_error(const PacketBufferView old_packet, uint8_t type, uint8_t code, uint32_t data)
- {
- bytevector buffer;
- buffer.resize(std::min(4+40+8+old_packet.size(), static_cast<size_t>(1280u)));
- PacketBufferView packet(buffer);
- tun_frame_header &tun_header = packet.next<tun_frame_header>();
- tun_header.proto = htons(ETH_P_IPV6);
- const IPv6Header &old_header = old_packet.peek<IPv6Header>();
-
- IPv6Header &ipv6_header = packet.next<IPv6Header>();
- ipv6_header.version(6);
- ipv6_header.payload_length(packet.size());
- ipv6_header.next_header(IPPROTO_ICMPV6);
- ipv6_header.hop_limit(64);
- ipv6_header.source() = old_header.destination();
- ipv6_header.destination() = old_header.source();
- ICMPv6Header &icmpv6_header = packet.next<ICMPv6Header>();
- icmpv6_header.type(type);
- icmpv6_header.code(code);
- packet.next<uint32_t>() = htonl(data);
- packet.fill_from(old_packet);
- icmpv6_header.set_checksum(ipv6_header);
- tun.send(buffer);
- }
- void Tor6TunApplication::send_tcp_rst(PacketBufferView old_packet)
- {
- bytevector buffer;
- buffer.resize(4+40+20);
- PacketBufferView packet(buffer);
- tun_frame_header &tun_header = packet.next<tun_frame_header>();
- tun_header.proto = htons(ETH_P_IPV6);
-
- const IPv6Header &old_header = old_packet.next<IPv6Header>();
-
- IPv6Header &ipv6_header = packet.next<IPv6Header>();
- ipv6_header.version(6);
- ipv6_header.payload_length(packet.size());
- ipv6_header.next_header(IPPROTO_TCP);
- ipv6_header.hop_limit(64);
- ipv6_header.source() = old_header.destination();
- ipv6_header.destination() = old_header.source();
-
- const TCPHeader &old_tcp = old_packet.next<TCPHeader>();
-
- TCPHeader &tcp = packet.next<TCPHeader>();
- tcp.source_port(old_tcp.destination_port());
- tcp.destination_port(old_tcp.source_port());
- tcp.data_offset(5);
- uint32_t seq = old_tcp.ack();
- uint32_t ack = old_tcp.seq();
- if ((old_tcp.flags() & 0x3F) == 2) { // SYN
- ack += 1;
- seq = 0;
- }
- tcp.seq(seq);
- tcp.ack(ack);
- tcp.flags(4+16);
- tcp.set_checksum(ipv6_header);
-
- tun.send(buffer);
- }
- void Tor6TunApplication::send_router_adv(bool active)
- {
- bytevector buffer;
- buffer.resize(4+40+4+12+32+32+24);
- PacketBufferView packet(buffer);
- tun_frame_header &tun_header = packet.next<tun_frame_header>();
- tun_header.proto = htons(ETH_P_IPV6);
-
- IPv6Header &ipv6_header = packet.next<IPv6Header>();
- ipv6_header.version(6);
- ipv6_header.payload_length(packet.size());
- ipv6_header.next_header(IPPROTO_ICMPV6);
- ipv6_header.hop_limit(255);
- ipv6_header.source() = "fe90::";
- ipv6_header.destination() = "ff02::1";
- ICMPv6Header &icmpv6_header = packet.next<ICMPv6Header>();
- icmpv6_header.type(134);
- icmpv6_header.code(0);
-
- RouterAdvertisementHeader &ra_header = packet.next<RouterAdvertisementHeader>();
- ra_header.hop_limit(8);
- ra_header.flags(24);
- ra_header.router_lifetime(0);
- ra_header.reachable_time(0);
- ra_header.retrans_timer(0);
-
- NDPrefixInformation &ra_prefix1 = packet.next<NDPrefixInformation>();
- ra_prefix1.type(3);
- ra_prefix1.length(32);
- ra_prefix1.prefix_length(48);
- ra_prefix1.flags(active ? 128 : 0);
- ra_prefix1.valid_lifetime(active ? 120 : 1);
- ra_prefix1.preferred_lifetime(active ? 90 : 0);
- ra_prefix1.prefix() = onion_network;
-
- NDPrefixInformation &ra_prefix2 = packet.next<NDPrefixInformation>();
- ra_prefix2.type(3);
- ra_prefix2.length(32);
- ra_prefix2.prefix_length(10);
- ra_prefix2.flags(active ? 128 : 0);
- ra_prefix2.valid_lifetime(-1);
- ra_prefix2.preferred_lifetime(-1);
- ra_prefix2.prefix() = "fe80::";
-
- NDRouteInformation &ra_route = packet.next<NDRouteInformation>();
- ra_route.type(24);
- ra_route.length(24);
- ra_route.prefix_length(48);
- ra_route.flags(8);
- ra_route.route_lifetime(active ? 120 : 0);
- ra_route.prefix() = onion_network;
-
- icmpv6_header.set_checksum(ipv6_header);
- tun.send(buffer);
- }