// OpenVPN -- An application to securely tunnel IP networks // over a single port, with support for SSL/TLS-based // session authentication and key exchange, // packet encryption, packet authentication, and // packet compression. // // Copyright (C) 2012-2020 OpenVPN Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License Version 3 // as published by the Free Software Foundation. // // 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 Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program in the COPYING file. // If not, see <http://www.gnu.org/licenses/>. #ifndef OPENVPN_TRANSPORT_PROTOCOL_H #define OPENVPN_TRANSPORT_PROTOCOL_H #include <string> #include <cstdint> // for std::uint32_t, etc. #include <openvpn/common/exception.hpp> #include <openvpn/common/option_error.hpp> #include <openvpn/common/string.hpp> #include <openvpn/addr/ip.hpp> namespace openvpn { // A class that encapsulates a transport protocol. class Protocol { public: enum Type { NONE, UDPv4, TCPv4, UDPv6, TCPv6, TLSv4, // TLS over IPv4 TLSv6, // TLS over IPv6 UnixStream, // unix domain socket (stream) UnixDGram, // unix domain socket (datagram) NamedPipe, // named pipe (Windows only) UDP=UDPv4, TCP=TCPv4, TLS=TLSv4, }; enum AllowSuffix { NO_SUFFIX, CLIENT_SUFFIX, SERVER_SUFFIX, }; Protocol() : type_(NONE) {} explicit Protocol(const Type t) : type_(t) {} Type operator()() const { return type_; } bool defined() const { return type_ != NONE; } void reset() { type_ = NONE; } bool is_udp() const { return type_ == UDPv4 || type_ == UDPv6; } bool is_tcp() const { return type_ == TCPv4 || type_ == TCPv6; } bool is_tls() const { return type_ == TLSv4 || type_ == TLSv6; } bool is_reliable() const { return is_tcp() || is_tls(); } bool is_ipv6() const { return type_ == UDPv6 || type_ == TCPv6 || type_ == TLSv6; } bool is_unix() const { return type_ == UnixStream || type_ == UnixDGram; } bool is_named_pipe() const { return type_ == NamedPipe; } bool is_local() const { return is_unix() || is_named_pipe(); } bool operator==(const Protocol& other) const { return type_ == other.type_; } bool operator!=(const Protocol& other) const { return type_ != other.type_; } bool transport_match(const Protocol& other) const { return transport_proto() == other.transport_proto(); } unsigned int extra_transport_bytes() const { return (is_tcp() || is_tls()) ? sizeof(std::uint16_t) : 0; } void mod_addr_version(const IP::Addr& addr) { switch (addr.version()) { case IP::Addr::UNSPEC: break; case IP::Addr::V4: if (is_udp()) type_ = UDPv4; else if (is_tcp()) type_ = TCPv4; else if (is_tls()) type_ = TLSv4; break; case IP::Addr::V6: if (is_udp()) type_ = UDPv6; else if (is_tcp()) type_ = TCPv6; else if (is_tls()) type_ = TLSv6; break; } } static Protocol parse(const std::string& str, const AllowSuffix allow_suffix, const char *title = nullptr) { Protocol ret; if (string::strcasecmp(str, "adaptive") == 0) return ret; ret.type_ = parse_type(str, allow_suffix); if (ret.type_ == NONE) { if (!title) title = "protocol"; OPENVPN_THROW(option_error, "error parsing " << title << ": " << str); } return ret; } static bool is_local_type(const std::string& str) { if (str.empty()) return false; if (str[0] != 'u' && str[0] != 'U' // unix fast path && str[0] != 'n' && str[0] != 'N') // named pipe fast path return false; const Type type = parse_type(str, NO_SUFFIX); return type == UnixStream || type == UnixDGram || type == NamedPipe; } int transport_proto() const { switch (type_) { case UDPv4: return 0; case TCPv4: return 1; case UDPv6: return 0; case TCPv6: return 1; case UnixDGram: return 2; case UnixStream: return 3; case NamedPipe: return 4; case TLSv4: case TLSv6: return 5; default: return -1; } } const char *str() const { switch (type_) { case UDPv4: return "UDPv4"; case TCPv4: return "TCPv4"; case UDPv6: return "UDPv6"; case TCPv6: return "TCPv6"; case TLSv4: return "TLS/TCPv4"; case TLSv6: return "TLS/TCPv6"; case UnixStream: return "UnixStream"; case UnixDGram: return "UnixDGram"; case NamedPipe: return "NamedPipe"; default: return "UNDEF_PROTO"; } } /* This function returns a parseable string representation of the used * transport protocol. NOTE: returns nullptr if there is no mapping */ const char *protocol_to_string() const { switch (type_) { case UDPv4: return "udp4"; case TCPv4: return "tcp4"; case UDPv6: return "udp6"; case TCPv6: return "tcp6"; case TLSv4: return "tls4"; case TLSv6: return "tls6"; case UnixStream: return "unix-stream"; case UnixDGram: return "unix-dgram"; case NamedPipe: return "named-pipe"; case NONE: return "adaptive"; default: return nullptr; } } const char *str_client(const bool force_ipv4) const { switch (type_) { case UDPv4: return "UDPv4"; case TCPv4: return "TCPv4_CLIENT"; case UDPv6: return force_ipv4 ? "UDPv4" : "UDPv6"; case TCPv6: return force_ipv4 ? "TCPv4_CLIENT" : "TCPv6_CLIENT"; case TLSv4: return "TLSv4"; case TLSv6: return force_ipv4 ? "TLSv4" : "TLSv6"; default: return "UNDEF_PROTO"; } } private: static Type parse_type(const std::string& str, const AllowSuffix allow_suffix) { Type ret = NONE; std::string s = str; string::to_lower(s); switch (allow_suffix) { case NO_SUFFIX: break; case CLIENT_SUFFIX: if (string::ends_with(s, "-client")) s = s.substr(0, s.length()-7); break; case SERVER_SUFFIX: if (string::ends_with(s, "-server")) s = s.substr(0, s.length()-7); break; } if (string::starts_with(s, "unix")) // unix domain socket { if (s == "unix-stream") ret = UnixStream; else if (s == "unix-dgram") ret = UnixDGram; } else if (s == "named-pipe") // Windows named pipe ret = NamedPipe; else if (s.length() >= 3) // udp/tcp { const std::string s1 = s.substr(0, 3); const std::string s2 = s.substr(3); if (s2 == "" || s2 == "4" || s2 == "v4") { if (s1 == "udp") ret = UDPv4; else if (s1 == "tcp") ret = TCPv4; else if (s1 == "tls") ret = TLSv4; } else if (s2 == "6" || s2 == "v6") { if (s1 == "udp") ret = UDPv6; else if (s1 == "tcp") ret = TCPv6; else if (s1 == "tls") ret = TLSv6; } } return ret; } Type type_; }; } // namespace openvpn #endif // OPENVPN_TRANSPORT_PROTOCOL_H