diff --git a/tunnels/adapters/connector/tcp/tcp_connector.c b/tunnels/adapters/connector/tcp/tcp_connector.c index d4430872..96c0a4c1 100644 --- a/tunnels/adapters/connector/tcp/tcp_connector.c +++ b/tunnels/adapters/connector/tcp/tcp_connector.c @@ -1,11 +1,13 @@ #include "tcp_connector.h" #include "basic_types.h" +#include "frand.h" #include "hsocket.h" #include "loggers/network_logger.h" #include "sync_dns.h" #include "tunnel.h" #include "types.h" #include "utils/jsonutils.h" +#include "utils/mathutils.h" #include "utils/sockutils.h" static void cleanup(tcp_connector_con_state_t *cstate, bool write_queue) @@ -242,19 +244,62 @@ static void upStream(tunnel_t *self, context_t *c) break; } - // sockaddr_set_ipport(&(dest_ctx.addr), "127.0.0.1", 443); - // LOGD("TcpConnector: initiating connection"); - - if (dest_ctx->address_type == kSatDomainName && ! dest_ctx->domain_resolved) + switch (dest_ctx->address_type) { - if (! resolveContextSync(dest_ctx)) + case kSatDomainName: + if (! dest_ctx->domain_resolved) + { + if (! resolveContextSync(dest_ctx)) + { + CSTATE_DROP(c); + cleanup(cstate, false); + goto fail; + } + } + break; + + case kSatIPV4: + if (state->outbound_ip_range > 0) + { + unsigned int seed = fastRand(); + // no probelm if overflows +#ifdef OS_UNIX + const uint32_t large_random = 1 + (((uint32_t) rand_r(&seed)) % state->outbound_ip_range); +#else + const uint32_t large_random = 1 + (((uint32_t) rand_s(&seed)) % state->outbound_ip_range); +#endif + uint32_t calc = htonl(ntohl((uint32_t) dest_ctx->address.sin.sin_addr.s_addr) + large_random); + memcpy(&(dest_ctx->address.sin.sin_addr), &calc, sizeof(struct in_addr)); + } + break; + case kSatIPV6: + + if (state->outbound_ip_range > 0) { - CSTATE_DROP(c); - cleanup(cstate, false); - goto fail; + unsigned int seed = fastRand(); + // no probelm if overflows +#ifdef OS_UNIX + const uint64_t large_random = 1 + (((uint64_t) rand_r(&seed)) % state->outbound_ip_range); +#else + const uint64_t large_random = 1 + (((uint64_t) rand_s(&seed)) % state->outbound_ip_range); +#endif + uint64_t *addr_ptr = (uint64_t *) &dest_ctx->address.sin6.sin6_addr; + addr_ptr += 64 / (sizeof(uint64_t)); + uint64_t calc = htonll(ntohll(*addr_ptr) + large_random); + memcpy(&(dest_ctx->address.sin.sin_addr), &calc, sizeof(struct in_addr)); } + + break; + + default: + LOGE("TcpConnector: invalid destination address type"); + CSTATE_DROP(c); + cleanup(cstate, false); + goto fail; } + // sockaddr_set_ipport(&(dest_ctx.addr), "127.0.0.1", 443); + hloop_t *loop = loops[c->line->tid]; int sockfd = socket(dest_ctx->address.sa.sa_family, SOCK_STREAM, 0); if (sockfd < 0) @@ -270,7 +315,6 @@ static void upStream(tunnel_t *self, context_t *c) tcp_nodelay(sockfd, 1); } - if (state->tcp_fast_open) { const int yes = 1; @@ -375,7 +419,90 @@ tunnel_t *newTcpConnector(node_instance_context_t *instance_info) } if (state->dest_addr_selected.status == kDvsConstant) { - state->constant_dest_addr.address_type = getHostAddrType(state->dest_addr_selected.value_ptr); + char *slash = strchr(state->dest_addr_selected.value_ptr, '/'); + if (slash != NULL) + { + *slash = '\0'; + int prefix_length = atoi(slash + 1); + state->constant_dest_addr.address_type = getHostAddrType(state->dest_addr_selected.value_ptr); + + if (0 > prefix_length || prefix_length > 64) // 64-bits are the maximum + { + LOGF("TcpConnector: outbound ip/subnet range is invalid"); + exit(1); + } + + if (state->constant_dest_addr.address_type != kSatIPV6) + { + if (prefix_length > 32) + { + LOGF("TcpConnector: outbound ip/subnet range is invalid"); + exit(1); + } + + if (prefix_length > 0) + { + state->outbound_ip_range = htonl(0xFFFFFFFF & (0x1 << (32 - prefix_length))); + state->outbound_ip_range -= 1; + } + else + { + state->outbound_ip_range = 0; + } + state->outbound_ip_range = 0xFFFFFFFF & (0xFFFFFFFF << (32 - prefix_length)); + + uint32_t mask; + if (prefix_length > 0) + { + mask = htonl(0xFFFFFFFF & (0xFFFFFFFF << (32 - prefix_length))); + } + else + { + mask = 0; + } + uint32_t calc = ((uint32_t) state->constant_dest_addr.address.sin.sin_addr.s_addr) & mask; + memcpy(&(state->constant_dest_addr.address.sin.sin_addr), &calc, sizeof(struct in_addr)); + } + else + { + if (64 > prefix_length) // limit to 64 + { + LOGF("TcpConnector: outbound ip/subnet range is invalid"); + exit(1); + } + + if (prefix_length > 0) + { + if (prefix_length == 64) + { + state->outbound_ip_range = 0xFFFFFFFFFFFFFFFFULL; + } + else + { + state->outbound_ip_range = htonl(0xFFFFFFFFFFFFFFFFULL & (0x1ULL << (128 - prefix_length))); + state->outbound_ip_range -= 1; + } + } + else + { + state->outbound_ip_range = 0; + } + + uint8_t *addr_ptr = (uint8_t *) &(state->constant_dest_addr.address.sin6.sin6_addr); + + for (int i = 0; i < 16; i++) + { + int bits = prefix_length >= 8 ? 8 : prefix_length; + addr_ptr[i] = bits == 0 ? 0 : addr_ptr[i] & (0xFF << (8 - bits)); + prefix_length -= bits; + } + } + } + else + { + state->constant_dest_addr.address_type = getHostAddrType(state->dest_addr_selected.value_ptr); + } + if (state->constant_dest_addr.address_type == kSatDomainName) { socketContextDomainSetConstMem(&(state->constant_dest_addr), state->dest_addr_selected.value_ptr, @@ -383,6 +510,7 @@ tunnel_t *newTcpConnector(node_instance_context_t *instance_info) } else { + sockaddr_set_ip(&(state->constant_dest_addr.address), state->dest_addr_selected.value_ptr); } } diff --git a/tunnels/adapters/connector/tcp/types.h b/tunnels/adapters/connector/tcp/types.h index 1f17ee61..15a41a0c 100644 --- a/tunnels/adapters/connector/tcp/types.h +++ b/tunnels/adapters/connector/tcp/types.h @@ -17,11 +17,12 @@ typedef struct tcp_connector_state_s // settings bool tcp_no_delay; bool tcp_fast_open; - bool reuse_addr; + bool reuse_addr; /* 8-bit pad*/ int domain_strategy; dynamic_value_t dest_addr_selected; dynamic_value_t dest_port_selected; socket_context_t constant_dest_addr; + uint64_t outbound_ip_range; } tcp_connector_state_t; diff --git a/ww/managers/socket_manager.c b/ww/managers/socket_manager.c index b29317b2..234c9819 100644 --- a/ww/managers/socket_manager.c +++ b/ww/managers/socket_manager.c @@ -280,21 +280,13 @@ int parseIPWithSubnetMask(struct in6_addr *base_addr, const char *input, struct return -1; } uint32_t mask; - if (sizeof(unsigned long long) == 8) + if (prefix_length > 0) { - mask = htonl(0xFFFFFFFF & - ((unsigned long long) 0x00000000FFFFFFFF << (unsigned long long) (32 - prefix_length))); + mask = htonl(0xFFFFFFFF & (0xFFFFFFFF << (32 - prefix_length))); } else { - if (prefix_length > 0) - { - mask = htonl(0xFFFFFFFF & (0xFFFFFFFF << (32 - prefix_length))); - } - else - { - mask = 0; - } + mask = 0; } struct in_addr mask_addr = {.s_addr = mask}; memcpy(subnet_mask, &mask_addr, 4); @@ -309,8 +301,8 @@ int parseIPWithSubnetMask(struct in6_addr *base_addr, const char *input, struct fprintf(stderr, "Invalid subnet mask length.\n"); return -1; } - int i; - for (i = 0; i < 16; i++) + + for (int i = 0; i < 16; i++) { int bits = prefix_length >= 8 ? 8 : prefix_length; ((uint8_t *) subnet_mask)[i] = bits == 0 ? 0 : (0xFF << (8 - bits)); diff --git a/ww/utils/mathutils.h b/ww/utils/mathutils.h index 59061cf0..dc47706e 100644 --- a/ww/utils/mathutils.h +++ b/ww/utils/mathutils.h @@ -22,3 +22,11 @@ static inline ssize_t max(ssize_t x, ssize_t y) #define SMAXOF(t) (((0x1ULL << ((sizeof(t) * 8ULL) - 1ULL)) - 1ULL) | (0x7ULL << ((sizeof(t) * 8ULL) - 4ULL))) #define MAXOF(t) ((unsigned long long) (ISSIGNED(t) ? SMAXOF(t) : UMAXOF(t))) + +#if __BIG_ENDIAN__ +#define htonll(x) (x) +#define ntohll(x) (x) +#else +#define htonll(x) (((uint64_t) htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32)) // NOLINT +#define ntohll(x) (((uint64_t) ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32)) // NOLINT +#endif