mirror of
https://github.com/EndPositive/slipstream.git
synced 2025-10-08 12:25:04 +00:00
cli
This commit is contained in:
parent
163a02dbd4
commit
cb4a29a239
12 changed files with 518 additions and 218 deletions
|
|
@ -1,6 +1,5 @@
|
||||||
*
|
*
|
||||||
!certs/
|
!certs/
|
||||||
!src/
|
|
||||||
!extern/
|
!extern/
|
||||||
!include/
|
!include/
|
||||||
!src/
|
!src/
|
||||||
|
|
|
||||||
|
|
@ -20,13 +20,9 @@ if(NOT CMAKE_BUILD_TYPE)
|
||||||
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE)
|
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build." FORCE)
|
||||||
endif()
|
endif()
|
||||||
if(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
|
if(${CMAKE_BUILD_TYPE} STREQUAL "Debug")
|
||||||
list(APPEND PICOQUIC_ADDITIONAL_C_FLAGS -Og)
|
|
||||||
list(APPEND PICOQUIC_ADDITIONAL_CXX_FLAGS -Og)
|
|
||||||
list(APPEND PICOQUIC_COMPILE_DEFINITIONS BUILD_LOGLIB)
|
list(APPEND PICOQUIC_COMPILE_DEFINITIONS BUILD_LOGLIB)
|
||||||
set(BUILD_LOGLIB ON)
|
set(BUILD_LOGLIB ON)
|
||||||
else()
|
else()
|
||||||
list(APPEND PICOQUIC_ADDITIONAL_C_FLAGS -O3)
|
|
||||||
list(APPEND PICOQUIC_ADDITIONAL_CXX_FLAGS -O3)
|
|
||||||
set(BUILD_LOGLIB OFF)
|
set(BUILD_LOGLIB OFF)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
@ -35,18 +31,13 @@ if(POLICY CMP0048)
|
||||||
endif()
|
endif()
|
||||||
add_subdirectory(extern/picoquic)
|
add_subdirectory(extern/picoquic)
|
||||||
|
|
||||||
add_executable(slipstream
|
# Common source files for both client and server
|
||||||
src/slipstream.c
|
set(COMMON_SOURCES
|
||||||
src/slipstream_client.c
|
|
||||||
src/slipstream_inline_dots.c
|
src/slipstream_inline_dots.c
|
||||||
src/slipstream_resolver_addresses.c
|
|
||||||
src/slipstream_server.c
|
|
||||||
src/slipstream_server_cc.c
|
|
||||||
src/slipstream_sockloop.c
|
src/slipstream_sockloop.c
|
||||||
src/slipstream_utils.c
|
src/slipstream_utils.c
|
||||||
include/slipstream.h
|
include/slipstream.h
|
||||||
include/slipstream_inline_dots.h
|
include/slipstream_inline_dots.h
|
||||||
include/slipstream_resolver_addresses.h
|
|
||||||
include/slipstream_server_cc.h
|
include/slipstream_server_cc.h
|
||||||
include/slipstream_slot.h
|
include/slipstream_slot.h
|
||||||
include/slipstream_sockloop.h
|
include/slipstream_sockloop.h
|
||||||
|
|
@ -66,13 +57,34 @@ add_executable(slipstream
|
||||||
extern/SPCDNS/src/output.c
|
extern/SPCDNS/src/output.c
|
||||||
extern/SPCDNS/src/output.h
|
extern/SPCDNS/src/output.h
|
||||||
)
|
)
|
||||||
target_link_libraries(slipstream PRIVATE m)
|
|
||||||
target_link_libraries(slipstream PRIVATE picoquic-core)
|
# Client binary
|
||||||
|
add_executable(slipstream-client
|
||||||
|
src/slipstream_client_cli.c
|
||||||
|
src/slipstream_client.c
|
||||||
|
${COMMON_SOURCES}
|
||||||
|
)
|
||||||
|
target_link_libraries(slipstream-client PRIVATE m)
|
||||||
|
target_link_libraries(slipstream-client PRIVATE picoquic-core)
|
||||||
if (BUILD_LOGLIB)
|
if (BUILD_LOGLIB)
|
||||||
target_link_libraries(slipstream PRIVATE picoquic-log)
|
target_link_libraries(slipstream-client PRIVATE picoquic-log)
|
||||||
endif ()
|
endif ()
|
||||||
|
target_include_directories(slipstream-client PRIVATE include)
|
||||||
|
target_include_directories(slipstream-client PRIVATE extern)
|
||||||
|
set_picoquic_compile_settings(slipstream-client)
|
||||||
|
|
||||||
target_include_directories(slipstream PRIVATE include)
|
# Server binary
|
||||||
target_include_directories(slipstream PRIVATE extern)
|
add_executable(slipstream-server
|
||||||
|
src/slipstream_server_cli.c
|
||||||
set_picoquic_compile_settings(slipstream)
|
src/slipstream_server.c
|
||||||
|
src/slipstream_server_cc.c
|
||||||
|
${COMMON_SOURCES}
|
||||||
|
)
|
||||||
|
target_link_libraries(slipstream-server PRIVATE m)
|
||||||
|
target_link_libraries(slipstream-server PRIVATE picoquic-core)
|
||||||
|
if (BUILD_LOGLIB)
|
||||||
|
target_link_libraries(slipstream-server PRIVATE picoquic-log)
|
||||||
|
endif ()
|
||||||
|
target_include_directories(slipstream-server PRIVATE include)
|
||||||
|
target_include_directories(slipstream-server PRIVATE extern)
|
||||||
|
set_picoquic_compile_settings(slipstream-server)
|
||||||
|
|
|
||||||
24
Dockerfile
24
Dockerfile
|
|
@ -1,4 +1,4 @@
|
||||||
FROM debian:bookworm-slim AS development
|
FROM debian:bookworm-slim AS builder
|
||||||
|
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
|
|
@ -12,8 +12,6 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||||
ninja-build \
|
ninja-build \
|
||||||
clang
|
clang
|
||||||
|
|
||||||
FROM development AS builder
|
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN --mount=type=cache,target=/usr/src/app/cmake-build-release \
|
RUN --mount=type=cache,target=/usr/src/app/cmake-build-release \
|
||||||
|
|
@ -27,22 +25,20 @@ RUN --mount=type=cache,target=/usr/src/app/cmake-build-release \
|
||||||
-B /usr/src/app/cmake-build-release && \
|
-B /usr/src/app/cmake-build-release && \
|
||||||
cmake \
|
cmake \
|
||||||
--build /usr/src/app/cmake-build-release \
|
--build /usr/src/app/cmake-build-release \
|
||||||
--target slipstream \
|
--target slipstream-client slipstream-server \
|
||||||
-j 18 && cp cmake-build-release/slipstream .
|
-j 18 && \
|
||||||
|
cp cmake-build-release/slipstream-client . && \
|
||||||
|
cp cmake-build-release/slipstream-server .
|
||||||
|
|
||||||
FROM debian:bookworm-slim
|
FROM gcr.io/distroless/base-debian12
|
||||||
|
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
|
|
||||||
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
|
||||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
|
||||||
apt-get update && apt-get install -y \
|
|
||||||
libssl3
|
|
||||||
|
|
||||||
COPY ./certs/ ./certs/
|
COPY ./certs/ ./certs/
|
||||||
|
|
||||||
RUN mkdir -p ./qlog/
|
ENV PATH=/usr/src/app/:$PATH
|
||||||
|
|
||||||
COPY --from=builder /usr/src/app/slipstream .
|
COPY --from=builder --chmod=755 /usr/src/app/slipstream-client ./client
|
||||||
|
COPY --from=builder --chmod=755 /usr/src/app/slipstream-server ./server
|
||||||
|
|
||||||
ENTRYPOINT ["/usr/src/app/slipstream"]
|
ENTRYPOINT []
|
||||||
|
|
|
||||||
126
README.md
126
README.md
|
|
@ -1 +1,127 @@
|
||||||
# Slipstream
|
# Slipstream
|
||||||
|
|
||||||
|
A high-performance covert channel over DNS, powered by QUIC multipath.
|
||||||
|
|
||||||
|
## Highlights
|
||||||
|
|
||||||
|
* Adaptive congestion control for rate-limited resolvers
|
||||||
|
* Parallel routing over multiple multiple rate-limited resolvers
|
||||||
|
* 60% lower header overhead than DNSTT
|
||||||
|
|
||||||
|
## Quickstart
|
||||||
|
|
||||||
|
Download a release binary from GitHub.
|
||||||
|
The binary contains both the client and the server.
|
||||||
|
|
||||||
|
The server listens for DNS messages and attempts to decode QUIC message from them.
|
||||||
|
Any new QUIC streams opened will be forwarded to a specified TCP service.
|
||||||
|
For example, we can start a simple nc listener and configure the slipstream server to connect to it.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ nc -l -p 5201
|
||||||
|
$ slipstream-server \
|
||||||
|
--dns-listen-port=8853 \
|
||||||
|
--cert=certs/cert.pem \
|
||||||
|
--key=certs/key.pem \
|
||||||
|
--target=127.0.0.1:5201 \
|
||||||
|
--domain=test.com
|
||||||
|
```
|
||||||
|
|
||||||
|
The client listens on a TCP port for incoming connections.
|
||||||
|
It opens a QUIC connection through the resolver specified.
|
||||||
|
For every TCP connection it accepts, a new QUIC stream will be opened.
|
||||||
|
In this example, we connect to the slipstream server running on port 8853.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ echo "127.0.0.1 8853" > resolvers.txt
|
||||||
|
$ slipstream-client \
|
||||||
|
--tcp-listen-port=7000 \
|
||||||
|
--resolver=127.0.0.1:8853 \
|
||||||
|
--domain=test.com
|
||||||
|
Adding 127.0.0.1:8853
|
||||||
|
Starting connection to 127.0.0.1
|
||||||
|
Initial connection ID: 54545454
|
||||||
|
Listening on port 7000...
|
||||||
|
Connection completed, almost ready.
|
||||||
|
Connection confirmed.
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then connect to the slipstream client on port 7000 as if you were connecting to the nc client on port 5201.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ base64 /dev/urandom | head -c 5000000 | nc 127.0.0.1 7000
|
||||||
|
|
||||||
|
# slipstream client wakes up
|
||||||
|
[0:9] accept: connection
|
||||||
|
[0:9] wakeup
|
||||||
|
[0:9] activate: stream
|
||||||
|
[0:9] recv->quic_send: empty, disactivate
|
||||||
|
[0:9] wakeup
|
||||||
|
[0:9] activate: stream
|
||||||
|
[0:9] recv->quic_send: empty, disactivate
|
||||||
|
[0:9] wakeup
|
||||||
|
[0:9] activate: stream
|
||||||
|
[0:9] recv->quic_send: empty, disactivate
|
||||||
|
[0:9] recv: closed stream
|
||||||
|
|
||||||
|
# base64 data arrives on the server
|
||||||
|
S9w3u5up+c39u6vrkBtxKbSxOJA2UElczDgc3x4h3TtZtzvgMX05Ig4whEYDvY5MP8g4dJ1QsXX1
|
||||||
|
fSDm0y6mOlQ4fQhYchkyKt18fV0tpBkLrPwv6MkW+IaksKe7Qo61s3gxu2jrPBlC1yxML+rYZU93
|
||||||
|
MYNB7rFC6s3a0eHmfdsfbtBbFIF809X91fqd6gYiKPtWAHc0J5OsEyqMI3QcUGSDJd4Sw+iAC5X7
|
||||||
|
```
|
||||||
|
|
||||||
|
## Real network scenario
|
||||||
|
|
||||||
|
You can try this out on a real network (if you have permission).
|
||||||
|
First, you need to have a server outside of the network you want to escape.
|
||||||
|
For a domain name you own, setup the DNS records to point to your nameserver.
|
||||||
|
This ensures that queries for subdomains of `test.com` will be forwarded to your server.
|
||||||
|
|
||||||
|
```
|
||||||
|
test.com NS ns.test.com
|
||||||
|
ns.test.com A 12.23.34.45
|
||||||
|
```
|
||||||
|
|
||||||
|
Then run the slipstream server on port 53 (requires elevated privileges) and instruct the client to use a real DNS resolver.
|
||||||
|
|
||||||
|
# Benchmarks
|
||||||
|
|
||||||
|
Comparison of slipstream and other existing DNS tunneling tools can be found in the [EndPositive/dns-tunneling-benchmark]([https://github.com/EndPositive/dns-tunneling-benchmark]) repository.
|
||||||
|
|
||||||
|
Main findings:
|
||||||
|
|
||||||
|
* 42x faster than dnstt for direct connections
|
||||||
|
* 23/19 Mbps upload/download speed for direction connections
|
||||||
|
* automatically maximizes query rate according to resolver rate-limit
|
||||||
|
|
||||||
|
# Building from source
|
||||||
|
|
||||||
|
```shell
|
||||||
|
# build deps on debian: cmake, pkg-config, libssl-dev, ninja-build, clang
|
||||||
|
$ git clone --recurse-submodules https://github.com/EndPositive/slipstream.git
|
||||||
|
$ cd slipstream/
|
||||||
|
$ cmake \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DCMAKE_MAKE_PROGRAM=ninja \
|
||||||
|
-DCMAKE_C_COMPILER=clang \
|
||||||
|
-DCMAKE_CXX_COMPILER=clang++ \
|
||||||
|
-G Ninja \
|
||||||
|
-S . \
|
||||||
|
-B ./build
|
||||||
|
$ cmake \
|
||||||
|
--build ./build \
|
||||||
|
--target slipstream \
|
||||||
|
-j 18
|
||||||
|
# mark as executable and install to your system
|
||||||
|
$ chmod +x ./build/slipstream
|
||||||
|
$ mv ./build/slipstream ~/.local/bin
|
||||||
|
```
|
||||||
|
|
||||||
|
# Acknowledgements
|
||||||
|
|
||||||
|
David Fifield's DNSTT and Turbo Tunnel concept has been a massive source of inspiration.
|
||||||
|
Although slipstream inherits no code, this work could not have been possible without his ideas.
|
||||||
|
|
||||||
|
# License
|
||||||
|
|
||||||
|
This work is licensed under the Apache License, Version 2.0.
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,16 @@ extern "C" {
|
||||||
#define SLIPSTREAM_QLOG_DIR "./qlog";
|
#define SLIPSTREAM_QLOG_DIR "./qlog";
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
typedef struct st_address_t {
|
||||||
|
struct sockaddr_storage server_address;
|
||||||
|
bool added;
|
||||||
|
} address_t;
|
||||||
|
|
||||||
int picoquic_slipstream_client(int listen_port, char const* resolver_addresses_filename, const char* domain_name,
|
int picoquic_slipstream_client(int listen_port, struct st_address_t* server_addresses, size_t server_address_count, const char* domain_name,
|
||||||
const char* cc_algo_id, bool gso);
|
const char* cc_algo_id, bool gso);
|
||||||
|
|
||||||
int picoquic_slipstream_server(int server_port, const char* pem_cert, const char* pem_key, char const* upstream_name,
|
int picoquic_slipstream_server(int server_port, const char* pem_cert, const char* pem_key,
|
||||||
int upstream_port, const char* domain_name);
|
struct sockaddr_storage* target_address, const char* domain_name);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
#ifndef SLIPSTREAM_RESOLVERS_H
|
|
||||||
#define SLIPSTREAM_RESOLVERS_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
|
|
||||||
typedef struct st_address_t {
|
|
||||||
struct sockaddr_storage server_address;
|
|
||||||
bool added;
|
|
||||||
} address_t;
|
|
||||||
|
|
||||||
struct st_address_t* read_resolver_addresses(const char *resolver_addresses_filename, size_t *count);
|
|
||||||
|
|
||||||
#endif //SLIPSTREAM_RESOLVERS_H
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "slipstream.h"
|
|
||||||
|
|
||||||
static void usage(char const * sample_name)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Usage:\n");
|
|
||||||
fprintf(stderr, " %s client listen_port slipstream_server_name slipstream_server_port domain_name\n", sample_name);
|
|
||||||
fprintf(stderr, " %s server listen_port cert key target_server_name target_server_port domain_name\n", sample_name);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int get_port(char const* sample_name, char const* port_arg)
|
|
||||||
{
|
|
||||||
int server_port = atoi(port_arg);
|
|
||||||
if (server_port <= 0) {
|
|
||||||
fprintf(stderr, "Invalid port: %s\n", port_arg);
|
|
||||||
usage(sample_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return server_port;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
|
||||||
{
|
|
||||||
int exit_code = 0;
|
|
||||||
#ifdef _WINDOWS
|
|
||||||
WSADATA wsaData = { 0 };
|
|
||||||
(void)WSA_START(MAKEWORD(2, 2), &wsaData);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
setbuf(stdout, NULL);
|
|
||||||
setbuf(stderr, NULL);
|
|
||||||
if (argc < 2) {
|
|
||||||
usage(argv[0]);
|
|
||||||
}
|
|
||||||
else if (strcmp(argv[1], "client") == 0) {
|
|
||||||
if (argc != 7) {
|
|
||||||
usage(argv[0]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
int local_port = atoi(argv[2]);
|
|
||||||
char const* resolver_addresses_filename = argv[3];
|
|
||||||
const char* domain_name = argv[4];
|
|
||||||
const char* cc_algo_id = argv[5];
|
|
||||||
bool gso = strcmp(argv[6], "true") == 0;
|
|
||||||
exit_code = picoquic_slipstream_client(local_port, resolver_addresses_filename, domain_name, cc_algo_id, gso);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (strcmp(argv[1], "server") == 0) {
|
|
||||||
if (argc != 8) {
|
|
||||||
usage(argv[0]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
int server_port = get_port(argv[0], argv[2]);
|
|
||||||
int remote_port = get_port(argv[0], argv[6]);
|
|
||||||
const char* domain_name = argv[7];
|
|
||||||
exit_code = picoquic_slipstream_server(server_port, argv[3], argv[4], argv[5], remote_port, domain_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
usage(argv[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
exit(exit_code);
|
|
||||||
}
|
|
||||||
|
|
@ -21,11 +21,11 @@
|
||||||
#include "picoquic_config.h"
|
#include "picoquic_config.h"
|
||||||
#include "slipstream.h"
|
#include "slipstream.h"
|
||||||
#include "slipstream_inline_dots.h"
|
#include "slipstream_inline_dots.h"
|
||||||
#include "slipstream_resolver_addresses.h"
|
|
||||||
#include "slipstream_utils.h"
|
#include "slipstream_utils.h"
|
||||||
#include "SPCDNS/src/dns.h"
|
#include "SPCDNS/src/dns.h"
|
||||||
#include "SPCDNS/src/mappings.h"
|
#include "SPCDNS/src/mappings.h"
|
||||||
|
|
||||||
|
|
||||||
typedef struct st_slipstream_client_stream_ctx_t {
|
typedef struct st_slipstream_client_stream_ctx_t {
|
||||||
struct st_slipstream_client_stream_ctx_t* next_stream;
|
struct st_slipstream_client_stream_ctx_t* next_stream;
|
||||||
struct st_slipstream_client_stream_ctx_t* previous_stream;
|
struct st_slipstream_client_stream_ctx_t* previous_stream;
|
||||||
|
|
@ -683,7 +683,7 @@ static int slipstream_connect(struct sockaddr_storage* server_address,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int picoquic_slipstream_client(int listen_port, char const* resolver_addresses_filename, const char* domain_name, const char* cc_algo_id, bool gso) {
|
int picoquic_slipstream_client(int listen_port, struct st_address_t* server_addresses, size_t server_address_count, const char* domain_name, const char* cc_algo_id, bool gso) {
|
||||||
/* Start: start the QUIC process */
|
/* Start: start the QUIC process */
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
uint64_t current_time = 0;
|
uint64_t current_time = 0;
|
||||||
|
|
@ -691,9 +691,9 @@ int picoquic_slipstream_client(int listen_port, char const* resolver_addresses_f
|
||||||
client_domain_name = strdup(domain_name);
|
client_domain_name = strdup(domain_name);
|
||||||
client_domain_name_len = strlen(domain_name);
|
client_domain_name_len = strlen(domain_name);
|
||||||
|
|
||||||
// int mtu = 1200;
|
double mtu_d = 240 - (double) client_domain_name_len;
|
||||||
// int mtu = 129;
|
mtu_d = mtu_d / 1.6;
|
||||||
int mtu = 145;
|
int mtu = (int) mtu_d;
|
||||||
|
|
||||||
/* Create config */
|
/* Create config */
|
||||||
picoquic_quic_config_t config;
|
picoquic_quic_config_t config;
|
||||||
|
|
@ -736,12 +736,9 @@ int picoquic_slipstream_client(int listen_port, char const* resolver_addresses_f
|
||||||
// picoquic_set_log_level(quic, 1);
|
// picoquic_set_log_level(quic, 1);
|
||||||
// TODO: idle timeout?
|
// TODO: idle timeout?
|
||||||
|
|
||||||
/* Read the server address list from the file */
|
/* Parse the server addresses directly */
|
||||||
client_ctx.server_addresses = read_resolver_addresses(resolver_addresses_filename, &client_ctx.server_address_count);
|
client_ctx.server_addresses = server_addresses;
|
||||||
if (!client_ctx.server_addresses) {
|
client_ctx.server_address_count = server_address_count;
|
||||||
printf("Failed to read IP addresses\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
picoquic_cnx_t* cnx = NULL;
|
picoquic_cnx_t* cnx = NULL;
|
||||||
ret = slipstream_connect(&client_ctx.server_addresses[0].server_address, quic, &cnx, &client_ctx);
|
ret = slipstream_connect(&client_ctx.server_addresses[0].server_address, quic, &cnx, &client_ctx);
|
||||||
|
|
|
||||||
179
src/slipstream_client_cli.c
Normal file
179
src/slipstream_client_cli.c
Normal file
|
|
@ -0,0 +1,179 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <argp.h>
|
||||||
|
#include <picosocks.h>
|
||||||
|
#include "slipstream.h"
|
||||||
|
|
||||||
|
const char* argp_program_version = "slipstream-client 0.1";
|
||||||
|
const char* argp_program_bug_address = "github.com/EndPositive/slipstream";
|
||||||
|
|
||||||
|
/* Program documentation */
|
||||||
|
static char doc[] = "slipstream-client - A high-performance covert channel over DNS (client)\v";
|
||||||
|
|
||||||
|
/* A description of the arguments we accept. */
|
||||||
|
static char args_doc[] = "";
|
||||||
|
|
||||||
|
/* Client mode options */
|
||||||
|
static struct argp_option options[] = {
|
||||||
|
{"tcp-listen-port", 'l', "PORT", 0, "Listen port (default: 5201)", 0},
|
||||||
|
{"resolver", 'r', "RESOLVER", 0, "Slipstream server resolver address (e.g., 1.1.1.1 or 8.8.8.8:53). Can be specified multiple times. (Required)", 0},
|
||||||
|
{"congestion-control", 'c', "ALGO", 0, "Congestion control algorithm (bbr, dcubic) (default: dcubic)", 0},
|
||||||
|
{"gso", 'g', "BOOL", OPTION_ARG_OPTIONAL, "GSO enabled (true/false) (default: false). Use --gso or --gso=true to enable.", 0},
|
||||||
|
{"domain", 'd', "DOMAIN", 0, "Domain name used for the covert channel (Required)", 0},
|
||||||
|
{0} // End of options
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Used by main to communicate with parse options. */
|
||||||
|
struct arguments {
|
||||||
|
int listen_port;
|
||||||
|
char* domain_name;
|
||||||
|
struct st_address_t* resolver_addresses;
|
||||||
|
size_t resolver_count;
|
||||||
|
char* cc_algo_id;
|
||||||
|
bool gso;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Client mode parser */
|
||||||
|
static error_t parse_opt(int key, char* arg, struct argp_state* state) {
|
||||||
|
struct arguments* arguments = state->input;
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case 'd':
|
||||||
|
arguments->domain_name = arg;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
arguments->listen_port = atoi(arg);
|
||||||
|
if (arguments->listen_port <= 0 || arguments->listen_port > 65535) {
|
||||||
|
argp_error(state, "Invalid TCP listen port number: %s", arg);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
{
|
||||||
|
// Allocate or reallocate the resolver addresses array
|
||||||
|
struct st_address_t* new_resolvers = realloc(arguments->resolver_addresses,
|
||||||
|
(arguments->resolver_count + 1) * sizeof(struct st_address_t));
|
||||||
|
if (!new_resolvers) {
|
||||||
|
argp_error(state, "Memory allocation failed for resolver address");
|
||||||
|
return ARGP_ERR_UNKNOWN; // Signal error
|
||||||
|
}
|
||||||
|
arguments->resolver_addresses = new_resolvers;
|
||||||
|
|
||||||
|
char server_name[256]; // Increased size for FQDNs
|
||||||
|
int server_port = 53; // Default DNS port
|
||||||
|
|
||||||
|
// Try parsing with port first, then without
|
||||||
|
if (sscanf(arg, "%255[^:]:%d", server_name, &server_port) < 1) {
|
||||||
|
// If sscanf fails, maybe it's just an IP/hostname without port?
|
||||||
|
strncpy(server_name, arg, sizeof(server_name) -1);
|
||||||
|
server_name[sizeof(server_name)-1] = '\0'; // Ensure null termination
|
||||||
|
// Keep default port 53
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server_port <= 0 || server_port > 65535) {
|
||||||
|
argp_error(state, "Invalid port number in resolver address: %s", arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_name = 0; // We don't use this flag downstream currently
|
||||||
|
if (picoquic_get_server_address(server_name, server_port, &arguments->resolver_addresses[arguments->resolver_count].server_address, &is_name) != 0) {
|
||||||
|
argp_error(state, "Cannot resolve resolver address '%s' port %d", server_name, server_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
arguments->resolver_count++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'c':
|
||||||
|
// Consider adding validation for supported algorithms here
|
||||||
|
arguments->cc_algo_id = arg;
|
||||||
|
break;
|
||||||
|
case 'g':
|
||||||
|
// Handle optional argument for --gso
|
||||||
|
if (arg == NULL || strcmp(arg, "true") == 0) {
|
||||||
|
arguments->gso = true;
|
||||||
|
} else if (strcmp(arg, "false") == 0) {
|
||||||
|
arguments->gso = false;
|
||||||
|
} else {
|
||||||
|
argp_error(state, "Invalid boolean value for --gso: '%s'. Use 'true' or 'false'.", arg);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ARGP_KEY_ARG:
|
||||||
|
// No positional arguments expected
|
||||||
|
argp_usage(state);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return ARGP_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define the argp structure for client
|
||||||
|
static struct argp argp = {options, parse_opt, args_doc, doc, 0, 0, 0};
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
int exit_code = 0;
|
||||||
|
struct arguments arguments;
|
||||||
|
|
||||||
|
#ifdef _WINDOWS
|
||||||
|
WSADATA wsaData = { 0 };
|
||||||
|
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||||
|
if (iResult != 0) {
|
||||||
|
fprintf(stderr, "WSAStartup failed: %d\n", iResult);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Default values */
|
||||||
|
memset(&arguments, 0, sizeof(arguments));
|
||||||
|
arguments.listen_port = 5201; // Default TCP listen port
|
||||||
|
arguments.cc_algo_id = "dcubic"; // Default CC algo
|
||||||
|
arguments.gso = false; // Default GSO state
|
||||||
|
arguments.resolver_addresses = NULL;
|
||||||
|
arguments.resolver_count = 0;
|
||||||
|
|
||||||
|
// Ensure output buffers are flushed immediately (useful for debugging/logging)
|
||||||
|
setbuf(stdout, NULL);
|
||||||
|
setbuf(stderr, NULL);
|
||||||
|
|
||||||
|
// Parse command line arguments
|
||||||
|
error_t parse_err = argp_parse(&argp, argc, argv, 0, NULL, &arguments);
|
||||||
|
if (parse_err) {
|
||||||
|
// argp should have printed an error message already
|
||||||
|
exit(1); // Exit if parsing failed
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check mandatory client arguments */
|
||||||
|
bool client_args_ok = true;
|
||||||
|
if (arguments.domain_name == NULL) {
|
||||||
|
fprintf(stderr, "Client error: Missing required --domain option\n");
|
||||||
|
client_args_ok = false;
|
||||||
|
}
|
||||||
|
if (arguments.resolver_count == 0) {
|
||||||
|
fprintf(stderr, "Client error: Missing required --resolver option (at least one required)\n");
|
||||||
|
client_args_ok = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!client_args_ok) {
|
||||||
|
// Show specific client help message
|
||||||
|
argp_help(&argp, stderr, ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR, "slipstream-client");
|
||||||
|
// argp_help with ARGP_HELP_EXIT_ERR will exit, no need for exit(1) here
|
||||||
|
}
|
||||||
|
|
||||||
|
exit_code = picoquic_slipstream_client(
|
||||||
|
arguments.listen_port,
|
||||||
|
arguments.resolver_addresses,
|
||||||
|
arguments.resolver_count,
|
||||||
|
arguments.domain_name,
|
||||||
|
arguments.cc_algo_id,
|
||||||
|
arguments.gso
|
||||||
|
);
|
||||||
|
|
||||||
|
// Free allocated memory for resolver addresses
|
||||||
|
free(arguments.resolver_addresses);
|
||||||
|
|
||||||
|
#ifdef _WINDOWS
|
||||||
|
WSACleanup();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
exit(exit_code);
|
||||||
|
}
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
#include "slipstream_resolver_addresses.h"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <picosocks.h>
|
|
||||||
|
|
||||||
#define MAX_IP_LENGTH 20
|
|
||||||
#define INITIAL_CAPACITY 10
|
|
||||||
#define MAX_LINE_LENGTH 50
|
|
||||||
#define DEFAULT_PORT 53
|
|
||||||
|
|
||||||
struct st_address_t* read_resolver_addresses(const char *resolver_addresses_filename, size_t *count) {
|
|
||||||
*count = 0;
|
|
||||||
|
|
||||||
FILE *fp = fopen(resolver_addresses_filename, "r");
|
|
||||||
if (!fp) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int capacity = INITIAL_CAPACITY;
|
|
||||||
struct st_address_t* server_address = calloc(capacity, sizeof(struct st_address_t));
|
|
||||||
if (!server_address) {
|
|
||||||
fclose(fp);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
char line[MAX_LINE_LENGTH];
|
|
||||||
int valid_addresses = 0;
|
|
||||||
|
|
||||||
while (fgets(line, MAX_LINE_LENGTH, fp)) {
|
|
||||||
// Remove newline
|
|
||||||
line[strcspn(line, "\n")] = '\0';
|
|
||||||
|
|
||||||
// Skip empty or whitespace-only lines
|
|
||||||
if (strlen(line) == 0 || strspn(line, " \t") == strlen(line)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resize array if needed
|
|
||||||
if (valid_addresses == capacity) {
|
|
||||||
capacity *= 2;
|
|
||||||
struct st_address_t* temp = realloc(server_address, capacity * sizeof(struct st_address_t));
|
|
||||||
if (!temp) {
|
|
||||||
fprintf(stderr, "Memory allocation failed\n");
|
|
||||||
free(server_address);
|
|
||||||
fclose(fp);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
server_address = temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
char server_name[MAX_IP_LENGTH];
|
|
||||||
int server_port = DEFAULT_PORT;
|
|
||||||
|
|
||||||
// Parse line for IP and optional port
|
|
||||||
if (sscanf(line, "%s %d", server_name, &server_port) < 1) {
|
|
||||||
continue; // Invalid format
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Adding %s:%d\n", server_name, server_port);
|
|
||||||
|
|
||||||
int is_name = 0;
|
|
||||||
if (picoquic_get_server_address(server_name, server_port, &server_address[valid_addresses].server_address, &is_name) != 0) {
|
|
||||||
fprintf(stderr, "Cannot get the IP address for <%s> port <%d>\n", server_name, server_port);
|
|
||||||
continue; // Skip invalid addresses instead of failing
|
|
||||||
}
|
|
||||||
valid_addresses++;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(fp);
|
|
||||||
*count = valid_addresses;
|
|
||||||
|
|
||||||
// Trim excess memory if needed
|
|
||||||
if (valid_addresses < capacity) {
|
|
||||||
struct st_address_t* temp = realloc(server_address, valid_addresses * sizeof(struct st_address_t));
|
|
||||||
if (temp) {
|
|
||||||
server_address = temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return server_address;
|
|
||||||
}
|
|
||||||
|
|
@ -640,15 +640,14 @@ void server_sighandler(int signum) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int picoquic_slipstream_server(int server_port, const char* server_cert, const char* server_key,
|
int picoquic_slipstream_server(int server_port, const char* server_cert, const char* server_key,
|
||||||
char const* upstream_name, int upstream_port, const char* domain_name) {
|
struct sockaddr_storage* target_address, const char* domain_name) {
|
||||||
/* Start: start the QUIC process with cert and key files */
|
/* Start: start the QUIC process with cert and key files */
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
uint64_t current_time = 0;
|
uint64_t current_time = 0;
|
||||||
slipstream_server_ctx_t default_context = {0};
|
slipstream_server_ctx_t default_context = {0};
|
||||||
DBG_PRINTF("Starting Picoquic Sample server on port %d", server_port);
|
|
||||||
|
|
||||||
int is_name = 0;
|
// Store the target address directly - no need to resolve it here anymore
|
||||||
picoquic_get_server_address(upstream_name, upstream_port, &default_context.upstream_addr, &is_name);
|
memcpy(&default_context.upstream_addr, target_address, sizeof(struct sockaddr_storage));
|
||||||
|
|
||||||
server_domain_name = strdup(domain_name);
|
server_domain_name = strdup(domain_name);
|
||||||
server_domain_name_len = strlen(domain_name);
|
server_domain_name_len = strlen(domain_name);
|
||||||
|
|
@ -731,3 +730,4 @@ int picoquic_slipstream_server(int server_port, const char* server_cert, const c
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
154
src/slipstream_server_cli.c
Normal file
154
src/slipstream_server_cli.c
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <argp.h>
|
||||||
|
#include <picosocks.h>
|
||||||
|
#include "slipstream.h"
|
||||||
|
|
||||||
|
const char* argp_program_version = "slipstream-server 0.1";
|
||||||
|
const char* argp_program_bug_address = "github.com/EndPositive/slipstream";
|
||||||
|
|
||||||
|
/* Program documentation */
|
||||||
|
static char doc[] = "slipstream-server - A high-performance covert channel over DNS (server)\v";
|
||||||
|
|
||||||
|
/* A description of the arguments we accept. */
|
||||||
|
static char args_doc[] = "";
|
||||||
|
|
||||||
|
/* Server mode options */
|
||||||
|
static struct argp_option options[] = {
|
||||||
|
{"dns-listen-port", 'l', "PORT", 0, "DNS listen port (default: 53)", 0},
|
||||||
|
{"target-address", 'a', "ADDRESS", 0, "Target server address (default: 127.0.0.1:5201)", 0},
|
||||||
|
{"cert", 'c', "CERT", 0, "Certificate file path (default: certs/cert.pem)", 0},
|
||||||
|
{"key", 'k', "KEY", 0, "Private key file path (default: certs/key.pem)", 0},
|
||||||
|
{"domain", 'd', "DOMAIN", 0, "Domain name this server is authoritative for (Required)", 0},
|
||||||
|
{0} // End of options
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Used by main to communicate with parse options. */
|
||||||
|
struct arguments {
|
||||||
|
int listen_port;
|
||||||
|
char* domain_name;
|
||||||
|
char* cert_file;
|
||||||
|
char* key_file;
|
||||||
|
struct sockaddr_storage target_address;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Server mode parser */
|
||||||
|
static error_t parse_opt(int key, char* arg, struct argp_state* state) {
|
||||||
|
struct arguments* arguments = state->input;
|
||||||
|
|
||||||
|
switch (key) {
|
||||||
|
case 'd':
|
||||||
|
arguments->domain_name = arg;
|
||||||
|
break;
|
||||||
|
case 'l':
|
||||||
|
arguments->listen_port = atoi(arg);
|
||||||
|
if (arguments->listen_port <= 0 || arguments->listen_port > 65535) {
|
||||||
|
argp_error(state, "Invalid DNS listen port number: %s", arg);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'c':
|
||||||
|
arguments->cert_file = arg;
|
||||||
|
break;
|
||||||
|
case 'k':
|
||||||
|
arguments->key_file = arg;
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
{
|
||||||
|
char server_name[256]; // Increased size for FQDNs
|
||||||
|
int server_port = 5201; // Default upstream port
|
||||||
|
|
||||||
|
// Try parsing with port first, then without
|
||||||
|
if (sscanf(arg, "%255[^:]:%d", server_name, &server_port) < 1) {
|
||||||
|
// If sscanf fails, maybe it's just an IP/hostname without port?
|
||||||
|
strncpy(server_name, arg, sizeof(server_name) - 1);
|
||||||
|
server_name[sizeof(server_name) - 1] = '\0'; // Ensure null termination
|
||||||
|
// Keep default port 5201
|
||||||
|
}
|
||||||
|
|
||||||
|
if (server_port <= 0 || server_port > 65535) {
|
||||||
|
argp_error(state, "Invalid port number in target address: %s", arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
int is_name = 0;
|
||||||
|
if (picoquic_get_server_address(server_name, server_port, &arguments->target_address, &is_name) != 0) {
|
||||||
|
argp_error(state, "Cannot resolve target address '%s' port %d", server_name, server_port);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ARGP_KEY_ARG:
|
||||||
|
// No positional arguments expected
|
||||||
|
argp_usage(state);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return ARGP_ERR_UNKNOWN;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define the argp structure for server
|
||||||
|
static struct argp argp = {options, parse_opt, args_doc, doc, 0, 0, 0};
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
int exit_code = 0;
|
||||||
|
struct arguments arguments;
|
||||||
|
|
||||||
|
#ifdef _WINDOWS
|
||||||
|
WSADATA wsaData = { 0 };
|
||||||
|
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||||
|
if (iResult != 0) {
|
||||||
|
fprintf(stderr, "WSAStartup failed: %d\n", iResult);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Default values */
|
||||||
|
memset(&arguments, 0, sizeof(arguments));
|
||||||
|
arguments.listen_port = 53; // Default DNS listen port
|
||||||
|
arguments.cert_file = "certs/cert.pem"; // Default cert path
|
||||||
|
arguments.key_file = "certs/key.pem"; // Default key path
|
||||||
|
|
||||||
|
// Set default target address (127.0.0.1:5201)
|
||||||
|
int is_name = 0;
|
||||||
|
if (picoquic_get_server_address("127.0.0.1", 5201, &arguments.target_address, &is_name) != 0) {
|
||||||
|
fprintf(stderr, "Failed to set default target address\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure output buffers are flushed immediately
|
||||||
|
setbuf(stdout, NULL);
|
||||||
|
setbuf(stderr, NULL);
|
||||||
|
|
||||||
|
// Parse command line arguments
|
||||||
|
error_t parse_err = argp_parse(&argp, argc, argv, 0, NULL, &arguments);
|
||||||
|
if (parse_err) {
|
||||||
|
// argp should have printed an error message already
|
||||||
|
exit(1); // Exit if parsing failed
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check mandatory server arguments */
|
||||||
|
bool server_args_ok = true;
|
||||||
|
if (arguments.domain_name == NULL) {
|
||||||
|
fprintf(stderr, "Server error: Missing required --domain option\n");
|
||||||
|
server_args_ok = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!server_args_ok) {
|
||||||
|
argp_help(&argp, stderr, ARGP_HELP_USAGE | ARGP_HELP_EXIT_ERR, "slipstream-server");
|
||||||
|
}
|
||||||
|
|
||||||
|
exit_code = picoquic_slipstream_server(
|
||||||
|
arguments.listen_port,
|
||||||
|
arguments.cert_file,
|
||||||
|
arguments.key_file,
|
||||||
|
&arguments.target_address,
|
||||||
|
arguments.domain_name
|
||||||
|
);
|
||||||
|
|
||||||
|
#ifdef _WINDOWS
|
||||||
|
WSACleanup();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
exit(exit_code);
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue