Skip to content

Commit

Permalink
initial connector support
Browse files Browse the repository at this point in the history
- use connector for TLS stream
- implement HTTP proxy connector
  • Loading branch information
ekoby committed May 6, 2024
1 parent 7ed84a1 commit d72c67e
Show file tree
Hide file tree
Showing 8 changed files with 486 additions and 27 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ set(tlsuv_sources
src/p11.c
src/p11.h
src/util.h
src/connector.c
)

if(USE_OPENSSL)
Expand Down
52 changes: 52 additions & 0 deletions include/tlsuv/connector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright (c) NetFoundry Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef TLSUV_CONNECTOR_H
#define TLSUV_CONNECTOR_H

#include <uv.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef enum tlsuv_proxy_type {
tlsuv_PROXY_HTTP,
// maybe other kinds in the future
} tlsuv_proxy_t;

// connector creates connected sockets
typedef struct tlsuv_connector_s tlsuv_connector_t;
typedef void (*tlsuv_connect_cb)(uv_os_sock_t sock, int status, void *ctx);
typedef int (*tlsuv_connect)(uv_loop_t *loop, const tlsuv_connector_t *connector,
const char *host, int port,
tlsuv_connect_cb cb, void *ctx);

extern void tlsuv_set_global_connector(const tlsuv_connector_t* connector);
const tlsuv_connector_t *tlsuv_global_connector();

// proxy connector connects to proxy and does proxy negotiation
tlsuv_connector_t *tlsuv_new_proxy_connector(tlsuv_proxy_t type, const char* host, int port);

struct tlsuv_connector_s {
tlsuv_connect connect;
void (*free)(void *self);
};


#ifdef __cplusplus
}
#endif

#endif //TLSUV_CONNECTOR_H
9 changes: 8 additions & 1 deletion include/tlsuv/tlsuv.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include <uv.h>

