Repository: lxrite/azure-http-proxy
Branch: master
Commit: c90005d35cab
Files: 35
Total size: 144.4 KB
Directory structure:
gitextract_3nt7pewi/
├── .github/
│ └── workflows/
│ └── cmake.yml
├── .gitignore
├── .gitmodules
├── CMakeLists.txt
├── Dockerfile
├── LICENSE
├── README.md
├── example/
│ ├── client.json
│ └── server.json
└── src/
├── authentication.cpp
├── authentication.hpp
├── configs/
│ └── ahp_mbedtls_config.h
├── encrypt.hpp
├── hash_utils.cpp
├── hash_utils.hpp
├── http_chunk_checker.hpp
├── http_header_parser.cpp
├── http_header_parser.hpp
├── http_proxy_client.cpp
├── http_proxy_client.hpp
├── http_proxy_client_config.cpp
├── http_proxy_client_config.hpp
├── http_proxy_client_connection.cpp
├── http_proxy_client_connection.hpp
├── http_proxy_client_main.cpp
├── http_proxy_server.cpp
├── http_proxy_server.hpp
├── http_proxy_server_config.cpp
├── http_proxy_server_config.hpp
├── http_proxy_server_connection.cpp
├── http_proxy_server_connection.hpp
├── http_proxy_server_connection_context.hpp
├── http_proxy_server_main.cpp
├── key_generator.hpp
└── version.hpp
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/cmake.yml
================================================
name: Build
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
env:
BUILD_TYPE: Release
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Checkout submodules
run: git submodule update --init --recursive
- name: Configure CMake
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
================================================
FILE: .gitignore
================================================
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
/.idea/
/build/
================================================
FILE: .gitmodules
================================================
[submodule "third_party/jsonxx"]
path = third_party/jsonxx
url = https://github.com/hjiang/jsonxx.git
[submodule "third_party/networking-ts-impl"]
path = third_party/networking-ts-impl
url = https://github.com/chriskohlhoff/networking-ts-impl.git
[submodule "third_party/curi"]
path = third_party/curi
url = https://github.com/lxrite/curi.git
[submodule "third_party/mbedtls"]
path = third_party/mbedtls
url = https://github.com/Mbed-TLS/mbedtls.git
================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required(VERSION 2.6)
project(azure-http-proxy)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
include_directories("third_party/networking-ts-impl/include")
set(ENABLE_PROGRAMS OFF CACHE BOOL "" FORCE)
set(ENABLE_TESTING OFF CACHE BOOL "" FORCE)
set(MBEDTLS_CONFIG_FILE "${CMAKE_CURRENT_SOURCE_DIR}/src/configs/ahp_mbedtls_config.h" CACHE STRING "" FORCE)
add_subdirectory("third_party/mbedtls")
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU|Clang")
target_compile_options(mbedcrypto PRIVATE "-Wno-unreachable-code")
endif()
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
add_compile_options("/std:c++17")
endif()
include_directories("third_party/jsonxx")
add_library(jsonxx STATIC third_party/jsonxx/jsonxx.cc)
include_directories("third_party/curi/src")
add_subdirectory("third_party/curi/src")
add_executable(ahpc src/http_proxy_client_main.cpp src/http_proxy_client.cpp src/http_proxy_client_config.cpp src/http_proxy_client_connection.cpp src/hash_utils.cpp)
target_link_libraries(ahpc mbedtls jsonxx)
add_executable(ahps src/http_proxy_server_main.cpp src/http_proxy_server.cpp src/http_proxy_server_config.cpp src/http_proxy_server_connection.cpp src/http_header_parser.cpp src/hash_utils.cpp src/authentication.cpp)
target_link_libraries(ahps mbedtls jsonxx curi)
if(UNIX)
target_link_libraries(ahpc pthread)
target_link_libraries(ahps pthread)
endif()
if(WIN32)
if(MINGW)
target_link_libraries(ahpc ws2_32 wsock32)
target_link_libraries(ahps ws2_32 wsock32)
endif()
endif()
install(TARGETS ahpc ahps DESTINATION bin)
================================================
FILE: Dockerfile
================================================
FROM alpine:3.18 as builder
RUN apk update \
&& apk add alpine-sdk cmake linux-headers
WORKDIR /ahp
COPY . .
RUN cmake -B build -DCMAKE_BUILD_TYPE=Release \
&& cmake --build build
FROM alpine:3.18
RUN apk update && apk add libgcc libstdc++
COPY --from=builder /ahp/build/ahps /usr/local/bin/ahps
COPY --from=builder /ahp/build/ahpc /usr/local/bin/ahpc
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2013-2015 limhiaoing <blog.poxiao.me>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# azure-http-proxy
## 简介
AHP(Azure Http Proxy)是一款高速、安全、轻量级和跨平台的HTTP代理,使用对称加密算法AES对传输的数据进行加密,使用非对称加密算法RSA传输密钥。
## 特性
- 一连接一密钥,AHP会对每个连接使用一个随机生成的密钥和初始化向量,避免重复使用同一密钥
- 使用非对称加密算法RSA传输密钥,只需对客户端公开RSA公钥
- 对目标域名的解析在服务端进行,可以解决本地DNS污染的问题
- 服务端同时支持多种数据加密方式,数据加密方式可由客户端任意指定,客户端可以权衡机器性能以及安全需求选择合适的加密方式
- 多线程并发处理,充分利用多处理器的优势,能同时处理成千上万的并发连接
- 多用户支持,允许为每个用户使用独立的auth_key `(1.1及以上版本)`
## 编译和安装
### 拉取代码
``` shell
$ git clone --recursive https://github.com/lxrite/azure-http-proxy.git
```
### 编译器
AHP使用了部分C++17特性,所以对编译器的版本有较高要求,下面列出了部分已测试过可以用来编译AHP的编译器
- Microsoft Visual Studio >= 2017
- GCC >= 7.3
- Clang >= 6.0
### 编译
AHP使用自动化构建工具CMake来实现跨平台构建
- CMake >= 2.8
```shell
$ cd azure-http-proxy
$ mkdir build
$ cd build
$ cmake -DCMAKE_BUILD_TYPE=Release ..
$ cmake --build .
```
如果编译成功会生成ahpc(客户端)和ahps(服务端)。
OpenWrt/LEDE 编译参考 [openwrt-ahp](https://github.com/lxrite/openwrt-ahp)
## 配置和运行
完整的配置示例见这里: https://github.com/lxrite/azure-http-proxy/tree/master/example
注意:不要使用示例配置中的RSA私钥和公钥,因为私钥一公开就是不安全的了。
如果你要运行的是服务端,那么你首先需要生成一对RSA密钥对,AHP支持任意长度不小于1024位的RSA密钥。下面的命令使用openssl生成2048位的私钥和公钥
```shell
$ openssl genrsa -out rsa_private_key.pem 2048
$ openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
```
服务端保留私钥并将公钥告诉客户端。
### 配置服务端
编辑`server.json`文件
```json
{
"bind_address": "0.0.0.0",
"listen_port": 8090,
"rsa_private_key": "-----BEGIN RSA PRIVATE KEY----- ...... -----END RSA PRIVATE KEY-----",
"timeout": 240,
"workers": 4,
"auth": true,
"auth_key_list": [
"testing_key",
"Bob",
"Alice"
]
}
```
字段名 | 描述 | 是否必选 | 默认值 |
----------------|--------------------|------------------|-----------|
bind_address | 服务端绑定的IP地址 | 否 | "0.0.0.0" |
listen_port | 服务端绑定的端口 | 否 | 8090 |
rsa_private_key | RSA私钥 | 是 | 无 |
timeout | 超时时间(秒) | 否 | 240 |
workers | 并发工作线程数 | 否 | 4 |
auth | 启用代理身份验证 | 否 | false |
auth_key_list | auth_key列表 | auth为true时必选 | 无 |
### 配置客户端
编辑`client.json`文件
```json
{
"proxy_server_address": "127.0.0.1",
"proxy_server_port": 8090,
"bind_address": "127.0.0.1",
"listen_port": 8089,
"rsa_public_key": "-----BEGIN PUBLIC KEY----- ...... -----END PUBLIC KEY-----",
"cipher": "aes-256-cfb",
"timeout": 240,
"workers": 2,
"auth_key": "testing_key"
}
```
字段名 | 描述 | 是否必选 | 默认值 |
---------------------|----------------------|------------------|---------------|
proxy_server_address | 服务端的IP地址或域名 | 是 | 无 |
proxy_server_port | 服务端的端口 | 是 | 无 |
bind_address | 客户端绑定的IP地址 | 否 | "127.0.0.1" |
listen_port | 客户端的监听端口 | 否 | 8089 |
rsa_public_key | RSA公钥 | 是 | 无 |
cipher | 加密方法 | 否 | "aes-256-cfb" |
timeout | 超时时间(秒) | 否 | 240 |
workers | 并发工作线程数 | 否 | 2 |
auth_key | 用于身份验证的字符串 | 否 | 值为空字符串或没有这个字段时,请求不携带auth_key,仅当server的auth为false时才能成功建立连接|
#### 支持的加密方法
- aes-xyz-cfb
- <del>aes-xyz-cfb8</del> (自1.2版本开始不再支持)
- <del>aes-xyz-cfb1</del> (自1.2版本开始不再支持)
- aes-xyz-ofb
- aes-xyz-ctr
中间的xyz可以为128、192或256。
## 运行
确定配置无误后就可以运行AHP了。
### 运行服务端
Linux或其他类Unix系统
```shell
$ ./ahps -c server.json
```
Windows
```shell
$ ahps.exe -c server.json
```
### 运行客户端
Linux或其他类Unix系统
```shell
$ ./ahpc -c client.json
```
Windows
```shell
$ ahpc.exe -c client.json
```
## 使用Docker
```shell
# 拉取镜像
docker pull ghcr.io/lxrite/azure-http-proxy:latest
# 启动 ahps
docker run -d -p 8090:8090 -v $PWD/server.json:/data/ahp/server.json ghcr.io/lxrite/azure-http-proxy ahps -c /data/ahp/server.json
```
================================================
FILE: example/client.json
================================================
{
"proxy_server_address": "127.0.0.1",
"proxy_server_port": 8090,
"bind_address": "127.0.0.1",
"listen_port": 8089,
"rsa_public_key": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxPbPBU61RYBI0rUDVso+\nTzkQ7bXO1j4GWxbYZ3nEL6sLbrftv7rpYq5uPLi9DdJ3ZoEUjxlnO+VUOOtm7LpR\nGmWqUQdNnYHuiZU1UuH7pDIXejQwwSC698FB1kwnoxV4LICkiA1a4qucqlnG8nN6\ngBFs3/1K2DuUs0Hg1hZKlkOq/ONR82XGhXkB/HVwmfgQlZpVbWHQDsZiOv1SUnQW\n8Zs6E/JmW6llBkWtsQT9nQ2uzcV1JGzV0ltB4N0CMC8u2zv/LLTSgS4IKrVicAqO\n9TWkGOFmGowV7PpEAEQC1WcBXThLpUYk2QqiSvTTLTdFNmwEH+hKa1ZBPqOcaTA1\ndQIDAQAB\n-----END PUBLIC KEY-----",
"cipher": "aes-256-cfb",
"timeout": 240,
"workers": 2,
"auth_key": "testing_key"
}
================================================
FILE: example/server.json
================================================
{
"bind_address": "0.0.0.0",
"listen_port": 8090,
"rsa_private_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAxPbPBU61RYBI0rUDVso+TzkQ7bXO1j4GWxbYZ3nEL6sLbrft\nv7rpYq5uPLi9DdJ3ZoEUjxlnO+VUOOtm7LpRGmWqUQdNnYHuiZU1UuH7pDIXejQw\nwSC698FB1kwnoxV4LICkiA1a4qucqlnG8nN6gBFs3/1K2DuUs0Hg1hZKlkOq/ONR\n82XGhXkB/HVwmfgQlZpVbWHQDsZiOv1SUnQW8Zs6E/JmW6llBkWtsQT9nQ2uzcV1\nJGzV0ltB4N0CMC8u2zv/LLTSgS4IKrVicAqO9TWkGOFmGowV7PpEAEQC1WcBXThL\npUYk2QqiSvTTLTdFNmwEH+hKa1ZBPqOcaTA1dQIDAQABAoIBAQC0FTSSlbQHJ5Nt\nkzLKV39Az9w6es/x8iO9hNW3Rg2px6lcQa6ObkaUgwcnXYD88kFY5wv1CjTo/nRS\n1mf0aSVeDTX7f8HnELUX9eQbM6LwLRxqDu3EpjhL7dZUKKzU3vxRNA06l3tRY7aJ\nyiur4QlPrp7s7JlRuqfqfYEkPJ1VZE5CN/0chY21rD9nK30Y9+Rttq/oIW0NyK74\nOJYgottIoiBRwgc2ZTxXalWq5yKWXBVZZlcTSOxH8lmDY5Z0md2oYfgTBqUUEzlz\nLm9S88+ydBtUVXSlDN4PPh+N3arshFnRZBAxNtxiIGi4PYfYJmb2U6a1lq0nj12g\nKs6Wk0QBAoGBAOiiJBdhy3iL/29Z7floQoa5iQgPxsSh8hv6jgIos7B2n/Nkre68\ngK/ULw9utsj/lq6Lg9amMPv+hW4rbdL88UfQFFcvjixhgqk9RNNvt51Lg03AIM6w\npF+FPWqQOisrQmPgxWnKNoIZsfEayBm/mTPHk6VBIVq7HmeF9hxMjcilAoGBANi/\nes4zzfB7PmvuBJxLHe6YWIqNCk8aS/uZ4gZMGN9stLFOZPeeshhlq4OTu7m37MzD\nD5RERDFA3y+OnpUnsB5nXwYqIslIPQ1H6WeNia/xusE45OthbBu6ZgpDI6TmTQ5S\nAfLJMIaurkxSzi9jtotCtYVQ8iBvXIlVqNuAFlCRAoGAUepONSGNiTwazPXosysA\nSfppAzqy7ihsXWfDu4TjiR6sQgNQr8EWu1NG4vNET9CYGYws91d75jAAggOu312M\nBJKDpxOqx3vqi3d0ldM/35ZofOdLZhyTNMNxFeYFZZANB6htO0wmF3e+zbx1e4OZ\nCb+cANPHT/CVyzRDrkFSp7UCgYAaPjzhQJZ7uoBZUw1N2y55mAqIZAFOiJGQmhYq\nywXr716FZUeGT7miiJTWrol5OBK8zBVTz1wuVntvZ6Y9yvthAwUXWvyxQETcGcvh\n1NpJ6kvBX2EgOsB7Lvtx3KUxLw0/YaCIw+FTPU0vQotiY4FTpTUTOjTGxpT+r9dt\nFDofcQKBgCy5lnl7oAEK7sPNSxjyi1jELBcMl6bcE6sLkyXli7gpQn33NEFKEM16\nSfKNttCJFKx+3LtJNjMZ6xKQsEGHAQTXu9MxDd+g16ws8W+tIeMLf+Hn+sI+UFND\nfToIgs+zqOC67B/MVPDtRkJskhcUFRjqmcFGJIlkK9TiYHQm1ldW\n-----END RSA PRIVATE KEY-----",
"timeout": 240,
"workers": 4,
"auth": true,
"auth_key_list": [
"testing_key",
"Bob",
"Alice",
"+ZI1w$u9N65lTw*nL@$",
"SoHPa4xNMBWoXxSOp+6snUtqtdXFH(MO"
]
}
================================================
FILE: src/authentication.cpp
================================================
/*
* authentication.cpp:
*
* Copyright (C) 2015-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.
*
*/
#include <algorithm>
#include <iterator>
#include "authentication.hpp"
#include "hash_utils.hpp"
namespace azure_proxy {
authentication::authentication()
{
}
bool authentication::auth(const auth_key_hash_t& auth_key_hash) const
{
return this->auth_keys_map.find(auth_key_hash) != this->auth_keys_map.end();
}
void authentication::add_auth_key(const std::string& auth_key)
{
auto auth_key_hash = hash_utils::sha256(reinterpret_cast<const unsigned char*>(auth_key.data()), auth_key.size());
this->auth_keys_map[auth_key_hash] = auth_key;
}
void authentication::remove_all_auth_keys()
{
this->auth_keys_map.clear();
}
authentication& authentication::get_instance()
{
static authentication instance;
return instance;
}
} // namespace azure_proxy
================================================
FILE: src/authentication.hpp
================================================
/*
* authentication.hpp:
*
* Copyright (C) 2015-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.
*
*/
#ifndef AZURE_AUTHENTICATION_HPP
#define AZURE_AUTHENTICATION_HPP
#include <array>
#include <map>
#include <string>
namespace azure_proxy {
using auth_key_hash_t = std::array<unsigned char, 32>;
class authentication {
std::map<auth_key_hash_t, std::string> auth_keys_map;
private:
authentication();
public:
bool auth(const auth_key_hash_t& auth_key_hash) const;
void add_auth_key(const std::string& auth_key);
void remove_all_auth_keys();
static authentication& get_instance();
};
} // namespace azure_proxy
#endif
================================================
FILE: src/configs/ahp_mbedtls_config.h
================================================
#define MBEDTLS_HAVE_TIME
#define MBEDTLS_HAVE_TIME_DATE
#define MBEDTLS_CIPHER_MODE_CFB
#define MBEDTLS_CIPHER_MODE_CTR
#define MBEDTLS_CIPHER_MODE_OFB
#define MBEDTLS_ERROR_STRERROR_DUMMY
#define MBEDTLS_AES_C
#define MBEDTLS_ASN1_PARSE_C
#define MBEDTLS_BASE64_C
#define MBEDTLS_CIPHER_C
#define MBEDTLS_ENTROPY_C
#define MBEDTLS_PEM_PARSE_C
#define MBEDTLS_PLATFORM_C
#define MBEDTLS_SHA1_C
#define MBEDTLS_SHA256_C
#define MBEDTLS_TIMING_C
#define MBEDTLS_VERSION_C
#define MBEDTLS_MD_C
#define MBEDTLS_OID_C
#define MBEDTLS_BIGNUM_C
#define MBEDTLS_RSA_C
#define MBEDTLS_PK_C
#define MBEDTLS_PK_PARSE_C
#define MBEDTLS_PKCS1_V21
#define MBEDTLS_CTR_DRBG_C
================================================
FILE: src/encrypt.hpp
================================================
/*
* encrypt.hpp:
*
* Copyright (C) 2014-2024 Light Lin <lxrite@gmail.com> All Rights Reserved.
*
*/
#ifndef AZURE_ENCRYPT_HPP
#define AZURE_ENCRYPT_HPP
#include <cassert>
#include <cstring>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <mbedtls/cipher.h>
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/entropy.h>
#include <mbedtls/error.h>
#include <mbedtls/pk.h>
#include <mbedtls/rsa.h>
namespace azure_proxy {
class stream_encryptor {
public:
virtual void encrypt(const unsigned char* in, unsigned char* out, std::size_t length) = 0;
virtual ~stream_encryptor() {}
};
class stream_decryptor {
public:
virtual void decrypt(const unsigned char* in, unsigned char* out, std::size_t length) = 0;
virtual ~stream_decryptor() {}
};
class copy_encryptor : public stream_encryptor {
public:
copy_encryptor() {};
virtual void encrypt(const unsigned char* in, unsigned char* out, std::size_t length) {
assert(in && out);
std::memcpy(out, in, length);
}
virtual ~copy_encryptor() {}
};
class copy_decryptor : public stream_decryptor {
public:
copy_decryptor() {};
virtual void decrypt(const unsigned char* in, unsigned char* out, std::size_t length) {
assert(in && out);
std::memcpy(out, in, length);
}
virtual ~copy_decryptor() {}
};
class aes_stream_encryptor : public stream_encryptor {
mbedtls_cipher_context_t aes_ctx_;
public:
aes_stream_encryptor(const unsigned char* key, const mbedtls_cipher_info_t* cipher_info, unsigned char* ivec) {
assert(key && cipher_info && ivec);
mbedtls_cipher_init(&aes_ctx_);
std::unique_ptr<mbedtls_cipher_context_t, decltype(&mbedtls_cipher_free)> auto_free(&aes_ctx_, mbedtls_cipher_free);
int ret = mbedtls_cipher_setup(&aes_ctx_, cipher_info);
if (ret != 0) {
std::cerr << "mbedtls_cipher_setup error: " << ret << std::endl;
throw std::runtime_error("mbedtls_cipher_setup error");
}
ret = mbedtls_cipher_setkey(&aes_ctx_, key, mbedtls_cipher_info_get_key_bitlen(cipher_info), MBEDTLS_ENCRYPT);
if (ret != 0) {
std::cerr << "mbedtls_cipher_setkey error: " << ret << std::endl;
throw std::runtime_error("mbedtls_cipher_setkey error");
}
ret = mbedtls_cipher_set_iv(&aes_ctx_, ivec, mbedtls_cipher_info_get_iv_size(cipher_info));
if (ret != 0) {
std::cerr << "mbedtls_cipher_set_iv error: " << ret << std::endl;
throw std::runtime_error("mbedtls_cipher_set_iv error");
}
auto_free.release();
}
virtual void encrypt(const unsigned char* in, unsigned char* out, std::size_t length) override {
assert(in && out);
std::size_t out_len = 0;
int ret = mbedtls_cipher_update(&aes_ctx_, in, length, out, &out_len);
if (ret != 0) {
std::cerr << "mbedtls_cipher_update error: " << ret << std::endl;
throw std::runtime_error("mbedtls_cipher_update error");
}
assert(out_len == length);
}
virtual ~aes_stream_encryptor() override {
mbedtls_cipher_free(&aes_ctx_);
}
};
class aes_stream_decryptor : public stream_decryptor {
mbedtls_cipher_context_t aes_ctx_;
public:
aes_stream_decryptor(const unsigned char* key, const mbedtls_cipher_info_t* cipher_info, unsigned char* ivec) {
assert(key && cipher_info && ivec);
mbedtls_cipher_init(&aes_ctx_);
std::unique_ptr<mbedtls_cipher_context_t, decltype(&mbedtls_cipher_free)> auto_free(&aes_ctx_, mbedtls_cipher_free);
int ret = mbedtls_cipher_setup(&aes_ctx_, cipher_info);
if (ret != 0) {
std::cerr << "mbedtls_cipher_setup error: " << ret << std::endl;
throw std::runtime_error("mbedtls_cipher_setup error");
}
ret = mbedtls_cipher_setkey(&aes_ctx_, key, mbedtls_cipher_info_get_key_bitlen(cipher_info), MBEDTLS_DECRYPT);
if (ret != 0) {
std::cerr << "mbedtls_cipher_setkey error: " << ret << std::endl;
throw std::runtime_error("mbedtls_cipher_setkey error");
}
ret = mbedtls_cipher_set_iv(&aes_ctx_, ivec, mbedtls_cipher_info_get_iv_size(cipher_info));
if (ret != 0) {
std::cerr << "mbedtls_cipher_set_iv error: " << ret << std::endl;
throw std::runtime_error("mbedtls_cipher_set_iv error");
}
auto_free.release();
}
virtual void decrypt(const unsigned char* in, unsigned char* out, std::size_t length) override {
assert(in && out);
std::size_t out_len = 0;
int ret = mbedtls_cipher_update(&aes_ctx_, in, length, out, &out_len);
if (ret != 0) {
std::cerr << "mbedtls_cipher_update error: " << ret << std::endl;
throw std::runtime_error("mbedtls_cipher_update error");
}
assert(out_len == length);
}
virtual ~aes_stream_decryptor() override {
mbedtls_cipher_free(&aes_ctx_);
}
};
static const mbedtls_cipher_info_t* aes_cfb128_cipher(std::size_t key_bits) {
assert(key_bits == 128 || key_bits == 192 || key_bits == 256);
mbedtls_cipher_type_t cipher_type;
switch (key_bits) {
case 128:
cipher_type = mbedtls_cipher_type_t::MBEDTLS_CIPHER_AES_128_CFB128;
break;
case 192:
cipher_type = mbedtls_cipher_type_t::MBEDTLS_CIPHER_AES_192_CFB128;
break;
default:
cipher_type = mbedtls_cipher_type_t::MBEDTLS_CIPHER_AES_256_CFB128;
}
return mbedtls_cipher_info_from_type(cipher_type);
}
static const mbedtls_cipher_info_t* aes_ofb128_cipher(std::size_t key_bits) {
assert(key_bits == 128 || key_bits == 192 || key_bits == 256);
mbedtls_cipher_type_t cipher_type;
switch (key_bits) {
case 128:
cipher_type = mbedtls_cipher_type_t::MBEDTLS_CIPHER_AES_128_OFB;
break;
case 192:
cipher_type = mbedtls_cipher_type_t::MBEDTLS_CIPHER_AES_192_OFB;
break;
default:
cipher_type = mbedtls_cipher_type_t::MBEDTLS_CIPHER_AES_256_OFB;
}
return mbedtls_cipher_info_from_type(cipher_type);
}
static const mbedtls_cipher_info_t* aes_ctr128_cipher(std::size_t key_bits) {
assert(key_bits == 128 || key_bits == 192 || key_bits == 256);
mbedtls_cipher_type_t cipher_type;
switch (key_bits) {
case 128:
cipher_type = mbedtls_cipher_type_t::MBEDTLS_CIPHER_AES_128_CTR;
break;
case 192:
cipher_type = mbedtls_cipher_type_t::MBEDTLS_CIPHER_AES_192_CTR;
break;
default:
cipher_type = mbedtls_cipher_type_t::MBEDTLS_CIPHER_AES_256_CTR;
}
return mbedtls_cipher_info_from_type(cipher_type);
}
class aes_cfb128_encryptor : public aes_stream_encryptor {
public:
aes_cfb128_encryptor(const unsigned char* key, std::size_t key_bits, unsigned char* ivec) :
aes_stream_encryptor(key, aes_cfb128_cipher(key_bits), ivec) {
}
};
class aes_cfb128_decryptor : public aes_stream_decryptor {
public:
aes_cfb128_decryptor(const unsigned char* key, std::size_t key_bits, unsigned char* ivec) :
aes_stream_decryptor(key, aes_cfb128_cipher(key_bits), ivec) {
}
};
class aes_ofb128_encryptor : public aes_stream_encryptor {
public:
aes_ofb128_encryptor(const unsigned char* key, std::size_t key_bits, unsigned char* ivec) :
aes_stream_encryptor(key, aes_ofb128_cipher(key_bits), ivec) {
}
};
class aes_ofb128_decryptor : public aes_stream_decryptor {
public:
aes_ofb128_decryptor(const unsigned char* key, std::size_t key_bits, unsigned char* ivec) :
aes_stream_decryptor(key, aes_ofb128_cipher(key_bits), ivec) {
}
};
class aes_ctr128_encryptor : public aes_stream_encryptor {
public:
aes_ctr128_encryptor(const unsigned char* key, std::size_t key_bits, unsigned char* ivec) :
aes_stream_encryptor(key, aes_ctr128_cipher(key_bits), ivec) {
}
};
class aes_ctr128_decryptor : public aes_stream_decryptor {
public:
aes_ctr128_decryptor(const unsigned char* key, std::size_t key_bits, unsigned char* ivec) :
aes_stream_decryptor(key, aes_ctr128_cipher(key_bits), ivec) {
}
};
class rsa {
bool is_pub_key_;
mbedtls_pk_context pk_;
public:
rsa(const std::string& key) {
if (key.size() > 26 && std::equal(key.begin(), key.begin() + 26, "-----BEGIN PUBLIC KEY-----")) {
is_pub_key_ = true;
}
else if (key.size() > 31 && std::equal(key.begin(), key.begin() + 31, "-----BEGIN RSA PRIVATE KEY-----")) {
is_pub_key_ = false;
}
else {
throw std::invalid_argument("invalid RSA key");
}
mbedtls_pk_init(&pk_);
std::unique_ptr<mbedtls_pk_context, decltype(&mbedtls_pk_free)> auto_free_pk(&pk_, mbedtls_pk_free);
int ret = 0;
if (is_pub_key_) {
ret = mbedtls_pk_parse_public_key(&pk_, reinterpret_cast<const unsigned char*>(key.c_str()), key.size() + 1);
} else {
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_entropy_init(&entropy);
mbedtls_ctr_drbg_init(&ctr_drbg);
std::unique_ptr<mbedtls_entropy_context, decltype(&mbedtls_entropy_free)> auto_free_entropy(&entropy, mbedtls_entropy_free);
std::unique_ptr<mbedtls_ctr_drbg_context, decltype(&mbedtls_ctr_drbg_free)> auto_free_ctr_drbg(&ctr_drbg, mbedtls_ctr_drbg_free);
int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, nullptr, 0);
if (ret != 0) {
std::cerr << "mbedtls_ctr_drbg_seed error: " << ret << std::endl;
throw std::runtime_error("mbedtls_ctr_drbg_seed error");
}
ret = mbedtls_pk_parse_key(&pk_, reinterpret_cast<const unsigned char*>(key.c_str()), key.size() + 1, nullptr, 0, mbedtls_ctr_drbg_random, &ctr_drbg);
}
if (ret != 0) {
if (is_pub_key_) {
std::cerr << "mbedtls_pk_parse_public_key error: " << ret << std::endl;
} else {
std::cerr << "mbedtls_pk_parse_key error: " << ret << std::endl;
}
throw std::invalid_argument("invalid RSA key");
}
auto pk_type = mbedtls_pk_get_type(&pk_);
if (pk_type != MBEDTLS_PK_RSA) {
std::cerr << "mbedtls_pk_get_type: " << pk_type << std::endl;
throw std::invalid_argument("invalid RSA key");
}
ret = mbedtls_rsa_set_padding(mbedtls_pk_rsa(pk_), MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA1);
if (ret != 0) {
mbedtls_pk_free(&pk_);
std::cerr << "mbedtls_rsa_set_padding error: " << ret << std::endl;
throw std::runtime_error("mbedtls_rsa_set_padding error");
}
auto_free_pk.release();
}
~rsa() {
mbedtls_pk_free(&pk_);
}
int encrypt(int flen, unsigned char* from, unsigned char* to) {
assert(from && to);
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_entropy_init(&entropy);
mbedtls_ctr_drbg_init(&ctr_drbg);
std::unique_ptr<mbedtls_entropy_context, decltype(&mbedtls_entropy_free)> auto_free_entropy(&entropy, mbedtls_entropy_free);
std::unique_ptr<mbedtls_ctr_drbg_context, decltype(&mbedtls_ctr_drbg_free)> auto_free_ctr_drbg(&ctr_drbg, mbedtls_ctr_drbg_free);
int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, nullptr, 0);
if (ret != 0) {
std::cerr << "mbedtls_ctr_drbg_seed error: " << ret << std::endl;
return 0;
}
ret = mbedtls_rsa_pkcs1_encrypt(mbedtls_pk_rsa(pk_), mbedtls_ctr_drbg_random, &ctr_drbg, flen, from, to);
if (ret != 0) {
std::cerr << "mbedtls_rsa_pkcs1_encrypt error: " << ret << std::endl;
return 0;
}
return modulus_size();
}
int decrypt(int flen, unsigned char* from, unsigned char* to) {
assert(from && to);
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_entropy_init(&entropy);
mbedtls_ctr_drbg_init(&ctr_drbg);
std::unique_ptr<mbedtls_entropy_context, decltype(&mbedtls_entropy_free)> auto_free_entropy(&entropy, mbedtls_entropy_free);
std::unique_ptr<mbedtls_ctr_drbg_context, decltype(&mbedtls_ctr_drbg_free)> auto_free_ctr_drbg(&ctr_drbg, mbedtls_ctr_drbg_free);
int ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, nullptr, 0);
if (ret != 0) {
std::cerr << "mbedtls_ctr_drbg_seed error: " << ret << std::endl;
return 0;
}
std::size_t out_len = 0;
ret = mbedtls_rsa_pkcs1_decrypt(mbedtls_pk_rsa(pk_), mbedtls_ctr_drbg_random, &ctr_drbg, &out_len, from, to, modulus_size());
if (ret != 0) {
std::cerr << "mbedtls_rsa_pkcs1_decrypt error: " << ret << std::endl;
return 0;
}
return static_cast<int>(out_len);
}
int modulus_size() const {
return mbedtls_pk_get_bitlen(&pk_) / 8;
}
};
} // namespace azure_proxy
#endif
================================================
FILE: src/hash_utils.cpp
================================================
/*
* hash_utils.cpp:
*
* Copyright (C) 2023-2024 Light Lin <lxrite@gmail.com> All Rights Reserved.
*
*/
#include "hash_utils.hpp"
#include "mbedtls/sha256.h"
namespace azure_proxy {
namespace hash_utils {
std::array<unsigned char, 32> sha256(const unsigned char* data, std::size_t count) {
std::array<unsigned char, 32> result;
mbedtls_sha256_context ctx;
mbedtls_sha256_init(&ctx);
mbedtls_sha256_starts(&ctx, 0);
mbedtls_sha256_update(&ctx, data, count);
mbedtls_sha256_finish(&ctx, result.data());
mbedtls_sha256_free(&ctx);
return result;
}
} // namespace hash_utils
} // namespace azure_proxy
================================================
FILE: src/hash_utils.hpp
================================================
/*
* hash_utils.hpp:
*
* Copyright (C) 2023 Light Lin <lxrite@gmail.com> All Rights Reserved.
*
*/
#ifndef AZURE_HASH_UTILS_HPP
#define AZURE_HASH_UTILS_HPP
#include <array>
namespace azure_proxy {
namespace hash_utils {
std::array<unsigned char, 32> sha256(const unsigned char* data, std::size_t count);
} // namespace hash_utils
} // namespace azure_proxy
#endif // AZURE_HASH_UTILS_HPP
================================================
FILE: src/http_chunk_checker.hpp
================================================
/*
* http_chunk_checker.hpp:
*
* Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.
*
*/
#ifndef AZURE_HTTP_CHUNCK_CHCKER_HPP
#define AZURE_HTTP_CHUNCK_CHCKER_HPP
#include <cassert>
#include <cctype>
#include <iterator>
#include <type_traits>
namespace azure_proxy {
enum class http_chunk_check_state {
chunk_size_start,
chunk_size,
chunk_ext,
chunk_size_cr,
chunk_data,
chunk_data_cr,
chunk_last,
chunk_last_cr,
chunk_complete,
chunk_check_failed
};
class http_chunk_checker {
http_chunk_check_state state;
std::uint32_t current_chunk_size;
std::uint32_t current_chunk_size_has_read;
public:
http_chunk_checker() : state(http_chunk_check_state::chunk_size_start), current_chunk_size(0), current_chunk_size_has_read(0) {}
bool is_complete() const {
return this->state == http_chunk_check_state::chunk_complete;
}
bool is_fail() const {
return this->state == http_chunk_check_state::chunk_check_failed;
}
template <typename ForwardIterator>
bool check(ForwardIterator begin, ForwardIterator end) {
static_assert(std::is_same<char, typename std::iterator_traits<ForwardIterator>::value_type>::value ||
std::is_same<signed char, typename std::iterator_traits<ForwardIterator>::value_type>::value ||
std::is_same<unsigned char, typename std::iterator_traits<ForwardIterator>::value_type>::value, "error");
assert(!this->is_fail());
for (auto iter = begin; iter != end; ++iter) {
switch (this->state) {
case http_chunk_check_state::chunk_size_start:
if (std::isxdigit(static_cast<unsigned char>(*iter))) {
this->current_chunk_size = (*iter) >= 'A' ? std::toupper(static_cast<unsigned char>(*iter)) - 'A' + 10 : *iter - '0';
this->current_chunk_size_has_read = 0;
this->state = http_chunk_check_state::chunk_size;
continue;
}
break;
case http_chunk_check_state::chunk_size:
if (std::isxdigit(static_cast<unsigned char>(*iter))) {
this->current_chunk_size = this->current_chunk_size * 16 + ((*iter) >= 'A' ? std::toupper(static_cast<unsigned char>(*iter)) - 'A' + 10 : *iter - '0');
continue;
}
else if (*iter == ';' || *iter == ' ') {
this->state = http_chunk_check_state::chunk_ext;
continue;
}
else if (*iter == '\r') {
this->state = http_chunk_check_state::chunk_size_cr;
continue;
}
break;
case http_chunk_check_state::chunk_ext:
if (*iter == '\r') {
this->state = http_chunk_check_state::chunk_size_cr;
}
continue;
case http_chunk_check_state::chunk_size_cr:
if (*iter == '\n') {
if (this->current_chunk_size == 0) {
this->state = http_chunk_check_state::chunk_last;
}
else {
this->state = http_chunk_check_state::chunk_data;
}
continue;
}
break;
case http_chunk_check_state::chunk_data:
if (this->current_chunk_size_has_read < this->current_chunk_size) {
++this->current_chunk_size_has_read;
continue;
}
else {
if (*iter == '\r') {
this->state = http_chunk_check_state::chunk_data_cr;
continue;
}
}
break;
case http_chunk_check_state::chunk_data_cr:
if (*iter == '\n') {
this->state = http_chunk_check_state::chunk_size_start;
continue;
}
break;
case http_chunk_check_state::chunk_last:
if (*iter == '\r') {
this->state = http_chunk_check_state::chunk_last_cr;
continue;
}
break;
case http_chunk_check_state::chunk_last_cr:
if (*iter == '\n') {
this->state = http_chunk_check_state::chunk_complete;
continue;
}
break;
case http_chunk_check_state::chunk_complete:
break;
default:
assert(false);
break;
}
this->state = http_chunk_check_state::chunk_check_failed;
return false;
}
return true;
}
};
} // namespace azure_proxy
#endif
================================================
FILE: src/http_header_parser.cpp
================================================
/*
* http_header_parser.cpp:
*
* Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.
*
*/
#include <cassert>
#include <iterator>
#include <regex>
#include "http_header_parser.hpp"
#include <curi.h>
namespace azure_proxy {
http_request_header::http_request_header() : _port(80)
{
}
const std::string& http_request_header::method() const
{
return this->_method;
}
const std::string& http_request_header::scheme() const
{
return this->_scheme;
}
const std::string& http_request_header::host() const
{
return this->_host;
}
unsigned short http_request_header::port() const
{
return this->_port;
}
const std::string& http_request_header::path_and_query() const
{
return this->_path_and_query;
}
const std::string& http_request_header::http_version() const
{
return this->_http_version;
}
std::optional<std::string> http_request_header::get_header_value(const std::string& name) const
{
auto iter = this->_headers_map.find(name);
if (iter == this->_headers_map.end()) {
return std::nullopt;
}
return std::get<1>(*iter);
}
std::size_t http_request_header::erase_header(const std::string& name)
{
return this->_headers_map.erase(name);
}
const http_headers_container& http_request_header::get_headers_map() const
{
return this->_headers_map;
}
http_response_header::http_response_header()
{
}
const std::string& http_response_header::http_version() const
{
return this->_http_version;
}
unsigned int http_response_header::status_code() const
{
return this->_status_code;
}
const std::string& http_response_header::status_description() const
{
return this->_status_description;
}
std::optional<std::string> http_response_header::get_header_value(const std::string& name) const
{
auto iter = this->_headers_map.find(name);
if (iter == this->_headers_map.end()) {
return std::nullopt;
}
return std::get<1>(*iter);
}
std::size_t http_response_header::erase_header(const std::string& name)
{
return this->_headers_map.erase(name);
}
const http_headers_container& http_response_header::get_headers_map() const
{
return this->_headers_map;
}
http_headers_container http_header_parser::parse_headers(std::string::const_iterator begin, std::string::const_iterator end)
{
http_headers_container headers;
auto is_digit = [](char ch) -> bool {
return '0' <= ch && ch <= '9';
};
auto is_alpha = [](char ch) -> bool {
return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z');
};
auto is_token_char = [&is_alpha, &is_digit](char ch) -> bool {
return is_alpha(ch) || is_digit(ch) || (
ch == '!' || ch == '#' ||
ch == '$' || ch == '%' ||
ch == '&' || ch == '\'' ||
ch == '`' || ch == '*' ||
ch == '+' || ch == '-' ||
ch == '.' || ch == '^' ||
ch == '_' || ch == '|' ||
ch == '~');
};
enum class parse_header_state {
header_field_name_start,
header_field_name,
header_field_value_left_ows,
header_field_value,
header_field_cr,
header_field_crlf,
header_field_crlfcr,
header_compelete,
header_parse_failed
};
parse_header_state state = parse_header_state::header_field_name_start;
std::string header_field_name;
std::string header_field_value;
for (std::string::const_iterator iter = begin; iter != end && state != parse_header_state::header_compelete && state != parse_header_state::header_parse_failed; ++iter) {
switch (state) {
case parse_header_state::header_field_name_start:
if (is_token_char(*iter)) {
header_field_name.push_back(*iter);
state = parse_header_state::header_field_name;
}
else if (iter == begin && *iter == '\r') {
state = parse_header_state::header_field_crlfcr;
}
else {
state = parse_header_state::header_parse_failed;
}
break;
case parse_header_state::header_field_name:
if (is_token_char(*iter) || *iter == ' ') {
header_field_name.push_back(*iter);
}
else if (*iter == ':') {
state = parse_header_state::header_field_value_left_ows;
}
else {
state = parse_header_state::header_parse_failed;
}
break;
case parse_header_state::header_field_value_left_ows:
if (*iter == ' ' || *iter == '\t') {
continue;
}
else if (*iter == '\r') {
state = parse_header_state::header_field_cr;
}
else {
header_field_value.push_back(*iter);
state = parse_header_state::header_field_value;
}
break;
case parse_header_state::header_field_value:
if (*iter == '\r') {
state = parse_header_state::header_field_cr;
}
else {
header_field_value.push_back(*iter);
}
break;
case parse_header_state::header_field_cr:
if (*iter == '\n') {
state = parse_header_state::header_field_crlf;
}
else {
state = parse_header_state::header_parse_failed;
}
break;
case parse_header_state::header_field_crlf:
if (*iter == ' ' || *iter == '\t') {
header_field_value.push_back(*iter);
state = parse_header_state::header_field_value;
}
else {
while (!header_field_name.empty() && (header_field_name[header_field_name.size() - 1] == ' ')) {
header_field_name.resize(header_field_name.size() - 1);
}
assert(!header_field_name.empty());
while (!header_field_value.empty() && (header_field_value[header_field_value.size() - 1] == ' ' || (header_field_value[header_field_value.size() - 1] == '\t'))) {
header_field_value.resize(header_field_value.size() - 1);
}
headers.insert(std::make_pair(std::move(header_field_name), std::move(header_field_value)));
if (*iter == '\r') {
state = parse_header_state::header_field_crlfcr;
}
else if (is_token_char(*iter)) {
header_field_name.push_back(*iter);
state = parse_header_state::header_field_name;
}
else {
state = parse_header_state::header_parse_failed;
}
}
break;
case parse_header_state::header_field_crlfcr:
if (*iter == '\n') {
state = parse_header_state::header_compelete;
}
break;
default:
assert(false);
}
}
if (state != parse_header_state::header_compelete) {
throw std::runtime_error("failed to parse");
}
return headers;
}
std::optional<http_request_header> http_header_parser::parse_request_header(std::string::const_iterator begin, std::string::const_iterator end)
{
auto iter = begin;
auto tmp = iter;
for (;iter != end && *iter != ' ' && *iter != '\r'; ++iter)
;
if (iter == tmp || iter == end || *iter != ' ') return std::nullopt;
http_request_header header;
header._method = std::string(tmp, iter);
tmp = ++iter;
for (;iter != end && *iter != ' ' && *iter != '\r'; ++iter)
;
if (iter == tmp || iter == end || *iter != ' ') return std::nullopt;
auto request_uri = std::string(tmp, iter);
if (header.method() == "CONNECT") {
std::regex regex("(.+?):(\\d+)");
std::match_results<std::string::iterator> match_results;
if (!std::regex_match(request_uri.begin(), request_uri.end(), match_results, regex)) {
return std::nullopt;
}
header._host = match_results[1];
try {
header._port = static_cast<unsigned short>(std::stoul(std::string(match_results[2])));
}
catch (const std::exception&) {
return std::nullopt;
}
}
else {
struct parse_user_data {
std::string scheme;
std::string host;
unsigned int port = 80;
std::string path;
std::string query;
};
auto user_data = parse_user_data{};
curi_settings settings;
curi_default_settings(&settings);
settings.scheme_callback = [](void* user_data, const char* scheme, size_t scheme_len) -> int {
reinterpret_cast<parse_user_data*>(user_data)->scheme = std::string(scheme, scheme_len);
return 1;
};
settings.host_callback = [](void* user_data, const char* host, size_t host_len) -> int {
reinterpret_cast<parse_user_data*>(user_data)->host = std::string(host, host_len);
return 1;
};
settings.port_callback = [](void* user_data, unsigned int port) -> int {
reinterpret_cast<parse_user_data*>(user_data)->port = port;
return 1;
};
settings.path_callback = [](void* user_data, const char* path, size_t path_len) -> int {
reinterpret_cast<parse_user_data*>(user_data)->path = std::string(path, path_len);
return 1;
};
settings.query_callback = [](void* user_data, const char* query, size_t query_len) -> int {
reinterpret_cast<parse_user_data*>(user_data)->query = std::string(query, query_len);
return 1;
};
if (curi_status_success != curi_parse_full_uri(request_uri.c_str(), request_uri.size(), &settings, &user_data)) {
return std::nullopt;
}
if (user_data.scheme.empty() || user_data.host.empty() || user_data.path.empty()) {
return std::nullopt;
}
header._scheme = user_data.scheme;
header._host = user_data.host;
header._port = user_data.port;
header._path_and_query = user_data.path;
if (!user_data.query.empty()) {
header._path_and_query.push_back('?');
header._path_and_query += user_data.query;
}
}
tmp = ++iter;
for (;iter != end && *iter != '\r'; ++iter)
;
// HTTP/x.y
if (iter == end || std::distance(tmp, iter) < 6 || !std::equal(tmp, tmp + 5, "HTTP/")) return std::nullopt;
header._http_version = std::string(tmp + 5, iter);
++iter;
if (iter == end || *iter != '\n') return std::nullopt;
++iter;
try {
header._headers_map = parse_headers(iter, end);
}
catch (const std::exception&) {
return std::nullopt;
}
return header;
}
std::optional<http_response_header> http_header_parser::parse_response_header(std::string::const_iterator begin, std::string::const_iterator end)
{
auto iter = begin;
auto tmp = iter;
for (;iter != end && *iter != ' ' && *iter != '\r'; ++iter)
;
if (std::distance(tmp, iter) < 6 || iter == end || *iter != ' ' || !std::equal(tmp, tmp + 5, "HTTP/")) return std::nullopt;
http_response_header header;
header._http_version = std::string(tmp + 5, iter);
tmp = ++iter;
for (;iter != end && *iter != ' ' && *iter != '\r'; ++iter)
;
if (tmp == iter || iter == end) return std::nullopt;
try {
header._status_code = std::stoul(std::string(tmp, iter));
}
catch(const std::exception&) {
return std::nullopt;
}
if (*iter == ' ') {
tmp = ++iter;
for (;iter != end && *iter != '\r'; ++iter)
;
if (iter == end || *iter != '\r') return std::nullopt;
header._status_description = std::string(tmp, iter);
}
if (*iter != '\r') return std::nullopt;
if (iter == end || *(++iter) != '\n') return std::nullopt;
++iter;
try {
header._headers_map = parse_headers(iter, end);
}
catch (const std::exception&) {
return std::nullopt;
}
return header;
}
}; // namespace azure_proxy
================================================
FILE: src/http_header_parser.hpp
================================================
/*
* http_header_parser.hpp:
*
* Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.
*
*/
#ifndef AZURE_HTTP_HEADER_PARSER_HPP
#define AZURE_HTTP_HEADER_PARSER_HPP
#include <algorithm>
#include <cctype>
#include <map>
#include <optional>
#include <string>
namespace azure_proxy {
struct default_filed_name_compare {
bool operator() (const std::string& str1, const std::string& str2) const {
return std::lexicographical_compare(str1.begin(), str1.end(), str2.begin(), str2.end(), [](const char ch1, const char ch2) -> bool {
return std::tolower(static_cast<unsigned char>(ch1)) < std::tolower(static_cast<unsigned char>(ch2));
});
}
};
typedef std::multimap<const std::string, std::string, default_filed_name_compare> http_headers_container;
class http_request_header {
friend class http_header_parser;
std::string _method;
std::string _scheme;
std::string _host;
unsigned short _port;
std::string _path_and_query;
std::string _http_version;
http_headers_container _headers_map;
http_request_header();
public:
const std::string& method() const;
const std::string& scheme() const;
const std::string& host() const;
unsigned short port() const;
const std::string& path_and_query() const;
const std::string& http_version() const;
std::optional<std::string> get_header_value(const std::string& name) const;
std::size_t erase_header(const std::string& name);
const http_headers_container& get_headers_map() const;
};
class http_response_header {
friend class http_header_parser;
std::string _http_version;
unsigned int _status_code;
std::string _status_description;
http_headers_container _headers_map;
http_response_header();
public:
const std::string& http_version() const;
unsigned int status_code() const;
const std::string& status_description() const;
std::optional<std::string> get_header_value(const std::string& name) const;
std::size_t erase_header(const std::string& name);
const http_headers_container& get_headers_map() const;
};
class http_header_parser {
static http_headers_container parse_headers(std::string::const_iterator begin, std::string::const_iterator end);
public:
static std::optional<http_request_header> parse_request_header(std::string::const_iterator begin, std::string::const_iterator end);
static std::optional<http_response_header> parse_response_header(std::string::const_iterator begin, std::string::const_iterator end);
};
}; // namespace azure_proxy
#endif
================================================
FILE: src/http_proxy_client.cpp
================================================
/*
* http_proxy_client.cpp:
*
* Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.
*
*/
#include <iostream>
#include <string>
#include <thread>
#include <utility>
#include <vector>
#include "http_proxy_client.hpp"
#include "http_proxy_client_connection.hpp"
#include "http_proxy_client_config.hpp"
namespace azure_proxy {
http_proxy_client::http_proxy_client(net::io_context& io_ctx) :
io_ctx(io_ctx),
acceptor(io_ctx)
{
}
void http_proxy_client::run()
{
const auto& config = http_proxy_client_config::get_instance();
net::ip::tcp::endpoint endpoint(net::ip::make_address(config.get_bind_address()), config.get_listen_port());
this->acceptor.open(endpoint.protocol());
#ifndef _WIN32
this->acceptor.set_option(net::ip::tcp::acceptor::reuse_address(true));
#endif
this->acceptor.bind(endpoint);
this->acceptor.listen(net::socket_base::max_listen_connections);
this->start_accept();
std::vector<std::thread> td_vec;
for (auto i = 0u; i < config.get_workers(); ++i) {
td_vec.emplace_back([this]() {
try {
this->io_ctx.run();
}
catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
});
}
for (auto& td : td_vec) {
td.join();
}
}
void http_proxy_client::start_accept()
{
this->acceptor.async_accept([this](const std::error_code& error, net::ip::tcp::socket socket) {
if (!error) {
auto connection = http_proxy_client_connection::create(this->io_ctx, std::move(socket));
connection->start();
}
this->start_accept();
});
}
} // namespace azure_proxy
================================================
FILE: src/http_proxy_client.hpp
================================================
/*
* http_proxy_client.hpp:
*
* Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.
*
*/
#ifndef AZURE_HTTP_PROXY_CLIENT_HPP
#define AZURE_HTTP_PROXY_CLIENT_HPP
#include <experimental/net>
namespace net = std::experimental::net;
namespace azure_proxy {
class http_proxy_client {
net::io_context& io_ctx;
net::ip::tcp::acceptor acceptor;
public:
http_proxy_client(net::io_context& io_ctx);
void run();
private:
void start_accept();
};
} // namespace azure_proxy
#endif
================================================
FILE: src/http_proxy_client_config.cpp
================================================
/*
* http_proxy_client_config.cpp:
*
* Copyright (C) 2013-2024 Light Lin <lxrite@gmail.com> All Rights Reserved.
*
*/
#include <algorithm>
#include <cctype>
#include <fstream>
#include <memory>
#include <string>
#ifdef _WIN32
#include <codecvt>
#include <Windows.h>
#endif
#include "encrypt.hpp"
#include "http_proxy_client_config.hpp"
#include <jsonxx.h>
namespace azure_proxy {
http_proxy_client_config::http_proxy_client_config()
{}
bool http_proxy_client_config::load_config_data(const std::string& config_data)
{
bool rollback = true;
std::shared_ptr<bool> auto_rollback(&rollback, [this](bool* rollback) {
if (*rollback) {
this->config_map.clear();
}
});
jsonxx::Object json_obj;
if (!json_obj.parse(config_data)) {
std::cerr << "Failed to parse config" << std::endl;
return false;
}
if (!json_obj.has<jsonxx::String>("proxy_server_address")) {
std::cerr << "Could not find \"proxy_server_address\" in config or it's value is not a string" << std::endl;
return false;
}
this->config_map["proxy_server_address"] = std::string(json_obj.get<jsonxx::String>("proxy_server_address"));
if (!json_obj.has<jsonxx::Number>("proxy_server_port")) {
std::cerr << "Could not find \"proxy_server_port\" in config or it's value is not a number" << std::endl;
return false;
}
this->config_map["proxy_server_port"] = static_cast<unsigned short>(json_obj.get<jsonxx::Number>("proxy_server_port"));
if (json_obj.has<jsonxx::String>("bind_address")) {
this->config_map["bind_address"] = std::string(json_obj.get<jsonxx::String>("bind_address"));
}
else {
this->config_map["bind_address"] = std::string("127.0.0.1");
}
if (json_obj.has<jsonxx::Number>("listen_port")) {
this->config_map["listen_port"] = static_cast<unsigned short>(json_obj.get<jsonxx::Number>("listen_port"));
}
else {
this->config_map["listen_port"] = static_cast<unsigned short>(8089);
}
if (!json_obj.has<jsonxx::String>("rsa_public_key")) {
std::cerr << "Could not find \"rsa_public_key\" in config or it's value is not a string" << std::endl;
return false;
}
const std::string& rsa_public_key = json_obj.get<jsonxx::String>("rsa_public_key");
try {
rsa rsa_pub(rsa_public_key);
if (rsa_pub.modulus_size() < 128) {
std::cerr << "Must use RSA keys of at least 1024 bits" << std::endl;
return false;
}
}
catch (const std::exception&) {
std::cerr << "The value of rsa_public_key is bad" << std::endl;
return false;
}
this->config_map["rsa_public_key"] = rsa_public_key;
if (json_obj.has<jsonxx::String>("cipher")) {
std::string cipher = std::string(json_obj.get<jsonxx::String>("cipher"));
for (auto& ch : cipher) {
ch = std::tolower(static_cast<unsigned char>(ch));
}
bool is_supported_cipher = false;
if (cipher.size() > 3 && std::equal(cipher.begin(), cipher.begin() + 4, "aes-")) {
if (cipher.size() > 8 && cipher[7] == '-'
&& (std::equal(cipher.begin() + 4, cipher.begin() + 7, "128")
|| std::equal(cipher.begin() + 4, cipher.begin() + 7, "192")
|| std::equal(cipher.begin() + 4, cipher.begin() + 7, "256")
)) {
if (std::equal(cipher.begin() + 8, cipher.end(), "cfb")
|| std::equal(cipher.begin() + 8, cipher.end(), "cfb128")
|| std::equal(cipher.begin() + 8, cipher.end(), "ofb")
|| std::equal(cipher.begin() + 8, cipher.end(), "ofb128")
|| std::equal(cipher.begin() + 8, cipher.end(), "ctr")
|| std::equal(cipher.begin() + 8, cipher.end(), "ctr128")) {
is_supported_cipher = true;
}
}
}
if (!is_supported_cipher) {
std::cerr << "Unsupported cipher: " << cipher << std::endl;
return false;
}
this->config_map["cipher"] = cipher;
}
else {
this->config_map["cipher"] = std::string("aes-256-cfb");
}
if (json_obj.has<jsonxx::Number>("timeout")) {
int timeout = static_cast<int>(json_obj.get<jsonxx::Number>("timeout"));
this->config_map["timeout"] = static_cast<unsigned int>(timeout < 30 ? 30 : timeout);
}
else {
this->config_map["timeout"] = 240u;
}
if (json_obj.has<jsonxx::Number>("workers")) {
int threads = static_cast<int>(json_obj.get<jsonxx::Number>("workers"));
this->config_map["workers"] = static_cast<unsigned int>(threads < 1 ? 1 : (threads > 16 ? 16 : threads));
}
else {
this->config_map["workers"] = 2u;
}
std::string auth_key;
if (json_obj.has<jsonxx::String>("auth_key")) {
auth_key = std::string(json_obj.get<jsonxx::String>("auth_key"));
}
this->config_map["auth_key"] = auth_key;
rollback = false;
return true;
}
bool http_proxy_client_config::load_config(const std::string& config_path)
{
std::string config_data;
#ifdef _WIN32
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
std::wstring config_file_path = converter.from_bytes(config_path);
std::shared_ptr<std::remove_pointer<HANDLE>::type> config_file_handle(
CreateFileW(config_file_path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL),
[](HANDLE native_handle) {
if (native_handle != INVALID_HANDLE_VALUE) {
CloseHandle(native_handle);
}
});
if (config_file_handle.get() == INVALID_HANDLE_VALUE) {
std::cerr << "Failed to open config file \"client.json\"" << std::endl;
return false;
}
char ch;
DWORD size_read = 0;
BOOL read_result = ReadFile(config_file_handle.get(), &ch, 1, &size_read, NULL);
while (read_result != FALSE && size_read != 0) {
config_data.push_back(ch);
read_result = ReadFile(config_file_handle.get(), &ch, 1, &size_read, NULL);
}
if (read_result == FALSE) {
std::cerr << "Failed to read data from config file" << std::endl;
return false;
}
#else
std::ifstream ifile(config_path.c_str());
if (!ifile.is_open()) {
std::cerr << "Failed to open \"" << config_path << "\"" << std::endl;
return false;
}
char ch;
while (ifile.get(ch)) {
config_data.push_back(ch);
}
#endif
return this->load_config_data(config_data);
}
const std::string& http_proxy_client_config::get_proxy_server_address() const
{
return this->get_config_value<const std::string&>("proxy_server_address");
}
unsigned short http_proxy_client_config::get_proxy_server_port() const
{
return this->get_config_value<unsigned short>("proxy_server_port");
}
const std::string& http_proxy_client_config::get_bind_address() const
{
return this->get_config_value<const std::string&>("bind_address");
}
unsigned short http_proxy_client_config::get_listen_port() const
{
return this->get_config_value<unsigned short>("listen_port");
}
const std::string& http_proxy_client_config::get_rsa_public_key() const
{
return this->get_config_value<const std::string&>("rsa_public_key");
}
const std::string& http_proxy_client_config::get_cipher() const
{
return this->get_config_value<const std::string&>("cipher");
}
unsigned int http_proxy_client_config::get_timeout() const
{
return this->get_config_value<unsigned int>("timeout");
}
unsigned int http_proxy_client_config::get_workers() const
{
return this->get_config_value<unsigned int>("workers");
}
const std::string& http_proxy_client_config::get_auth_key() const
{
return this->get_config_value<const std::string&>("auth_key");
}
http_proxy_client_config& http_proxy_client_config::get_instance()
{
static http_proxy_client_config instance;
return instance;
}
} // namespace azure_proxy
================================================
FILE: src/http_proxy_client_config.hpp
================================================
/*
* http_proxy_client_config.hpp:
*
* Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.
*
*/
#ifndef AZURE_HTTP_PROXY_CLIENT_CONFIG_HPP
#define AZURE_HTTP_PROXY_CLIENT_CONFIG_HPP
#include <any>
#include <cassert>
#include <map>
#include <stdexcept>
#include <string>
namespace azure_proxy {
class http_proxy_client_config {
std::map<std::string, std::any> config_map;
private:
template<typename T>
T get_config_value(const std::string& key) const {
assert(!this->config_map.empty());
auto iter = this->config_map.find(key);
if (iter == this->config_map.end()) {
throw std::invalid_argument("invalid argument");
}
return std::any_cast<T>(iter->second);
}
http_proxy_client_config();
bool load_config_data(const std::string& config_data);
public:
bool load_config(const std::string& config_path);
const std::string& get_proxy_server_address() const;
unsigned short get_proxy_server_port() const;
const std::string& get_bind_address() const;
unsigned short get_listen_port() const;
const std::string& get_rsa_public_key() const;
const std::string& get_cipher() const;
unsigned int get_timeout() const;
unsigned int get_workers() const;
const std::string& get_auth_key() const;
static http_proxy_client_config& get_instance();
};
} // namespace azure_proxy
#endif
================================================
FILE: src/http_proxy_client_connection.cpp
================================================
/*
* http_proxy_client_connection.cpp:
*
* Copyright (C) 2013-2024 Light Lin <lxrite@gmail.com> All Rights Reserved.
*
*/
#include <algorithm>
#include <cstring>
#include <utility>
#include "http_proxy_client_config.hpp"
#include "http_proxy_client_connection.hpp"
#include "key_generator.hpp"
#include "hash_utils.hpp"
namespace azure_proxy {
http_proxy_client_connection::http_proxy_client_connection(net::io_context& io_ctx, net::ip::tcp::socket&& ua_socket) :
strand(io_ctx.get_executor()),
user_agent_socket(std::move(ua_socket)),
proxy_server_socket(io_ctx),
resolver(io_ctx),
connection_state(proxy_connection_state::ready),
timer(io_ctx),
timeout(std::chrono::seconds(http_proxy_client_config::get_instance().get_timeout()))
{
}
http_proxy_client_connection::~http_proxy_client_connection()
{
}
std::shared_ptr<http_proxy_client_connection> http_proxy_client_connection::create(net::io_context& io_ctx, net::ip::tcp::socket&& ua_socket)
{
return std::shared_ptr<http_proxy_client_connection>(new http_proxy_client_connection(io_ctx, std::move(ua_socket)));
}
void http_proxy_client_connection::start()
{
std::array<unsigned char, 86> cipher_info_raw;
cipher_info_raw.fill(0);
// 0 ~ 2
cipher_info_raw[0] = 'A';
cipher_info_raw[1] = 'H';
cipher_info_raw[2] = 'P';
// 3 protocol version
// - 0: version 1.0
// - 1: since version 1.1, support 32 bytes auth_key_hash
unsigned char protocol_version = 1;
const auto &auth_key = http_proxy_client_config::get_instance().get_auth_key();
bool has_auth_key = !auth_key.empty();
if (!has_auth_key) {
// Use old protocol to be compatible with version 1.0
protocol_version = 0;
}
// cipher code
// 0x00 aes-128-cfb
// 0x01 aes-128-cfb8
// 0x02 aes-128-cfb1
// 0x03 aes-128-ofb
// 0x04 aes-128-ctr
// 0x05 aes-192-cfb
// 0x06 aes-192-cfb8
// 0x07 aes-192-cfb1
// 0x08 aes-192-ofb
// 0x09 aes-192-ctr
// 0x0A aes-256-cfb
// 0x0B aes-256-cfb8
// 0x0C aes-256-cfb1
// 0x0D aes-256-ofb
// 0x0E aes-256-ctr
unsigned char cipher_code = 0;
std::vector<unsigned char> ivec(16);
std::vector<unsigned char> key_vec;
const auto& cipher_name = http_proxy_client_config::get_instance().get_cipher();
if (cipher_name.size() > 7 && std::equal(cipher_name.begin(), cipher_name.begin() + 3, "aes")) {
// aes
assert(cipher_name[3] == '-' && cipher_name[7] == '-');
if (std::strcmp(cipher_name.c_str() + 8, "cfb") == 0 || std::strcmp(cipher_name.c_str() + 8, "cfb128") == 0) {
// aes-xxx-cfb
if (std::equal(cipher_name.begin() + 4, cipher_name.begin() + 7, "128")) {
cipher_code = 0x00;
key_vec.resize(128 / 8);
}
else if (std::equal(cipher_name.begin() + 4, cipher_name.begin() + 7, "192")) {
cipher_code = 0x05;
key_vec.resize(192 / 8);
}
else {
cipher_code = 0x0A;
key_vec.resize(256 / 8);
}
key_generator::get_instance().generate(ivec.data(), ivec.size());
key_generator::get_instance().generate(key_vec.data(), key_vec.size());
this->encryptor = std::unique_ptr<stream_encryptor>(new aes_cfb128_encryptor(key_vec.data(), key_vec.size() * 8, ivec.data()));
this->decryptor = std::unique_ptr<stream_decryptor>(new aes_cfb128_decryptor(key_vec.data(), key_vec.size() * 8, ivec.data()));
}
else if (std::strcmp(cipher_name.c_str() + 8, "cfb8") == 0) {
// aes-xxx-cfb8(deprecated)
}
else if (std::strcmp(cipher_name.c_str() + 8, "cfb1") == 0) {
// aes-xxx-cfb1(deprecated)
}
else if (std::strcmp(cipher_name.c_str() + 8, "ofb") == 0) {
// aes-xxx-ofb
if (std::equal(cipher_name.begin() + 4, cipher_name.begin() + 7, "128")) {
cipher_code = 0x03;
key_vec.resize(128 / 8);
}
else if (std::equal(cipher_name.begin() + 4, cipher_name.begin() + 7, "192")) {
cipher_code = 0x08;
key_vec.resize(192 / 8);
}
else {
cipher_code = 0x0D;
key_vec.resize(256 / 8);
}
key_generator::get_instance().generate(ivec.data(), ivec.size());
key_generator::get_instance().generate(key_vec.data(), key_vec.size());
this->encryptor = std::unique_ptr<stream_encryptor>(new aes_ofb128_encryptor(key_vec.data(), key_vec.size() * 8, ivec.data()));
this->decryptor = std::unique_ptr<stream_decryptor>(new aes_ofb128_decryptor(key_vec.data(), key_vec.size() * 8, ivec.data()));
}
else if (std::strcmp(cipher_name.c_str() + 8, "ctr") == 0) {
// aes-xxx-ctr
if (std::equal(cipher_name.begin() + 4, cipher_name.begin() + 7, "128")) {
cipher_code = 0x04;
key_vec.resize(128 / 8);
}
else if (std::equal(cipher_name.begin() + 4, cipher_name.begin() + 7, "192")) {
cipher_code = 0x09;
key_vec.resize(192 / 8);
}
else {
cipher_code = 0x0E;
key_vec.resize(256 / 8);
}
std::fill(ivec.begin(), ivec.end(), 0);
key_generator::get_instance().generate(key_vec.data(), key_vec.size());
this->encryptor = std::unique_ptr<stream_encryptor>(new aes_ctr128_encryptor(key_vec.data(), key_vec.size() * 8, ivec.data()));
this->decryptor = std::unique_ptr<stream_decryptor>(new aes_ctr128_decryptor(key_vec.data(), key_vec.size() * 8, ivec.data()));
}
}
if (!this->encryptor || !this->decryptor) {
return;
}
// 3 protocol version
cipher_info_raw[3] = protocol_version;
if (protocol_version == 0) {
// 4 zero
// 5 cipher code
cipher_info_raw[5] = cipher_code;
// 6 zero
// 7 ~ 22 ivec
std::copy(ivec.cbegin(), ivec.cend(), cipher_info_raw.begin() + 7);
// 23 ~ (38/46/54) cipher key
std::copy(key_vec.cbegin(), key_vec.cend(), cipher_info_raw.begin() + 23);
} else {
// 4 auth type
// - 0 no authentication
// - 1 32 bytes auth_key_hash
unsigned char auth_type = has_auth_key ? 1 : 0;
cipher_info_raw[4] = auth_type;
// 5 cipher code
cipher_info_raw[5] = cipher_code;
// 6 ~ 37 auth_key_hash
if (auth_type == 1) {
auto auth_key_hash = hash_utils::sha256(reinterpret_cast<const unsigned char*>(auth_key.data()), auth_key.size());
std::copy(auth_key_hash.cbegin(), auth_key_hash.cend(), cipher_info_raw.begin() + 6);
}
// 38 ~ 53 ivec
std::copy(ivec.cbegin(), ivec.cend(), cipher_info_raw.begin() + 38);
// 54 ~ (69/77/85) cipher key
std::copy(key_vec.cbegin(), key_vec.cend(), cipher_info_raw.begin() + 54);
}
rsa rsa_pub(http_proxy_client_config::get_instance().get_rsa_public_key());
if (rsa_pub.modulus_size() < 128) {
return;
}
this->encrypted_cipher_info.resize(rsa_pub.modulus_size());
if(this->encrypted_cipher_info.size() != rsa_pub.encrypt(cipher_info_raw.size(), cipher_info_raw.data(), this->encrypted_cipher_info.data())) {
return;
}
auto self(this->shared_from_this());
const auto& proxy_server_address = http_proxy_client_config::get_instance().get_proxy_server_address();
auto proxy_server_port = std::to_string(http_proxy_client_config::get_instance().get_proxy_server_port());
this->connection_state = proxy_connection_state::resolve_proxy_server_address;
this->set_timer();
this->resolver.async_resolve(proxy_server_address, proxy_server_port, net::bind_executor(this->strand, [this, self](const std::error_code& error, net::ip::tcp::resolver::results_type results) {
if (this->cancel_timer()) {
if (!error) {
this->connection_state = proxy_connection_state::connecte_to_proxy_server;
this->set_timer();
this->proxy_server_socket.async_connect(*results.cbegin(), net::bind_executor(this->strand, [this, self](const std::error_code& error) {
if (this->cancel_timer()) {
if (!error) {
this->on_connection_established();
}
else {
this->on_error(error);
}
}
}));
}
else {
this->on_error(error);
}
}
}));
}
void http_proxy_client_connection::async_read_data_from_user_agent()
{
auto self(this->shared_from_this());
this->set_timer();
this->user_agent_socket.async_read_some(net::buffer(this->upgoing_buffer_read.data(), this->upgoing_buffer_read.size()), net::bind_executor(this->strand, [this, self](const std::error_code& error, std::size_t bytes_transferred) {
if (this->cancel_timer()) {
if (!error) {
this->encryptor->encrypt(reinterpret_cast<const unsigned char*>(&this->upgoing_buffer_read[0]), reinterpret_cast<unsigned char*>(&this->upgoing_buffer_write[0]), bytes_transferred);
this->async_write_data_to_proxy_server(this->upgoing_buffer_write.data(), 0, bytes_transferred);
}
else {
this->on_error(error);
}
}
}));
}
void http_proxy_client_connection::async_read_data_from_proxy_server(bool set_timer)
{
auto self(this->shared_from_this());
if (set_timer) {
this->set_timer();
}
this->proxy_server_socket.async_read_some(net::buffer(this->downgoing_buffer_read.data(), this->downgoing_buffer_read.size()), net::bind_executor(this->strand, [this, self](const std::error_code& error, std::size_t bytes_transferred) {
if (this->cancel_timer()) {
if (!error) {
this->decryptor->decrypt(reinterpret_cast<const unsigned char*>(&this->downgoing_buffer_read[0]), reinterpret_cast<unsigned char*>(&this->downgoing_buffer_write[0]), bytes_transferred);
this->async_write_data_to_user_agent(this->downgoing_buffer_write.data(), 0, bytes_transferred);
}
else {
this->on_error(error);
}
}
}));
}
void http_proxy_client_connection::async_write_data_to_user_agent(const char* write_buffer, std::size_t offset, std::size_t size)
{
auto self(this->shared_from_this());
this->set_timer();
this->user_agent_socket.async_write_some(net::buffer(write_buffer + offset, size),
net::bind_executor(this->strand, [this, self, write_buffer, offset, size](const std::error_code& error, std::size_t bytes_transferred) {
if (this->cancel_timer()) {
if (!error) {
if (bytes_transferred < size) {
this->async_write_data_to_user_agent(write_buffer, offset + bytes_transferred, size - bytes_transferred);
}
else {
this->async_read_data_from_proxy_server();
}
}
else {
this->on_error(error);
}
}
}));
}
void http_proxy_client_connection::async_write_data_to_proxy_server(const char* write_buffer, std::size_t offset, std::size_t size)
{
auto self(this->shared_from_this());
this->set_timer();
this->proxy_server_socket.async_write_some(net::buffer(write_buffer + offset, size),
net::bind_executor(this->strand, [this, self, write_buffer, offset, size](const std::error_code& error, std::size_t bytes_transferred) {
if (this->cancel_timer()) {
if (!error) {
if (bytes_transferred < size) {
this->async_write_data_to_proxy_server(write_buffer, offset + bytes_transferred, size - bytes_transferred);
}
else {
this->async_read_data_from_user_agent();
}
}
else {
this->on_error(error);
}
}
})
);
}
void http_proxy_client_connection::set_timer()
{
if (this->timer.expires_after(this->timeout) != 0) {
assert(false);
}
auto self(this->shared_from_this());
this->timer.async_wait(net::bind_executor(this->strand, [this, self](const std::error_code& error) {
if (error != net::error::operation_aborted) {
this->on_timeout();
}
}));
}
bool http_proxy_client_connection::cancel_timer()
{
std::size_t ret = this->timer.cancel();
assert(ret <= 1);
return ret == 1;
}
void http_proxy_client_connection::on_connection_established()
{
this->async_write_data_to_proxy_server(reinterpret_cast<const char*>(this->encrypted_cipher_info.data()), 0, this->encrypted_cipher_info.size());
this->async_read_data_from_proxy_server(false);
}
void http_proxy_client_connection::on_error(const std::error_code& error)
{
this->cancel_timer();
std::error_code ec;
if (this->proxy_server_socket.is_open()) {
this->proxy_server_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);
this->proxy_server_socket.close(ec);
}
if (this->user_agent_socket.is_open()) {
this->user_agent_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);
this->user_agent_socket.close(ec);
}
}
void http_proxy_client_connection::on_timeout()
{
if (this->connection_state == proxy_connection_state::resolve_proxy_server_address) {
this->resolver.cancel();
}
else {
std::error_code ec;
if (this->proxy_server_socket.is_open()) {
this->proxy_server_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);
this->proxy_server_socket.close(ec);
}
if (this->user_agent_socket.is_open()) {
this->user_agent_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);
this->user_agent_socket.close(ec);
}
}
}
} // namespace azure_proxy
================================================
FILE: src/http_proxy_client_connection.hpp
================================================
/*
* http_proxy_client_connection.hpp:
*
* Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.
*
*/
#ifndef AZURE_HTTP_PROXY_CLIENT_CONNECTION_HPP
#define AZURE_HTTP_PROXY_CLIENT_CONNECTION_HPP
#include <array>
#include <chrono>
#include <experimental/net>
#include <memory>
#include <vector>
#include "encrypt.hpp"
const std::size_t BUFFER_LENGTH = 2048;
namespace net = std::experimental::net;
namespace azure_proxy {
class http_proxy_client_connection : public std::enable_shared_from_this<http_proxy_client_connection> {
enum class proxy_connection_state {
ready,
resolve_proxy_server_address,
connecte_to_proxy_server,
tunnel_transfer
};
private:
net::strand<net::io_context::executor_type> strand;
net::ip::tcp::socket user_agent_socket;
net::ip::tcp::socket proxy_server_socket;
net::ip::tcp::resolver resolver;
proxy_connection_state connection_state;
net::basic_waitable_timer<std::chrono::steady_clock> timer;
std::vector<unsigned char> encrypted_cipher_info;
std::array<char, BUFFER_LENGTH> upgoing_buffer_read;
std::array<char, BUFFER_LENGTH> upgoing_buffer_write;
std::array<char, BUFFER_LENGTH> downgoing_buffer_read;
std::array<char, BUFFER_LENGTH> downgoing_buffer_write;
std::unique_ptr<stream_encryptor> encryptor;
std::unique_ptr<stream_decryptor> decryptor;
std::chrono::seconds timeout;
private:
http_proxy_client_connection(net::io_context& io_ctx, net::ip::tcp::socket&& ua_socket);
public:
~http_proxy_client_connection();
static std::shared_ptr<http_proxy_client_connection> create(net::io_context& io_ctx, net::ip::tcp::socket&& ua_socket);
void start();
private:
void async_read_data_from_user_agent();
void async_read_data_from_proxy_server(bool set_timer = true);
void async_write_data_to_user_agent(const char* write_buffer, std::size_t offset, std::size_t size);
void async_write_data_to_proxy_server(const char* write_buffer, std::size_t offset, std::size_t size);
void set_timer();
bool cancel_timer();
void on_connection_established();
void on_error(const std::error_code& error);
void on_timeout();
};
} // namespace azure_proxy
#endif
================================================
FILE: src/http_proxy_client_main.cpp
================================================
/*
* http_proxy_client_main.cpp:
*
* Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.
*
*/
#include <experimental/net>
#include <csignal>
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
#include "http_proxy_client.hpp"
#include "http_proxy_client_config.hpp"
#include "version.hpp"
#ifdef _WIN32
#include <codecvt>
#include <shellapi.h>
#endif
namespace net = std::experimental::net;
struct ClientArgs {
std::string config_file = "client.json";
};
void print_usage() {
#ifdef _WIN32
const char *prog = "ahpc.exe";
#else
const char *prog = "ahpc";
#endif
std::cout << "Usage: " << prog << " [options]\n\n"
<< "options:\n"
<< " -h, --help Show this help message and exit\n"
<< " -v, --version Print the program version and exit\n"
<< " -c, --config PATH Configuration file path (default: client.json)\n";
}
static ClientArgs parse_args(const std::vector<std::string>& argv) {
std::string arg;
bool invalid_param = false;
ClientArgs args;
for (std::size_t i = 1; i < argv.size(); ++i) {
arg = argv[i];
if (arg == "-h" || arg == "--help") {
print_usage();
std::exit(EXIT_SUCCESS);
} else if (arg == "-v" || arg == "--version") {
std::cout << "Version: " << AHP_VERSION_STRING << std::endl;
std::exit(EXIT_SUCCESS);
} else if (arg == "-c" || arg == "--config") {
if (++i >= argv.size()) {
invalid_param = true;
break;
}
args.config_file = argv[i];
} else {
std::cerr << "Unknown argument: " << arg << std::endl;
print_usage();
std::exit(EXIT_FAILURE);
}
}
if (invalid_param) {
std::cerr << "Invalid parameter for argument: " << arg << std::endl;
std::exit(EXIT_FAILURE);
}
return args;
}
static ClientArgs parse_args(int argc, char** argv) {
std::vector<std::string> argv_vec;
argv_vec.reserve(argc);
#ifdef _WIN32
LPWSTR *wargs = CommandLineToArgvW(GetCommandLineW(), &argc);
if (wargs == nullptr) {
std::cerr << "Failed to retrieve command line arguments" << std::endl;
std::exit(EXIT_FAILURE);
}
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
for (std::size_t i = 0; i < argc; ++i) {
argv_vec.emplace_back(converter.to_bytes(wargs[i]));
}
LocalFree(wargs);
#else
for (std::size_t i = 0; i < argc; ++i) {
argv_vec.emplace_back(argv[i]);
}
#endif
return parse_args(argv_vec);
}
std::weak_ptr<net::io_context> wp_io_ctx;
static void signal_handler(int signal)
{
auto io_ctx = wp_io_ctx.lock();
if (io_ctx) {
io_ctx->stop();
}
}
int main(int argc, char** argv)
{
using namespace azure_proxy;
auto args = parse_args(argc, argv);
try {
auto& config = http_proxy_client_config::get_instance();
if (config.load_config(args.config_file)) {
std::cout << "AHP client version " << AHP_VERSION_STRING << std::endl;
std::cout << "server address: " << config.get_proxy_server_address() << ':' << config.get_proxy_server_port() << std::endl;
std::cout << "local address: " << config.get_bind_address() << ':' << config.get_listen_port() << std::endl;
std::cout << "cipher: " << config.get_cipher() << std::endl;
auto io_ctx = std::make_shared<net::io_context>();
wp_io_ctx = io_ctx;
http_proxy_client client(*io_ctx);
std::signal(SIGINT, signal_handler);
std::signal(SIGTERM, signal_handler);
client.run();
}
}
catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
}
}
================================================
FILE: src/http_proxy_server.cpp
================================================
/*
* http_proxy_server.cpp:
*
* Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.
*
*/
#include <iostream>
#include <memory>
#include <thread>
#include <vector>
#include "http_proxy_server.hpp"
#include "http_proxy_server_config.hpp"
#include "http_proxy_server_connection.hpp"
namespace azure_proxy {
http_proxy_server::http_proxy_server(net::io_context& io_ctx) :
io_ctx(io_ctx),
acceptor(io_ctx)
{
}
void http_proxy_server::run()
{
const auto& config = http_proxy_server_config::get_instance();
net::ip::tcp::endpoint endpoint(net::ip::make_address(config.get_bind_address()), config.get_listen_port());
this->acceptor.open(endpoint.protocol());
#ifndef _WIN32
this->acceptor.set_option(net::ip::tcp::acceptor::reuse_address(true));
#endif
this->acceptor.bind(endpoint);
this->acceptor.listen(net::socket_base::max_listen_connections);
this->start_accept();
std::vector<std::thread> td_vec;
for (auto i = 0u; i < config.get_workers(); ++i) {
td_vec.emplace_back([this]() {
try {
this->io_ctx.run();
}
catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
});
}
for (auto& td : td_vec) {
td.join();
}
}
void http_proxy_server::start_accept()
{
this->acceptor.async_accept([this](const std::error_code& error, net::ip::tcp::socket socket) {
if (!error) {
auto connection = http_proxy_server_connection::create(this->io_ctx, std::move(socket));
connection->start();
}
this->start_accept();
});
}
} //namespace azure_proxy
================================================
FILE: src/http_proxy_server.hpp
================================================
/*
* http_proxy_server.hpp:
*
* Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.
*
*/
#ifndef AZURE_HTTP_PROXY_SERVER_HPP
#define AZURE_HTTP_PROXY_SERVER_HPP
#include <experimental/net>
namespace net = std::experimental::net;
namespace azure_proxy {
class http_proxy_server {
net::io_context& io_ctx;
net::ip::tcp::acceptor acceptor;
public:
http_proxy_server(net::io_context& io_ctx);
void run();
private:
void start_accept();
};
} // namespace azure_proxy
#endif
================================================
FILE: src/http_proxy_server_config.cpp
================================================
/*
* http_proxy_server_config.cpp:
*
* Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.
*
*/
#include <algorithm>
#include <cctype>
#include <fstream>
#include <memory>
#include <string>
#ifdef _WIN32
#include <codecvt>
#include <Windows.h>
#endif
#include "authentication.hpp"
#include "encrypt.hpp"
#include "http_proxy_server_config.hpp"
#include <jsonxx.h>
namespace azure_proxy {
http_proxy_server_config::http_proxy_server_config()
{
}
bool http_proxy_server_config::load_config_data(const std::string& config_data)
{
bool rollback = true;
std::shared_ptr<bool> auto_rollback(&rollback, [this](bool* rollback) {
if (*rollback) {
this->config_map.clear();
authentication::get_instance().remove_all_auth_keys();
}
});
jsonxx::Object json_obj;
if (!json_obj.parse(config_data)) {
std::cerr << "Failed to parse config" << std::endl;
return false;
}
if (json_obj.has<jsonxx::String>("bind_address")) {
this->config_map["bind_address"] = std::string(json_obj.get<jsonxx::String>("bind_address"));
}
else {
this->config_map["bind_address"] = std::string("0.0.0.0");
}
if (json_obj.has<jsonxx::Number>("listen_port")) {
this->config_map["listen_port"] = static_cast<unsigned short>(json_obj.get<jsonxx::Number>("listen_port"));
}
else {
this->config_map["listen_port"] = static_cast<unsigned short>(8090);
}
if (!json_obj.has<jsonxx::String>("rsa_private_key")) {
std::cerr << "Could not find \"rsa_private_key\" in config or it's value is not a string" << std::endl;
return false;
}
const std::string& rsa_private_key = json_obj.get<jsonxx::String>("rsa_private_key");
try {
rsa rsa_pri(rsa_private_key);
if (rsa_pri.modulus_size() < 128) {
std::cerr << "Must use RSA keys of at least 1024 bits" << std::endl;
return false;
}
}
catch (const std::exception&) {
std::cerr << "The value of rsa_private_key is bad" << std::endl;
return false;
}
this->config_map["rsa_private_key"] = rsa_private_key;
if (json_obj.has<jsonxx::Number>("timeout")) {
int timeout = static_cast<int>(json_obj.get<jsonxx::Number>("timeout"));
this->config_map["timeout"] = static_cast<unsigned int>(timeout < 30 ? 30 : timeout);
}
else {
this->config_map["timeout"] = 240u;
}
if (json_obj.has<jsonxx::Number>("workers")) {
int threads = static_cast<int>(json_obj.get<jsonxx::Number>("workers"));
this->config_map["workers"] = static_cast<unsigned int>(threads < 1 ? 1 : (threads > 16 ? 16 : threads));
}
else {
this->config_map["workers"] = 4u;
}
if (json_obj.has<jsonxx::Boolean>("auth") && json_obj.get<jsonxx::Boolean>("auth")) {
this->config_map["auth"] = true;
if (!json_obj.has<jsonxx::Array>("auth_key_list")) {
std::cerr << "Could not find \"auth_key_list\" in config or it's value is not a array" << std::endl;
return false;
}
const jsonxx::Array& auth_key_list = json_obj.get<jsonxx::Array>("auth_key_list");
for (size_t i = 0; i < auth_key_list.size(); ++i) {
if (!auth_key_list.has<jsonxx::String>(i)) {
std::cerr << "The value of \"auth_key_list\" contains unexpected element" << std::endl;
return false;
}
const auto &auth_key = auth_key_list.get<jsonxx::String>(i);
if (auth_key.empty()) {
std::cerr << "Ignore empty \"auth_key\" at index: " << i << std::endl;
continue;
}
authentication::get_instance().add_auth_key(auth_key);
}
}
else {
this->config_map["auth"] = false;
}
rollback = false;
return true;
}
bool http_proxy_server_config::load_config(const std::string& config_path)
{
std::string config_data;
#ifdef _WIN32
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
std::wstring config_file_path = converter.from_bytes(config_path);
std::shared_ptr<std::remove_pointer<HANDLE>::type> config_file_handle(
CreateFileW(config_file_path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL),
[](HANDLE native_handle) {
if (native_handle != INVALID_HANDLE_VALUE) {
CloseHandle(native_handle);
}
});
if (config_file_handle.get() == INVALID_HANDLE_VALUE) {
std::cerr << "Failed to open config file \"server.json\"" << std::endl;
return false;
}
char ch;
DWORD size_read = 0;
BOOL read_result = ReadFile(config_file_handle.get(), &ch, 1, &size_read, NULL);
while (read_result != FALSE && size_read != 0) {
config_data.push_back(ch);
read_result = ReadFile(config_file_handle.get(), &ch, 1, &size_read, NULL);
}
if (read_result == FALSE) {
std::cerr << "Failed to read data from config file" << std::endl;
return false;
}
#else
std::ifstream ifile(config_path.c_str());
if (!ifile.is_open()) {
std::cerr << "Failed to open \"" << config_path << "\"" << std::endl;
return false;
}
char ch;
while (ifile.get(ch)) {
config_data.push_back(ch);
}
#endif
return this->load_config_data(config_data);
}
const std::string& http_proxy_server_config::get_bind_address() const
{
return this->get_config_value<const std::string&>("bind_address");
}
unsigned short http_proxy_server_config::get_listen_port() const
{
return this->get_config_value<unsigned short>("listen_port");
}
const std::string& http_proxy_server_config::get_rsa_private_key() const
{
return this->get_config_value<const std::string&>("rsa_private_key");
}
unsigned int http_proxy_server_config::get_timeout() const
{
return this->get_config_value<unsigned int>("timeout");
}
unsigned int http_proxy_server_config::get_workers() const
{
return this->get_config_value<unsigned int>("workers");
}
bool http_proxy_server_config::enable_auth() const
{
return this->get_config_value<bool>("auth");
}
http_proxy_server_config& http_proxy_server_config::get_instance()
{
static http_proxy_server_config instance;
return instance;
}
} // namespace azure_proxy
================================================
FILE: src/http_proxy_server_config.hpp
================================================
/*
* http_proxy_server_config.hpp:
*
* Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.
*
*/
#ifndef AZURE_HTTP_PROXY_SERVER_CONFIG_HPP
#define AZURE_HTTP_PROXY_SERVER_CONFIG_HPP
#include <any>
#include <cassert>
#include <map>
#include <stdexcept>
#include <string>
namespace azure_proxy {
class http_proxy_server_config {
std::map<std::string, std::any> config_map;
private:
template<typename T>
T get_config_value(const std::string& key) const {
assert(!this->config_map.empty());
auto iter = this->config_map.find(key);
if (iter == this->config_map.end()) {
throw std::invalid_argument("invalid argument");
}
return std::any_cast<T>(iter->second);
}
http_proxy_server_config();
bool load_config_data(const std::string& config_data);
public:
bool load_config(const std::string& config_path);
const std::string& get_bind_address() const;
unsigned short get_listen_port() const;
const std::string& get_rsa_private_key() const;
unsigned int get_timeout() const;
unsigned int get_workers() const;
bool enable_auth() const;
static http_proxy_server_config& get_instance();
};
} // namespace azure_proxy
#endif
================================================
FILE: src/http_proxy_server_connection.cpp
================================================
/*
* http_proxy_server_connection.cpp:
*
* Copyright (C) 2013-2024 Light Lin <lxrite@gmail.com> All Rights Reserved.
*
*/
#include <cctype>
#include <algorithm>
#include <cassert>
#include <cstring>
#include "authentication.hpp"
#include "http_proxy_server_config.hpp"
#include "http_proxy_server_connection.hpp"
static const std::size_t MAX_REQUEST_HEADER_LENGTH = 10240;
static const std::size_t MAX_RESPONSE_HEADER_LENGTH = 10240;
namespace azure_proxy {
http_proxy_server_connection::http_proxy_server_connection(net::io_context& io_ctx, net::ip::tcp::socket&& proxy_client_socket) :
strand(io_ctx.get_executor()),
proxy_client_socket(std::move(proxy_client_socket)),
origin_server_socket(io_ctx),
resolver(io_ctx),
timer(io_ctx),
rsa_pri(http_proxy_server_config::get_instance().get_rsa_private_key())
{
this->connection_context.connection_state = proxy_connection_state::read_cipher_data;
}
http_proxy_server_connection::~http_proxy_server_connection()
{
}
std::shared_ptr<http_proxy_server_connection> http_proxy_server_connection::create(net::io_context& io_ctx, net::ip::tcp::socket&& client_socket)
{
return std::shared_ptr<http_proxy_server_connection>(new http_proxy_server_connection(io_ctx, std::move(client_socket)));
}
void http_proxy_server_connection::start()
{
this->connection_context.connection_state = proxy_connection_state::read_cipher_data;
this->async_read_data_from_proxy_client(1, std::min<std::size_t>(this->rsa_pri.modulus_size(), BUFFER_LENGTH));
}
void http_proxy_server_connection::async_read_data_from_proxy_client(std::size_t at_least_size, std::size_t at_most_size)
{
assert(at_least_size <= at_most_size && at_most_size <= BUFFER_LENGTH);
auto self(this->shared_from_this());
this->set_timer();
net::async_read(this->proxy_client_socket,
net::buffer(&this->upgoing_buffer_read[0], at_most_size),
net::transfer_at_least(at_least_size),
net::bind_executor(this->strand, [this, self](const std::error_code& error, std::size_t bytes_transferred) {
if (this->cancel_timer()) {
if (!error) {
this->on_proxy_client_data_arrived(bytes_transferred);
}
else {
this->on_error(error);
}
}
})
);
}
void http_proxy_server_connection::async_read_data_from_origin_server(bool set_timer, std::size_t at_least_size, std::size_t at_most_size)
{
auto self(this->shared_from_this());
if (set_timer) {
this->set_timer();
}
net::async_read(this->origin_server_socket,
net::buffer(&this->downgoing_buffer_read[0], at_most_size),
net::transfer_at_least(at_least_size),
net::bind_executor(this->strand, [this, self](const std::error_code& error, std::size_t bytes_transferred) {
if (this->cancel_timer()) {
if (!error) {
this->on_origin_server_data_arrived(bytes_transferred);
}
else {
this->on_error(error);
}
}
})
);
}
void http_proxy_server_connection::async_connect_to_origin_server()
{
this->connection_context.reconnect_on_error = false;
if (this->origin_server_socket.is_open()) {
if (this->request_header->method() == "CONNECT") {
std::error_code ec;
this->origin_server_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);
this->origin_server_socket.close(ec);
}
}
if (this->origin_server_socket.is_open() &&
this->request_header->host() == this->connection_context.origin_server_name &&
this->request_header->port() == this->connection_context.origin_server_port) {
this->connection_context.reconnect_on_error = true;
this->on_connect();
}
else {
this->connection_context.origin_server_name = this->request_header->host();
this->connection_context.origin_server_port = this->request_header->port();
auto self(this->shared_from_this());
this->connection_context.connection_state = proxy_connection_state::resolve_origin_server_address;
this->set_timer();
auto host = this->request_header->host();
if (host.size() >= 2 && host[0] == '[' && host[host.size() - 1] == ']') {
host = host.substr(1, host.size() - 2);
}
this->resolver.async_resolve(host, std::to_string(this->connection_context.origin_server_port),
net::bind_executor(this->strand, [this, self](const std::error_code& error, net::ip::tcp::resolver::results_type results) {
if (this->cancel_timer()) {
if (!error) {
this->resolve_results = results;
this->on_resolved(results.cbegin());
}
else {
this->on_error(error);
}
}
})
);
}
}
void http_proxy_server_connection::async_write_request_header_to_origin_server()
{
auto request_content_begin = this->request_data.begin() + this->request_data.find("\r\n\r\n") + 4;
this->modified_request_data = this->request_header->method();
this->modified_request_data.push_back(' ');
this->modified_request_data += this->request_header->path_and_query();
this->modified_request_data += " HTTP/";
this->modified_request_data += this->request_header->http_version();
this->modified_request_data += "\r\n";
this->request_header->erase_header("Proxy-Connection");
this->request_header->erase_header("Proxy-Authorization");
for (const auto& header: this->request_header->get_headers_map()) {
this->modified_request_data += std::get<0>(header);
this->modified_request_data += ": ";
this->modified_request_data += std::get<1>(header);
this->modified_request_data += "\r\n";
}
this->modified_request_data += "\r\n";
this->modified_request_data.append(request_content_begin, this->request_data.end());
this->connection_context.connection_state = proxy_connection_state::write_http_request_header;
this->async_write_data_to_origin_server(this->modified_request_data.data(), 0, this->modified_request_data.size());
}
void http_proxy_server_connection::async_write_response_header_to_proxy_client()
{
auto response_content_begin = this->response_data.begin() + this->response_data.find("\r\n\r\n") + 4;
this->modified_response_data = "HTTP/";
this->modified_response_data += this->response_header->http_version();
this->modified_response_data.push_back(' ');
this->modified_response_data += std::to_string(this->response_header->status_code());
if (!this->response_header->status_description().empty()) {
this->modified_response_data.push_back(' ');
this->modified_response_data += this->response_header->status_description();
}
this->modified_response_data += "\r\n";
for (const auto& header: this->response_header->get_headers_map()) {
this->modified_response_data += std::get<0>(header);
this->modified_response_data += ": ";
this->modified_response_data += std::get<1>(header);
this->modified_response_data += "\r\n";
}
this->modified_response_data += "\r\n";
this->modified_response_data.append(response_content_begin, this->response_data.end());
unsigned char temp_buffer[256];
std::size_t blocks = this->modified_response_data.size() / 256;
if (this->modified_response_data.size() % 256 != 0) {
blocks += 1;
}
for (std::size_t i = 0; i < blocks; ++i) {
std::size_t block_length = 256;
if ((i + 1) * 256 > this->modified_response_data.size()) {
block_length = this->modified_response_data.size() % 256;
}
std::copy(reinterpret_cast<const unsigned char*>(&this->modified_response_data[i * 256]), reinterpret_cast<const unsigned char*>(&this->modified_response_data[i * 256 + block_length]), temp_buffer);
this->encryptor->encrypt(temp_buffer, reinterpret_cast<unsigned char*>(&this->modified_response_data[i * 256]), block_length);
}
this->connection_context.connection_state = proxy_connection_state::write_http_response_header;
this->async_write_data_to_proxy_client(this->modified_response_data.data(), 0, this->modified_response_data.size());
}
void http_proxy_server_connection::async_write_data_to_origin_server(const char* write_buffer, std::size_t offset, std::size_t size)
{
auto self(this->shared_from_this());
this->set_timer();
this->origin_server_socket.async_write_some(net::buffer(write_buffer + offset, size),
net::bind_executor(this->strand, [this, self, write_buffer, offset, size](const std::error_code& error, std::size_t bytes_transferred) {
if (this->cancel_timer()) {
if (!error) {
this->connection_context.reconnect_on_error = false;
if (bytes_transferred < size) {
this->async_write_data_to_origin_server(write_buffer, offset + bytes_transferred, size - bytes_transferred);
}
else {
this->on_origin_server_data_written();
}
}
else {
this->on_error(error);
}
}
})
);
}
void http_proxy_server_connection::async_write_data_to_proxy_client(const char* write_buffer, std::size_t offset, std::size_t size)
{
auto self(this->shared_from_this());
this->set_timer();
this->proxy_client_socket.async_write_some(net::buffer(write_buffer + offset, size),
net::bind_executor(this->strand, [this, self, write_buffer, offset, size](const std::error_code& error, std::size_t bytes_transferred) {
if (this->cancel_timer()) {
if (!error) {
if (bytes_transferred < size) {
this->async_write_data_to_proxy_client(write_buffer, offset + bytes_transferred, size - bytes_transferred);
}
else {
this->on_proxy_client_data_written();
}
}
else {
this->on_error(error);
}
}
})
);
}
void http_proxy_server_connection::start_tunnel_transfer()
{
this->connection_context.connection_state = proxy_connection_state::tunnel_transfer;
this->async_read_data_from_proxy_client();
this->async_read_data_from_origin_server(false);
}
void http_proxy_server_connection::report_error(const std::string& status_code, const std::string& status_description, const std::string& error_message)
{
this->modified_response_data.clear();
this->modified_response_data += "HTTP/1.1 ";
this->modified_response_data += status_code;
if (!status_description.empty()) {
this->modified_response_data.push_back(' ');
this->modified_response_data += status_description;
}
this->modified_response_data += "\r\n";
this->modified_response_data += "Content-Type: text/html\r\n";
this->modified_response_data += "Server: AzureHttpProxy\r\n";
this->modified_response_data += "Content-Length: ";
std::string response_content;
response_content = "<!DOCTYPE html><html><head><title>";
response_content += status_code;
response_content += ' ';
response_content += status_description;
response_content += "</title></head><body bgcolor=\"white\"><center><h1>";
response_content += status_code;
response_content += ' ';
response_content += status_description;
response_content += "</h1>";
if (!error_message.empty()) {
response_content += "<br/>";
response_content += error_message;
response_content += "</center>";
}
response_content += "<hr><center>";
response_content += "azure http proxy server";
response_content += "</center></body></html>";
this->modified_response_data += std::to_string(response_content.size());
this->modified_response_data += "\r\n";
this->modified_response_data += "Proxy-Connection: close\r\n";
this->modified_response_data += "\r\n";
if (!this->request_header || this->request_header->method() != "HEAD") {
this->modified_response_data += response_content;
}
unsigned char temp_buffer[16];
for (std::size_t i = 0; i * 16 < this->modified_response_data.size(); ++i) {
std::size_t block_length = 16;
if (this->modified_response_data.size() - i * 16 < 16) {
block_length = this->modified_response_data.size() % 16;
}
this->encryptor->encrypt(reinterpret_cast<const unsigned char*>(&this->modified_response_data[i * 16]), temp_buffer, block_length);
std::copy(temp_buffer, temp_buffer + block_length, reinterpret_cast<unsigned char*>(&this->modified_response_data[i * 16]));
}
this->connection_context.connection_state = proxy_connection_state::report_error;
auto self(this->shared_from_this());
this->async_write_data_to_proxy_client(this->modified_response_data.data(), 0 ,this->modified_response_data.size());
}
void http_proxy_server_connection::set_timer()
{
if (this->timer.expires_after(std::chrono::seconds(http_proxy_server_config::get_instance().get_timeout())) != 0) {
assert(false);
}
auto self(this->shared_from_this());
this->timer.async_wait(net::bind_executor(this->strand, [this, self](const std::error_code& error) {
if (error != net::error::operation_aborted) {
this->on_timeout();
}
}));
}
bool http_proxy_server_connection::cancel_timer()
{
std::size_t ret = this->timer.cancel();
assert(ret <= 1);
return ret == 1;
}
void http_proxy_server_connection::on_resolved(net::ip::tcp::resolver::results_type::const_iterator endpoint_iterator)
{
if (this->origin_server_socket.is_open()) {
for (auto iter = endpoint_iterator; iter != this->resolve_results.cend(); ++iter) {
if (this->connection_context.origin_server_endpoint == iter->endpoint()) {
this->connection_context.reconnect_on_error = true;
this->on_connect();
return;
}
std::error_code ec;
this->origin_server_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);
this->origin_server_socket.close(ec);
}
}
this->connection_context.origin_server_endpoint = endpoint_iterator->endpoint();
auto self(this->shared_from_this());
this->connection_context.connection_state = proxy_connection_state::connect_to_origin_server;
this->set_timer();
this->origin_server_socket.async_connect(endpoint_iterator->endpoint(),
net::bind_executor(this->strand, [this, self, endpoint_iterator](const std::error_code& error) mutable {
if (this->cancel_timer()) {
if (!error) {
this->on_connect();
}
else {
std::error_code ec;
this->origin_server_socket.close(ec);
if (++endpoint_iterator != this->resolve_results.cend()) {
this->on_resolved(endpoint_iterator);
}
else {
this->on_error(error);
}
}
}
})
);
}
void http_proxy_server_connection::on_connect()
{
if (this->request_header->method() == "CONNECT") {
const unsigned char response_message[] = "HTTP/1.1 200 Connection Established\r\nConnection: Close\r\n\r\n";
this->modified_response_data.resize(sizeof(response_message) - 1);
this->encryptor->encrypt(response_message, const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(&this->modified_response_data[0])), this->modified_response_data.size());
this->connection_context.connection_state = proxy_connection_state::report_connection_established;
this->async_write_data_to_proxy_client(&this->modified_response_data[0], 0, this->modified_response_data.size());
}
else {
this->async_write_request_header_to_origin_server();
}
}
void http_proxy_server_connection::on_proxy_client_data_arrived(std::size_t bytes_transferred)
{
if (this->connection_context.connection_state == proxy_connection_state::read_cipher_data) {
std::copy(this->upgoing_buffer_read.begin(), this->upgoing_buffer_read.begin() + bytes_transferred, std::back_inserter(this->encrypted_cipher_info));
if (this->encrypted_cipher_info.size() < this->rsa_pri.modulus_size()) {
this->async_read_data_from_proxy_client(1, std::min(static_cast<std::size_t>(this->rsa_pri.modulus_size()) - this->encrypted_cipher_info.size(), BUFFER_LENGTH));
return;
}
assert(this->encrypted_cipher_info.size() == this->rsa_pri.modulus_size());
std::vector<unsigned char> decrypted_cipher_info(this->rsa_pri.modulus_size());
if (86 != this->rsa_pri.decrypt(this->rsa_pri.modulus_size(), this->encrypted_cipher_info.data(), decrypted_cipher_info.data())) {
return;
}
if (decrypted_cipher_info[0] != 'A' ||
decrypted_cipher_info[1] != 'H' ||
decrypted_cipher_info[2] != 'P') {
return;
}
unsigned char protocol_version = decrypted_cipher_info[3];
auto need_auth = http_proxy_server_config::get_instance().enable_auth();
if (protocol_version == 0) {
if (need_auth || decrypted_cipher_info[4] != 0 || decrypted_cipher_info[6] != 0) {
return;
}
} else if (protocol_version == 1) {
if (need_auth) {
// 4 auth type
// - 0 no authentication
// - 1 32 bytes auth_key
auto auth_type = decrypted_cipher_info[4];
if (auth_type != 1) {
return;
}
// 6 ~ 37 auth_key
std::array<unsigned char, 32> auth_key_hash;
std::copy(decrypted_cipher_info.cbegin() + 6, decrypted_cipher_info.cbegin() + 38, auth_key_hash.begin());
if (!authentication::get_instance().auth(auth_key_hash)) {
return;
}
}
} else {
return;
}
// 5 cipher code
// 0x00 aes-128-cfb
// 0x01 aes-128-cfb8
// 0x02 aes-128-cfb1
// 0x03 aes-128-ofb
// 0x04 aes-128-ctr
// 0x05 aes-192-cfb
// 0x06 aes-192-cfb8
// 0x07 aes-192-cfb1
// 0x08 aes-192-ofb
// 0x09 aes-192-ctr
// 0x0A aes-256-cfb
// 0x0B aes-256-cfb8
// 0x0C aes-256-cfb1
// 0x0D aes-256-ofb
// 0x0E aes-256-ctr
unsigned char cipher_code = decrypted_cipher_info[5];
std::size_t ivec_offset = protocol_version == 0 ? 7 : 38;
std::size_t cipher_key_offset = protocol_version == 0 ? 23 : 54;
if (cipher_code == '\x00' || cipher_code == '\x05' || cipher_code == '\x0A') {
// aes-xxx-cfb
std::size_t ivec_size = 16;
std::size_t key_bits = 256; // aes-256-cfb
if (cipher_code == '\x00') {
// aes-128-cfb
key_bits = 128;
}
else if (cipher_code == '\x05') {
// aes-192-cfb
key_bits = 192;
}
this->encryptor = std::unique_ptr<stream_encryptor>(new aes_cfb128_encryptor(&decrypted_cipher_info[cipher_key_offset], key_bits, &decrypted_cipher_info[ivec_offset]));
this->decryptor = std::unique_ptr<stream_decryptor>(new aes_cfb128_decryptor(&decrypted_cipher_info[cipher_key_offset], key_bits, &decrypted_cipher_info[ivec_offset]));
}
else if (cipher_code == '\x01' || cipher_code == '\x06' || cipher_code == '\x0B') {
// ase-xxx-cfb8(deprecated)
}
else if (cipher_code == '\x02' || cipher_code == '\x07' || cipher_code == '\x0C') {
// ase-xxx-cfb1(deprecated)
}
else if (cipher_code == '\x03' || cipher_code == '\x08' || cipher_code == '\x0D') {
// ase-xxx-ofb
std::size_t ivec_size = 16;
std::size_t key_bits = 256; // aes-256-ofb
if (cipher_code == '\x03') {
// aes-128-ofb
key_bits = 128;
}
else if (cipher_code == '\x08') {
// aes-192-ofb
key_bits = 192;
}
this->encryptor = std::unique_ptr<stream_encryptor>(new aes_ofb128_encryptor(&decrypted_cipher_info[cipher_key_offset], key_bits, &decrypted_cipher_info[ivec_offset]));
this->decryptor = std::unique_ptr<stream_decryptor>(new aes_ofb128_decryptor(&decrypted_cipher_info[cipher_key_offset], key_bits, &decrypted_cipher_info[ivec_offset]));
}
else if (cipher_code == '\x04' || cipher_code == '\x09' || cipher_code == '\x0E') {
// ase-xxx-ctr
std::size_t ivec_size = 16;
std::size_t key_bits = 256; // aes-256-ctr
if (cipher_code == '\x04') {
// aes-128-ctr
key_bits = 128;
}
else if (cipher_code == '\x09') {
// aes-192-ctr
key_bits = 192;
}
std::vector<unsigned char> ivec(ivec_size, 0);
this->encryptor = std::unique_ptr<stream_encryptor>(new aes_ctr128_encryptor(&decrypted_cipher_info[cipher_key_offset], key_bits, ivec.data()));
this->decryptor = std::unique_ptr<stream_decryptor>(new aes_ctr128_decryptor(&decrypted_cipher_info[cipher_key_offset], key_bits, ivec.data()));
}
if (this->encryptor == nullptr || this->decryptor == nullptr) {
return;
}
this->connection_context.connection_state = proxy_connection_state::read_http_request_header;
this->async_read_data_from_proxy_client();
return;
}
assert(this->encryptor != nullptr && this->decryptor != nullptr);
this->decryptor->decrypt(reinterpret_cast<const unsigned char*>(&this->upgoing_buffer_read[0]), reinterpret_cast<unsigned char*>(&this->upgoing_buffer_write[0]), bytes_transferred);
if (this->connection_context.connection_state == proxy_connection_state::read_http_request_header) {
const auto& decripted_data_buffer = this->upgoing_buffer_write;
this->request_data.append(decripted_data_buffer.begin(), decripted_data_buffer.begin() + bytes_transferred);
auto double_crlf_pos = this->request_data.find("\r\n\r\n");
if (double_crlf_pos == std::string::npos) {
if (this->request_data.size() < MAX_REQUEST_HEADER_LENGTH) {
this->async_read_data_from_proxy_client();
}
else {
this->report_error("400", "Bad Request", "Request header too long");
}
return;
}
this->request_header = http_header_parser::parse_request_header(this->request_data.begin(), this->request_data.begin() + double_crlf_pos + 4);
if (!this->request_header) {
this->report_error("400", "Bad Request", "Failed to parse the http request header");
return;
}
if (this->request_header->method() != "GET"
// && this->request_header->method() != "OPTIONS"
&& this->request_header->method() != "HEAD"
&& this->request_header->method() != "POST"
&& this->request_header->method() != "PUT"
&& this->request_header->method() != "DELETE"
// && this->request_header->method() != "TRACE"
&& this->request_header->method() != "CONNECT") {
this->report_error("405", "Method Not Allowed", std::string());
return;
}
if (this->request_header->http_version() != "1.1"
&& this->request_header->http_version() != "1.0") {
this->report_error("505", "HTTP Version Not Supported", std::string());
return;
}
if (this->request_header->method() == "CONNECT") {
this->async_connect_to_origin_server();
return;
}
else {
if (this->request_header->scheme() != "http") {
this->report_error("400", "Bad Request", "Unsupported scheme");
return;
}
auto proxy_connection_value = this->request_header->get_header_value("Proxy-Connection");
auto connection_value = this->request_header->get_header_value("Connection");
auto string_to_lower_case = [](std::string& str) {
for (auto iter = str.begin(); iter != str.end(); ++iter) {
*iter = std::tolower(static_cast<unsigned char>(*iter));
}
};
if (proxy_connection_value) {
string_to_lower_case(*proxy_connection_value);
if (this->request_header->http_version() == "1.1") {
this->read_request_context.is_proxy_client_keep_alive = true;
if (*proxy_connection_value == "close") {
this->read_request_context.is_proxy_client_keep_alive = false;
}
}
else {
assert(this->request_header->http_version() == "1.0");
this->read_request_context.is_proxy_client_keep_alive = false;
if (*proxy_connection_value == "keep-alive") {
this->read_request_context.is_proxy_client_keep_alive = true;
}
}
}
else {
if (this->request_header->http_version() == "1.1") {
this->read_request_context.is_proxy_client_keep_alive = true;
}
else {
this->read_request_context.is_proxy_client_keep_alive = false;
}
if (connection_value) {
string_to_lower_case(*connection_value);
if (this->request_header->http_version() == "1.1" && *connection_value == "close") {
this->read_request_context.is_proxy_client_keep_alive = false;
}
else if (this->request_header->http_version() == "1.0" && *connection_value == "keep-alive") {
this->read_request_context.is_proxy_client_keep_alive = true;
}
}
}
this->read_request_context.content_length = std::optional<std::uint64_t>();
this->read_request_context.content_length_has_read = 0;
this->read_request_context.chunk_checker = std::optional<http_chunk_checker>();
if (this->request_header->method() == "GET" || this->request_header->method() == "HEAD" || this->request_header->method() == "DELETE") {
this->read_request_context.content_length = std::optional<std::uint64_t>(0);
}
else if (this->request_header->method() == "POST" || this->request_header->method() == "PUT") {
auto content_length_value = this->request_header->get_header_value("Content-Length");
auto transfer_encoding_value = this->request_header->get_header_value("Transfer-Encoding");
if (content_length_value) {
try {
this->read_request_context.content_length = std::optional<std::uint64_t>(std::stoull(*content_length_value));
}
catch (const std::exception&) {
this->report_error("400", "Bad Request", "Invalid Content-Length in request");
return;
}
this->read_request_context.content_length_has_read = this->request_data.size() - (double_crlf_pos + 4);
}
else if (transfer_encoding_value) {
string_to_lower_case(*transfer_encoding_value);
if (*transfer_encoding_value == "chunked") {
if (!this->read_request_context.chunk_checker->check(this->request_data.begin() + double_crlf_pos + 4, this->request_data.end())) {
this->report_error("400", "Bad Request", "Failed to check chunked response");
return;
}
return;
}
else {
this->report_error("400", "Bad Request", "Unsupported Transfer-Encoding");
return;
}
}
else {
this->report_error("411", "Length Required", std::string());
return;
}
}
else {
assert(false);
return;
}
}
this->async_connect_to_origin_server();
}
else if (this->connection_context.connection_state == proxy_connection_state::read_http_request_content) {
if (this->read_request_context.content_length) {
this->read_request_context.content_length_has_read += bytes_transferred;
}
else {
assert(this->read_request_context.chunk_checker);
if (!this->read_request_context.chunk_checker->check(this->upgoing_buffer_write.begin(), this->upgoing_buffer_write.begin() + bytes_transferred)) {
return;
}
}
this->connection_context.connection_state = proxy_connection_state::write_http_request_content;
this->async_write_data_to_origin_server(this->upgoing_buffer_write.data(), 0, bytes_transferred);
}
else if (this->connection_context.connection_state == proxy_connection_state::tunnel_transfer) {
this->async_write_data_to_origin_server(this->upgoing_buffer_write.data(), 0, bytes_transferred);
}
}
void http_proxy_server_connection::on_origin_server_data_arrived(std::size_t bytes_transferred)
{
if (this->connection_context.connection_state == proxy_connection_state::read_http_response_header) {
this->response_data.append(this->downgoing_buffer_read.begin(), this->downgoing_buffer_read.begin() + bytes_transferred);
auto double_crlf_pos = this->response_data.find("\r\n\r\n");
if (double_crlf_pos == std::string::npos) {
if (this->response_data.size() < MAX_RESPONSE_HEADER_LENGTH) {
this->async_read_data_from_origin_server();
}
else {
this->report_error("502", "Bad Gateway", "Response header too long");
}
return;
}
this->response_header = http_header_parser::parse_response_header(this->response_data.begin(), this->response_data.begin() + double_crlf_pos + 4);
if (!this->response_header) {
this->report_error("502", "Bad Gateway", "Failed to parse response header");
return;
}
if (this->response_header->http_version() != "1.1" && this->response_header->http_version() != "1.0") {
this->report_error("502", "Bad Gateway", "HTTP version not supported");
return;
}
if (this->response_header->status_code() < 100 || this->response_header->status_code() > 700) {
this->report_error("502", "Bad Gateway", "Unexpected status code");
return;
}
this->read_response_context.content_length = std::optional<std::uint64_t>();
this->read_response_context.content_length_has_read = 0;
this->read_response_context.is_origin_server_keep_alive = false;
this->read_response_context.chunk_checker = std::optional<http_chunk_checker>();
auto connection_value = this->response_header->get_header_value("Connection");
if (this->response_header->http_version() == "1.1") {
this->read_response_context.is_origin_server_keep_alive = true;
}
else {
this->read_response_context.is_origin_server_keep_alive = false;
}
auto string_to_lower_case = [](std::string& str) {
for (auto iter = str.begin(); iter != str.end(); ++iter) {
*iter = std::tolower(static_cast<unsigned char>(*iter));
}
};
if (connection_value) {
string_to_lower_case(*connection_value);
if (*connection_value == "close") {
this->read_response_context.is_origin_server_keep_alive = false;
}
else if (*connection_value == "keep-alive") {
this->read_response_context.is_origin_server_keep_alive = true;
}
else {
this->report_error("502", "Bad Gateway", std::string());
return;
}
}
if (this->request_header->method() == "HEAD") {
this->read_response_context.content_length = std::optional<std::uint64_t>(0);
}
else if (this->response_header->status_code() == 204 || this->response_header->status_code() == 304) {
// 204 No Content
// 304 Not Modified
this->read_response_context.content_length = std::optional<std::uint64_t>(0);
}
else {
auto content_length_value = this->response_header->get_header_value("Content-Length");
auto transfer_encoding_value = this->response_header->get_header_value("Transfer-Encoding");
if (content_length_value) {
try {
this->read_response_context.content_length = std::optional<std::uint64_t>(std::stoull(*content_length_value));
}
catch(const std::exception&) {
this->report_error("502", "Bad Gateway", "Invalid Content-Length in response");
return;
}
this->read_response_context.content_length_has_read = this->response_data.size() - (double_crlf_pos + 4);
}
else if (transfer_encoding_value) {
string_to_lower_case(*transfer_encoding_value);
if (*transfer_encoding_value == "chunked") {
this->read_response_context.chunk_checker = std::optional<http_chunk_checker>(http_chunk_checker());
if (!this->read_response_context.chunk_checker->check(this->response_data.begin() + double_crlf_pos + 4, this->response_data.end())) {
this->report_error("502", "Bad Gateway", "Failed to check chunked response");
return;
}
}
else {
this->report_error("502", "Bad Gateway", "Unsupported Transfer-Encoding");
return;
}
}
else if (this->read_response_context.is_origin_server_keep_alive) {
this->report_error("502", "Bad Gateway", "Miss response length info");
return;
}
}
this->async_write_response_header_to_proxy_client();
}
else if (this->connection_context.connection_state == proxy_connection_state::read_http_response_content) {
if (this->read_response_context.content_length) {
this->read_response_context.content_length_has_read += bytes_transferred;
}
else if (this->read_response_context.chunk_checker) {
if (!this->read_response_context.chunk_checker->check(this->downgoing_buffer_read.begin(), this->downgoing_buffer_read.begin() + bytes_transferred)) {
return;
}
}
this->connection_context.connection_state = proxy_connection_state::write_http_response_content;
this->encryptor->encrypt(reinterpret_cast<const unsigned char*>(&this->downgoing_buffer_read[0]), reinterpret_cast<unsigned char*>(&this->downgoing_buffer_write[0]), bytes_transferred);
this->async_write_data_to_proxy_client(this->downgoing_buffer_write.data(), 0, bytes_transferred);
}
else if (this->connection_context.connection_state == proxy_connection_state::tunnel_transfer) {
this->encryptor->encrypt(reinterpret_cast<const unsigned char*>(&this->downgoing_buffer_read[0]), reinterpret_cast<unsigned char*>(&this->downgoing_buffer_write[0]), bytes_transferred);
this->async_write_data_to_proxy_client(this->downgoing_buffer_write.data(), 0, bytes_transferred);
}
}
void http_proxy_server_connection::on_proxy_client_data_written()
{
if (this->connection_context.connection_state == proxy_connection_state::tunnel_transfer) {
this->async_read_data_from_origin_server();
}
else if (this->connection_context.connection_state == proxy_connection_state::write_http_response_header
|| this->connection_context.connection_state == proxy_connection_state::write_http_response_content) {
if ((this->read_response_context.content_length && this->read_response_context.content_length_has_read >= *this->read_response_context.content_length)
|| (this->read_response_context.chunk_checker && this->read_response_context.chunk_checker->is_complete())) {
std::error_code ec;
if (!this->read_response_context.is_origin_server_keep_alive) {
this->origin_server_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);
this->origin_server_socket.close(ec);
}
if (this->read_request_context.is_proxy_client_keep_alive) {
this->request_data.clear();
this->response_data.clear();
this->request_header = std::optional<http_request_header>();
this->response_header = std::optional<http_response_header>();
this->read_request_context.content_length = std::optional<std::uint64_t>();
this->read_request_context.chunk_checker = std::optional<http_chunk_checker>();
this->read_response_context.content_length = std::optional<std::uint64_t>();
this->read_response_context.chunk_checker = std::optional<http_chunk_checker>();
this->connection_context.connection_state = proxy_connection_state::read_http_request_header;
this->async_read_data_from_proxy_client();
}
else {
this->proxy_client_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);
this->proxy_client_socket.close(ec);
}
}
else {
this->connection_context.connection_state = proxy_connection_state::read_http_response_content;
this->async_read_data_from_origin_server();
}
}
else if (this->connection_context.connection_state == proxy_connection_state::report_connection_established) {
this->start_tunnel_transfer();
}
else if (this->connection_context.connection_state == proxy_connection_state::report_error) {
std::error_code ec;
if (this->origin_server_socket.is_open()) {
this->origin_server_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);
this->origin_server_socket.close(ec);
}
if (this->proxy_client_socket.is_open()) {
this->proxy_client_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);
this->proxy_client_socket.close(ec);
}
}
}
void http_proxy_server_connection::on_origin_server_data_written()
{
if (this->connection_context.connection_state == proxy_connection_state::tunnel_transfer) {
this->async_read_data_from_proxy_client();
}
else if (this->connection_context.connection_state == proxy_connection_state::write_http_request_header
|| this->connection_context.connection_state == proxy_connection_state::write_http_request_content) {
if (this->read_request_context.content_length) {
if (this->read_request_context.content_length_has_read < *this->read_request_context.content_length) {
this->connection_context.connection_state = proxy_connection_state::read_http_request_content;
this->async_read_data_from_proxy_client();
}
else {
this->connection_context.connection_state = proxy_connection_state::read_http_response_header;
this->async_read_data_from_origin_server();
}
}
else {
assert(this->read_request_context.chunk_checker);
if (!this->read_request_context.chunk_checker->is_complete()) {
this->connection_context.connection_state = proxy_connection_state::read_http_request_content;
this->async_read_data_from_proxy_client();
}
else {
this->connection_context.connection_state = proxy_connection_state::read_http_response_header;
this->async_read_data_from_origin_server();
}
}
}
}
void http_proxy_server_connection::on_error(const std::error_code& error)
{
if (this->connection_context.connection_state == proxy_connection_state::resolve_origin_server_address) {
this->report_error("504", "Gateway Timeout", "Failed to resolve the hostname");
}
else if (this->connection_context.connection_state == proxy_connection_state::connect_to_origin_server) {
this->report_error("502", "Bad Gateway", "Failed to connect to origin server");
}
else if (this->connection_context.connection_state == proxy_connection_state::write_http_request_header && this->connection_context.reconnect_on_error) {
std::error_code ec;
this->origin_server_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);
this->origin_server_socket.close(ec);
this->async_connect_to_origin_server();
}
else {
std::error_code ec;
if (this->origin_server_socket.is_open()) {
this->origin_server_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);
this->origin_server_socket.close(ec);
}
if (this->proxy_client_socket.is_open()) {
this->proxy_client_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);
this->proxy_client_socket.close(ec);
}
}
}
void http_proxy_server_connection::on_timeout()
{
std::error_code ec;
if (this->origin_server_socket.is_open()) {
this->origin_server_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);
this->origin_server_socket.close(ec);
}
if (this->proxy_client_socket.is_open()) {
this->proxy_client_socket.shutdown(net::ip::tcp::socket::shutdown_both, ec);
this->proxy_client_socket.close(ec);
}
}
} // namespace azure_proxy
================================================
FILE: src/http_proxy_server_connection.hpp
================================================
/*
* http_proxy_server_connection.hpp:
*
* Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.
*
*/
#ifndef AZURE_HTTP_PROXY_SERVER_CONNECTION_HPP
#define AZURE_HTTP_PROXY_SERVER_CONNECTION_HPP
#include <array>
#include <chrono>
#include <experimental/net>
#include <optional>
#include "encrypt.hpp"
#include "http_header_parser.hpp"
#include "http_proxy_server_connection_context.hpp"
namespace net = std::experimental::net;
namespace azure_proxy {
const std::size_t BUFFER_LENGTH = 2048;
class http_proxy_server_connection : public std::enable_shared_from_this<http_proxy_server_connection> {
net::strand<net::io_context::executor_type> strand;
net::ip::tcp::socket proxy_client_socket;
net::ip::tcp::socket origin_server_socket;
net::ip::tcp::resolver resolver;
net::ip::tcp::resolver::results_type resolve_results;
net::basic_waitable_timer<std::chrono::steady_clock> timer;
std::array<char, BUFFER_LENGTH> upgoing_buffer_read;
std::array<char, BUFFER_LENGTH> upgoing_buffer_write;
std::array<char, BUFFER_LENGTH> downgoing_buffer_read;
std::array<char, BUFFER_LENGTH> downgoing_buffer_write;
rsa rsa_pri;
std::vector<unsigned char> encrypted_cipher_info;
std::unique_ptr<stream_encryptor> encryptor;
std::unique_ptr<stream_decryptor> decryptor;
std::string request_data;
std::string modified_request_data;
std::string response_data;
std::string modified_response_data;
std::optional<http_request_header> request_header;
std::optional<http_response_header> response_header;
http_proxy_server_connection_context connection_context;
http_proxy_server_connection_read_request_context read_request_context;
http_proxy_server_connection_read_response_context read_response_context;
private:
http_proxy_server_connection(net::io_context& io_ctx, net::ip::tcp::socket&& proxy_client_socket);
public:
~http_proxy_server_connection();
static std::shared_ptr<http_proxy_server_connection> create(net::io_context& io_ctx, net::ip::tcp::socket&& client_socket);
void start();
private:
void async_read_data_from_proxy_client(std::size_t at_least_size = 1, std::size_t at_most_size = BUFFER_LENGTH);
void async_read_data_from_origin_server(bool set_timer = true, std::size_t at_least_size = 1, std::size_t at_most_size = BUFFER_LENGTH);
void async_connect_to_origin_server();
void async_write_request_header_to_origin_server();
void async_write_response_header_to_proxy_client();
void async_write_data_to_origin_server(const char* write_buffer, std::size_t offset, std::size_t size);
void async_write_data_to_proxy_client(const char* write_buffer, std::size_t offset, std::size_t size);
void start_tunnel_transfer();
void report_error(const std::string& status_code, const std::string& status_description, const std::string& error_message);
void set_timer();
bool cancel_timer();
void on_resolved(net::ip::tcp::resolver::results_type::const_iterator endpoint_iterator);
void on_connect();
void on_proxy_client_data_arrived(std::size_t bytes_transferred);
void on_origin_server_data_arrived(std::size_t bytes_transferred);
void on_proxy_client_data_written();
void on_origin_server_data_written();
void on_error(const std::error_code& error);
void on_timeout();
};
} // namespace azure_proxy
#endif
================================================
FILE: src/http_proxy_server_connection_context.hpp
================================================
/*
* http_proxy_server_connection_context.hpp:
*
* Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.
*
*/
#ifndef AZURE_HTTP_PROXY_SERVER_CONNECTION_CONTEXT_HPP
#define AZURE_HTTP_PROXY_SERVER_CONNECTION_CONTEXT_HPP
#include <cstdint>
#include <experimental/net>
#include <optional>
#include "http_chunk_checker.hpp"
namespace net = std::experimental::net;
namespace azure_proxy {
enum class proxy_connection_state {
read_cipher_data,
resolve_origin_server_address,
connect_to_origin_server,
tunnel_transfer,
read_http_request_header,
write_http_request_header,
read_http_request_content,
write_http_request_content,
read_http_response_header,
write_http_response_header,
read_http_response_content,
write_http_response_content,
report_connection_established,
report_error
};
struct http_proxy_server_connection_context {
proxy_connection_state connection_state;
bool reconnect_on_error;
std::string origin_server_name;
unsigned short origin_server_port;
std::optional<net::ip::tcp::endpoint> origin_server_endpoint;
};
struct http_proxy_server_connection_read_request_context {
bool is_proxy_client_keep_alive;
std::optional<std::uint64_t> content_length;
std::uint64_t content_length_has_read;
std::optional<http_chunk_checker> chunk_checker;
};
struct http_proxy_server_connection_read_response_context {
bool is_origin_server_keep_alive;
std::optional<std::uint64_t> content_length;
std::uint64_t content_length_has_read;
std::optional<http_chunk_checker> chunk_checker;
};
} // namespace azure_proxy
#endif
================================================
FILE: src/http_proxy_server_main.cpp
================================================
/*
* http_proxy_server_main.cpp:
*
* Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.
*
*/
#include <experimental/net>
#include <csignal>
#include <cstdlib>
#include <iostream>
#include <string>
#include <vector>
#include "http_proxy_server_config.hpp"
#include "http_proxy_server.hpp"
#include "version.hpp"
#ifdef _WIN32
#include <codecvt>
#include <shellapi.h>
#endif
namespace net = std::experimental::net;
struct ServerArgs {
std::string config_file = "server.json";
};
void print_usage() {
#ifdef _WIN32
const char *prog = "ahps.exe";
#else
const char *prog = "ahps";
#endif
std::cout << "Usage: " << prog << " [options]\n\n"
<< "options:\n"
<< " -h, --help Show this help message and exit\n"
<< " -v, --version Print the program version and exit\n"
<< " -c, --config PATH Configuration file path (default: server.json)\n";
}
static ServerArgs parse_args(const std::vector<std::string>& argv) {
std::string arg;
bool invalid_param = false;
ServerArgs args;
for (std::size_t i = 1; i < argv.size(); ++i) {
arg = argv[i];
if (arg == "-h" || arg == "--help") {
print_usage();
std::exit(EXIT_SUCCESS);
} else if (arg == "-v" || arg == "--version") {
std::cout << "Version: " << AHP_VERSION_STRING << std::endl;
std::exit(EXIT_SUCCESS);
} else if (arg == "-c" || arg == "--config") {
if (++i >= argv.size()) {
invalid_param = true;
break;
}
args.config_file = argv[i];
} else {
std::cerr << "Unknown argument: " << arg << std::endl;
print_usage();
std::exit(EXIT_FAILURE);
}
}
if (invalid_param) {
std::cerr << "Invalid parameter for argument: " << arg << std::endl;
std::exit(EXIT_FAILURE);
}
return args;
}
static ServerArgs parse_args(int argc, char** argv) {
std::vector<std::string> argv_vec;
argv_vec.reserve(argc);
#ifdef _WIN32
LPWSTR *wargs = CommandLineToArgvW(GetCommandLineW(), &argc);
if (wargs == nullptr) {
std::cerr << "Failed to retrieve command line arguments" << std::endl;
std::exit(EXIT_FAILURE);
}
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
for (std::size_t i = 0; i < argc; ++i) {
argv_vec.emplace_back(converter.to_bytes(wargs[i]));
}
LocalFree(wargs);
#else
for (std::size_t i = 0; i < argc; ++i) {
argv_vec.emplace_back(argv[i]);
}
#endif
return parse_args(argv_vec);
}
std::weak_ptr<net::io_context> wp_io_ctx;
static void signal_handler(int signal)
{
auto io_ctx = wp_io_ctx.lock();
if (io_ctx) {
io_ctx->stop();
}
}
int main(int argc, char** argv)
{
using namespace azure_proxy;
auto args = parse_args(argc, argv);
try {
auto& config = http_proxy_server_config::get_instance();
if (config.load_config(args.config_file)) {
std::cout << "AHP server version " << AHP_VERSION_STRING << std::endl;
std::cout << "bind address: " << config.get_bind_address() << ':' << config.get_listen_port() << std::endl;
auto io_ctx = std::make_shared<net::io_context>();
wp_io_ctx = io_ctx;
http_proxy_server server(*io_ctx);
std::signal(SIGINT, signal_handler);
std::signal(SIGTERM, signal_handler);
server.run();
}
}
catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
return 0;
}
================================================
FILE: src/key_generator.hpp
================================================
/*
* key_gennerator.hpp:
*
* Copyright (C) 2014-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.
*
*/
#ifndef AZURE_KEY_GENERATOR_HPP
#define AZURE_KEY_GENERATOR_HPP
#include <random>
#include <memory>
#include <cassert>
#include <chrono>
#include <mutex>
namespace azure_proxy {
class key_generator {
std::mt19937 gen;
std::mutex mtx;
key_generator() {
std::uint64_t seed = reinterpret_cast<std::uint64_t>(std::unique_ptr<int>(new int(0)).get()) ^ static_cast<std::uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count());
this->gen.seed(seed);
}
public:
void generate(unsigned char* out, std::size_t length) {
assert(out);
std::uniform_int_distribution<unsigned short> dis(0, 255);
std::lock_guard<std::mutex> lck(this->mtx);
for (std::size_t i = 0; i < length; ++i) {
out[i] = static_cast<unsigned char>(dis(this->gen));
}
}
static key_generator& get_instance() {
static key_generator instance;
return instance;
}
};
}
#endif
================================================
FILE: src/version.hpp
================================================
/*
* version.hpp:
*
* Copyright (C) 2023 Light Lin <lxrite@gmail.com> All Rights Reserved.
*
*/
#ifndef AZURE_VERSION_HPP
#define AZURE_VERSION_HPP
#define AHP_MAJOR_VERSION 1
#define AHP_MINOR_VERSION 2
#define AHP_PATCH_VERSION 0
#define AHP_VERSION_TO_STR_(x) #x
#define AHP_VERSION_TO_STR(x) AHP_VERSION_TO_STR_(x)
#define AHP_VERSION_DOT AHP_VERSION_TO_STR_(.)
#if AHP_PATCH_VERSION > 0
#define AHP_VERSION_STRING AHP_VERSION_TO_STR(AHP_MAJOR_VERSION) AHP_VERSION_DOT AHP_VERSION_TO_STR(AHP_MINOR_VERSION) AHP_VERSION_DOT AHP_VERSION_TO_STR(AHP_PATCH_VERSION)
#else
#define AHP_VERSION_STRING AHP_VERSION_TO_STR(AHP_MAJOR_VERSION) AHP_VERSION_DOT AHP_VERSION_TO_STR(AHP_MINOR_VERSION)
#endif
#endif // AZURE_VERSION_HPP
gitextract_3nt7pewi/
├── .github/
│ └── workflows/
│ └── cmake.yml
├── .gitignore
├── .gitmodules
├── CMakeLists.txt
├── Dockerfile
├── LICENSE
├── README.md
├── example/
│ ├── client.json
│ └── server.json
└── src/
├── authentication.cpp
├── authentication.hpp
├── configs/
│ └── ahp_mbedtls_config.h
├── encrypt.hpp
├── hash_utils.cpp
├── hash_utils.hpp
├── http_chunk_checker.hpp
├── http_header_parser.cpp
├── http_header_parser.hpp
├── http_proxy_client.cpp
├── http_proxy_client.hpp
├── http_proxy_client_config.cpp
├── http_proxy_client_config.hpp
├── http_proxy_client_connection.cpp
├── http_proxy_client_connection.hpp
├── http_proxy_client_main.cpp
├── http_proxy_server.cpp
├── http_proxy_server.hpp
├── http_proxy_server_config.cpp
├── http_proxy_server_config.hpp
├── http_proxy_server_connection.cpp
├── http_proxy_server_connection.hpp
├── http_proxy_server_connection_context.hpp
├── http_proxy_server_main.cpp
├── key_generator.hpp
└── version.hpp
SYMBOL INDEX (106 symbols across 24 files)
FILE: src/authentication.cpp
type azure_proxy (line 14) | namespace azure_proxy {
function authentication (line 36) | authentication& authentication::get_instance()
FILE: src/authentication.hpp
type azure_proxy (line 15) | namespace azure_proxy {
class authentication (line 19) | class authentication {
FILE: src/encrypt.hpp
type azure_proxy (line 24) | namespace azure_proxy {
class stream_encryptor (line 26) | class stream_encryptor {
class stream_decryptor (line 32) | class stream_decryptor {
class copy_encryptor (line 38) | class copy_encryptor : public stream_encryptor {
method copy_encryptor (line 40) | copy_encryptor() {}
method encrypt (line 41) | virtual void encrypt(const unsigned char* in, unsigned char* out, st...
class copy_decryptor (line 48) | class copy_decryptor : public stream_decryptor {
method copy_decryptor (line 50) | copy_decryptor() {}
method decrypt (line 51) | virtual void decrypt(const unsigned char* in, unsigned char* out, st...
class aes_stream_encryptor (line 58) | class aes_stream_encryptor : public stream_encryptor {
method aes_stream_encryptor (line 61) | aes_stream_encryptor(const unsigned char* key, const mbedtls_cipher_...
method encrypt (line 83) | virtual void encrypt(const unsigned char* in, unsigned char* out, st...
class aes_stream_decryptor (line 99) | class aes_stream_decryptor : public stream_decryptor {
method aes_stream_decryptor (line 102) | aes_stream_decryptor(const unsigned char* key, const mbedtls_cipher_...
method decrypt (line 124) | virtual void decrypt(const unsigned char* in, unsigned char* out, st...
function mbedtls_cipher_info_t (line 140) | static const mbedtls_cipher_info_t* aes_cfb128_cipher(std::size_t key_...
function mbedtls_cipher_info_t (line 156) | static const mbedtls_cipher_info_t* aes_ofb128_cipher(std::size_t key_...
function mbedtls_cipher_info_t (line 172) | static const mbedtls_cipher_info_t* aes_ctr128_cipher(std::size_t key_...
class aes_cfb128_encryptor (line 188) | class aes_cfb128_encryptor : public aes_stream_encryptor {
method aes_cfb128_encryptor (line 190) | aes_cfb128_encryptor(const unsigned char* key, std::size_t key_bits,...
class aes_cfb128_decryptor (line 195) | class aes_cfb128_decryptor : public aes_stream_decryptor {
method aes_cfb128_decryptor (line 197) | aes_cfb128_decryptor(const unsigned char* key, std::size_t key_bits,...
class aes_ofb128_encryptor (line 202) | class aes_ofb128_encryptor : public aes_stream_encryptor {
method aes_ofb128_encryptor (line 204) | aes_ofb128_encryptor(const unsigned char* key, std::size_t key_bits,...
class aes_ofb128_decryptor (line 209) | class aes_ofb128_decryptor : public aes_stream_decryptor {
method aes_ofb128_decryptor (line 211) | aes_ofb128_decryptor(const unsigned char* key, std::size_t key_bits,...
class aes_ctr128_encryptor (line 216) | class aes_ctr128_encryptor : public aes_stream_encryptor {
method aes_ctr128_encryptor (line 218) | aes_ctr128_encryptor(const unsigned char* key, std::size_t key_bits,...
class aes_ctr128_decryptor (line 223) | class aes_ctr128_decryptor : public aes_stream_decryptor {
method aes_ctr128_decryptor (line 225) | aes_ctr128_decryptor(const unsigned char* key, std::size_t key_bits,...
class rsa (line 230) | class rsa {
method rsa (line 234) | rsa(const std::string& key) {
method encrypt (line 291) | int encrypt(int flen, unsigned char* from, unsigned char* to) {
method decrypt (line 315) | int decrypt(int flen, unsigned char* from, unsigned char* to) {
method modulus_size (line 340) | int modulus_size() const {
FILE: src/hash_utils.cpp
type azure_proxy (line 11) | namespace azure_proxy {
type hash_utils (line 12) | namespace hash_utils {
function sha256 (line 14) | std::array<unsigned char, 32> sha256(const unsigned char* data, std:...
FILE: src/hash_utils.hpp
type azure_proxy (line 13) | namespace azure_proxy {
type hash_utils (line 14) | namespace hash_utils {
FILE: src/http_chunk_checker.hpp
type azure_proxy (line 16) | namespace azure_proxy {
type http_chunk_check_state (line 18) | enum class http_chunk_check_state {
class http_chunk_checker (line 31) | class http_chunk_checker {
method http_chunk_checker (line 36) | http_chunk_checker() : state(http_chunk_check_state::chunk_size_star...
method is_complete (line 38) | bool is_complete() const {
method is_fail (line 42) | bool is_fail() const {
method check (line 47) | bool check(ForwardIterator begin, ForwardIterator end) {
FILE: src/http_header_parser.cpp
type azure_proxy (line 15) | namespace azure_proxy {
function http_headers_container (line 65) | const http_headers_container& http_request_header::get_headers_map() c...
function http_headers_container (line 103) | const http_headers_container& http_response_header::get_headers_map() ...
function http_headers_container (line 108) | http_headers_container http_header_parser::parse_headers(std::string::...
type parse_user_data (line 268) | struct parse_user_data {
FILE: src/http_header_parser.hpp
type azure_proxy (line 17) | namespace azure_proxy {
type default_filed_name_compare (line 19) | struct default_filed_name_compare {
class http_request_header (line 28) | class http_request_header {
class http_response_header (line 50) | class http_response_header {
class http_header_parser (line 66) | class http_header_parser {
FILE: src/http_proxy_client.cpp
type azure_proxy (line 18) | namespace azure_proxy {
FILE: src/http_proxy_client.hpp
type azure_proxy (line 15) | namespace azure_proxy {
class http_proxy_client (line 17) | class http_proxy_client {
FILE: src/http_proxy_client_config.cpp
type azure_proxy (line 23) | namespace azure_proxy {
function http_proxy_client_config (line 223) | http_proxy_client_config& http_proxy_client_config::get_instance()
FILE: src/http_proxy_client_config.hpp
type azure_proxy (line 17) | namespace azure_proxy {
class http_proxy_client_config (line 19) | class http_proxy_client_config {
method T (line 23) | T get_config_value(const std::string& key) const {
FILE: src/http_proxy_client_connection.cpp
type azure_proxy (line 17) | namespace azure_proxy {
FILE: src/http_proxy_client_connection.hpp
type azure_proxy (line 23) | namespace azure_proxy {
class http_proxy_client_connection (line 25) | class http_proxy_client_connection : public std::enable_shared_from_th...
type proxy_connection_state (line 26) | enum class proxy_connection_state {
FILE: src/http_proxy_client_main.cpp
type ClientArgs (line 26) | struct ClientArgs {
function print_usage (line 30) | void print_usage() {
function ClientArgs (line 43) | static ClientArgs parse_args(const std::vector<std::string>& argv) {
function ClientArgs (line 78) | static ClientArgs parse_args(int argc, char** argv) {
function signal_handler (line 105) | static void signal_handler(int signal)
function main (line 113) | int main(int argc, char** argv)
FILE: src/http_proxy_server.cpp
type azure_proxy (line 17) | namespace azure_proxy {
FILE: src/http_proxy_server.hpp
type azure_proxy (line 15) | namespace azure_proxy {
class http_proxy_server (line 17) | class http_proxy_server {
FILE: src/http_proxy_server_config.cpp
type azure_proxy (line 24) | namespace azure_proxy {
function http_proxy_server_config (line 188) | http_proxy_server_config& http_proxy_server_config::get_instance()
FILE: src/http_proxy_server_config.hpp
type azure_proxy (line 17) | namespace azure_proxy {
class http_proxy_server_config (line 19) | class http_proxy_server_config {
method T (line 23) | T get_config_value(const std::string& key) const {
FILE: src/http_proxy_server_connection.cpp
type azure_proxy (line 20) | namespace azure_proxy {
FILE: src/http_proxy_server_connection.hpp
type azure_proxy (line 22) | namespace azure_proxy {
class http_proxy_server_connection (line 26) | class http_proxy_server_connection : public std::enable_shared_from_th...
FILE: src/http_proxy_server_connection_context.hpp
type azure_proxy (line 19) | namespace azure_proxy {
type proxy_connection_state (line 21) | enum class proxy_connection_state {
type http_proxy_server_connection_context (line 38) | struct http_proxy_server_connection_context {
type http_proxy_server_connection_read_request_context (line 46) | struct http_proxy_server_connection_read_request_context {
type http_proxy_server_connection_read_response_context (line 53) | struct http_proxy_server_connection_read_response_context {
FILE: src/http_proxy_server_main.cpp
type ServerArgs (line 26) | struct ServerArgs {
function print_usage (line 30) | void print_usage() {
function ServerArgs (line 43) | static ServerArgs parse_args(const std::vector<std::string>& argv) {
function ServerArgs (line 78) | static ServerArgs parse_args(int argc, char** argv) {
function signal_handler (line 105) | static void signal_handler(int signal)
function main (line 113) | int main(int argc, char** argv)
FILE: src/key_generator.hpp
type azure_proxy (line 17) | namespace azure_proxy {
class key_generator (line 19) | class key_generator {
method key_generator (line 22) | key_generator() {
method generate (line 27) | void generate(unsigned char* out, std::size_t length) {
method key_generator (line 35) | static key_generator& get_instance() {
Condensed preview — 35 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (160K chars).
[
{
"path": ".github/workflows/cmake.yml",
"chars": 515,
"preview": "name: Build\n\non:\n push:\n branches: [ \"master\" ]\n pull_request:\n branches: [ \"master\" ]\n\nenv:\n BUILD_TYPE: Relea"
},
{
"path": ".gitignore",
"chars": 193,
"preview": "# Compiled Object files\n*.slo\n*.lo\n*.o\n*.obj\n\n# Compiled Dynamic libraries\n*.so\n*.dylib\n*.dll\n\n# Compiled Static librari"
},
{
"path": ".gitmodules",
"chars": 458,
"preview": "[submodule \"third_party/jsonxx\"]\n\tpath = third_party/jsonxx\n\turl = https://github.com/hjiang/jsonxx.git\n[submodule \"thir"
},
{
"path": "CMakeLists.txt",
"chars": 1796,
"preview": "cmake_minimum_required(VERSION 2.6)\nproject(azure-http-proxy)\n\nif(NOT CMAKE_BUILD_TYPE)\n set(CMAKE_BUILD_TYPE Release)\n"
},
{
"path": "Dockerfile",
"chars": 367,
"preview": "FROM alpine:3.18 as builder\n\nRUN apk update \\\n && apk add alpine-sdk cmake linux-headers\n\nWORKDIR /ahp\n\nCOPY . .\n\nRUN"
},
{
"path": "LICENSE",
"chars": 1098,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2013-2015 limhiaoing <blog.poxiao.me>\n\nPermission is hereby granted, free of charge"
},
{
"path": "README.md",
"chars": 3949,
"preview": "# azure-http-proxy\n\n## 简介\n\nAHP(Azure Http Proxy)是一款高速、安全、轻量级和跨平台的HTTP代理,使用对称加密算法AES对传输的数据进行加密,使用非对称加密算法RSA传输密钥。\n\n## 特性\n "
},
{
"path": "example/client.json",
"chars": 726,
"preview": "{\r\n \"proxy_server_address\": \"127.0.0.1\",\r\n \"proxy_server_port\": 8090,\r\n \"bind_address\": \"127.0.0.1\",\r\n \"list"
},
{
"path": "example/server.json",
"chars": 2016,
"preview": "{\r\n \"bind_address\": \"0.0.0.0\",\r\n \"listen_port\": 8090,\r\n \"rsa_private_key\": \"-----BEGIN RSA PRIVATE KEY-----\\nMI"
},
{
"path": "src/authentication.cpp",
"chars": 936,
"preview": "/*\r\n * authentication.cpp:\r\n *\r\n * Copyright (C) 2015-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n"
},
{
"path": "src/authentication.hpp",
"chars": 699,
"preview": "/*\r\n * authentication.hpp:\r\n *\r\n * Copyright (C) 2015-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n"
},
{
"path": "src/configs/ahp_mbedtls_config.h",
"chars": 662,
"preview": "#define MBEDTLS_HAVE_TIME\n#define MBEDTLS_HAVE_TIME_DATE\n#define MBEDTLS_CIPHER_MODE_CFB\n#define MBEDTLS_CIPHER_MODE_CTR"
},
{
"path": "src/encrypt.hpp",
"chars": 13676,
"preview": "/*\r\n * encrypt.hpp:\r\n *\r\n * Copyright (C) 2014-2024 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n */\r\n\r\n"
},
{
"path": "src/hash_utils.cpp",
"chars": 645,
"preview": "/*\n * hash_utils.cpp:\n *\n * Copyright (C) 2023-2024 Light Lin <lxrite@gmail.com> All Rights Reserved.\n *\n */\n\n#inc"
},
{
"path": "src/hash_utils.hpp",
"chars": 406,
"preview": "/*\n * hash_utils.hpp:\n *\n * Copyright (C) 2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\n *\n */\n\n#ifndef A"
},
{
"path": "src/http_chunk_checker.hpp",
"chars": 5381,
"preview": "/*\r\n * http_chunk_checker.hpp:\r\n *\r\n * Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r"
},
{
"path": "src/http_header_parser.cpp",
"chars": 13023,
"preview": "/*\r\n * http_header_parser.cpp:\r\n *\r\n * Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n"
},
{
"path": "src/http_header_parser.hpp",
"chars": 2672,
"preview": "/*\r\n * http_header_parser.hpp:\r\n *\r\n * Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n"
},
{
"path": "src/http_proxy_client.cpp",
"chars": 1787,
"preview": "/*\r\n * http_proxy_client.cpp:\r\n *\r\n * Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n"
},
{
"path": "src/http_proxy_client.hpp",
"chars": 558,
"preview": "/*\r\n * http_proxy_client.hpp:\r\n *\r\n * Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n"
},
{
"path": "src/http_proxy_client_config.cpp",
"chars": 8354,
"preview": "/*\r\n * http_proxy_client_config.cpp:\r\n *\r\n * Copyright (C) 2013-2024 Light Lin <lxrite@gmail.com> All Rights Reser"
},
{
"path": "src/http_proxy_client_config.hpp",
"chars": 1473,
"preview": "/*\r\n * http_proxy_client_config.hpp:\r\n *\r\n * Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reser"
},
{
"path": "src/http_proxy_client_connection.cpp",
"chars": 14759,
"preview": "/*\r\n * http_proxy_client_connection.cpp:\r\n *\r\n * Copyright (C) 2013-2024 Light Lin <lxrite@gmail.com> All Rights "
},
{
"path": "src/http_proxy_client_connection.hpp",
"chars": 2333,
"preview": "/*\r\n * http_proxy_client_connection.hpp:\r\n *\r\n * Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights "
},
{
"path": "src/http_proxy_client_main.cpp",
"chars": 4037,
"preview": "/*\r\n * http_proxy_client_main.cpp:\r\n *\r\n * Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserv"
},
{
"path": "src/http_proxy_server.cpp",
"chars": 1768,
"preview": "/*\r\n * http_proxy_server.cpp:\r\n *\r\n * Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n"
},
{
"path": "src/http_proxy_server.hpp",
"chars": 594,
"preview": "/*\r\n * http_proxy_server.hpp:\r\n *\r\n * Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n"
},
{
"path": "src/http_proxy_server_config.cpp",
"chars": 6616,
"preview": "/*\r\n * http_proxy_server_config.cpp:\r\n *\r\n * Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reser"
},
{
"path": "src/http_proxy_server_config.hpp",
"chars": 1306,
"preview": "/*\r\n * http_proxy_server_config.hpp:\r\n *\r\n * Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reser"
},
{
"path": "src/http_proxy_server_connection.cpp",
"chars": 44116,
"preview": "/*\r\n * http_proxy_server_connection.cpp:\r\n *\r\n * Copyright (C) 2013-2024 Light Lin <lxrite@gmail.com> All Rights R"
},
{
"path": "src/http_proxy_server_connection.hpp",
"chars": 3499,
"preview": "/*\r\n * http_proxy_server_connection.hpp:\r\n *\r\n * Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights "
},
{
"path": "src/http_proxy_server_connection_context.hpp",
"chars": 1728,
"preview": "/*\r\n * http_proxy_server_connection_context.hpp:\r\n *\r\n * Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All "
},
{
"path": "src/http_proxy_server_main.cpp",
"chars": 3834,
"preview": "/*\r\n * http_proxy_server_main.cpp:\r\n *\r\n * Copyright (C) 2013-2023 Light Lin <lxrite@gmail.com> All Rights Reserv"
},
{
"path": "src/key_generator.hpp",
"chars": 1188,
"preview": "/*\r\n * key_gennerator.hpp:\r\n *\r\n * Copyright (C) 2014-2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\r\n *\r\n"
},
{
"path": "src/version.hpp",
"chars": 743,
"preview": "/*\n * version.hpp:\n *\n * Copyright (C) 2023 Light Lin <lxrite@gmail.com> All Rights Reserved.\n *\n */\n\n \n#ifndef AZ"
}
]
About this extraction
This page contains the full source code of the lxrite/azure-http-proxy GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 35 files (144.4 KB), approximately 36.1k tokens, and a symbol index with 106 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.