Skip to content

Commit 27e82f7

Browse files
committed
Add an optional config entry ssl.alpn_port_override
that changes the remote port according to the ALPN. Fixes trojan-gfw#171 and trojan-gfw#226.
1 parent 5074793 commit 27e82f7

File tree

6 files changed

+24
-3
lines changed

6 files changed

+24
-3
lines changed

docs/config.md

+4
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,9 @@ The NAT config is for transparent proxy. You'll need to [setup iptables rules](h
174174
"alpn": [
175175
"http/1.1"
176176
],
177+
"alpn_port_override" : {
178+
"h2": 81
179+
},
177180
"reuse_session": true,
178181
"session_ticket": false,
179182
"session_timeout": 600,
@@ -215,6 +218,7 @@ The NAT config is for transparent proxy. You'll need to [setup iptables rules](h
215218
- `cipher_tls13`: a cipher list for TLS 1.3 to use
216219
- `prefer_server_cipher`: whether to prefer server cipher list in a connection
217220
- `alpn`: a list of `ALPN` protocols to reply
221+
- alpn_port_override: overrides the remote port to the specified value if an ALPN is matched. Useful for running nginx http1.1 and http2 on different ports (#226).
218222
- `reuse_session`: whether to reuse `SSL` session
219223
- `session_ticket`: whether to use session tickets for session resumption
220224
- `session_timeout`: if `reuse_session` is set to `true`, specify `SSL` session timeout

examples/server.json-example

+3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
"alpn": [
2020
"http/1.1"
2121
],
22+
"alpn_port_override" : {
23+
"h2": 81
24+
},
2225
"reuse_session": true,
2326
"session_ticket": false,
2427
"session_timeout": 600,

src/core/config.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ void Config::populate(const ptree &tree) {
6565
}
6666
udp_timeout = tree.get("udp_timeout", 60);
6767
log_level = static_cast<Log::Level>(tree.get("log_level", 1));
68+
map<string, uint16_t>().swap(alpn_port);
6869
ssl.verify = tree.get("ssl.verify", true);
6970
ssl.verify_hostname = tree.get("ssl.verify_hostname", true);
7071
ssl.cert = tree.get("ssl.cert", string());
@@ -75,10 +76,15 @@ void Config::populate(const ptree &tree) {
7576
ssl.prefer_server_cipher = tree.get("ssl.prefer_server_cipher", true);
7677
ssl.sni = tree.get("ssl.sni", string());
7778
ssl.alpn = "";
79+
auto alpn_port_override = tree.get_child_optional("ssl.alpn_port_override");
7880
for (auto& item: tree.get_child("ssl.alpn")) {
7981
string proto = item.second.get_value<string>();
8082
ssl.alpn += (char)((unsigned char)(proto.length()));
8183
ssl.alpn += proto;
84+
if (alpn_port_override) {
85+
auto it = alpn_port_override->find(proto);
86+
alpn_port[proto] = it != alpn_port_override->not_found() ? it->second.get_value<uint16_t>() : remote_port;
87+
}
8288
}
8389
ssl.reuse_session = tree.get("ssl.reuse_session", true);
8490
ssl.session_ticket = tree.get("ssl.session_ticket", false);

src/core/config.h

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ class Config {
4242
std::map<std::string, std::string> password;
4343
int udp_timeout;
4444
Log::Level log_level;
45+
std::map<std::string, uint16_t> alpn_port;
4546
class SSLConfig {
4647
public:
4748
bool verify;

src/session/serversession.cpp

+9-3
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ ServerSession::ServerSession(const Config &config, boost::asio::io_context &io_c
3131
out_socket(io_context),
3232
udp_resolver(io_context),
3333
auth(auth),
34-
plain_http_response(plain_http_response) {}
34+
plain_http_response(plain_http_response),
35+
remote_port(0) {}
3536

3637
tcp::socket& ServerSession::accept_socket() {
3738
return (tcp::socket&)in_socket.next_layer();
@@ -59,6 +60,11 @@ void ServerSession::start() {
5960
destroy();
6061
return;
6162
}
63+
const unsigned char *alpn_out = nullptr;
64+
unsigned int alpn_len = 0;
65+
SSL_get0_alpn_selected(in_socket.native_handle(), &alpn_out, &alpn_len);
66+
auto it = config.alpn_port.find(std::string(alpn_out, alpn_out + alpn_len));
67+
remote_port = (it != config.alpn_port.end()) ? it->second : config.remote_port;
6268
in_async_read();
6369
});
6470
}
@@ -153,7 +159,7 @@ void ServerSession::in_recv(const string &data) {
153159
}
154160
}
155161
string query_addr = valid ? req.address.address : config.remote_addr;
156-
string query_port = to_string(valid ? req.address.port : config.remote_port);
162+
string query_port = to_string(valid ? req.address.port : remote_port);
157163
if (valid) {
158164
out_write_buf = req.payload;
159165
if (req.command == TrojanRequest::UDP_ASSOCIATE) {
@@ -166,7 +172,7 @@ void ServerSession::in_recv(const string &data) {
166172
Log::log_with_endpoint(in_endpoint, "requested connection to " + req.address.address + ':' + to_string(req.address.port), Log::INFO);
167173
}
168174
} else {
169-
Log::log_with_endpoint(in_endpoint, "not trojan request, connecting to " + config.remote_addr + ':' + to_string(config.remote_port), Log::WARN);
175+
Log::log_with_endpoint(in_endpoint, "not trojan request, connecting to " + config.remote_addr + ':' + to_string(remote_port), Log::WARN);
170176
out_write_buf = data;
171177
}
172178
sent_len += out_write_buf.length();

src/session/serversession.h

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class ServerSession : public Session {
3838
Authenticator *auth;
3939
std::string auth_password;
4040
const std::string &plain_http_response;
41+
uint16_t remote_port;
4142
void destroy();
4243
void in_async_read();
4344
void in_async_write(const std::string &data);

0 commit comments

Comments
 (0)