#include "connector.h"
#include "tcp_src.h"
#include "tls_engine.h"
#include "tls_link.h"
Expand All @@ -34,6 +35,8 @@ typedef void(*tlsuv_log_func)(int level, const char *file, unsigned int line, co
void tlsuv_set_debug(int level, tlsuv_log_func output_f);

int tlsuv_stream_init(uv_loop_t *l, tlsuv_stream_t *clt, tls_context *tls);
void tlsuv_stream_set_connector(tlsuv_stream_t *clt, const tlsuv_connector_t *connector);

int tlsuv_stream_set_protocols(tlsuv_stream_t *clt, int num, const char *protocols[]);
const char* tlsuv_stream_get_protocol(tlsuv_stream_t *clt);
int tlsuv_stream_keepalive(tlsuv_stream_t *clt, int keepalive, unsigned int delay);
Expand Down Expand Up @@ -122,14 +125,19 @@ int tlsuv_stream_close(tlsuv_stream_t *clt, uv_close_cb close_cb);

int tlsuv_stream_free(tlsuv_stream_t *clt);

int tlsuv_stream_peername(const tlsuv_stream_t *clt, struct sockaddr *addr, int *namelen);

typedef struct tlsuv_write_s tlsuv_write_t;

struct tlsuv_stream_s {
uv_loop_t *loop;
void *data;

const tlsuv_connector_t *connector;

tls_context *tls;
tlsuv_engine_t tls_engine;

int alpn_count;
const char **alpn_protocols;

Expand All @@ -140,7 +148,6 @@ struct tlsuv_stream_s {
uv_alloc_cb alloc_cb;

uv_os_sock_t sock;
uv_getaddrinfo_t *resolve_req;
uv_poll_t watcher;

TAILQ_HEAD(reqs, tlsuv_write_s) queue;
Expand Down
222 changes: 222 additions & 0 deletions src/connector.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
// Copyright (c) NetFoundry Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <tlsuv/connector.h>
#include <stdlib.h>

#include "util.h"

#if _WIN32
#include "win32_compat.h"
#include <winsock2.h>
#define ioctl ioctlsocket
#define get_error() WSAGetLastError()
#else
#define get_error() errno
#include <sys/ioctl.h>
#include <unistd.h>
#include "um_debug.h"

#endif


struct conn_req_s {
uv_getaddrinfo_t resolve;
void *ctx;
tlsuv_connect_cb cb;
uv_tcp_t sock;
uv_connect_t connect;

};

static uv_os_sock_t new_socket(const struct addrinfo *addr);
static int default_connect(uv_loop_t *l, const tlsuv_connector_t *self,
const char *host, int port, tlsuv_connect_cb cb, void *ctx);
static int proxy_connect(uv_loop_t *l, const tlsuv_connector_t *self,
const char *host, int port, tlsuv_connect_cb cb, void *ctx);

// prevent freeing default connector
static void default_connector_free(void *self){
(void)self;
}

static tlsuv_connector_t default_connector = {
.connect = default_connect,
.free = default_connector_free,
};
static const tlsuv_connector_t *global_connector = &default_connector;

struct tlsuv_proxy_connector_s {
tlsuv_connect connect;
void (*free)(tlsuv_connector_t *self);

tlsuv_proxy_t type;
char *host;
int port;
};

void tlsuv_set_global_connector(const tlsuv_connector_t *c) {
global_connector = c ? c : &default_connector;
}

const tlsuv_connector_t* tlsuv_global_connector() {
return global_connector;
}

static void on_close(uv_handle_t *h) {
struct conn_req_s *req = h->data;
free(req);
}

static void on_connect(uv_connect_t *connect, int status) {
struct conn_req_s *req = connect->data;
if (status != 0) {
req->cb(-1, status, req->ctx);
} else {
uv_os_sock_t s;
uv_fileno((const uv_handle_t *) &req->sock, &s);
uv_os_sock_t sock = dup(s);
req->cb(sock, 0, req->ctx);
}
uv_close((uv_handle_t *) &req->sock, on_close);
}

static void on_resolve(uv_getaddrinfo_t *req, int status, struct addrinfo *addr) {
struct conn_req_s *r = req->data;
if (status != 0) {
r->cb(-1, status, r->ctx);
free(r);
} else {
r->connect.data = r;
r->sock.data = r;
uv_tcp_init(req->loop, &r->sock);
uv_tcp_connect(&r->connect, &r->sock, addr->ai_addr, on_connect);
}
uv_freeaddrinfo(addr);
}

int default_connect(uv_loop_t *loop, const tlsuv_connector_t *self,
const char *host, int port,
tlsuv_connect_cb cb, void *ctx) {
struct conn_req_s *r = calloc(1, sizeof(*r));
r->ctx = ctx;
r->resolve.data = r;
r->cb = cb;

struct addrinfo hints = {
.ai_socktype = SOCK_STREAM,
};
char portstr[6];
snprintf(portstr, sizeof portstr, "%d", port);
return uv_getaddrinfo(loop, &r->resolve, on_resolve, host, portstr, &hints);
}

struct proxy_connect_req {
uv_work_t work;
const struct tlsuv_proxy_connector_s *proxy;
void *data;
const char *host;
int port;
tlsuv_connect_cb cb;
uv_os_sock_t sock;
int err;

};

static void proxy_work(uv_work_t *wr) {
struct proxy_connect_req *r = container_of(wr, struct proxy_connect_req, work);

int flags = fcntl(r->sock, F_GETFL);

Check failure on line 140 in src/connector.c

View workflow job for this annotation

GitHub Actions / build (windows, openssl)

'F_GETFL': undeclared identifier

Check failure on line 140 in src/connector.c

View workflow job for this annotation

GitHub Actions / build (windows, mbedtls)

'F_GETFL': undeclared identifier
fcntl(r->sock, F_SETFL, flags ^ O_NONBLOCK);

Check failure on line 141 in src/connector.c

View workflow job for this annotation

GitHub Actions / build (windows, openssl)

'F_SETFL': undeclared identifier

Check failure on line 141 in src/connector.c

View workflow job for this annotation

GitHub Actions / build (windows, openssl)

'O_NONBLOCK': undeclared identifier

Check failure on line 141 in src/connector.c

View workflow job for this annotation

GitHub Actions / build (windows, mbedtls)

'F_SETFL': undeclared identifier

Check failure on line 141 in src/connector.c

View workflow job for this annotation

GitHub Actions / build (windows, mbedtls)

'O_NONBLOCK': undeclared identifier

char req[1024];
size_t reqlen = snprintf(req, sizeof(req),
"CONNECT %s:%d HTTP/1.1\r\n"
"Host: %s:%d\r\n"
"Proxy-Connection: keep-alive\r\n"
"\r\n",
r->host, r->port, r->host, r->port);
ssize_t res = write(r->sock, req, reqlen);
if (res < 0) {
r->err = (int)res;
close(r->sock);
r->sock = -1;
return;
}

res = read(r->sock, req, sizeof(req));
if (res < 0) {
r->err = (int)res;
close(r->sock);
r->sock = -1;
return;
}
req[res] = 0;

int code = 0;
res = sscanf(req, "HTTP/1.%*c %d ", &code);
if (res != 1 || code != 200) {
r->err = ECONNREFUSED;
close(r->sock);
r->sock = -1;
}
}

static void proxy_work_cb(uv_work_t *wr, int status) {
struct proxy_connect_req *r = container_of(wr, struct proxy_connect_req, work);
r->cb(r->sock, r->err, r->data);
free(r);
}

static void on_proxy_connect(uv_os_sock_t fd, int status, void *req) {
struct proxy_connect_req *r = req;
if (status != 0) {
r->cb(-1, status, r->data);
free(r);
} else {
r->sock = fd;
uv_queue_work(r->work.loop, &r->work, proxy_work, proxy_work_cb);
}
}

int proxy_connect(uv_loop_t *loop, const tlsuv_connector_t *self,
const char *host, int port, tlsuv_connect_cb cb, void *ctx) {
const struct tlsuv_proxy_connector_s *proxy = (const struct tlsuv_proxy_connector_s *) self;
struct proxy_connect_req *r = malloc(sizeof(*r));
r->proxy = proxy;
r->data = ctx;
r->cb = cb;
r->host = host;
r->port = port;
r->work.loop = loop;
return default_connect(loop, &default_connector, proxy->host, proxy->port, on_proxy_connect, r);
}

void proxy_free(tlsuv_connector_t *self) {
struct tlsuv_proxy_connector_s *c = (struct tlsuv_proxy_connector_s *) self;
free(c->host);
free(c);
}

tlsuv_connector_t *tlsuv_new_proxy_connector(tlsuv_proxy_t type, const char* host, int port) {
struct tlsuv_proxy_connector_s *c = malloc(sizeof(*c));
c->type = type;
c->host = strdup(host);

Check warning on line 215 in src/connector.c

View workflow job for this annotation

GitHub Actions / build (ubuntu, openssl)

implicit declaration of function ‘strdup’ [-Wimplicit-function-declaration]

Check warning on line 215 in src/connector.c

View workflow job for this annotation

GitHub Actions / build (ubuntu, openssl)

incompatible implicit declaration of built-in function ‘strdup’ [-Wbuiltin-declaration-mismatch]

Check warning on line 215 in src/connector.c

View workflow job for this annotation

GitHub Actions / build (ubuntu, mbedtls)

implicit declaration of function ‘strdup’ [-Wimplicit-function-declaration]

Check warning on line 215 in src/connector.c

View workflow job for this annotation

GitHub Actions / build (ubuntu, mbedtls)

incompatible implicit declaration of built-in function ‘strdup’ [-Wbuiltin-declaration-mismatch]
c->port = port;
c->connect = proxy_connect;
c->free = proxy_free;
return (tlsuv_connector_t *)c;
}


Loading

0 comments on commit d72c67e

Please sign in to comment.