Merge commit '86cc97e55fe346502462284d2e636a2b3708163e' as 'Sources/OpenVPN3'

This commit is contained in:
Sergey Abramchuk
2020-02-24 14:43:11 +03:00
655 changed files with 146468 additions and 0 deletions
+112
View File
@@ -0,0 +1,112 @@
// 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-2017 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/>.
// Denote the data in an HTTP header
#ifndef OPENVPN_HTTP_HEADER_H
#define OPENVPN_HTTP_HEADER_H
#include <string>
#include <sstream>
#include <utility>
#include <openvpn/common/size.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/string.hpp>
namespace openvpn {
namespace HTTP {
struct Header {
Header() {}
Header(std::string name_arg, std::string value_arg)
: name(std::move(name_arg)), value(std::move(value_arg)) {}
bool name_match(const std::string& n) const
{
return string::strcasecmp(n, name) == 0;
}
std::string to_string() const
{
std::ostringstream out;
out << name << '=' << value;
return out.str();
}
std::string name;
std::string value;
};
struct HeaderList : public std::vector<Header>
{
const Header* get(const std::string& key) const
{
for (auto &h : *this)
{
if (h.name_match(key))
return &h;
}
return nullptr;
}
Header* get(const std::string& key)
{
for (auto &h : *this)
{
if (h.name_match(key))
return &h;
}
return nullptr;
}
std::string get_value(const std::string& key) const
{
const Header* h = get(key);
if (h)
return h->value;
else
return "";
}
std::string get_value_trim(const std::string& key) const
{
return string::trim_copy(get_value(key));
}
std::string get_value_trim_lower(const std::string& key) const
{
return string::to_lower_copy(get_value_trim(key));
}
std::string to_string() const
{
std::ostringstream out;
for (size_t i = 0; i < size(); ++i)
out << '[' << i << "] " << (*this)[i].to_string() << std::endl;
return out.str();
}
};
}
}
#endif
+283
View File
@@ -0,0 +1,283 @@
// 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-2017 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/>.
// A state machine to skip extraneous HTML in an
// HTTP CONNECT proxy response.
// Matches on typical HTML blocks:
//
// <!doctype html> ... </html>
// <html> ... </html>
//
// Notes:
// 1. Case insensitive
// 2. </html> can be followed by CR/LF chars
#ifndef OPENVPN_HTTP_HTMLSKIP_H
#define OPENVPN_HTTP_HTMLSKIP_H
#include <openvpn/buffer/buffer.hpp>
namespace openvpn {
namespace HTTP {
class HTMLSkip
{
public:
enum Status {
PENDING,
MATCH,
NOMATCH,
};
HTMLSkip()
: state(INITIAL),
residual(64, 0),
bytes(0)
{}
Status add(unsigned char c)
{
bool retain = false;
++bytes;
switch (state)
{
case INITIAL:
retain = true;
if (c == '<')
state = O_OPEN;
else
state = FAIL;
break;
case O_OPEN:
retain = true;
if (c == '!')
state = O_BANG;
else if (c == 'h' || c == 'H')
state = O_HTML_H;
else
state = FAIL;
break;
case O_BANG:
retain = true;
if (c == 'd' || c == 'D')
state = O_DOCTYPE_D;
else
state = FAIL;
break;
case O_DOCTYPE_D:
retain = true;
if (c == 'o' || c == 'O')
state = O_DOCTYPE_O;
else
state = FAIL;
break;
case O_DOCTYPE_O:
retain = true;
if (c == 'c' || c == 'C')
state = O_DOCTYPE_C;
else
state = FAIL;
break;
case O_DOCTYPE_C:
retain = true;
if (c == 't' || c == 'T')
state = O_DOCTYPE_T;
else
state = FAIL;
break;
case O_DOCTYPE_T:
retain = true;
if (c == 'y' || c == 'Y')
state = O_DOCTYPE_Y;
else
state = FAIL;
break;
case O_DOCTYPE_Y:
retain = true;
if (c == 'p' || c == 'P')
state = O_DOCTYPE_P;
else
state = FAIL;
break;
case O_DOCTYPE_P:
retain = true;
if (c == 'e' || c == 'E')
state = O_DOCTYPE_SPACE;
else
state = FAIL;
break;
case O_DOCTYPE_SPACE:
retain = true;
if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
break;
else if (c == 'h' || c == 'H')
state = O_HTML_H;
else
state = FAIL;
break;
case O_HTML_H:
retain = true;
if (c == 't' || c == 'T')
state = O_HTML_T;
else
state = FAIL;
break;
case O_HTML_T:
retain = true;
if (c == 'm' || c == 'M')
state = O_HTML_M;
else
state = FAIL;
break;
case O_HTML_M:
if (c == 'l' || c == 'L')
{
state = CONTENT;
residual.reset_content();
}
else
{
state = FAIL;
retain = true;
}
break;
case CONTENT:
if (c == '<')
state = C_OPEN;
break;
case C_OPEN:
if (c == '/')
state = C_SLASH;
else
state = CONTENT;
break;
case C_SLASH:
if (c == 'h' || c == 'H')
state = C_HTML_H;
else
state = CONTENT;
break;
case C_HTML_H:
if (c == 't' || c == 'T')
state = C_HTML_T;
else
state = CONTENT;
break;
case C_HTML_T:
if (c == 'm' || c == 'M')
state = C_HTML_M;
else
state = CONTENT;
break;
case C_HTML_M:
if (c == 'l' || c == 'L')
state = C_HTML_L;
else
state = CONTENT;
break;
case C_HTML_L:
if (c == '>')
state = C_CRLF;
else
state = CONTENT;
break;
case C_CRLF:
if (!(c == '\n' || c == '\r'))
{
residual.reset_content();
residual.push_back(c);
state = DONE;
}
break;
default:
retain = true;
break;
}
if (retain)
residual.push_back(c);
switch (state)
{
case DONE:
return MATCH;
case FAIL:
return NOMATCH;
default:
return PENDING;
}
}
void get_residual(BufferAllocated& buf) const
{
if (residual.size() <= buf.offset())
{
buf.prepend(residual.c_data(), residual.size());
}
else
{
BufferAllocated newbuf(residual.size() + buf.size(), 0);
newbuf.write(residual.c_data(), residual.size());
newbuf.write(buf.c_data(), buf.size());
buf.move(newbuf);
}
}
unsigned long n_bytes() const { return bytes; }
private:
enum State {
DONE,
FAIL,
INITIAL,
O_OPEN,
O_BANG,
O_DOCTYPE_D,
O_DOCTYPE_O,
O_DOCTYPE_C,
O_DOCTYPE_T,
O_DOCTYPE_Y,
O_DOCTYPE_P,
O_DOCTYPE_SPACE,
O_HTML_H,
O_HTML_T,
O_HTML_M,
CONTENT,
C_OPEN,
C_SLASH,
C_HTML_H,
C_HTML_T,
C_HTML_M,
C_HTML_L,
C_CRLF,
};
State state;
BufferAllocated residual;
unsigned long bytes;
};
}
}
#endif
+47
View File
@@ -0,0 +1,47 @@
// 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-2017 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_HTTP_METHOD_H
#define OPENVPN_HTTP_METHOD_H
namespace openvpn {
namespace HTTP {
namespace Method {
enum Type {
OTHER,
GET,
POST,
};
Type parse(const std::string& methstr)
{
if (methstr == "GET")
return GET;
else if (methstr == "POST")
return POST;
else
return OTHER;
}
}
}
}
#endif
@@ -0,0 +1,86 @@
// 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-2017 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/>.
//
// Adapted from code Copyright (c) 2003-2012 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// Common utility methods for HTTP classes
#ifndef OPENVPN_HTTP_PARSEUTIL_H
#define OPENVPN_HTTP_PARSEUTIL_H
namespace openvpn {
namespace HTTP {
namespace Util {
// Check if a byte is an HTTP character.
inline bool is_char(const unsigned char c)
{
return c <= 127;
}
// Check if a byte is an HTTP control character.
inline bool is_ctl(const unsigned char c)
{
return (c <= 31)|| (c == 127);
}
// Check if a byte is defined as an HTTP tspecial character.
inline bool is_tspecial(const unsigned char c)
{
switch (c)
{
case '(': case ')': case '<': case '>': case '@':
case ',': case ';': case ':': case '\\': case '"':
case '/': case '[': case ']': case '?': case '=':
case '{': case '}': case ' ': case '\t':
return true;
default:
return false;
}
}
// Check if a byte is a digit.
inline bool is_digit(const unsigned char c)
{
return c >= '0' && c <= '9';
}
// Check if char should be URL-escaped
inline bool is_escaped(const unsigned char c)
{
if (c >= 'a' && c <= 'z')
return false;
if (c >= 'A' && c <= 'Z')
return false;
if (c >= '0' && c <= '9')
return false;
if (c == '.' || c == '-' || c == '_')
return false;
return true;
}
}
}
}
#endif
+398
View File
@@ -0,0 +1,398 @@
// 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-2017 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/>.
//
// Adapted from code Copyright (c) 2003-2012 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// Parse an HTTP reply
#ifndef OPENVPN_HTTP_REPLY_H
#define OPENVPN_HTTP_REPLY_H
#include <openvpn/http/header.hpp>
#include <openvpn/http/parseutil.hpp>
namespace openvpn {
namespace HTTP {
struct Reply {
Reply() : http_version_major(0), http_version_minor(0), status_code(0) {}
void reset()
{
http_version_major = 0;
http_version_minor = 0;
status_code = 0;
status_text = "";
headers.clear();
}
std::string to_string() const
{
std::ostringstream out;
out << "HTTP Reply" << std::endl;
out << "version=" << http_version_major << '/' << http_version_minor << std::endl;
out << "status_code=" << status_code << std::endl;
out << "status_text=" << status_text << std::endl;
out << headers.to_string();
return out.str();
}
int http_version_major;
int http_version_minor;
int status_code;
std::string status_text;
HeaderList headers;
};
class ReplyParser {
enum state
{
http_version_h,
http_version_t_1,
http_version_t_2,
http_version_p,
http_version_slash,
http_version_major_start,
http_version_major,
http_version_minor_start,
http_version_minor,
status_code_start,
status_code,
status_text_start,
status_text,
expecting_newline_1,
header_line_start,
header_lws,
header_name,
space_before_header_value,
header_value,
expecting_newline_2,
expecting_newline_3
};
public:
enum status {
pending,
fail,
success,
};
ReplyParser()
: state_(http_version_h)
{
}
// Reset to initial parser state.
void reset()
{
state_ = http_version_h;
}
// Parse some HTTP reply data.
status consume(Reply& req, const unsigned char input)
{
switch (state_)
{
case http_version_h:
if (input == 'H')
{
state_ = http_version_t_1;
return pending;
}
else
{
return fail;
}
case http_version_t_1:
if (input == 'T')
{
state_ = http_version_t_2;
return pending;
}
else
{
return fail;
}
case http_version_t_2:
if (input == 'T')
{
state_ = http_version_p;
return pending;
}
else
{
return fail;
}
case http_version_p:
if (input == 'P')
{
state_ = http_version_slash;
return pending;
}
else
{
return fail;
}
case http_version_slash:
if (input == '/')
{
req.http_version_major = 0;
req.http_version_minor = 0;
state_ = http_version_major_start;
return pending;
}
else
{
return fail;
}
case http_version_major_start:
if (Util::is_digit(input))
{
req.http_version_major = req.http_version_major * 10 + input - '0';
state_ = http_version_major;
return pending;
}
else
{
return fail;
}
case http_version_major:
if (input == '.')
{
state_ = http_version_minor_start;
return pending;
}
else if (Util::is_digit(input))
{
req.http_version_major = req.http_version_major * 10 + input - '0';
return pending;
}
else
{
return fail;
}
case http_version_minor_start:
if (Util::is_digit(input))
{
req.http_version_minor = req.http_version_minor * 10 + input - '0';
state_ = http_version_minor;
return pending;
}
else
{
return fail;
}
case http_version_minor:
if (input == ' ')
{
state_ = status_code_start;
return pending;
}
else if (Util::is_digit(input))
{
req.http_version_minor = req.http_version_minor * 10 + input - '0';
return pending;
}
else
{
return fail;
}
case status_code_start:
if (Util::is_digit(input))
{
req.status_code = req.status_code * 10 + input - '0';
state_ = status_code;
return pending;
}
else
{
return fail;
}
case status_code:
if (input == ' ')
{
state_ = status_text_start;
return pending;
}
else if (Util::is_digit(input))
{
req.status_code = req.status_code * 10 + input - '0';
return pending;
}
else
{
return fail;
}
case status_text_start:
if (!Util::is_char(input) || Util::is_ctl(input) || Util::is_tspecial(input))
{
return fail;
}
else
{
state_ = status_text;
req.status_text.push_back(input);
return pending;
}
case status_text:
if (input == '\r')
{
state_ = expecting_newline_1;
return pending;
}
else if (!Util::is_char(input) || Util::is_ctl(input))
{
return fail;
}
else
{
req.status_text.push_back(input);
return pending;
}
case expecting_newline_1:
if (input == '\n')
{
state_ = header_line_start;
return pending;
}
else
{
return fail;
}
case header_line_start:
if (input == '\r')
{
state_ = expecting_newline_3;
return pending;
}
else if (!req.headers.empty() && (input == ' ' || input == '\t'))
{
state_ = header_lws;
return pending;
}
else if (!Util::is_char(input) || Util::is_ctl(input) || Util::is_tspecial(input))
{
return fail;
}
else
{
req.headers.push_back(Header());
req.headers.back().name.push_back(input);
state_ = header_name;
return pending;
}
case header_lws:
if (input == '\r')
{
state_ = expecting_newline_2;
return pending;
}
else if (input == ' ' || input == '\t')
{
return pending;
}
else if (Util::is_ctl(input))
{
return fail;
}
else
{
state_ = header_value;
req.headers.back().value.push_back(input);
return pending;
}
case header_name:
if (input == ':')
{
state_ = space_before_header_value;
return pending;
}
else if (!Util::is_char(input) || Util::is_ctl(input) || Util::is_tspecial(input))
{
return fail;
}
else
{
req.headers.back().name.push_back(input);
return pending;
}
case space_before_header_value:
if (input == ' ')
{
state_ = header_value;
return pending;
}
else
{
return fail;
}
case header_value:
if (input == '\r')
{
state_ = expecting_newline_2;
return pending;
}
else if (Util::is_ctl(input))
{
return fail;
}
else
{
req.headers.back().value.push_back(input);
return pending;
}
case expecting_newline_2:
if (input == '\n')
{
state_ = header_line_start;
return pending;
}
else
{
return fail;
}
case expecting_newline_3:
if (input == '\n')
return success;
else
return fail;
default:
return fail;
}
}
private:
// The current state of the parser.
state state_;
};
struct ReplyType
{
typedef Reply State;
typedef ReplyParser Parser;
};
}
}
#endif
+398
View File
@@ -0,0 +1,398 @@
// 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-2017 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/>.
//
// Adapted from code Copyright (c) 2003-2012 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// Parse an HTTP request
#ifndef OPENVPN_HTTP_REQUEST_H
#define OPENVPN_HTTP_REQUEST_H
#include <openvpn/http/header.hpp>
#include <openvpn/http/parseutil.hpp>
namespace openvpn {
namespace HTTP {
struct Request {
Request() : http_version_major(0), http_version_minor(0) {}
void reset()
{
method = "";
uri = "";
http_version_major = 0;
http_version_minor = 0;
headers.clear();
}
std::string to_string() const
{
std::ostringstream out;
out << "HTTP Request" << std::endl;
out << "method=" << method << std::endl;
out << "uri=" << uri << std::endl;
out << "version=" << http_version_major << '/' << http_version_minor << std::endl;
out << headers.to_string();
return out.str();
}
std::string to_string_compact() const
{
std::ostringstream out;
out << method << ' ' << uri << " HTTP/" << http_version_major << '.' << http_version_minor;
return out.str();
}
bool at_least_http_1_1() const
{
return http_version_major > 1 || (http_version_major == 1 && http_version_minor >= 1);
}
std::string method;
std::string uri;
int http_version_major;
int http_version_minor;
HeaderList headers;
};
class RequestParser {
enum state
{
method_start,
method,
uri,
http_version_h,
http_version_t_1,
http_version_t_2,
http_version_p,
http_version_slash,
http_version_major_start,
http_version_major,
http_version_minor_start,
http_version_minor,
expecting_newline_1,
header_line_start,
header_lws,
header_name,
space_before_header_value,
header_value,
expecting_newline_2,
expecting_newline_3
};
public:
enum status {
pending,
fail,
success,
};
RequestParser()
: state_(method_start)
{
}
// Reset to initial parser state.
void reset()
{
state_ = method_start;
}
// Parse some HTTP request data.
status consume(Request& req, const unsigned char input)
{
switch (state_)
{
case method_start:
if (!Util::is_char(input) || Util::is_ctl(input) || Util::is_tspecial(input))
{
return fail;
}
else
{
state_ = method;
req.method.push_back(input);
return pending;
}
case method:
if (input == ' ')
{
state_ = uri;
return pending;
}
else if (!Util::is_char(input) || Util::is_ctl(input) || Util::is_tspecial(input))
{
return fail;
}
else
{
req.method.push_back(input);
return pending;
}
case uri:
if (input == ' ')
{
state_ = http_version_h;
return pending;
}
else if (Util::is_ctl(input))
{
return fail;
}
else
{
req.uri.push_back(input);
return pending;
}
case http_version_h:
if (input == 'H')
{
state_ = http_version_t_1;
return pending;
}
else
{
return fail;
}
case http_version_t_1:
if (input == 'T')
{
state_ = http_version_t_2;
return pending;
}
else
{
return fail;
}
case http_version_t_2:
if (input == 'T')
{
state_ = http_version_p;
return pending;
}
else
{
return fail;
}
case http_version_p:
if (input == 'P')
{
state_ = http_version_slash;
return pending;
}
else
{
return fail;
}
case http_version_slash:
if (input == '/')
{
req.http_version_major = 0;
req.http_version_minor = 0;
state_ = http_version_major_start;
return pending;
}
else
{
return fail;
}
case http_version_major_start:
if (Util::is_digit(input))
{
req.http_version_major = req.http_version_major * 10 + input - '0';
state_ = http_version_major;
return pending;
}
else
{
return fail;
}
case http_version_major:
if (input == '.')
{
state_ = http_version_minor_start;
return pending;
}
else if (Util::is_digit(input))
{
req.http_version_major = req.http_version_major * 10 + input - '0';
return pending;
}
else
{
return fail;
}
case http_version_minor_start:
if (Util::is_digit(input))
{
req.http_version_minor = req.http_version_minor * 10 + input - '0';
state_ = http_version_minor;
return pending;
}
else
{
return fail;
}
case http_version_minor:
if (input == '\r')
{
state_ = expecting_newline_1;
return pending;
}
else if (Util::is_digit(input))
{
req.http_version_minor = req.http_version_minor * 10 + input - '0';
return pending;
}
else
{
return fail;
}
case expecting_newline_1:
if (input == '\n')
{
state_ = header_line_start;
return pending;
}
else
{
return fail;
}
case header_line_start:
if (input == '\r')
{
state_ = expecting_newline_3;
return pending;
}
else if (!req.headers.empty() && (input == ' ' || input == '\t'))
{
state_ = header_lws;
return pending;
}
else if (!Util::is_char(input) || Util::is_ctl(input) || Util::is_tspecial(input))
{
return fail;
}
else
{
req.headers.push_back(Header());
req.headers.back().name.push_back(input);
state_ = header_name;
return pending;
}
case header_lws:
if (input == '\r')
{
state_ = expecting_newline_2;
return pending;
}
else if (input == ' ' || input == '\t')
{
return pending;
}
else if (Util::is_ctl(input))
{
return fail;
}
else
{
state_ = header_value;
req.headers.back().value.push_back(input);
return pending;
}
case header_name:
if (input == ':')
{
state_ = space_before_header_value;
return pending;
}
else if (!Util::is_char(input) || Util::is_ctl(input) || Util::is_tspecial(input))
{
return fail;
}
else
{
req.headers.back().name.push_back(input);
return pending;
}
case space_before_header_value:
if (input == ' ')
{
state_ = header_value;
return pending;
}
else
{
return fail;
}
case header_value:
if (input == '\r')
{
state_ = expecting_newline_2;
return pending;
}
else if (Util::is_ctl(input))
{
return fail;
}
else
{
req.headers.back().value.push_back(input);
return pending;
}
case expecting_newline_2:
if (input == '\n')
{
state_ = header_line_start;
return pending;
}
else
{
return fail;
}
case expecting_newline_3:
if (input == '\n')
return success;
else
return fail;
default:
return fail;
}
}
private:
// The current state of the parser.
state state_;
};
struct RequestType
{
typedef Request State;
typedef RequestParser Parser;
};
}
}
#endif
+79
View File
@@ -0,0 +1,79 @@
// 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-2017 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_HTTP_STATUS_H
#define OPENVPN_HTTP_STATUS_H
// HTTP status codes
namespace openvpn {
namespace HTTP {
namespace Status {
enum {
OK=200,
Connected=200,
SwitchingProtocols=101,
BadRequest=400,
Unauthorized=401,
Forbidden=403,
NotFound=404,
ProxyAuthenticationRequired=407,
PreconditionFailed=412,
InternalServerError=500,
ProxyError=502,
ServiceUnavailable=503,
};
inline const char *to_string(const int status)
{
switch (status)
{
case OK:
return "OK";
case SwitchingProtocols:
return "Switching Protocols";
case BadRequest:
return "Bad Request";
case Unauthorized:
return "Unauthorized";
case Forbidden:
return "Forbidden";
case NotFound:
return "Not Found";
case ProxyAuthenticationRequired:
return "Proxy Authentication Required";
case PreconditionFailed:
return "Precondition Failed";
case InternalServerError:
return "Internal Server Error";
case ProxyError:
return "Proxy Error";
case ServiceUnavailable:
return "Service Unavailable";
default:
return "";
}
}
}
}
}
#endif
+120
View File
@@ -0,0 +1,120 @@
// 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-2017 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_HTTP_URLENCODE_H
#define OPENVPN_HTTP_URLENCODE_H
#include <string>
#include <vector>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/split.hpp>
#include <openvpn/common/string.hpp>
#include <openvpn/common/hexstr.hpp>
#include <openvpn/common/unicode.hpp>
#include <openvpn/http/parseutil.hpp>
namespace openvpn {
namespace URL {
OPENVPN_EXCEPTION(url_error);
inline std::string encode(const std::string& str)
{
std::string ret;
ret.reserve(str.length() * 2); // just a guess
for (auto &c : str)
{
if (HTTP::Util::is_escaped(c))
{
ret += '%';
ret += render_hex_number((unsigned char)c, true);
}
else
ret += c;
}
return ret;
}
inline std::string decode(const std::string& encoded)
{
enum State {
TEXT,
PERCENT,
DIGIT
};
int value = 0;
State state = TEXT;
std::string ret;
ret.reserve(encoded.size()); // just a guess
for (auto &c : encoded)
{
switch (state)
{
case TEXT:
{
if (c == '%')
state = PERCENT;
else
ret += c;
break;
}
case PERCENT:
{
const int v = parse_hex_char(c);
if (v < 0)
throw url_error(std::string("decode error after %: ") + encoded);
value = v;
state = DIGIT;
break;
}
case DIGIT:
{
const int v = parse_hex_char(c);
if (v < 0)
throw url_error(std::string("decode error after %: ") + encoded);
ret += static_cast<unsigned char>((value * 16) + v);
state = TEXT;
}
}
}
if (state != TEXT)
throw url_error(std::string("decode error: %-encoding item not closed out: ") + encoded);
if (!Unicode::is_valid_utf8(ret))
throw url_error(std::string("not UTF-8: ") + encoded);
return ret;
}
inline std::vector<std::string> decode_path(std::string path)
{
std::vector<std::string> list;
if (!path.empty() && path[0] == '/')
path = path.substr(1);
Split::by_char_void<decltype(list), NullLex, Split::NullLimit>(list, path, '/');
for (auto &i : list)
i = decode(i);
return list;
}
}
}
#endif
+211
View File
@@ -0,0 +1,211 @@
// 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-2017 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_HTTP_URLPARM_H
#define OPENVPN_HTTP_URLPARM_H
#include <string>
#include <sstream>
#include <vector>
#include <openvpn/common/number.hpp>
#include <openvpn/http/urlencode.hpp>
#include <openvpn/http/webexcept.hpp>
#include <openvpn/common/string.hpp>
namespace openvpn {
namespace URL {
OPENVPN_EXCEPTION(url_parameter_error);
struct Parm
{
Parm() {}
Parm(const std::string& name_arg, const std::string& value_arg)
: name(name_arg), value(value_arg)
{
}
std::string to_string() const
{
std::ostringstream out;
out << name << '=' << value;
return out.str();
}
std::string name;
std::string value;
};
class ParmList : public std::vector<Parm>
{
public:
ParmList(const std::string& uri)
{
try {
const std::vector<std::string> req_parms = string::split(uri, '?', 1);
request_ = req_parms[0];
if (req_parms.size() == 2)
{
const std::vector<std::string> kv_list = string::split(req_parms[1], '&');
for (auto &kvstr : kv_list)
{
const std::vector<std::string> kv = string::split(kvstr, '=', 1);
Parm p;
p.name = decode(kv[0]);
if (kv.size() == 2)
p.value = decode(kv[1]);
push_back(std::move(p));
}
}
}
catch (const std::exception& e)
{
throw HTTP::WebException(HTTP::Status::BadRequest, e.what());
}
}
const Parm* get(const std::string& key) const
{
for (auto &p : *this)
{
if (key == p.name)
return &p;
}
return nullptr;
}
std::string get_value(const std::string& key) const
{
const Parm* p = get(key);
if (p)
return p->value;
else
return "";
}
const std::string& get_value_required(const std::string& key) const
{
const Parm* p = get(key);
if (p)
return p->value;
else
throw url_parameter_error(key + " : not found");
}
template <typename T>
T get_num(const std::string& name, const std::string& short_name, const T default_value) const
{
const Parm* p = get(name);
if (!p && !short_name.empty())
p = get(short_name);
if (p)
return parse_number_throw<T>(p->value, name);
else
return default_value;
}
template <typename T>
T get_num_required(const std::string& name, const std::string& short_name) const
{
const Parm* p = get(name);
if (!p && !short_name.empty())
p = get(short_name);
if (!p)
throw url_parameter_error(name + " : not found");
return parse_number_throw<T>(p->value, name);
}
bool get_bool(const std::string& name, const std::string& short_name, const bool default_value) const
{
const Parm* p = get(name);
if (!p && !short_name.empty())
p = get(short_name);
if (p)
{
if (p->value == "0")
return false;
else if (p->value == "1")
return true;
else
throw url_parameter_error(name + ": parameter must be 0 or 1");
}
else
return default_value;
}
std::string get_string(const std::string& name, const std::string& short_name) const
{
const Parm* p = get(name);
if (!p && !short_name.empty())
p = get(short_name);
if (p)
return p->value;
else
return "";
}
std::string get_string_required(const std::string& name, const std::string& short_name) const
{
const Parm* p = get(name);
if (!p && !short_name.empty())
p = get(short_name);
if (!p)
throw url_parameter_error(name + " : not found");
return p->value;
}
std::string to_string() const
{
std::ostringstream out;
for (size_t i = 0; i < size(); ++i)
out << '[' << i << "] " << (*this)[i].to_string() << std::endl;
return out.str();
}
std::string request(const bool remove_leading_slash) const
{
std::string ret = request_;
if (remove_leading_slash)
{
if (ret.length() > 0 && ret[0] == '/')
ret = ret.substr(1);
else
throw HTTP::WebException(HTTP::Status::BadRequest, "URI missing leading slash");
}
if (ret.empty())
throw HTTP::WebException(HTTP::Status::BadRequest, "URI resource is empty");
return ret;
}
const std::string& request() const
{
return request_;
}
private:
std::string request_;
};
}
}
#endif
+262
View File
@@ -0,0 +1,262 @@
// 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-2017 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_HTTP_URLPARSE_H
#define OPENVPN_HTTP_URLPARSE_H
#include <string>
#include <openvpn/common/platform.hpp>
#include <openvpn/common/exception.hpp>
#include <openvpn/common/string.hpp>
#include <openvpn/common/hostport.hpp>
#include <openvpn/common/format.hpp>
#include <openvpn/http/validate_uri.hpp>
#include <openvpn/http/parseutil.hpp>
namespace openvpn {
namespace URL {
OPENVPN_EXCEPTION(url_parse_error);
class Parse
{
public:
Parse() {}
Parse(const std::string& url,
const bool set_default_port=false,
const bool loose_validation=false)
{
enum State {
Scheme,
PostSchemeSlash1,
PostSchemeSlash2,
StartHost,
Host,
BracketedHost,
PostBracketedHost,
Port,
URI,
};
State state = Scheme;
for (auto &c : url)
{
reprocess:
switch (state)
{
case Scheme:
if (c == ':')
state = PostSchemeSlash1;
else if (is_valid_scheme_char(c))
scheme += c;
else
throw url_parse_error("bad scheme char");
break;
case PostSchemeSlash1:
if (c == '/')
state = PostSchemeSlash2;
else
throw url_parse_error("expected '://' after scheme");
break;
case PostSchemeSlash2:
if (c == '/')
state = StartHost;
else
throw url_parse_error("expected '://' after scheme");
break;
case StartHost:
if (c == '[')
state = BracketedHost;
else
{
state = Host;
goto reprocess;
}
break;
case Host:
if (c == ':')
state = Port;
else if (c == '/')
{
state = URI;
goto reprocess;
}
else
host += c;
break;
case BracketedHost:
if (c == ']')
state = PostBracketedHost;
else
host += c;
break;
case PostBracketedHost:
if (c == ':')
{
state = Port;
break;
}
else
{
state = URI;
goto reprocess;
}
break;
case Port:
if (c == '/')
{
state = URI;
goto reprocess;
}
else
port += c;
break;
case URI:
if (!HTTP::is_valid_uri_char(c) && !loose_validation)
throw url_parse_error("bad URI char");
uri += c;
break;
}
}
if (set_default_port)
default_port();
if (uri.empty())
uri = "/";
validate();
}
// Note that special address types such as unix domain
// sockets or windows named pipes store a tag such as
// "unix" or "np" as the port component of an address/port
// tuple. Here, we move such tags into the scheme.
static Parse from_components(const bool https,
const std::string& host,
const std::string& port,
const std::string& uri)
{
Parse p;
p.scheme = https ? "https" : "http";
p.host = host;
if (port.size() >= 1 && !string::is_digit(port[0])) // non-INET address
p.scheme = port;
else
p.port = port;
p.uri = uri;
return p;
}
void validate() const
{
if (scheme.empty())
throw url_parse_error("undefined scheme");
if (host.empty())
throw url_parse_error("undefined host");
if (uri.empty())
throw url_parse_error("undefined uri");
if (!port.empty() && !HostPort::is_valid_port(port))
throw url_parse_error("bad port");
if ((scheme == "http" || scheme == "https") && !HostPort::is_valid_host(host))
throw url_parse_error("bad host");
}
void default_port()
{
if (port.empty())
{
if (scheme == "http")
port = "80";
else if (scheme == "https")
port = "443";
}
}
bool port_implied() const
{
return (scheme == "http" && port == "80") || (scheme == "https" && port == "443");
}
std::string to_string() const
{
const bool bracket_host = (host.find_first_of(":/\\") != std::string::npos);
std::string ret;
ret.reserve(256);
ret += scheme;
ret += "://";
if (bracket_host)
ret += '[';
ret += host;
if (bracket_host)
ret += ']';
if (!port.empty() && !port_implied())
{
ret += ':';
ret += port;
}
ret += uri;
return ret;
}
std::string format_components() const
{
return printfmt("[scheme=%r host=%r port=%r uri=%r]", scheme, host, port, uri);
}
// Note that special address types such as unix domain
// sockets or windows named pipes store a tag such as
// "unix" or "np" as the port component of an address/port
// tuple. This method returns the port number for INET
// addresses or a special tag for non-INET addresses.
// Internally, we store the tag as an alternative
// scheme such as "unix" or "np".
std::string port_for_scheme() const
{
#ifdef OPENVPN_PLATFORM_WIN
if (scheme == "np") // named pipe
return scheme;
#else
if (scheme == "unix") // unix domain socket
return scheme;
#endif
if (scheme == "http" || scheme == "https")
return port;
throw url_parse_error("unknown scheme");
}
std::string scheme;
std::string host;
std::string port;
std::string uri;
private:
bool is_valid_scheme_char(const char c)
{
return (c >= 'a' && c <= 'z') || c == '_';
}
};
}
}
#endif
@@ -0,0 +1,52 @@
// 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-2017 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/>.
#pragma once
#include <openvpn/common/exception.hpp>
namespace openvpn {
namespace HTTP {
inline bool is_valid_uri_char(const unsigned char c)
{
return c >= 0x21 && c <= 0x7E;
}
inline bool is_valid_uri_char(const char c)
{
return is_valid_uri_char((unsigned char)c);
}
inline void validate_uri(const std::string& uri, const std::string& title)
{
if (uri.empty())
throw Exception(title + " : URI is empty");
if (uri[0] != '/')
throw Exception(title + " : URI must begin with '/'");
for (auto &c : uri)
{
if (!is_valid_uri_char(c))
throw Exception(title + " : URI contains illegal character");
}
}
}
}
@@ -0,0 +1,70 @@
// 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-2017 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_HTTP_EXCEPT_H
#define OPENVPN_HTTP_EXCEPT_H
#include <string>
#include <sstream>
#include <exception>
#include <openvpn/http/status.hpp>
# define OPENVPN_THROW_WEB(exc, status, stuff) \
do { \
std::ostringstream _ovpn_exc; \
_ovpn_exc << stuff; \
throw exc(status, _ovpn_exc.str()); \
} while (0)
namespace openvpn {
namespace HTTP {
class WebException : public std::exception
{
public:
WebException(const int status, const std::string& error)
: status_(status),
error_(error),
formatted(std::string(Status::to_string(status_)) + " : " + error_)
{
}
WebException(const int status)
: status_(status),
error_(Status::to_string(status_)),
formatted(error_)
{
}
int status() const { return status_; }
const std::string& error() const { return error_; }
virtual const char* what() const throw() { return formatted.c_str(); }
private:
const int status_;
const std::string error_;
const std::string formatted;
};
}
}
#endif