diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..0e40fe8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+
+# Default ignored files
+/workspace.xml
\ No newline at end of file
diff --git a/.idea/MTProxy.iml b/.idea/MTProxy.iml
new file mode 100644
index 0000000..6711606
--- /dev/null
+++ b/.idea/MTProxy.iml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..65531ca
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..ec8da76
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/sha256.c b/common/sha256.c
index 5a6a6e8..08fd90d 100644
--- a/common/sha256.c
+++ b/common/sha256.c
@@ -18,9 +18,12 @@
2016 Nikolai Durov
*/
-#include
#include "sha256.h"
+#include
+
+#include
+
void sha256_starts (sha256_context *ctx) {
EVP_MD_CTX_init (ctx);
EVP_DigestInit_ex (ctx, EVP_sha256(), NULL);
@@ -52,3 +55,10 @@ void sha256_two_chunks (const unsigned char *input1, int ilen1, const unsigned c
sha256_finish (ctx, output);
EVP_MD_CTX_free (ctx);
}
+
+void sha256_hmac (unsigned char *key, int keylen, unsigned char *input, int ilen, unsigned char output[32]) {
+ unsigned int len = 0;
+ unsigned char *result = HMAC(EVP_sha256(), key, keylen, input, ilen, output, &len);
+ assert (result == output);
+ assert (len == 32);
+}
diff --git a/common/sha256.h b/common/sha256.h
index cc60726..bad4c65 100644
--- a/common/sha256.h
+++ b/common/sha256.h
@@ -35,3 +35,5 @@ void sha256_update (sha256_context *ctx, const unsigned char *input, int ilen);
void sha256_finish (sha256_context *ctx, unsigned char output[32]);
void sha256 (const unsigned char *input, int ilen, unsigned char output[32]);
void sha256_two_chunks (const unsigned char *input1, int ilen1, const unsigned char *input2, int ilen2, unsigned char output[32]);
+
+void sha256_hmac (unsigned char *key, int keylen, unsigned char *input, int ilen, unsigned char output[32]);
diff --git a/crypto/aesni256.c b/crypto/aesni256.c
index f3bdc1a..4c95a32 100644
--- a/crypto/aesni256.c
+++ b/crypto/aesni256.c
@@ -22,115 +22,20 @@
*/
#include "crypto/aesni256.h"
-#include
-#include
-#include
-#include "common/cpuid.h"
-
-#include
-
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L
-#include
-void AES_ctr128_encrypt(
- const unsigned char *in,
- unsigned char *out,
- size_t length,
- const AES_KEY *key,
- unsigned char ivec[AES_BLOCK_SIZE],
- unsigned char ecount_buf[AES_BLOCK_SIZE],
- unsigned int *num) {
- CRYPTO_ctr128_encrypt(in, out, length, key, ivec, ecount_buf, num, (block128_f)AES_encrypt);
-}
-#endif
-
-void tg_ssl_aes_ctr_crypt (tg_aes_ctx_t *ctx, const unsigned char *in, unsigned char *out, int size, unsigned char iv[16], unsigned long long offset) {
- unsigned char iv_copy[16];
- memcpy (iv_copy, iv, 16);
- unsigned long long *p = (unsigned long long *) (iv_copy + 8);
- (*p) += offset >> 4;
- union {
- unsigned char c[16];
- unsigned long long d[2];
- } u;
- int i = offset & 15, l;
- if (i) {
- AES_encrypt (iv_copy, u.c, &ctx->u.key);
- (*p)++;
- l = i + size;
- if (l > 16) {
- l = 16;
- }
- size -= l - i;
- do {
- *out++ = (*in++) ^ u.c[i++];
- } while (i < l);
- }
- const unsigned long long *I = (const unsigned long long *) in;
- unsigned long long *O = (unsigned long long *) out;
- int n = size >> 4;
- while (--n >= 0) {
- AES_encrypt (iv_copy, (unsigned char *) u.d, &ctx->u.key);
- (*p)++;
- *O++ = (*I++) ^ u.d[0];
- *O++ = (*I++) ^ u.d[1];
- }
- l = size & 15;
- if (l) {
- AES_encrypt (iv_copy, u.c, &ctx->u.key);
- in = (const unsigned char *) I;
- out = (unsigned char *) O;
- i = 0;
- do {
- *out++ = (*in++) ^ u.c[i++];
- } while (i < l);
- }
-}
-
-
-static void tg_ssl_aes_cbc_encrypt (tg_aes_ctx_t *ctx, const unsigned char *in, unsigned char *out, int size, unsigned char iv[16]) {
- AES_cbc_encrypt (in, out, size, &ctx->u.key, iv, AES_ENCRYPT);
-}
-
-static void tg_ssl_aes_cbc_decrypt (tg_aes_ctx_t *ctx, const unsigned char *in, unsigned char *out, int size, unsigned char iv[16]) {
- AES_cbc_encrypt (in, out, size, &ctx->u.key, iv, AES_DECRYPT);
-}
-
-static void tg_ssl_aes_ige_encrypt (tg_aes_ctx_t *ctx, const unsigned char *in, unsigned char *out, int size, unsigned char iv[32]) {
- AES_ige_encrypt (in, out, size, &ctx->u.key, iv, AES_ENCRYPT);
-}
-
-static void tg_ssl_aes_ige_decrypt (tg_aes_ctx_t *ctx, const unsigned char *in, unsigned char *out, int size, unsigned char iv[32]) {
- AES_ige_encrypt (in, out, size, &ctx->u.key, iv, AES_DECRYPT);
-}
-void tg_ssl_aes_ctr128_crypt (struct tg_aes_ctx *ctx, const unsigned char *in, unsigned char *out, int size, unsigned char iv[16], unsigned char ecount_buf[16], unsigned int *num) {
- AES_ctr128_encrypt (in, out, size, &ctx->u.key, iv, ecount_buf, num);
-}
-
-static const struct tg_aes_methods ssl_aes_encrypt_methods = {
- .cbc_crypt = tg_ssl_aes_cbc_encrypt,
- .ige_crypt = tg_ssl_aes_ige_encrypt,
- .ctr_crypt = tg_ssl_aes_ctr_crypt,
- .ctr128_crypt = tg_ssl_aes_ctr128_crypt
-};
-
-void tg_aes_set_encrypt_key (tg_aes_ctx_t *ctx, unsigned char *key, int bits) {
- AES_set_encrypt_key (key, bits, &ctx->u.key);
- ctx->type = &ssl_aes_encrypt_methods;
-}
+#include
-static const struct tg_aes_methods ssl_aes_decrypt_methods = {
- .cbc_crypt = tg_ssl_aes_cbc_decrypt,
- .ige_crypt = tg_ssl_aes_ige_decrypt,
- .ctr_crypt = NULL,
- .ctr128_crypt = NULL
-};
+EVP_CIPHER_CTX *evp_cipher_ctx_init (const EVP_CIPHER *cipher, unsigned char *key, unsigned char iv[16], int is_encrypt) {
+ EVP_CIPHER_CTX *evp_ctx = EVP_CIPHER_CTX_new();
+ assert(evp_ctx);
-void tg_aes_set_decrypt_key (tg_aes_ctx_t *ctx, unsigned char *key, int bits) {
- AES_set_decrypt_key (key, bits, &ctx->u.key);
- ctx->type = &ssl_aes_decrypt_methods;
+ assert(EVP_CipherInit(evp_ctx, cipher, key, iv, is_encrypt) == 1);
+ assert(EVP_CIPHER_CTX_set_padding(evp_ctx, 0) == 1);
+ return evp_ctx;
}
-void tg_aes_ctx_cleanup (tg_aes_ctx_t *ctx) {
- memset (ctx, 0, sizeof (tg_aes_ctx_t));
+void evp_crypt (EVP_CIPHER_CTX *evp_ctx, const void *in, void *out, int size) {
+ int len;
+ assert (EVP_CipherUpdate(evp_ctx, out, &len, in, size) == 1);
+ assert (len == size);
}
diff --git a/crypto/aesni256.h b/crypto/aesni256.h
index f2d1d17..95be6f2 100644
--- a/crypto/aesni256.h
+++ b/crypto/aesni256.h
@@ -23,30 +23,8 @@
#pragma once
-#include
-
-struct aesni256_ctx {
- unsigned char a[256];
-};
-
-//TODO: move cbc_crypt, ige_crypt, ctr_crypt to the virtual method table
-struct tg_aes_ctx;
-
-struct tg_aes_methods {
- void (*cbc_crypt) (struct tg_aes_ctx *ctx, const unsigned char *in, unsigned char *out, int size, unsigned char iv[16]);
- void (*ige_crypt) (struct tg_aes_ctx *ctx, const unsigned char *in, unsigned char *out, int size, unsigned char iv[32]);
- void (*ctr_crypt) (struct tg_aes_ctx *ctx, const unsigned char *in, unsigned char *out, int size, unsigned char iv[16], unsigned long long offset);
- void (*ctr128_crypt) (struct tg_aes_ctx *ctx, const unsigned char *in, unsigned char *out, int size, unsigned char iv[16], unsigned char ecount_buf[16], unsigned int *num);
-};
-
-typedef struct tg_aes_ctx {
- union {
- AES_KEY key;
- struct aesni256_ctx ctx;
- } u;
- const struct tg_aes_methods *type;
-} tg_aes_ctx_t;
-
-void tg_aes_set_encrypt_key (tg_aes_ctx_t *ctx, unsigned char *key, int bits);
-void tg_aes_set_decrypt_key (tg_aes_ctx_t *ctx, unsigned char *key, int bits);
-void tg_aes_ctx_cleanup (tg_aes_ctx_t *ctx);
+#include
+
+EVP_CIPHER_CTX *evp_cipher_ctx_init (const EVP_CIPHER *cipher, unsigned char *key, unsigned char iv[16], int is_encrypt);
+
+void evp_crypt (EVP_CIPHER_CTX *evp_ctx, const void *in, void *out, int size);
diff --git a/engine/engine.c b/engine/engine.c
index a5bf687..d0c70d3 100644
--- a/engine/engine.c
+++ b/engine/engine.c
@@ -691,7 +691,7 @@ static void parse_option_engine_builtin (const char *name, int arg, int *var, in
assert (vasprintf (&h, help, ap) >= 0);
va_end (ap);
- parse_option_ex (name, arg, var, val, flags, f_parse_option_engine, h);
+ parse_option_ex (name, arg, var, val, flags, f_parse_option_engine, "%s", h);
free (h);
}
diff --git a/mtproto/mtproto-proxy.c b/mtproto/mtproto-proxy.c
index 5976262..f5de852 100644
--- a/mtproto/mtproto-proxy.c
+++ b/mtproto/mtproto-proxy.c
@@ -107,11 +107,11 @@ const char FullVersionStr[] = VERSION_STR " compiled at " __DATE__ " " __TIME__
#define MAX_MTFRONT_NB ((NB_max * 3) >> 2)
#endif
-double ping_interval = PING_INTERVAL;
-int window_clamp = DEFAULT_WINDOW_CLAMP;
+static double ping_interval = PING_INTERVAL;
+static int window_clamp;
#define PROXY_MODE_OUT 2
-int proxy_mode;
+static int proxy_mode;
#define IS_PROXY_IN 0
#define IS_PROXY_OUT 1
@@ -2076,23 +2076,32 @@ void cron (void) {
int sfd;
int http_ports_num;
int http_sfd[MAX_HTTP_LISTEN_PORTS], http_port[MAX_HTTP_LISTEN_PORTS];
+static int domain_count;
+static int secret_count;
// static double next_create_outbound;
// int outbound_connections_per_second = DEFAULT_OUTBOUND_CONNECTION_CREATION_RATE;
void mtfront_pre_loop (void) {
int i, enable_ipv6 = engine_check_ipv6_enabled () ? SM_IPV6 : 0;
- tcp_maximize_buffers = 1;
+ if (domain_count == 0) {
+ tcp_maximize_buffers = 1;
+ if (window_clamp == 0) {
+ window_clamp = DEFAULT_WINDOW_CLAMP;
+ }
+ }
if (!workers) {
for (i = 0; i < http_ports_num; i++) {
- init_listening_tcpv6_connection (http_sfd[i], &ct_tcp_rpc_ext_server_mtfront, &ext_rpc_methods, enable_ipv6 | SM_LOWPRIO | SM_NOQACK | (max_special_connections ? SM_SPECIAL : 0));
- // assert (setsockopt (http_sfd[i], IPPROTO_TCP, TCP_MAXSEG, (int[]){1410}, sizeof (int)) >= 0);
- // assert (setsockopt (http_sfd[i], IPPROTO_TCP, TCP_NODELAY, (int[]){1}, sizeof (int)) >= 0);
- listening_connection_job_t LC = Events[http_sfd[i]].data;
- assert (LC);
- CONN_INFO(LC)->window_clamp = window_clamp;
- if (setsockopt (http_sfd[i], IPPROTO_TCP, TCP_WINDOW_CLAMP, &window_clamp, 4) < 0) {
- vkprintf (0, "error while setting window size for socket %d to %d: %m\n", http_sfd[i], window_clamp);
+ init_listening_tcpv6_connection (http_sfd[i], &ct_tcp_rpc_ext_server_mtfront, &ext_rpc_methods, enable_ipv6 | SM_LOWPRIO | (domain_count == 0 ? SM_NOQACK : 0) | (max_special_connections ? SM_SPECIAL : 0));
+ // assert (setsockopt (http_sfd[i], IPPROTO_TCP, TCP_MAXSEG, (int[]){1410}, sizeof (int)) >= 0);
+ // assert (setsockopt (http_sfd[i], IPPROTO_TCP, TCP_NODELAY, (int[]){1}, sizeof (int)) >= 0);
+ if (window_clamp) {
+ listening_connection_job_t LC = Events[http_sfd[i]].data;
+ assert (LC);
+ CONN_INFO(LC)->window_clamp = window_clamp;
+ if (setsockopt (http_sfd[i], IPPROTO_TCP, TCP_WINDOW_CLAMP, &window_clamp, 4) < 0) {
+ vkprintf (0, "error while setting window size for socket #%d to %d: %m\n", http_sfd[i], window_clamp);
+ }
}
}
// create_all_outbound_connections ();
@@ -2180,6 +2189,10 @@ int f_parse_option (int val) {
engine_set_http_fallback (&ct_http_server, &http_methods_stats);
mtproto_front_functions.flags &= ~ENGINE_NO_PORT;
break;
+ case 'D':
+ tcp_rpc_add_proxy_domain (optarg);
+ domain_count++;
+ break;
case 'S':
case 'P':
{
@@ -2209,6 +2222,7 @@ int f_parse_option (int val) {
}
if (val == 'S') {
tcp_rpcs_set_ext_secret (secret);
+ secret_count++;
} else {
memcpy (proxy_tag, secret, sizeof (proxy_tag));
proxy_tag_set = 1;
@@ -2228,11 +2242,12 @@ void mtfront_prepare_parse_options (void) {
parse_option ("http-stats", no_argument, 0, 2000, "allow http server to answer on stats queries");
parse_option ("mtproto-secret", required_argument, 0, 'S', "16-byte secret in hex mode");
parse_option ("proxy-tag", required_argument, 0, 'P', "16-byte proxy tag in hex mode to be passed along with all forwarded queries");
+ parse_option ("domain", required_argument, 0, 'D', "adds allowed domain for TLS-transport mode, disables other transports; can be specified more than once");
parse_option ("max-special-connections", required_argument, 0, 'C', "sets maximal number of accepted client connections per worker");
parse_option ("window-clamp", required_argument, 0, 'W', "sets window clamp for client TCP connections");
parse_option ("http-ports", required_argument, 0, 'H', "comma-separated list of client (HTTP) ports to listen");
// parse_option ("outbound-connections-ps", required_argument, 0, 'o', "limits creation rate of outbound connections to mtproto-servers (default %d)", DEFAULT_OUTBOUND_CONNECTION_CREATION_RATE);
- parse_option ("slaves", required_argument, 0, 'M', "spawn several slave workers");
+ parse_option ("slaves", required_argument, 0, 'M', "spawn several slave workers; not recommended for TLS-transport mode for better replay protection");
parse_option ("ping-interval", required_argument, 0, 'T', "sets ping interval in second for local TCP connections (default %.3lf)", PING_INTERVAL);
parse_option ("random-padding-only", no_argument, 0, 'R', "allow only clients with random padding option enabled");
}
@@ -2259,12 +2274,24 @@ void mtfront_pre_init (void) {
vkprintf (1, "config loaded!\n");
+ if (domain_count) {
+ tcp_rpc_init_proxy_domains();
+
+ if (workers) {
+ kprintf ("It is recommended to not use workers with TLS-transport");
+ }
+ if (secret_count == 0) {
+ kprintf ("You must specify at least one mtproto-secret to use when using TLS-transport");
+ exit (2);
+ }
+ }
+
int i, enable_ipv6 = engine_check_ipv6_enabled () ? SM_IPV6 : 0;
for (i = 0; i < http_ports_num; i++) {
http_sfd[i] = server_socket (http_port[i], engine_state->settings_addr, engine_get_backlog (), enable_ipv6);
if (http_sfd[i] < 0) {
- fprintf (stderr, "cannot open http/tcp server socket at port %d: %m\n", http_port[i]);
+ kprintf ("cannot open http/tcp server socket at port %d: %m\n", http_port[i]);
exit (1);
}
}
diff --git a/net/net-connections.c b/net/net-connections.c
index a0d0553..d7a0cd7 100644
--- a/net/net-connections.c
+++ b/net/net-connections.c
@@ -333,17 +333,6 @@ static inline void cond_disable_qack (socket_connection_job_t C) {
}
/* }}} */
-/* {{{ cork
-static inline void cond_reset_cork (connection_job_t c) {
- if (c->flags & C_NOQACK) {
- vkprintf (2, "disable TCP_CORK for %d\n", c->fd);
- assert (setsockopt (c->fd, IPPROTO_TCP, TCP_CORK, (int[]){0}, sizeof (int)) >= 0);
- vkprintf (2, "enable TCP_CORK for %d\n", c->fd);
- assert (setsockopt (c->fd, IPPROTO_TCP, TCP_CORK, (int[]){1}, sizeof (int)) >= 0);
- }
-}
-}}} */
-
/* {{{ CPU PART OF CONNECTION */
@@ -493,14 +482,16 @@ int cpu_server_close_connection (connection_job_t C, int who) /* {{{ */ {
assert (c->io_conn);
job_signal (JOB_REF_PASS (c->io_conn), JS_ABORT);
- if (c->target) {
+ if (c->basic_type == ct_outbound) {
MODULE_STAT->outbound_connections --;
if (connection_is_active (c->flags)) {
MODULE_STAT->active_outbound_connections --;
}
- job_signal (JOB_REF_PASS (c->target), JS_RUN);
+ if (c->target) {
+ job_signal (JOB_REF_PASS (c->target), JS_RUN);
+ }
} else {
MODULE_STAT->inbound_connections --;
@@ -544,7 +535,9 @@ int do_connection_job (job_t job, int op, struct job_thread *JT) /* {{{ */ {
__sync_fetch_and_and (&c->flags, ~C_READY_PENDING);
MODULE_STAT->active_outbound_connections ++;
MODULE_STAT->active_connections ++;
- __sync_fetch_and_add (&CONN_TARGET_INFO(c->target)->active_outbound_connections, 1);
+ if (c->target) {
+ __sync_fetch_and_add (&CONN_TARGET_INFO(c->target)->active_outbound_connections, 1);
+ }
if (c->status == conn_connecting) {
if (!__sync_bool_compare_and_swap (&c->status, conn_connecting, conn_working)) {
assert (c->status == conn_error);
@@ -587,7 +580,7 @@ int do_connection_job (job_t job, int op, struct job_thread *JT) /* {{{ */ {
updates stats
creates socket_connection
*/
-connection_job_t alloc_new_connection (int cfd, conn_target_job_t CTJ, listening_connection_job_t LCJ, unsigned peer, unsigned char peer_ipv6[16], int peer_port) /* {{{ */ {
+connection_job_t alloc_new_connection (int cfd, conn_target_job_t CTJ, listening_connection_job_t LCJ, int basic_type, conn_type_t *conn_type, void *conn_extra, unsigned peer, unsigned char peer_ipv6[16], int peer_port) /* {{{ */ {
if (cfd < 0) {
return NULL;
}
@@ -598,7 +591,7 @@ connection_job_t alloc_new_connection (int cfd, conn_target_job_t CTJ, listening
unsigned flags;
if ((flags = fcntl (cfd, F_GETFL, 0) < 0) || fcntl (cfd, F_SETFL, flags | O_NONBLOCK) < 0) {
- kprintf ("cannot set O_NONBLOCK on accepted socket %d: %m\n", cfd);
+ kprintf ("cannot set O_NONBLOCK on accepted socket #%d: %m\n", cfd);
MODULE_STAT->accept_nonblock_set_failed ++;
close (cfd);
return NULL;
@@ -632,7 +625,7 @@ connection_job_t alloc_new_connection (int cfd, conn_target_job_t CTJ, listening
c->generation = new_conn_generation ();
c->flags = 0;//SS ? C_WANTWR : C_WANTRD;
- if (LC) {
+ if (basic_type == ct_inbound) {
c->flags = C_CONNECTED;
}
@@ -648,12 +641,12 @@ connection_job_t alloc_new_connection (int cfd, conn_target_job_t CTJ, listening
assert (0);
}
- c->type = CT ? CT->type : LC->type;
- c->extra = CT ? CT->extra : LC->extra;
+ c->type = conn_type;
+ c->extra = conn_extra;
assert (c->type);
- c->basic_type = CT ? ct_outbound : ct_inbound;
- c->status = CT ? conn_connecting : conn_working;
+ c->basic_type = basic_type;
+ c->status = (basic_type == ct_outbound) ? conn_connecting : conn_working;
c->flags |= c->type->flags & C_EXTERNAL;
if (LC) {
@@ -692,63 +685,67 @@ connection_job_t alloc_new_connection (int cfd, conn_target_job_t CTJ, listening
c->out_queue = alloc_mp_queue_w ();
//c->out_packet_queue = alloc_mp_queue_w ();
- if (CT) {
- vkprintf (1, "New connection %s:%d -> %s:%d\n", show_our_ip (C), c->our_port, show_remote_ip (C), c->remote_port);
+ if (basic_type == ct_outbound) {
+ vkprintf (1, "New outbound connection #%d %s:%d -> %s:%d\n", c->fd, show_our_ip (C), c->our_port, show_remote_ip (C), c->remote_port);
} else {
- vkprintf (1, "New connection %s:%d -> %s:%d\n", show_remote_ip (C), c->remote_port, show_our_ip (C), c->our_port);
+ vkprintf (1, "New inbound connection #%d %s:%d -> %s:%d\n", c->fd, show_remote_ip (C), c->remote_port, show_our_ip (C), c->our_port);
}
- int (*func)(connection_job_t) = CT ? CT->type->init_outbound : LC->type->init_accepted;
+ int (*func)(connection_job_t) = (basic_type == ct_outbound) ? c->type->init_outbound : c->type->init_accepted;
vkprintf (3, "func = %p\n", func);
if (func (C) >= 0) {
- if (CT) {
- job_incref (CTJ);
+ if (basic_type == ct_outbound) {
MODULE_STAT->outbound_connections ++;
MODULE_STAT->allocated_outbound_connections ++;
MODULE_STAT->outbound_connections_created ++;
- CT->outbound_connections ++;
+ if (CTJ) {
+ job_incref (CTJ);
+ CT->outbound_connections ++;
+ }
} else {
MODULE_STAT->inbound_connections_accepted ++;
MODULE_STAT->allocated_inbound_connections ++;
MODULE_STAT->inbound_connections ++;
MODULE_STAT->active_inbound_connections ++;
MODULE_STAT->active_connections ++;
+
+ if (LCJ) {
+ c->listening = LC->fd;
+ c->listening_generation = LC->generation;
+ if (LC->flags & C_NOQACK) {
+ c->flags |= C_NOQACK;
+ }
- c->listening = LC->fd;
- c->listening_generation = LC->generation;
- if (LC->flags & C_NOQACK) {
- c->flags |= C_NOQACK;
+ c->window_clamp = LC->window_clamp;
+
+ if (LC->flags & C_SPECIAL) {
+ c->flags |= C_SPECIAL;
+ __sync_fetch_and_add (&active_special_connections, 1);
+
+ if (active_special_connections > max_special_connections) {
+ vkprintf (active_special_connections >= max_special_connections + 16 ? 0 : 1, "ERROR: forced to accept connection when special connections limit was reached (%d of %d)\n", active_special_connections, max_special_connections);
+ }
+ if (active_special_connections >= max_special_connections) {
+ vkprintf (2, "**Invoking epoll_remove(%d)\n", LC->fd);
+ epoll_remove (LC->fd);
+ }
+ }
}
-
- c->window_clamp = LC->window_clamp;
if (c->window_clamp) {
if (setsockopt (cfd, IPPROTO_TCP, TCP_WINDOW_CLAMP, &c->window_clamp, 4) < 0) {
- vkprintf (0, "error while setting window size for socket %d to %d: %m\n", cfd, c->window_clamp);
+ vkprintf (0, "error while setting window size for socket #%d to %d: %m\n", cfd, c->window_clamp);
} else {
int t1 = -1, t2 = -1;
socklen_t s1 = 4, s2 = 4;
getsockopt (cfd, IPPROTO_TCP, TCP_WINDOW_CLAMP, &t1, &s1);
getsockopt (cfd, SOL_SOCKET, SO_RCVBUF, &t2, &s2);
- vkprintf (2, "window clamp for socket %d is %d, receive buffer is %d\n", cfd, t1, t2);
- }
- }
-
- if (LC->flags & C_SPECIAL) {
- c->flags |= C_SPECIAL;
- __sync_fetch_and_add (&active_special_connections, 1);
-
- if (active_special_connections > max_special_connections) {
- vkprintf (active_special_connections >= max_special_connections + 16 ? 0 : 1, "ERROR: forced to accept connection when special connections limit was reached (%d of %d)\n", active_special_connections, max_special_connections);
- }
- if (active_special_connections >= max_special_connections) {
- vkprintf (2, "**Invoking epoll_remove(%d)\n", LC->fd);
- epoll_remove (LC->fd);
+ vkprintf (2, "window clamp for socket #%d is %d, receive buffer is %d\n", cfd, t1, t2);
}
}
}
@@ -1123,7 +1120,7 @@ int net_server_socket_read_write_gateway (int fd, void *data, event_t *ev) /* {{
return EVA_REMOVE;
}
if (ev->epoll_ready & (EPOLLHUP | EPOLLERR | EPOLLRDHUP | EPOLLPRI)) {
- vkprintf (!(ev->epoll_ready & EPOLLPRI), "socket %d: disconnected (epoll_ready=%02x), cleaning\n", c->fd, ev->epoll_ready);
+ vkprintf (!(ev->epoll_ready & EPOLLPRI), "socket #%d: disconnected (epoll_ready=%02x), cleaning\n", c->fd, ev->epoll_ready);
job_signal (JOB_REF_CREATE_PASS (C), JS_ABORT);
return EVA_REMOVE;
@@ -1279,10 +1276,10 @@ int net_accept_new_connections (listening_connection_job_t LCJ) /* {{{ */ {
connection_job_t C;
if (peer.a4.sin_family == AF_INET) {
- C = alloc_new_connection (cfd, NULL, LCJ,
+ C = alloc_new_connection (cfd, NULL, LCJ, ct_inbound, LC->type, LC->extra,
ntohl (peer.a4.sin_addr.s_addr), NULL, ntohs (peer.a4.sin_port));
} else {
- C = alloc_new_connection (cfd, NULL, LCJ,
+ C = alloc_new_connection (cfd, NULL, LCJ, ct_inbound, LC->type, LC->extra,
0, peer.a6.sin6_addr.s6_addr, ntohs (peer.a6.sin6_port));
}
if (C) {
@@ -1711,12 +1708,14 @@ int create_new_connections (conn_target_job_t CTJ) /* {{{ */ {
}
while (CT->outbound_connections < need_c) {
+ int cfd = -1;
if (CT->target.s_addr) {
- vkprintf (1, "Creating NEW connection to %s:%d\n", inet_ntoa (CT->target), CT->port);
+ cfd = client_socket (CT->target.s_addr, CT->port, 0);
+ vkprintf (1, "Created NEW connection #%d to %s:%d\n", cfd, inet_ntoa (CT->target), CT->port);
} else {
- vkprintf (1, "Creating NEW ipv6 connection to [%s]:%d\n", show_ipv6 (CT->target_ipv6), CT->port);
+ cfd = client_socket_ipv6 (CT->target_ipv6, CT->port, SM_IPV6);
+ vkprintf (1, "Created NEW ipv6 connection #%d to [%s]:%d\n", cfd, show_ipv6 (CT->target_ipv6), CT->port);
}
- int cfd = CT->target.s_addr ? client_socket (CT->target.s_addr, CT->port, 0) : client_socket_ipv6 (CT->target_ipv6, CT->port, SM_IPV6);
if (cfd < 0) {
if (CT->target.s_addr) {
vkprintf (1, "error connecting to %s:%d: %m\n", inet_ntoa (CT->target), CT->port);
@@ -1726,7 +1725,7 @@ int create_new_connections (conn_target_job_t CTJ) /* {{{ */ {
break;
}
- connection_job_t C = alloc_new_connection (cfd, CTJ, NULL,
+ connection_job_t C = alloc_new_connection (cfd, CTJ, NULL, ct_outbound, CT->type, CT->extra,
ntohl (CT->target.s_addr), CT->target_ipv6, CT->port);
if (C) {
diff --git a/net/net-connections.h b/net/net-connections.h
index 943b3dc..5a70335 100644
--- a/net/net-connections.h
+++ b/net/net-connections.h
@@ -54,35 +54,36 @@
/* for connection flags */
-#define C_WANTRD 1
-#define C_WANTWR 2
-#define C_WANTRW (C_WANTRD | C_WANTWR)
-#define C_INCONN 4
-#define C_ERROR 8
-#define C_NORD 0x10
-#define C_NOWR 0x20
-#define C_NORW (C_NORD | C_NOWR)
-#define C_INQUERY 0x40
-#define C_FAILED 0x80
-#define C_ALARM 0x100
+#define C_WANTRD 1
+#define C_WANTWR 2
+#define C_WANTRW (C_WANTRD | C_WANTWR)
+#define C_INCONN 4
+#define C_ERROR 8
+#define C_NORD 0x10
+#define C_NOWR 0x20
+#define C_NORW (C_NORD | C_NOWR)
+#define C_INQUERY 0x40
+#define C_FAILED 0x80
+#define C_ALARM 0x100
#define C_AIO 0x200
#define C_INTIMEOUT 0x400
-#define C_STOPREAD 0x800
-#define C_REPARSE 0x1000
+#define C_STOPREAD 0x800
+#define C_REPARSE 0x1000
#define C_DFLUSH 0x2000
-#define C_IPV6 0x4000
+#define C_IPV6 0x4000
#define C_EXTERNAL 0x8000
#define C_SPECIAL 0x10000
-#define C_NOQACK 0x20000
-#define C_RAWMSG 0x40000
-#define C_NET_FAILED 0x80000
-#define C_CRYPTOIN 0x100000
-#define C_CRYPTOOUT 0x200000
-#define C_STOPPARSE 0x400000
+#define C_NOQACK 0x20000
+#define C_RAWMSG 0x40000
+#define C_NET_FAILED 0x80000
+#define C_CRYPTOIN 0x100000
+#define C_CRYPTOOUT 0x200000
+#define C_STOPPARSE 0x400000
#define C_ISDH 0x800000
#define C_READY_PENDING 0x1000000
-#define C_CONNECTED 0x2000000
-#define C_STOPWRITE 0x4000000
+#define C_CONNECTED 0x2000000
+#define C_STOPWRITE 0x4000000
+#define C_IS_TLS 0x8000000
#define C_PERMANENT (C_IPV6 | C_RAWMSG)
/* for connection status */
@@ -194,6 +195,24 @@ struct conn_target_info {
int global_refcnt;
};
+struct pseudo_conn_target_info {
+ struct event_timer timer;
+ int pad1;
+ int pad2;
+
+ void *pad3;
+ conn_type_t *type;
+ void *extra;
+ struct in_addr target;
+ unsigned char target_ipv6[16];
+ int port;
+ int active_outbound_connections, outbound_connections;
+ int ready_outbound_connections;
+
+ connection_job_t in_conn;
+ connection_job_t out_conn;
+};
+
struct connection_info {
struct event_timer timer;
int fd;
@@ -232,6 +251,7 @@ struct connection_info {
void *crypto_temp;
int listening, listening_generation;
int window_clamp;
+ int left_tls_packet_length;
struct raw_message in_u, in, out, out_p;
@@ -427,4 +447,4 @@ extern unsigned nat_info[MAX_NAT_INFO_RULES][2];
int net_add_nat_info (char *str);
unsigned nat_translate_ip (unsigned local_ip);
-connection_job_t alloc_new_connection (int cfd, conn_target_job_t SS, connection_job_t LL, unsigned peer, unsigned char peer_ipv6[16], int peer_port);
+connection_job_t alloc_new_connection (int cfd, conn_target_job_t CTJ, listening_connection_job_t LCJ, int basic_type, conn_type_t *conn_type, void *conn_extra, unsigned peer, unsigned char peer_ipv6[16], int peer_port);
diff --git a/net/net-crypto-aes.c b/net/net-crypto-aes.c
index b6bc155..accf24a 100644
--- a/net/net-crypto-aes.c
+++ b/net/net-crypto-aes.c
@@ -86,12 +86,8 @@ int aes_crypto_init (connection_job_t c, void *key_data, int key_data_len) {
MODULE_STAT->allocated_aes_crypto ++;
- tg_aes_set_decrypt_key (&T->read_aeskey, D->read_key, 256);
- memcpy (T->read_iv, D->read_iv, 16);
- tg_aes_set_encrypt_key (&T->write_aeskey, D->write_key, 256);
- memcpy (T->write_iv, D->write_iv, 16);
- // T->read_pos = T->write_pos = 0;
- T->read_num = T->write_num = 0;
+ T->read_aeskey = evp_cipher_ctx_init (EVP_aes_256_cbc(), D->read_key, D->read_iv, 0);
+ T->write_aeskey = evp_cipher_ctx_init (EVP_aes_256_cbc(), D->write_key, D->write_iv, 1);
CONN_INFO(c)->crypto = T;
return 0;
}
@@ -105,19 +101,19 @@ int aes_crypto_ctr128_init (connection_job_t c, void *key_data, int key_data_len
MODULE_STAT->allocated_aes_crypto ++;
- tg_aes_set_encrypt_key (&T->read_aeskey, D->read_key, 256); // NB: *_encrypt_key here!
- memcpy (T->read_iv, D->read_iv, 16);
- tg_aes_set_encrypt_key (&T->write_aeskey, D->write_key, 256);
- memcpy (T->write_iv, D->write_iv, 16);
- // T->read_pos = T->write_pos = 0;
- T->read_num = T->write_num = 0;
+ T->read_aeskey = evp_cipher_ctx_init (EVP_aes_256_ctr(), D->read_key, D->read_iv, 1); // NB: is_encrypt == 1 here!
+ T->write_aeskey = evp_cipher_ctx_init (EVP_aes_256_ctr(), D->write_key, D->write_iv, 1);
CONN_INFO(c)->crypto = T;
return 0;
}
int aes_crypto_free (connection_job_t c) {
- if (CONN_INFO(c)->crypto) {
- free (CONN_INFO(c)->crypto);
+ struct aes_crypto *crypto = CONN_INFO(c)->crypto;
+ if (crypto) {
+ EVP_CIPHER_CTX_free (crypto->read_aeskey);
+ EVP_CIPHER_CTX_free (crypto->write_aeskey);
+
+ free (crypto);
CONN_INFO(c)->crypto = 0;
MODULE_STAT->allocated_aes_crypto --;
}
diff --git a/net/net-crypto-aes.h b/net/net-crypto-aes.h
index cdc123b..b6a1c3f 100644
--- a/net/net-crypto-aes.h
+++ b/net/net-crypto-aes.h
@@ -70,12 +70,8 @@ struct aes_key_data {
/* for c->crypto */
struct aes_crypto {
- unsigned char read_iv[16], write_iv[16];
- unsigned char read_ebuf[16], write_ebuf[16]; /* for AES-CTR modes */
- tg_aes_ctx_t read_aeskey __attribute__ ((aligned (16)));
- tg_aes_ctx_t write_aeskey __attribute__ ((aligned (16)));
- unsigned int read_num, write_num; /* for AES-CTR modes */
- // long long read_pos, write_pos; /* for AES-CTR modes */
+ EVP_CIPHER_CTX *read_aeskey;
+ EVP_CIPHER_CTX *write_aeskey;
};
extern int aes_initialized;
diff --git a/net/net-crypto-dh.c b/net/net-crypto-dh.c
index 97d158e..b9aaa17 100644
--- a/net/net-crypto-dh.c
+++ b/net/net-crypto-dh.c
@@ -147,7 +147,7 @@ void create_g_a (unsigned char g_a[256], unsigned char a[256]) {
rpc_BN_ctx = BN_CTX_new ();
}
do {
- assert (RAND_pseudo_bytes (a, 256) >= 0); /* if you write '>0', the assert will fail. It's very sad */
+ assert (RAND_bytes (a, 256) >= 0); /* if you write '>0', the assert will fail. It's very sad */
BIGNUM *dh_power = BN_new ();
assert (BN_bin2bn (a, 256, dh_power) == dh_power);
diff --git a/net/net-events.c b/net/net-events.c
index 47cc678..af0336d 100644
--- a/net/net-events.c
+++ b/net/net-events.c
@@ -557,8 +557,9 @@ struct in_addr settings_addr;
int server_socket (int port, struct in_addr in_addr, int backlog, int mode) {
int socket_fd;
- struct linger ling = {0, 0};
+ // struct linger ling = {0, 0};
int flags = 1;
+ int enable_often_tcp_keep_alive = 0;
if ((socket_fd = new_socket (mode, 1)) == -1) {
return -1;
@@ -568,23 +569,25 @@ int server_socket (int port, struct in_addr in_addr, int backlog, int mode) {
maximize_sndbuf (socket_fd, 0);
maximize_rcvbuf (socket_fd, 0);
setsockopt (socket_fd, SOL_IP, IP_RECVERR, &flags, sizeof (flags));
-
} else {
setsockopt (socket_fd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof (flags));
if (tcp_maximize_buffers) {
maximize_sndbuf (socket_fd, 0);
maximize_rcvbuf (socket_fd, 0);
}
- assert (setsockopt (socket_fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof (flags)) >= 0);
assert (flags == 1);
- setsockopt (socket_fd, SOL_SOCKET, SO_LINGER, &ling, sizeof (ling));
+ // setsockopt (socket_fd, SOL_SOCKET, SO_LINGER, &ling, sizeof (ling));
setsockopt (socket_fd, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof (flags));
- int x = 40;
- assert (setsockopt (socket_fd, IPPROTO_TCP, TCP_KEEPIDLE, &x, sizeof (x)) >= 0);
- assert (setsockopt (socket_fd, IPPROTO_TCP, TCP_KEEPINTVL, &x, sizeof (x)) >= 0);
- x = 5;
- assert (setsockopt (socket_fd, IPPROTO_TCP, TCP_KEEPCNT, &x, sizeof (x)) >= 0);
+ assert (flags == 1);
+ assert (setsockopt (socket_fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof (flags)) >= 0);
+ if (enable_often_tcp_keep_alive) {
+ int x = 40;
+ assert (setsockopt (socket_fd, IPPROTO_TCP, TCP_KEEPIDLE, &x, sizeof (x)) >= 0);
+ assert (setsockopt (socket_fd, IPPROTO_TCP, TCP_KEEPINTVL, &x, sizeof (x)) >= 0);
+ x = 5;
+ assert (setsockopt (socket_fd, IPPROTO_TCP, TCP_KEEPCNT, &x, sizeof (x)) >= 0);
+ }
}
if (mode & SM_REUSE) {
@@ -689,7 +692,6 @@ int client_socket (in_addr_t in_addr, int port, int mode) {
}
return socket_fd;
-
}
int client_socket_ipv6 (const unsigned char in6_addr_ptr[16], int port, int mode) {
@@ -731,7 +733,6 @@ int client_socket_ipv6 (const unsigned char in6_addr_ptr[16], int port, int mode
}
return socket_fd;
-
}
unsigned get_my_ipv4 (void) {
@@ -899,4 +900,3 @@ const char *show_ipv6 (const unsigned char ipv6[16]) {
ptr += conv_ipv6_internal ((const unsigned short *) ipv6, ptr) + 1;
return res;
}
-
diff --git a/net/net-msg.c b/net/net-msg.c
index 71d2571..7cca1cf 100644
--- a/net/net-msg.c
+++ b/net/net-msg.c
@@ -1229,11 +1229,7 @@ struct rwm_encrypt_decrypt_tmp {
int left;
int block_size;
struct raw_message *raw;
- struct tg_aes_ctx *ctx;
- void (*crypt)(struct tg_aes_ctx *, const void *, void *, int, unsigned char *, void *, void *);
- unsigned char *iv;
- void *extra;
- void *extra2;
+ EVP_CIPHER_CTX *evp_ctx;
char buf[16] __attribute__((aligned(16)));
};
@@ -1261,12 +1257,12 @@ int rwm_process_encrypt_decrypt (struct rwm_encrypt_decrypt_tmp *x, const void *
data += to_fill;
x->bp = 0;
if (x->buf_left >= bsize) {
- x->crypt (x->ctx, x->buf, res->last->part->data + res->last_offset, bsize, x->iv, x->extra, x->extra2);
+ evp_crypt (x->evp_ctx, x->buf, res->last->part->data + res->last_offset, bsize);
res->last->data_end += bsize;
res->last_offset += bsize;
x->buf_left -= bsize;
} else {
- x->crypt (x->ctx, x->buf, x->buf, bsize, x->iv, x->extra, x->extra2);
+ evp_crypt (x->evp_ctx, x->buf, x->buf, bsize);
memcpy (res->last->part->data + res->last_offset, x->buf, x->buf_left);
int t = x->buf_left;
res->last->data_end += t;
@@ -1316,7 +1312,7 @@ int rwm_process_encrypt_decrypt (struct rwm_encrypt_decrypt_tmp *x, const void *
assert (x->buf_left + res->last_offset <= res->last->part->chunk->buffer_size);
if (len <= x->buf_left) {
assert (!(len & (bsize - 1)));
- x->crypt (x->ctx, data, (res->last->part->data + res->last_offset), len, x->iv, x->extra, x->extra2);
+ evp_crypt (x->evp_ctx, data, (res->last->part->data + res->last_offset), len);
res->last->data_end += len;
res->last_offset += len;
res->total_bytes += len;
@@ -1324,7 +1320,7 @@ int rwm_process_encrypt_decrypt (struct rwm_encrypt_decrypt_tmp *x, const void *
return 0;
} else {
int t = x->buf_left & -bsize;
- x->crypt (x->ctx, data, res->last->part->data + res->last_offset, t, x->iv, x->extra, x->extra2);
+ evp_crypt (x->evp_ctx, data, res->last->part->data + res->last_offset, t);
res->last->data_end += t;
res->last_offset += t;
res->total_bytes += t;
@@ -1336,7 +1332,7 @@ int rwm_process_encrypt_decrypt (struct rwm_encrypt_decrypt_tmp *x, const void *
}
-int rwm_encrypt_decrypt_to (struct raw_message *raw, struct raw_message *res, int bytes, struct tg_aes_ctx *ctx, void (*crypt)(struct tg_aes_ctx *ctx, const void *src, void *dst, int l, unsigned char *iv, void *extra, void *extra2), unsigned char *iv, int block_size, void *extra, void *extra2) {
+int rwm_encrypt_decrypt_to (struct raw_message *raw, struct raw_message *res, int bytes, EVP_CIPHER_CTX *evp_ctx, int block_size) {
assert (bytes >= 0);
assert (block_size && !(block_size & (block_size - 1)));
if (bytes > raw->total_bytes) {
@@ -1365,18 +1361,14 @@ int rwm_encrypt_decrypt_to (struct raw_message *raw, struct raw_message *res, in
}
struct rwm_encrypt_decrypt_tmp t;
t.bp = 0;
- t.crypt = crypt;
if (res->last->part->refcnt == 1) {
t.buf_left = res->last->part->chunk->buffer_size - res->last_offset;
} else {
t.buf_left = 0;
}
t.raw = res;
- t.ctx = ctx;
- t.iv = iv;
+ t.evp_ctx = evp_ctx;
t.left = bytes;
- t.extra = extra;
- t.extra2 = extra2;
t.block_size = block_size;
int r = rwm_process_and_advance (raw, bytes, (void *)rwm_process_encrypt_decrypt, &t);
if (locked) {
diff --git a/net/net-msg.h b/net/net-msg.h
index fb2c908..729584f 100644
--- a/net/net-msg.h
+++ b/net/net-msg.h
@@ -145,9 +145,7 @@ int rwm_process_from_offset (struct raw_message *raw, int bytes, int offset, int
int rwm_transform_from_offset (struct raw_message *raw, int bytes, int offset, int (*transform_block)(void *extra, void *data, int len), void *extra);
int rwm_process_and_advance (struct raw_message *raw, int bytes, int (*process_block)(void *extra, const void *data, int len), void *extra);
int rwm_sha1 (struct raw_message *raw, int bytes, unsigned char output[20]);
-// int rwm_encrypt_decrypt (struct raw_message *raw, int bytes, tg_aes_ctx_t *ctx, unsigned char iv[32]);
-// int rwm_encrypt_decrypt_cbc (struct raw_message *raw, int bytes, tg_aes_ctx_t *ctx, unsigned char iv[16]);
-int rwm_encrypt_decrypt_to (struct raw_message *raw, struct raw_message *res, int bytes, tg_aes_ctx_t *ctx, void (*crypt)(tg_aes_ctx_t *ctx, const void *src, void *dst, int l, unsigned char *iv, void *extra, void *extra2), unsigned char *iv, int block_size, void *extra, void *extra2);
+int rwm_encrypt_decrypt_to (struct raw_message *raw, struct raw_message *res, int bytes, EVP_CIPHER_CTX *evp_ctx, int block_size);
void *rwm_get_block_ptr (struct raw_message *raw);
int rwm_get_block_ptr_bytes (struct raw_message *raw);
diff --git a/net/net-tcp-connections.c b/net/net-tcp-connections.c
index 9b2f462..0f4ff8a 100644
--- a/net/net-tcp-connections.c
+++ b/net/net-tcp-connections.c
@@ -201,7 +201,7 @@ int cpu_tcp_aes_crypto_encrypt_output (connection_job_t C) /* {{{ */ {
int l = out->total_bytes;
l &= ~15;
if (l) {
- assert (rwm_encrypt_decrypt_to (&c->out, &c->out_p, l, &T->write_aeskey, (void *)T->write_aeskey.type->cbc_crypt, T->write_iv, 16, 0, 0) == l);
+ assert (rwm_encrypt_decrypt_to (&c->out, &c->out_p, l, T->write_aeskey, 16) == l);
}
return (-out->total_bytes) & 15;
@@ -218,7 +218,7 @@ int cpu_tcp_aes_crypto_decrypt_input (connection_job_t C) /* {{{ */ {
int l = in->total_bytes;
l &= ~15;
if (l) {
- assert (rwm_encrypt_decrypt_to (&c->in_u, &c->in, l, &T->read_aeskey, (void *)T->read_aeskey.type->cbc_crypt, T->read_iv, 16, 0, 0) == l);
+ assert (rwm_encrypt_decrypt_to (&c->in_u, &c->in, l, T->read_aeskey, 16) == l);
}
return (-in->total_bytes) & 15;
@@ -238,11 +238,22 @@ int cpu_tcp_aes_crypto_ctr128_encrypt_output (connection_job_t C) /* {{{ */ {
struct aes_crypto *T = c->crypto;
assert (c->crypto);
- struct raw_message *out = &c->out;
- int l = out->total_bytes;
- if (l) {
- assert (rwm_encrypt_decrypt_to (&c->out, &c->out_p, l, &T->write_aeskey, (void *)T->write_aeskey.type->ctr128_crypt, T->write_iv, 1, T->write_ebuf, &T->write_num) == l);
+ while (c->out.total_bytes) {
+ int len = c->out.total_bytes;
+ if (c->flags & C_IS_TLS) {
+ assert (c->left_tls_packet_length >= 0);
+ const int MAX_PACKET_LENGTH = 1425;
+ if (MAX_PACKET_LENGTH < len) {
+ len = MAX_PACKET_LENGTH;
+ }
+
+ unsigned char header[5] = {0x17, 0x03, 0x03, len >> 8, len & 255};
+ rwm_push_data (&c->out_p, header, 5);
+ vkprintf (2, "Send TLS-packet of length %d\n", len);
+ }
+
+ assert (rwm_encrypt_decrypt_to (&c->out, &c->out_p, len, T->write_aeskey, 1) == len);
}
return 0;
@@ -254,11 +265,37 @@ int cpu_tcp_aes_crypto_ctr128_decrypt_input (connection_job_t C) /* {{{ */ {
struct connection_info *c = CONN_INFO (C);
struct aes_crypto *T = c->crypto;
assert (c->crypto);
- struct raw_message *in = &c->in_u;
- int l = in->total_bytes;
- if (l) {
- assert (rwm_encrypt_decrypt_to (&c->in_u, &c->in, l, &T->read_aeskey, (void *)T->read_aeskey.type->ctr128_crypt, T->read_iv, 1, T->read_ebuf, &T->read_num) == l);
+ while (c->in_u.total_bytes) {
+ int len = c->in_u.total_bytes;
+ if (c->flags & C_IS_TLS) {
+ assert (c->left_tls_packet_length >= 0);
+ if (c->left_tls_packet_length == 0) {
+ if (len < 5) {
+ vkprintf (2, "Need %d more bytes to parse TLS header\n", 5 - len);
+ return 5 - len;
+ }
+
+ unsigned char header[5];
+ assert (rwm_fetch_lookup (&c->in_u, header, 5) == 5);
+ if (memcmp (header, "\x17\x03\x03", 3) != 0) {
+ vkprintf (1, "error while parsing packet: expect TLS header\n");
+ fail_connection (C, -1);
+ return 0;
+ }
+ c->left_tls_packet_length = 256 * header[3] + header[4];
+ vkprintf (2, "Receive TLS-packet of length %d\n", c->left_tls_packet_length);
+ assert (rwm_skip_data (&c->in_u, 5) == 5);
+ len -= 5;
+ }
+
+ if (c->left_tls_packet_length < len) {
+ len = c->left_tls_packet_length;
+ }
+ c->left_tls_packet_length -= len;
+ }
+ vkprintf (2, "Read %d bytes out of %d available\n", len, c->in_u.total_bytes);
+ assert (rwm_encrypt_decrypt_to (&c->in_u, &c->in, len, T->read_aeskey, 1) == len);
}
return 0;
diff --git a/net/net-tcp-rpc-common.c b/net/net-tcp-rpc-common.c
index 4e9958f..674bd5c 100644
--- a/net/net-tcp-rpc-common.c
+++ b/net/net-tcp-rpc-common.c
@@ -180,6 +180,11 @@ int tcp_rpc_write_packet_compact (connection_job_t C, struct raw_message *raw) {
rwm_union (&CONN_INFO(C)->out, raw);
return 0;
}
+ if ((CONN_INFO (C)->flags & C_IS_TLS) && CONN_INFO (C)->left_tls_packet_length == -1) {
+ // uninited TLS connection
+ rwm_union (&CONN_INFO(C)->out, raw);
+ return 0;
+ }
if (!(TCP_RPC_DATA(C)->flags & (RPC_F_COMPACT | RPC_F_MEDIUM))) {
return tcp_rpc_write_packet (C, raw);
diff --git a/net/net-tcp-rpc-common.h b/net/net-tcp-rpc-common.h
index ad43ece..8c273ff 100644
--- a/net/net-tcp-rpc-common.h
+++ b/net/net-tcp-rpc-common.h
@@ -94,18 +94,18 @@ void tcp_rpc_conn_send_data_im (JOB_REF_ARG (C), int len, void *Q);
int tcp_rpc_default_execute (connection_job_t C, int op, struct raw_message *raw);
/* for crypto_flags in struct tcp_rpc_data */
-#define RPCF_ALLOW_UNENC 1
-#define RPCF_ALLOW_ENC 2
-#define RPCF_REQ_DH 4
-#define RPCF_ALLOW_SKIP_DH 8
+#define RPCF_ALLOW_UNENC 1 // allow unencrypted
+#define RPCF_ALLOW_ENC 2 // allow encrypted
+#define RPCF_REQ_DH 4 // require DH
+#define RPCF_ALLOW_SKIP_DH 8 // crypto NONCE packet sent
#define RPCF_ENC_SENT 16
-#define RPCF_SEQNO_HOLES 256
-#define RPCF_QUICKACK 512
-#define RPCF_COMPACT_OFF 1024
-#define RPCF_USE_CRC32C 2048
+#define RPCF_SEQNO_HOLES 256 // packet numbers not sequential
+#define RPCF_QUICKACK 512 // allow quick ack packets
+#define RPCF_COMPACT_OFF 1024 // compact mode off
+#define RPCF_USE_CRC32C 2048 // use CRC32-C instead of CRC32
/* for flags in struct tcp_rpc_data */
-#define RPC_F_PAD 0x8000000
+#define RPC_F_PAD 0x8000000
#define RPC_F_DROPPED 0x10000000
#define RPC_F_MEDIUM 0x20000000
#define RPC_F_COMPACT 0x40000000
@@ -124,7 +124,7 @@ struct tcp_rpc_data {
int flags;
int in_packet_num;
int out_packet_num;
- int crypto_flags; /* 1 = allow unencrypted, 2 = allow encrypted, 4 = require DH, 8 = crypto NONCE packet sent, 256 = packet numbers not sequential, 512 = allow quick ack packets, 1024 = compact mode off, 2048 = use CRC32-C instead of CRC32 */
+ int crypto_flags; /* RPCF_* flags */
struct process_id remote_pid;
char nonce[16];
int nonce_time;
diff --git a/net/net-tcp-rpc-ext-server.c b/net/net-tcp-rpc-ext-server.c
index 6ec1463..02e406b 100644
--- a/net/net-tcp-rpc-ext-server.c
+++ b/net/net-tcp-rpc-ext-server.c
@@ -27,29 +27,38 @@
#define _FILE_OFFSET_BITS 64
#include
+#include
#include
#include
#include
#include
#include
-#include "crc32.h"
-#include "crc32c.h"
+#include
+#include
+
+#include "common/kprintf.h"
+#include "common/precise-time.h"
+#include "common/resolver.h"
+#include "common/rpc-const.h"
#include "common/sha256.h"
-#include "net/net-events.h"
-#include "kprintf.h"
-#include "precise-time.h"
#include "net/net-connections.h"
-#include "net/net-tcp-rpc-ext-server.h"
+#include "net/net-crypto-aes.h"
+#include "net/net-events.h"
#include "net/net-tcp-connections.h"
+#include "net/net-tcp-rpc-ext-server.h"
#include "net/net-thread.h"
-#include "rpc-const.h"
+#include "vv/vv-io.h"
-#include "net/net-crypto-aes.h"
-//#include "net/net-config.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
-#include "vv/vv-io.h"
/*
*
* EXTERNAL RPC SERVER INTERFACE
@@ -57,19 +66,21 @@
*/
int tcp_rpcs_compact_parse_execute (connection_job_t c);
+int tcp_rpcs_ext_alarm (connection_job_t c);
+int tcp_rpcs_ext_init_accepted (connection_job_t c);
conn_type_t ct_tcp_rpc_ext_server = {
.magic = CONN_FUNC_MAGIC,
.flags = C_RAWMSG,
.title = "rpc_ext_server",
- .init_accepted = tcp_rpcs_init_accepted_nohs,
+ .init_accepted = tcp_rpcs_ext_init_accepted,
.parse_execute = tcp_rpcs_compact_parse_execute,
.close = tcp_rpcs_close_connection,
.flush = tcp_rpc_flush,
.write_packet = tcp_rpc_write_packet_compact,
.connected = server_failed,
.wakeup = tcp_rpcs_wakeup,
- .alarm = tcp_rpcs_alarm,
+ .alarm = tcp_rpcs_ext_alarm,
.crypto_init = aes_crypto_ctr128_init,
.crypto_free = aes_crypto_free,
.crypto_encrypt_output = cpu_tcp_aes_crypto_ctr128_encrypt_output,
@@ -77,13 +88,70 @@ conn_type_t ct_tcp_rpc_ext_server = {
.crypto_needed_output_bytes = cpu_tcp_aes_crypto_ctr128_needed_output_bytes,
};
+int tcp_proxy_pass_parse_execute (connection_job_t C);
+int tcp_proxy_pass_close (connection_job_t C, int who);
+int tcp_proxy_pass_connected (connection_job_t C);
+int tcp_proxy_pass_write_packet (connection_job_t c, struct raw_message *raw);
+
+conn_type_t ct_proxy_pass = {
+ .magic = CONN_FUNC_MAGIC,
+ .flags = C_RAWMSG,
+ .title = "proxypass",
+ .init_accepted = server_failed,
+ .parse_execute = tcp_proxy_pass_parse_execute,
+ .connected = tcp_proxy_pass_connected,
+ .close = tcp_proxy_pass_close,
+ .write_packet = tcp_proxy_pass_write_packet,
+ .connected = server_noop,
+};
+
+int tcp_proxy_pass_connected (connection_job_t C) {
+ struct connection_info *c = CONN_INFO(C);
+ vkprintf (1, "proxy pass connected #%d %s:%d -> %s:%d\n", c->fd, show_our_ip (C), c->our_port, show_remote_ip (C), c->remote_port);
+ return 0;
+}
+
+int tcp_proxy_pass_parse_execute (connection_job_t C) {
+ struct connection_info *c = CONN_INFO(C);
+ if (!c->extra) {
+ fail_connection (C, -1);
+ return 0;
+ }
+ job_t E = job_incref (c->extra);
+ struct connection_info *e = CONN_INFO(E);
+
+ struct raw_message *r = malloc (sizeof (*r));
+ rwm_move (r, &c->in);
+ rwm_init (&c->in, 0);
+ vkprintf (3, "proxying %d bytes to %s:%d\n", r->total_bytes, show_remote_ip (E), e->remote_port);
+ mpq_push_w (e->out_queue, PTR_MOVE(r), 0);
+ job_signal (JOB_REF_PASS (E), JS_RUN);
+ return 0;
+}
+
+int tcp_proxy_pass_close (connection_job_t C, int who) {
+ struct connection_info *c = CONN_INFO(C);
+ vkprintf (1, "closing proxy pass connection #%d %s:%d -> %s:%d\n", c->fd, show_our_ip (C), c->our_port, show_remote_ip (C), c->remote_port);
+ if (c->extra) {
+ job_t E = PTR_MOVE (c->extra);
+ fail_connection (E, -23);
+ job_decref (JOB_REF_PASS (E));
+ }
+ return cpu_server_close_connection (C, who);
+}
+
+int tcp_proxy_pass_write_packet (connection_job_t C, struct raw_message *raw) {
+ rwm_union (&CONN_INFO(C)->out, raw);
+ return 0;
+}
+
int tcp_rpcs_default_execute (connection_job_t c, int op, struct raw_message *msg);
static unsigned char ext_secret[16][16];
static int ext_secret_cnt = 0;
static int ext_rand_pad_only = 0;
-void tcp_rpcs_set_ext_secret(unsigned char secret[16]) {
+void tcp_rpcs_set_ext_secret (unsigned char secret[16]) {
assert (ext_secret_cnt < 16);
memcpy (ext_secret[ext_secret_cnt ++], secret, 16);
}
@@ -92,31 +160,868 @@ void tcp_rpcs_set_ext_rand_pad_only(int set) {
ext_rand_pad_only = set;
}
-/*
-struct tcp_rpc_server_functions default_tcp_rpc_ext_server = {
- .execute = tcp_rpcs_default_execute,
- .check_ready = server_check_ready,
- .flush_packet = tcp_rpc_flush_packet,
- .rpc_wakeup = tcp_rpcs_do_wakeup,
- .rpc_alarm = tcp_rpcs_do_wakeup,
- .rpc_check_perm = tcp_rpcs_default_check_perm,
- .rpc_init_crypto = tcp_rpcs_init_crypto,
- .rpc_ready = server_noop,
+static int allow_only_tls;
+
+struct domain_info {
+ const char *domain;
+ struct in_addr target;
+ unsigned char target_ipv6[16];
+ short server_hello_encrypted_size;
+ char use_random_encrypted_size;
+ char is_reversed_extension_order;
+ struct domain_info *next;
};
-*/
+
+static struct domain_info *default_domain_info;
+
+#define DOMAIN_HASH_MOD 257
+static struct domain_info *domains[DOMAIN_HASH_MOD];
+
+static struct domain_info **get_domain_info_bucket (const char *domain, size_t len) {
+ size_t i;
+ unsigned hash = 0;
+ for (i = 0; i < len; i++) {
+ hash = hash * 239017 + (unsigned char)domain[i];
+ }
+ return domains + hash % DOMAIN_HASH_MOD;
+}
+
+static const struct domain_info *get_domain_info (const char *domain, size_t len) {
+ struct domain_info *info = *get_domain_info_bucket (domain, len);
+ while (info != NULL) {
+ if (strlen (info->domain) == len && memcmp (domain, info->domain, len) == 0) {
+ return info;
+ }
+ info = info->next;
+ }
+ return NULL;
+}
+
+static int get_domain_server_hello_encrypted_size (const struct domain_info *info) {
+ if (info->use_random_encrypted_size) {
+ int r = rand();
+ return info->server_hello_encrypted_size + ((r >> 1) & 1) - (r & 1);
+ } else {
+ return info->server_hello_encrypted_size;
+ }
+}
+
+#define TLS_REQUEST_LENGTH 517
+
+static BIGNUM *get_y2 (BIGNUM *x, const BIGNUM *mod, BN_CTX *big_num_context) {
+ // returns y^2 = x^3 + 486662 * x^2 + x
+ BIGNUM *y = BN_dup (x);
+ assert (y != NULL);
+ BIGNUM *coef = BN_new();
+ assert (BN_set_word (coef, 486662) == 1);
+ assert (BN_mod_add (y, y, coef, mod, big_num_context) == 1);
+ assert (BN_mod_mul (y, y, x, mod, big_num_context) == 1);
+ assert (BN_one (coef) == 1);
+ assert (BN_mod_add (y, y, coef, mod, big_num_context) == 1);
+ assert (BN_mod_mul (y, y, x, mod, big_num_context) == 1);
+ BN_clear_free (coef);
+ return y;
+}
+
+static BIGNUM *get_double_x (BIGNUM *x, const BIGNUM *mod, BN_CTX *big_num_context) {
+ // returns x_2 = (x^2 - 1)^2/(4*y^2)
+ BIGNUM *denominator = get_y2 (x, mod, big_num_context);
+ assert (denominator != NULL);
+ BIGNUM *coef = BN_new();
+ assert (BN_set_word (coef, 4) == 1);
+ assert (BN_mod_mul (denominator, denominator, coef, mod, big_num_context) == 1);
+
+ BIGNUM *numerator = BN_new();
+ assert (numerator != NULL);
+ assert (BN_mod_mul (numerator, x, x, mod, big_num_context) == 1);
+ assert (BN_one (coef) == 1);
+ assert (BN_mod_sub (numerator, numerator, coef, mod, big_num_context) == 1);
+ assert (BN_mod_mul (numerator, numerator, numerator, mod, big_num_context) == 1);
+
+ assert (BN_mod_inverse (denominator, denominator, mod, big_num_context) == denominator);
+ assert (BN_mod_mul (numerator, numerator, denominator, mod, big_num_context) == 1);
+
+ BN_clear_free (coef);
+ BN_clear_free (denominator);
+ return numerator;
+}
+
+static void generate_public_key (unsigned char key[32]) {
+ BIGNUM *mod = NULL;
+ assert (BN_hex2bn (&mod, "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed") == 64);
+ BIGNUM *pow = NULL;
+ assert (BN_hex2bn (&pow, "3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6") == 64);
+ BN_CTX *big_num_context = BN_CTX_new();
+ assert (big_num_context != NULL);
+
+ BIGNUM *x = BN_new();
+ while (1) {
+ assert (RAND_bytes (key, 32) == 1);
+ key[31] &= 127;
+ BN_bin2bn (key, 32, x);
+ assert (x != NULL);
+ assert (BN_mod_mul (x, x, x, mod, big_num_context) == 1);
+
+ BIGNUM *y = get_y2 (x, mod, big_num_context);
+
+ BIGNUM *r = BN_new();
+ assert (BN_mod_exp (r, y, pow, mod, big_num_context) == 1);
+ BN_clear_free (y);
+ if (BN_is_one (r)) {
+ BN_clear_free (r);
+ break;
+ }
+ BN_clear_free (r);
+ }
+
+ int i;
+ for (i = 0; i < 3; i++) {
+ BIGNUM *x2 = get_double_x (x, mod, big_num_context);
+ BN_clear_free (x);
+ x = x2;
+ }
+
+ int num_size = BN_num_bytes (x);
+ assert (num_size <= 32);
+ memset (key, '\0', 32 - num_size);
+ assert (BN_bn2bin (x, key + (32 - num_size)) == num_size);
+ for (i = 0; i < 16; i++) {
+ unsigned char t = key[i];
+ key[i] = key[31 - i];
+ key[31 - i] = t;
+ }
+
+ BN_clear_free (x);
+ BN_CTX_free (big_num_context);
+ BN_clear_free (pow);
+ BN_clear_free (mod);
+}
+
+static void add_string (unsigned char *str, int *pos, const char *data, int data_len) {
+ assert (*pos + data_len <= TLS_REQUEST_LENGTH);
+ memcpy (str + (*pos), data, data_len);
+ (*pos) += data_len;
+}
+
+static void add_random (unsigned char *str, int *pos, int random_len) {
+ assert (*pos + random_len <= TLS_REQUEST_LENGTH);
+ assert (RAND_bytes (str + (*pos), random_len) == 1);
+ (*pos) += random_len;
+}
+
+static void add_length (unsigned char *str, int *pos, int length) {
+ assert (*pos + 2 <= TLS_REQUEST_LENGTH);
+ str[*pos + 0] = (unsigned char)(length / 256);
+ str[*pos + 1] = (unsigned char)(length % 256);
+ (*pos) += 2;
+}
+
+static void add_grease (unsigned char *str, int *pos, const unsigned char *greases, int num) {
+ assert (*pos + 2 <= TLS_REQUEST_LENGTH);
+ str[*pos + 0] = greases[num];
+ str[*pos + 1] = greases[num];
+ (*pos) += 2;
+}
+
+static void add_public_key (unsigned char *str, int *pos) {
+ assert (*pos + 32 <= TLS_REQUEST_LENGTH);
+ generate_public_key (str + (*pos));
+ (*pos) += 32;
+}
+
+static unsigned char *create_request (const char *domain) {
+ unsigned char *result = malloc (TLS_REQUEST_LENGTH);
+ int pos = 0;
+
+#define MAX_GREASE 7
+ unsigned char greases[MAX_GREASE];
+ assert (RAND_bytes (greases, MAX_GREASE) == 1);
+ int i;
+ for (i = 0; i < MAX_GREASE; i++) {
+ greases[i] = (unsigned char)((greases[i] & 0xF0) + 0x0A);
+ }
+ for (i = 1; i < MAX_GREASE; i += 2) {
+ if (greases[i] == greases[i - 1]) {
+ greases[i] = (unsigned char)(0x10 ^ greases[i]);
+ }
+ }
+#undef MAX_GREASE
+
+ int domain_length = (int)strlen (domain);
+
+ add_string (result, &pos, "\x16\x03\x01\x02\x00\x01\x00\x01\xfc\x03\x03", 11);
+ add_random (result, &pos, 32);
+ add_string (result, &pos, "\x20", 1);
+ add_random (result, &pos, 32);
+ add_string (result, &pos, "\x00\x22", 2);
+ add_grease (result, &pos, greases, 0);
+ add_string (result, &pos, "\x13\x01\x13\x02\x13\x03\xc0\x2b\xc0\x2f\xc0\x2c\xc0\x30\xcc\xa9\xcc\xa8"
+ "\xc0\x13\xc0\x14\x00\x9c\x00\x9d\x00\x2f\x00\x35\x00\x0a\x01\x00\x01\x91", 36);
+ add_grease (result, &pos, greases, 2);
+ add_string (result, &pos, "\x00\x00\x00\x00", 4);
+ add_length (result, &pos, domain_length + 5);
+ add_length (result, &pos, domain_length + 3);
+ add_string (result, &pos, "\x00", 1);
+ add_length (result, &pos, domain_length);
+ add_string (result, &pos, domain, domain_length);
+ add_string (result, &pos, "\x00\x17\x00\x00\xff\x01\x00\x01\x00\x00\x0a\x00\x0a\x00\x08", 15);
+ add_grease (result, &pos, greases, 4);
+ add_string (result, &pos, "\x00\x1d\x00\x17\x00\x18\x00\x0b\x00\x02\x01\x00\x00\x23\x00\x00\x00\x10"
+ "\x00\x0e\x00\x0c\x02\x68\x32\x08\x68\x74\x74\x70\x2f\x31\x2e\x31\x00\x05"
+ "\x00\x05\x01\x00\x00\x00\x00\x00\x0d\x00\x14\x00\x12\x04\x03\x08\x04\x04"
+ "\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x02\x01\x00\x12\x00\x00\x00"
+ "\x33\x00\x2b\x00\x29", 77);
+ add_grease (result, &pos, greases, 4);
+ add_string (result, &pos, "\x00\x01\x00\x00\x1d\x00\x20", 7);
+ add_public_key (result, &pos);
+ add_string (result, &pos, "\x00\x2d\x00\x02\x01\x01\x00\x2b\x00\x0b\x0a", 11);
+ add_grease (result, &pos, greases, 6);
+ add_string (result, &pos, "\x03\x04\x03\x03\x03\x02\x03\x01\x00\x1b\x00\x03\x02\x00\x02", 15);
+ add_grease (result, &pos, greases, 3);
+ add_string (result, &pos, "\x00\x01\x00\x00\x15", 5);
+
+ int padding_length = TLS_REQUEST_LENGTH - 2 - pos;
+ assert (padding_length >= 0);
+ add_length (result, &pos, padding_length);
+ memset (result + pos, 0, TLS_REQUEST_LENGTH - pos);
+ return result;
+}
+
+static int read_length (const unsigned char *response, int *pos) {
+ *pos += 2;
+ return response[*pos - 2] * 256 + response[*pos - 1];
+}
+
+static int check_response (const unsigned char *response, int len, const unsigned char *request_session_id, int *is_reversed_extension_order, int *encrypted_application_data_length) {
+#define FAIL(error) { \
+ kprintf ("Failed to parse upstream TLS response: " error "\n"); \
+ return 0; \
+ }
+#define CHECK_LENGTH(length) \
+ if (pos + (length) > len) { \
+ FAIL("Too short"); \
+ }
+#define EXPECT_STR(pos, str, error) \
+ if (memcmp (response + pos, str, sizeof (str) - 1) != 0) { \
+ FAIL(error); \
+ }
+
+ int pos = 0;
+ CHECK_LENGTH(3);
+ EXPECT_STR(0, "\x16\x03\x03", "Non-TLS response or TLS <= 1.1");
+ pos += 3;
+ CHECK_LENGTH(2);
+ int server_hello_length = read_length (response, &pos);
+ if (server_hello_length <= 39) {
+ FAIL("Receive too short ServerHello");
+ }
+ CHECK_LENGTH(server_hello_length);
+
+ EXPECT_STR(5, "\x02\x00", "Non-TLS response 2");
+ EXPECT_STR(9, "\x03\x03", "Non-TLS response 3");
+
+ if (memcmp (response + 11, "\xcf\x21\xad\x74\xe5\x9a\x61\x11\xbe\x1d\x8c\x02\x1e\x65\xb8\x91"
+ "\xc2\xa2\x11\x16\x7a\xbb\x8c\x5e\x07\x9e\x09\xe2\xc8\xa8\x33\x9c", 32) == 0) {
+ FAIL("TLS 1.3 servers returning HelloRetryRequest are not supprted");
+ }
+ if (response[43] == '\x00') {
+ FAIL("TLS <= 1.2: empty session_id");
+ }
+ EXPECT_STR(43, "\x20", "Non-TLS response 4");
+ if (server_hello_length <= 75) {
+ FAIL("Receive too short server hello 2");
+ }
+ if (memcmp (response + 44, request_session_id, 32) != 0) {
+ FAIL("TLS <= 1.2: expected mirrored session_id");
+ }
+ EXPECT_STR(76, "\x13\x01\x00", "TLS <= 1.2: expected x25519 as a chosen cipher");
+ pos += 74;
+ int extensions_length = read_length (response, &pos);
+ if (extensions_length + 76 != server_hello_length) {
+ FAIL("Receive wrong extensions length");
+ }
+ int sum = 0;
+ while (pos < 5 + server_hello_length - 4) {
+ int extension_id = read_length (response, &pos);
+ if (extension_id != 0x33 && extension_id != 0x2b) {
+ FAIL("Receive unexpected extension");
+ }
+ if (pos == 83) {
+ *is_reversed_extension_order = (extension_id == 0x2b);
+ }
+ sum += extension_id;
+
+ int extension_length = read_length (response, &pos);
+ if (pos + extension_length > 5 + server_hello_length) {
+ FAIL("Receive wrong extension length");
+ }
+ if (extension_length != (extension_id == 0x33 ? 36 : 2)) {
+ FAIL("Unexpected extension length");
+ }
+ pos += extension_length;
+ }
+ if (sum != 0x33 + 0x2b) {
+ FAIL("Receive duplicate extensions");
+ }
+ if (pos != 5 + server_hello_length) {
+ FAIL("Receive wrong extensions list");
+ }
+
+ CHECK_LENGTH(9);
+ EXPECT_STR(pos, "\x14\x03\x03\x00\x01\x01", "Expected dummy ChangeCipherSpec");
+ EXPECT_STR(pos + 6, "\x17\x03\x03", "Expected encrypted application data");
+ pos += 9;
+
+ CHECK_LENGTH(2);
+ *encrypted_application_data_length = read_length (response, &pos);
+ if (*encrypted_application_data_length == 0) {
+ FAIL("Receive empty encrypted application data");
+ }
+
+ CHECK_LENGTH(*encrypted_application_data_length);
+ pos += *encrypted_application_data_length;
+ if (pos != len) {
+ FAIL("Too long");
+ }
+#undef FAIL
+#undef CHECK_LENGTH
+#undef EXPECT_STR
+
+ return 1;
+}
+
+static int update_domain_info (struct domain_info *info) {
+ const char *domain = info->domain;
+ struct hostent *host = kdb_gethostbyname (domain);
+ if (host == NULL || host->h_addr == NULL) {
+ kprintf ("Failed to resolve host %s\n", domain);
+ return 0;
+ }
+ assert (host->h_addrtype == AF_INET || host->h_addrtype == AF_INET6);
+
+ fd_set read_fd;
+ fd_set write_fd;
+ fd_set except_fd;
+ FD_ZERO(&read_fd);
+ FD_ZERO(&write_fd);
+ FD_ZERO(&except_fd);
+
+#define TRIES 20
+ int sockets[TRIES];
+ int i;
+ for (i = 0; i < TRIES; i++) {
+ sockets[i] = socket (host->h_addrtype, SOCK_STREAM, IPPROTO_TCP);
+ if (sockets[i] < 0) {
+ kprintf ("Failed to open socket for %s: %s\n", domain, strerror (errno));
+ return 0;
+ }
+ if (fcntl (sockets[i], F_SETFL, O_NONBLOCK) == -1) {
+ kprintf ("Failed to make socket non-blocking: %s\n", strerror (errno));
+ return 0;
+ }
+
+ int e_connect;
+ if (host->h_addrtype == AF_INET) {
+ info->target = *((struct in_addr *) host->h_addr);
+ memset (info->target_ipv6, 0, sizeof (info->target_ipv6));
+
+ struct sockaddr_in addr;
+ memset (&addr, 0, sizeof (addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons (443);
+ memcpy (&addr.sin_addr, host->h_addr, sizeof (struct in_addr));
+
+ e_connect = connect (sockets[i], &addr, sizeof (addr));
+ } else {
+ assert (sizeof (struct in6_addr) == sizeof (info->target_ipv6));
+ info->target.s_addr = 0;
+ memcpy (info->target_ipv6, host->h_addr, sizeof (struct in6_addr));
+
+ struct sockaddr_in6 addr;
+ memset (&addr, 0, sizeof (addr));
+ addr.sin6_family = AF_INET6;
+ addr.sin6_port = htons (443);
+ memcpy (&addr.sin6_addr, host->h_addr, sizeof (struct in6_addr));
+
+ e_connect = connect (sockets[i], &addr, sizeof (addr));
+ }
+
+ if (e_connect == -1 && errno != EINPROGRESS) {
+ kprintf ("Failed to connect to %s: %s\n", domain, strerror (errno));
+ return 0;
+ }
+ }
+
+ unsigned char *requests[TRIES];
+ for (i = 0; i < TRIES; i++) {
+ requests[i] = create_request (domain);
+ }
+ unsigned char *responses[TRIES] = {};
+ int response_len[TRIES] = {};
+ int is_encrypted_application_data_length_read[TRIES] = {};
+
+ int finished_count = 0;
+ int is_written[TRIES] = {};
+ int is_finished[TRIES] = {};
+ int read_pos[TRIES] = {};
+ double finish_time = get_utime_monotonic() + 5.0;
+ int encrypted_application_data_length_min = 0;
+ int encrypted_application_data_length_sum = 0;
+ int encrypted_application_data_length_max = 0;
+ int is_reversed_extension_order_min = 0;
+ int is_reversed_extension_order_max = 0;
+ int have_error = 0;
+ while (get_utime_monotonic() < finish_time && finished_count < TRIES && !have_error) {
+ struct timeval timeout_data;
+ timeout_data.tv_sec = (int)(finish_time - precise_now + 1);
+ timeout_data.tv_usec = 0;
+
+ int max_fd = 0;
+ for (i = 0; i < TRIES; i++) {
+ if (is_finished[i]) {
+ continue;
+ }
+ if (is_written[i]) {
+ FD_SET(sockets[i], &read_fd);
+ FD_CLR(sockets[i], &write_fd);
+ } else {
+ FD_CLR(sockets[i], &read_fd);
+ FD_SET(sockets[i], &write_fd);
+ }
+ FD_SET(sockets[i], &except_fd);
+ if (sockets[i] > max_fd) {
+ max_fd = sockets[i];
+ }
+ }
+
+ select (max_fd + 1, &read_fd, &write_fd, &except_fd, &timeout_data);
+
+ for (i = 0; i < TRIES; i++) {
+ if (is_finished[i]) {
+ continue;
+ }
+ if (FD_ISSET(sockets[i], &read_fd)) {
+ assert (is_written[i]);
+
+ unsigned char header[5];
+ if (responses[i] == NULL) {
+ ssize_t read_res = read (sockets[i], header, sizeof (header));
+ if (read_res != sizeof (header)) {
+ kprintf ("Failed to read response header for checking domain %s: %s\n", domain, read_res == -1 ? strerror (errno) : "Read less bytes than expected");
+ have_error = 1;
+ break;
+ }
+ if (memcmp (header, "\x16\x03\x03", 3) != 0) {
+ kprintf ("Non-TLS response, or TLS <= 1.1, or unsuccessful request to %s: receive \\x%02x\\x%02x\\x%02x\\x%02x\\x%02x...\n",
+ domain, header[0], header[1], header[2], header[3], header[4]);
+ have_error = 1;
+ break;
+ }
+ response_len[i] = 5 + header[3] * 256 + header[4] + 6 + 5;
+ responses[i] = malloc (response_len[i]);
+ memcpy (responses[i], header, sizeof (header));
+ read_pos[i] = 5;
+ } else {
+ ssize_t read_res = read (sockets[i], responses[i] + read_pos[i], response_len[i] - read_pos[i]);
+ if (read_res == -1) {
+ kprintf ("Failed to read response from %s: %s\n", domain, strerror (errno));
+ have_error = 1;
+ break;
+ }
+ read_pos[i] += read_res;
+
+ if (read_pos[i] == response_len[i]) {
+ if (!is_encrypted_application_data_length_read[i]) {
+ if (memcmp (responses[i] + response_len[i] - 11, "\x14\x03\x03\x00\x01\x01\x17\x03\x03", 9) != 0) {
+ kprintf ("Not found TLS 1.3 support on domain %s\n", domain);
+ have_error = 1;
+ break;
+ }
+
+ is_encrypted_application_data_length_read[i] = 1;
+ int encrypted_application_data_length = responses[i][response_len[i] - 2] * 256 + responses[i][response_len[i] - 1];
+ response_len[i] += encrypted_application_data_length;
+ unsigned char *new_buffer = realloc (responses[i], response_len[i]);
+ assert (new_buffer != NULL);
+ responses[i] = new_buffer;
+ continue;
+ }
+
+ int is_reversed_extension_order = -1;
+ int encrypted_application_data_length = -1;
+ if (check_response (responses[i], response_len[i], requests[i] + 44, &is_reversed_extension_order, &encrypted_application_data_length)) {
+ assert (is_reversed_extension_order != -1);
+ assert (encrypted_application_data_length != -1);
+ if (finished_count == 0) {
+ is_reversed_extension_order_min = is_reversed_extension_order;
+ is_reversed_extension_order_max = is_reversed_extension_order;
+ encrypted_application_data_length_min = encrypted_application_data_length;
+ encrypted_application_data_length_max = encrypted_application_data_length;
+ } else {
+ if (is_reversed_extension_order < is_reversed_extension_order_min) {
+ is_reversed_extension_order_min = is_reversed_extension_order;
+ }
+ if (is_reversed_extension_order > is_reversed_extension_order_max) {
+ is_reversed_extension_order_max = is_reversed_extension_order;
+ }
+ if (encrypted_application_data_length < encrypted_application_data_length_min) {
+ encrypted_application_data_length_min = encrypted_application_data_length;
+ }
+ if (encrypted_application_data_length > encrypted_application_data_length_max) {
+ encrypted_application_data_length_max = encrypted_application_data_length;
+ }
+ }
+ encrypted_application_data_length_sum += encrypted_application_data_length;
+
+ FD_CLR(sockets[i], &write_fd);
+ FD_CLR(sockets[i], &read_fd);
+ FD_CLR(sockets[i], &except_fd);
+ is_finished[i] = 1;
+ finished_count++;
+ } else {
+ have_error = 1;
+ break;
+ }
+ }
+ }
+ }
+ if (FD_ISSET(sockets[i], &write_fd)) {
+ assert (!is_written[i]);
+ ssize_t write_res = write (sockets[i], requests[i], TLS_REQUEST_LENGTH);
+ if (write_res != TLS_REQUEST_LENGTH) {
+ kprintf ("Failed to write request for checking domain %s: %s", domain, write_res == -1 ? strerror (errno) : "Written less bytes than expected");
+ have_error = 1;
+ break;
+ }
+ is_written[i] = 1;
+ }
+ if (FD_ISSET(sockets[i], &except_fd)) {
+ kprintf ("Failed to check domain %s: %s\n", domain, strerror (errno));
+ have_error = 1;
+ break;
+ }
+ }
+ }
+
+ for (i = 0; i < TRIES; i++) {
+ close (sockets[i]);
+ free (requests[i]);
+ free (responses[i]);
+ }
+
+ if (finished_count != TRIES) {
+ if (!have_error) {
+ kprintf ("Failed to check domain %s in 5 seconds\n", domain);
+ }
+ return 0;
+ }
+
+ if (is_reversed_extension_order_min != is_reversed_extension_order_max) {
+ kprintf ("Upstream server %s uses non-deterministic extension order\n", domain);
+ }
+
+ info->is_reversed_extension_order = (char)is_reversed_extension_order_min;
+
+ if (encrypted_application_data_length_min == encrypted_application_data_length_max) {
+ info->server_hello_encrypted_size = encrypted_application_data_length_min;
+ info->use_random_encrypted_size = 0;
+ } else if (encrypted_application_data_length_max - encrypted_application_data_length_min <= 3) {
+ info->server_hello_encrypted_size = encrypted_application_data_length_max - 1;
+ info->use_random_encrypted_size = 1;
+ } else {
+ kprintf ("Unrecognized encrypted application data length pattern with min = %d, max = %d, mean = %.3lf\n",
+ encrypted_application_data_length_min, encrypted_application_data_length_max, encrypted_application_data_length_sum * 1.0 / TRIES);
+ info->server_hello_encrypted_size = (int)(encrypted_application_data_length_sum * 1.0 / TRIES + 0.5);
+ info->use_random_encrypted_size = 1;
+ }
+
+ vkprintf (0, "Successfully checked domain %s in %.3lf seconds: is_reversed_extension_order = %d, server_hello_encrypted_size = %d, use_random_encrypted_size = %d\n",
+ domain, get_utime_monotonic() - (finish_time - 5.0), info->is_reversed_extension_order, info->server_hello_encrypted_size, info->use_random_encrypted_size);
+ if (info->is_reversed_extension_order && info->server_hello_encrypted_size <= 1250) {
+ kprintf ("Multiple encrypted client data packets are unsupported, so handshake with %s will not be fully emulated\n", domain);
+ }
+ return 1;
+#undef TRIES
+}
+
+#undef TLS_REQUEST_LENGTH
+
+static const struct domain_info *get_sni_domain_info (const unsigned char *request, int len) {
+#define CHECK_LENGTH(length) \
+ if (pos + (length) > len) { \
+ return NULL; \
+ }
+
+ int pos = 11 + 32 + 1 + 32;
+ CHECK_LENGTH(2);
+ int cipher_suites_length = read_length (request, &pos);
+ CHECK_LENGTH(cipher_suites_length + 4);
+ pos += cipher_suites_length + 4;
+ while (1) {
+ CHECK_LENGTH(4);
+ int extension_id = read_length (request, &pos);
+ int extension_length = read_length (request, &pos);
+ CHECK_LENGTH(extension_length);
+
+ if (extension_id == 0) {
+ // found SNI
+ CHECK_LENGTH(5);
+ int inner_length = read_length (request, &pos);
+ if (inner_length != extension_length - 2) {
+ return NULL;
+ }
+ if (request[pos++] != 0) {
+ return NULL;
+ }
+ int domain_length = read_length (request, &pos);
+ if (domain_length != extension_length - 5) {
+ return NULL;
+ }
+ int i;
+ for (i = 0; i < domain_length; i++) {
+ if (request[pos + i] == 0) {
+ return NULL;
+ }
+ }
+ const struct domain_info *info = get_domain_info ((const char *)(request + pos), domain_length);
+ if (info == NULL) {
+ vkprintf (1, "Receive request for unknown domain %.*s\n", domain_length, request + pos);
+ }
+ return info;
+ }
+
+ pos += extension_length;
+ }
+#undef CHECK_LENGTH
+}
+
+void tcp_rpc_add_proxy_domain (const char *domain) {
+ assert (domain != NULL);
+
+ struct domain_info *info = calloc (1, sizeof (struct domain_info));
+ assert (info != NULL);
+ info->domain = strdup (domain);
+
+ struct domain_info **bucket = get_domain_info_bucket (domain, strlen (domain));
+ info->next = *bucket;
+ *bucket = info;
+
+ if (!allow_only_tls) {
+ allow_only_tls = 1;
+ default_domain_info = info;
+ }
+}
+
+void tcp_rpc_init_proxy_domains() {
+ int i;
+ for (i = 0; i < DOMAIN_HASH_MOD; i++) {
+ struct domain_info *info = domains[i];
+ while (info != NULL) {
+ if (!update_domain_info (info)) {
+ kprintf ("Failed to update response data about %s, so default response settings wiil be used\n", info->domain);
+ // keep target addresses as is
+ info->is_reversed_extension_order = 0;
+ info->use_random_encrypted_size = 1;
+ info->server_hello_encrypted_size = 2500 + rand() % 1120;
+ }
+
+ info = info->next;
+ }
+ }
+}
+
+struct client_random {
+ unsigned char random[16];
+ struct client_random *next_by_time;
+ struct client_random *next_by_hash;
+ int time;
+};
+
+#define RANDOM_HASH_BITS 14
+static struct client_random *client_randoms[1 << RANDOM_HASH_BITS];
+
+static struct client_random *first_client_random;
+static struct client_random *last_client_random;
+
+static struct client_random **get_client_random_bucket (unsigned char random[16]) {
+ int i = RANDOM_HASH_BITS;
+ int pos = 0;
+ int id = 0;
+ while (i > 0) {
+ int bits = i < 8 ? i : 8;
+ id = (id << bits) | (random[pos++] & ((1 << bits) - 1));
+ i -= bits;
+ }
+ assert (0 <= id && id < (1 << RANDOM_HASH_BITS));
+ return client_randoms + id;
+}
+
+static int have_client_random (unsigned char random[16]) {
+ struct client_random *cur = *get_client_random_bucket (random);
+ while (cur != NULL) {
+ if (memcmp (random, cur->random, 16) == 0) {
+ return 1;
+ }
+ cur = cur->next_by_hash;
+ }
+ return 0;
+}
+
+static void add_client_random (unsigned char random[16]) {
+ struct client_random *entry = malloc (sizeof (struct client_random));
+ memcpy (entry->random, random, 16);
+ entry->time = now;
+ entry->next_by_time = NULL;
+ if (last_client_random == NULL) {
+ assert (first_client_random == NULL);
+ first_client_random = last_client_random = entry;
+ } else {
+ last_client_random->next_by_time = entry;
+ last_client_random = entry;
+ }
+
+ struct client_random **bucket = get_client_random_bucket (random);
+ entry->next_by_hash = *bucket;
+ *bucket = entry;
+}
+
+#define MAX_CLIENT_RANDOM_CACHE_TIME 2 * 86400
+
+static void delete_old_client_randoms() {
+ while (first_client_random != last_client_random) {
+ assert (first_client_random != NULL);
+ if (first_client_random->time > now - MAX_CLIENT_RANDOM_CACHE_TIME) {
+ return;
+ }
+
+ struct client_random *entry = first_client_random;
+ assert (entry->next_by_hash == NULL);
+
+ first_client_random = first_client_random->next_by_time;
+
+ struct client_random **cur = get_client_random_bucket (entry->random);
+ while (*cur != entry) {
+ cur = &(*cur)->next_by_hash;
+ }
+ *cur = NULL;
+
+ free (entry);
+ }
+}
+
+static int is_allowed_timestamp (int timestamp) {
+ if (timestamp > now + 3) {
+ // do not allow timestamps in the future
+ // after time synchronization client should always have time in the past
+ vkprintf (1, "Disallow request with timestamp %d from the future, now is %d\n", timestamp, now);
+ return 0;
+ }
+
+ // first_client_random->time is an exact time when corresponding request was received
+ // if the timestamp is bigger than (first_client_random->time + 3), then the current request could be accepted
+ // only after the request with first_client_random, so the client random still must be cached
+ // if the request wasn't accepted, then the client_random still will be cached for MAX_CLIENT_RANDOM_CACHE_TIME seconds,
+ // so we can miss duplicate request only after a lot of time has passed
+ if (first_client_random != NULL && timestamp > first_client_random->time + 3) {
+ vkprintf (1, "Allow new request with timestamp %d\n", timestamp);
+ return 1;
+ }
+
+ // allow all requests with timestamp recently in past, regardless of ability to check repeating client random
+ // the allowed error must be big enough to allow requests after time synchronization
+ const int MAX_ALLOWED_TIMESTAMP_ERROR = 10 * 60;
+ if (timestamp > now - MAX_ALLOWED_TIMESTAMP_ERROR) {
+ // this can happen only first (MAX_ALLOWED_TIMESTAMP_ERROR + 3) sceonds after first_client_random->time
+ vkprintf (1, "Allow recent request with timestamp %d without full check for client random duplication\n", timestamp);
+ return 1;
+ }
+
+ // the request is too old to check client random, do not allow it to force client to synchronize it's time
+ vkprintf (1, "Disallow too old request with timestamp %d\n", timestamp);
+ return 0;
+}
+
+static int proxy_connection (connection_job_t C, const struct domain_info *info) {
+ struct connection_info *c = CONN_INFO(C);
+ assert (check_conn_functions (&ct_proxy_pass, 0) >= 0);
+
+ const char zero[16] = {};
+ if (info->target.s_addr == 0 && !memcmp (info->target_ipv6, zero, 16)) {
+ vkprintf (0, "failed to proxy request to %s\n", info->domain);
+ fail_connection (C, -17);
+ return 0;
+ }
+
+ int port = c->our_port == 80 ? 80 : 443;
+
+ int cfd = -1;
+ if (info->target.s_addr) {
+ cfd = client_socket (info->target.s_addr, port, 0);
+ } else {
+ cfd = client_socket_ipv6 (info->target_ipv6, port, SM_IPV6);
+ }
+
+ if (cfd < 0) {
+ kprintf ("failed to create proxy pass connection: %d (%m)", errno);
+ fail_connection (C, -27);
+ return 0;
+ }
+
+ c->type->crypto_free (C);
+ job_incref (C);
+ job_t EJ = alloc_new_connection (cfd, NULL, NULL, ct_outbound, &ct_proxy_pass, C, ntohl (*(int *)&info->target.s_addr), (void *)info->target_ipv6, port);
+
+ if (!EJ) {
+ kprintf ("failed to create proxy pass connection (2)");
+ job_decref_f (C);
+ fail_connection (C, -37);
+ return 0;
+ }
+
+ c->type = &ct_proxy_pass;
+ c->extra = job_incref (EJ);
+
+ assert (CONN_INFO(EJ)->io_conn);
+ unlock_job (JOB_REF_PASS (EJ));
+
+ return c->type->parse_execute (C);
+}
+
+int tcp_rpcs_ext_alarm (connection_job_t C) {
+ struct tcp_rpc_data *D = TCP_RPC_DATA (C);
+ if (D->in_packet_num == -3 && default_domain_info != NULL) {
+ return proxy_connection (C, default_domain_info);
+ } else {
+ return 0;
+ }
+}
+
+int tcp_rpcs_ext_init_accepted (connection_job_t C) {
+ job_timer_insert (C, precise_now + 10);
+ return tcp_rpcs_init_accepted_nohs (C);
+}
int tcp_rpcs_compact_parse_execute (connection_job_t C) {
+#define RETURN_TLS_ERROR(info) \
+ return proxy_connection (C, info);
+
struct tcp_rpc_data *D = TCP_RPC_DATA (C);
if (D->crypto_flags & RPCF_COMPACT_OFF) {
+ if (D->in_packet_num != -3) {
+ job_timer_remove (C);
+ }
return tcp_rpcs_parse_execute (C);
}
struct connection_info *c = CONN_INFO (C);
int len;
- vkprintf (4, "%s. in_total_bytes = %d\n", __func__, c->in.total_bytes);
+ vkprintf (4, "%s. in_total_bytes = %d\n", __func__, c->in.total_bytes);
while (1) {
+ if (D->in_packet_num != -3) {
+ job_timer_remove (C);
+ }
if (c->flags & C_ERROR) {
return NEED_MORE_BYTES;
}
@@ -129,7 +1034,6 @@ int tcp_rpcs_compact_parse_execute (connection_job_t C) {
}
int min_len = (D->flags & RPC_F_MEDIUM) ? 4 : 1;
-
if (len < min_len + 8) {
return min_len + 8 - len;
}
@@ -138,7 +1042,7 @@ int tcp_rpcs_compact_parse_execute (connection_job_t C) {
assert (rwm_fetch_lookup (&c->in, &packet_len, 4) == 4);
if (D->in_packet_num == -3) {
- vkprintf (1, "trying to determine connection type\n");
+ vkprintf (1, "trying to determine type of connection from %s:%d\n", show_remote_ip (C), c->remote_port);
#if __ALLOW_UNOBFS__
if ((packet_len & 0xff) == 0xef) {
D->flags |= RPC_F_COMPACT;
@@ -168,10 +1072,191 @@ int tcp_rpcs_compact_parse_execute (connection_job_t C) {
vkprintf (1, "HTTP type\n");
return tcp_rpcs_parse_execute (C);
}
+#endif
+
+ // fake tls
+ if (c->flags & C_IS_TLS) {
+ if (len < 11) {
+ return 11 - len;
+ }
+
+ vkprintf (1, "Established TLS connection from %s:%d\n", show_remote_ip (C), c->remote_port);
+ unsigned char header[11];
+ assert (rwm_fetch_lookup (&c->in, header, 11) == 11);
+ if (memcmp (header, "\x14\x03\x03\x00\x01\x01\x17\x03\x03", 9) != 0) {
+ vkprintf (1, "error while parsing packet: bad client dummy ChangeCipherSpec\n");
+ fail_connection (C, -1);
+ return 0;
+ }
+
+ min_len = 11 + 256 * header[9] + header[10];
+ if (len < min_len) {
+ vkprintf (2, "Need %d bytes, but have only %d\n", min_len, len);
+ return min_len - len;
+ }
+
+ assert (rwm_skip_data (&c->in, 11) == 11);
+ len -= 11;
+ c->left_tls_packet_length = 256 * header[9] + header[10]; // store left length of current TLS packet in extra_int3
+ vkprintf (2, "Receive first TLS packet of length %d\n", c->left_tls_packet_length);
+
+ if (c->left_tls_packet_length < 64) {
+ vkprintf (1, "error while parsing packet: too short first TLS packet: %d\n", c->left_tls_packet_length);
+ fail_connection (C, -1);
+ return 0;
+ }
+ // now len >= c->left_tls_packet_length >= 64
+
+ assert (rwm_fetch_lookup (&c->in, &packet_len, 4) == 4);
+
+ c->left_tls_packet_length -= 64; // skip header length
+ } else if (packet_len == *(int *)"\x16\x03\x01\x02" && ext_secret_cnt > 0 && allow_only_tls) {
+ unsigned char header[5];
+ assert (rwm_fetch_lookup (&c->in, header, 5) == 5);
+ min_len = 5 + 256 * header[3] + header[4];
+ if (len < min_len) {
+ return min_len - len;
+ }
+
+ int read_len = len <= 4096 ? len : 4096;
+ unsigned char client_hello[read_len + 1]; // VLA
+ assert (rwm_fetch_lookup (&c->in, client_hello, read_len) == read_len);
+
+ const struct domain_info *info = get_sni_domain_info (client_hello, read_len);
+ if (info == NULL) {
+ RETURN_TLS_ERROR(default_domain_info);
+ }
+
+ vkprintf (1, "TLS type with domain %s from %s:%d\n", info->domain, show_remote_ip (C), c->remote_port);
+
+ if (c->our_port == 80) {
+ vkprintf (1, "Receive TLS request on port %d, proxying to %s\n", c->our_port, info->domain);
+ RETURN_TLS_ERROR(info);
+ }
+
+ if (len > min_len) {
+ vkprintf (1, "Too much data in ClientHello, receive %d instead of %d\n", len, min_len);
+ RETURN_TLS_ERROR(info);
+ }
+ if (len != read_len) {
+ vkprintf (1, "Too big ClientHello: receive %d bytes\n", len);
+ RETURN_TLS_ERROR(info);
+ }
+
+ unsigned char client_random[32];
+ memcpy (client_random, client_hello + 11, 32);
+ memset (client_hello + 11, '\0', 32);
+
+ if (have_client_random (client_random)) {
+ vkprintf (1, "Receive again request with the same client random\n");
+ RETURN_TLS_ERROR(info);
+ }
+ add_client_random (client_random);
+ delete_old_client_randoms();
+
+ unsigned char expected_random[32];
+ int secret_id;
+ for (secret_id = 0; secret_id < ext_secret_cnt; secret_id++) {
+ sha256_hmac (ext_secret[secret_id], 16, client_hello, len, expected_random);
+ if (memcmp (expected_random, client_random, 28) == 0) {
+ break;
+ }
+ }
+ if (secret_id == ext_secret_cnt) {
+ vkprintf (1, "Receive request with unmatched client random\n");
+ RETURN_TLS_ERROR(info);
+ }
+ int timestamp = *(int *)(expected_random + 28) ^ *(int *)(client_random + 28);
+ if (!is_allowed_timestamp (timestamp)) {
+ RETURN_TLS_ERROR(info);
+ }
+
+ int pos = 76;
+ int cipher_suites_length = read_length (client_hello, &pos);
+ if (pos + cipher_suites_length > read_len) {
+ vkprintf (1, "Too long cipher suites list of length %d\n", cipher_suites_length);
+ RETURN_TLS_ERROR(info);
+ }
+ while (cipher_suites_length >= 2 && (client_hello[pos] & 0x0F) == 0x0A && (client_hello[pos + 1] & 0x0F) == 0x0A) {
+ // skip grease
+ cipher_suites_length -= 2;
+ pos += 2;
+ }
+ if (cipher_suites_length <= 1 || client_hello[pos] != 0x13 || client_hello[pos + 1] < 0x01 || client_hello[pos + 1] > 0x03) {
+ vkprintf (1, "Can't find supported cipher suite\n");
+ RETURN_TLS_ERROR(info);
+ }
+ unsigned char cipher_suite_id = client_hello[pos + 1];
+
+ assert (rwm_skip_data (&c->in, len) == len);
+ c->flags |= C_IS_TLS;
+ c->left_tls_packet_length = -1;
+
+ int encrypted_size = get_domain_server_hello_encrypted_size (info);
+ int response_size = 127 + 6 + 5 + encrypted_size;
+ unsigned char *buffer = malloc (32 + response_size);
+ assert (buffer != NULL);
+ memcpy (buffer, client_random, 32);
+ unsigned char *response_buffer = buffer + 32;
+ memcpy (response_buffer, "\x16\x03\x03\x00\x7a\x02\x00\x00\x76\x03\x03", 11);
+ memset (response_buffer + 11, '\0', 32);
+ response_buffer[43] = '\x20';
+ memcpy (response_buffer + 44, client_hello + 44, 32);
+ memcpy (response_buffer + 76, "\x13\x01\x00\x00\x2e", 5);
+ response_buffer[77] = cipher_suite_id;
+
+ pos = 81;
+ int tls_server_extensions[3] = {0x33, 0x2b, -1};
+ if (info->is_reversed_extension_order) {
+ int t = tls_server_extensions[0];
+ tls_server_extensions[0] = tls_server_extensions[1];
+ tls_server_extensions[1] = t;
+ }
+ int i;
+ for (i = 0; tls_server_extensions[i] != -1; i++) {
+ if (tls_server_extensions[i] == 0x33) {
+ assert (pos + 40 <= response_size);
+ memcpy (response_buffer + pos, "\x00\x33\x00\x24\x00\x1d\x00\x20", 8);
+ generate_public_key (response_buffer + pos + 8);
+ pos += 40;
+ } else if (tls_server_extensions[i] == 0x2b) {
+ assert (pos + 5 <= response_size);
+ memcpy (response_buffer + pos, "\x00\x2b\x00\x02\x03\x04", 6);
+ pos += 6;
+ } else {
+ assert (0);
+ }
+ }
+ assert (pos == 127);
+ memcpy (response_buffer + 127, "\x14\x03\x03\x00\x01\x01\x17\x03\x03", 9);
+ pos += 9;
+ response_buffer[pos++] = encrypted_size / 256;
+ response_buffer[pos++] = encrypted_size % 256;
+ assert (pos + encrypted_size == response_size);
+ RAND_bytes (response_buffer + pos, encrypted_size);
+
+ unsigned char server_random[32];
+ sha256_hmac (ext_secret[secret_id], 16, buffer, 32 + response_size, server_random);
+ memcpy (response_buffer + 11, server_random, 32);
+
+ struct raw_message *m = calloc (sizeof (struct raw_message), 1);
+ rwm_create (m, response_buffer, response_size);
+ mpq_push_w (c->out_queue, m, 0);
+ job_signal (JOB_REF_CREATE_PASS (C), JS_RUN);
+
+ free (buffer);
+ return 11; // waiting for dummy ChangeCipherSpec and first packet
+ }
+ if (allow_only_tls && !(c->flags & C_IS_TLS)) {
+ vkprintf (1, "Expected TLS-transport\n");
+ RETURN_TLS_ERROR(default_domain_info);
+ }
+
+#if __ALLOW_UNOBFS__
int tmp[2];
assert (rwm_fetch_lookup (&c->in, &tmp, 8) == 8);
- if (!tmp[1]) {
+ if (!tmp[1] && !(c->flags & C_IS_TLS)) {
D->crypto_flags |= RPCF_COMPACT_OFF;
vkprintf (1, "Long type\n");
return tcp_rpcs_parse_execute (C);
@@ -179,6 +1264,7 @@ int tcp_rpcs_compact_parse_execute (connection_job_t C) {
#endif
if (len < 64) {
+ assert (!(c->flags & C_IS_TLS));
#if __ALLOW_UNOBFS__
vkprintf (1, "random 64-byte header: first 0x%08x 0x%08x, need %d more bytes to distinguish\n", tmp[0], tmp[1], 64 - len);
#else
@@ -225,10 +1311,14 @@ int tcp_rpcs_compact_parse_execute (connection_job_t C) {
assert (c->crypto);
struct aes_crypto *T = c->crypto;
- T->read_aeskey.type->ctr128_crypt (&T->read_aeskey, random_header, random_header, 64, T->read_iv, T->read_ebuf, &T->read_num);
+ evp_crypt (T->read_aeskey, random_header, random_header, 64);
unsigned tag = *(unsigned *)(random_header + 56);
if (tag == 0xdddddddd || ((tag == 0xeeeeeeee || tag == 0xefefefef) && !ext_rand_pad_only)) {
+ if (tag != 0xdddddddd && allow_only_tls) {
+ vkprintf (1, "Expected random padding mode\n");
+ RETURN_TLS_ERROR(default_domain_info);
+ }
assert (rwm_skip_data (&c->in, 64) == 64);
rwm_union (&c->in_u, &c->in);
rwm_init (&c->in, 0);
@@ -344,7 +1434,7 @@ int tcp_rpcs_compact_parse_execute (connection_job_t C) {
}
if (verbosity > 2) {
- fprintf (stderr, "received packet from connection %d (length %d, num %d, type %08x)\n", c->fd, packet_len, D->in_packet_num, packet_type);
+ kprintf ("received packet from connection %d (length %d, num %d, type %08x)\n", c->fd, packet_len, D->in_packet_num, packet_type);
rwm_dump (&msg);
}
@@ -364,6 +1454,7 @@ int tcp_rpcs_compact_parse_execute (connection_job_t C) {
D->in_packet_num++;
}
return NEED_MORE_BYTES;
+#undef RETURN_TLS_ERROR
}
/*
diff --git a/net/net-tcp-rpc-ext-server.h b/net/net-tcp-rpc-ext-server.h
index 2ab98ab..2f9f910 100644
--- a/net/net-tcp-rpc-ext-server.h
+++ b/net/net-tcp-rpc-ext-server.h
@@ -26,8 +26,12 @@
#include "net/net-connections.h"
extern conn_type_t ct_tcp_rpc_ext_server;
-// extern struct tcp_rpc_server_functions default_tcp_rpc_server;
int tcp_rpcs_compact_parse_execute (connection_job_t c);
+
void tcp_rpcs_set_ext_secret(unsigned char secret[16]);
void tcp_rpcs_set_ext_rand_pad_only(int set);
+
+void tcp_rpc_add_proxy_domain (const char *domain);
+
+void tcp_rpc_init_proxy_domains();
diff --git a/net/net-tcp-rpc-server.c b/net/net-tcp-rpc-server.c
index 167deab..ba1f14f 100644
--- a/net/net-tcp-rpc-server.c
+++ b/net/net-tcp-rpc-server.c
@@ -547,9 +547,6 @@ int tcp_rpcs_init_fake_crypto (connection_job_t c) {
return 1;
}
-#include "net/net-crypto-aes.h"
-#include "net/net-config.h"
-
int tcp_rpcs_default_check_perm (connection_job_t C) {
return RPCF_ALLOW_ENC | RPCF_REQ_DH | tcp_get_default_rpc_flags();
}