You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
224 lines
7.8 KiB
224 lines
7.8 KiB
// stcp_link.c — STCP link management implementation |
|
#include "stcp_link.h" |
|
#include "stcp.h" |
|
#include "stcp_server.h" |
|
#include "stcp_client.h" |
|
#include "secure_channel.h" |
|
#include "etcp.h" |
|
#include "etcp_router.h" |
|
#include "utun_instance.h" |
|
#include "../lib/debug_config.h" |
|
#include "../lib/ll_queue.h" |
|
#include "../lib/mem.h" |
|
#include <stdlib.h> |
|
#include <string.h> |
|
|
|
struct stcp_server { |
|
struct stcp_server *srv; // stcp_server from stcp_server.h |
|
struct stcp_link_config cfg; |
|
stcp_server_on_link_cb on_link; |
|
void *on_link_arg; |
|
}; |
|
|
|
struct stcp_link { |
|
struct stcp_link_config cfg; |
|
struct stcp_client *cli; // only 1 client link for now |
|
struct stcp_conn *conn; // STCP connection |
|
|
|
uint8_t ready; |
|
uint8_t peer_pubkey[SC_PUBKEY_SIZE]; |
|
|
|
stcp_link_cb on_ready_cb; |
|
void *ready_arg; |
|
void (*on_close_cb)(struct stcp_link *link, int err, void *arg); |
|
void *close_arg; |
|
|
|
struct ll_queue *tx_queue; // owned by this link |
|
struct ETCP_CONN etcp_conn; // lightweight, for etcp_recv_fn compat |
|
}; |
|
|
|
// ====== rx dispatch through inst->api_bindings ====== |
|
|
|
static void link_rx_cb(struct ll_queue *q, void *arg) { |
|
struct stcp_link *link = (struct stcp_link *)arg; |
|
struct ll_entry *e = queue_data_get(q); |
|
if (!e) { queue_resume_callback(q); return; } |
|
|
|
struct UTUN_INSTANCE *inst = link->cfg.inst; |
|
uint8_t id = (e->dgram && e->len > 0) ? e->dgram[0] : 0; |
|
|
|
if (inst && inst->api_bindings.callbacks[id]) { |
|
inst->api_bindings.callbacks[id](&link->etcp_conn, e); |
|
} else if (inst && inst->api_bindings.callbacks[0]) { |
|
inst->api_bindings.callbacks[0](&link->etcp_conn, e); |
|
} else { |
|
queue_dgram_free(e); |
|
queue_entry_free(e); |
|
} |
|
queue_resume_callback(q); |
|
} |
|
|
|
// ====== server accept → link ====== |
|
|
|
static void server_accept_cb(struct stcp_conn *conn, void *arg) { |
|
struct stcp_server *ss = (struct stcp_server *)arg; |
|
struct stcp_link *link = u_calloc(1, sizeof(struct stcp_link)); |
|
if (!link) { stcp_conn_free(conn); return; } |
|
link->cfg = ss->cfg; |
|
link->ready = 1; |
|
link->conn = conn; |
|
link->etcp_conn.instance = ss->cfg.inst; |
|
link->etcp_conn.transport_link = (void *)link; // backpointer for stcp_link |
|
|
|
struct ll_queue *rx = queue_new(conn->ua, 0, 0, 0, "srx"); |
|
queue_set_callback(rx, link_rx_cb, link); |
|
stcp_conn_set_rx_queue(conn, rx); |
|
link->tx_queue = queue_new(conn->ua, 0, 0, 0, "stx"); |
|
queue_set_threshold(link->tx_queue, 0, 0); |
|
queue_set_waiter_defer(link->tx_queue, 1); |
|
link->etcp_conn.send_input_q = link->tx_queue; |
|
stcp_conn_set_tx_queue(conn, link->tx_queue); |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "stcp_link: server accepted connection"); |
|
if (link->on_ready_cb) link->on_ready_cb(link, link->ready_arg); |
|
if (ss->on_link) ss->on_link(link, ss->on_link_arg); |
|
} |
|
|
|
// ====== client connect → link ====== |
|
|
|
static void client_ready_cb(struct stcp_conn *conn, void *arg) { |
|
struct stcp_link *link = (struct stcp_link *)arg; |
|
if (!conn) return; |
|
link->ready = 1; |
|
link->conn = conn; |
|
link->etcp_conn.instance = link->cfg.inst; |
|
link->etcp_conn.transport_link = (void *)link; |
|
|
|
struct ll_queue *rx = queue_new(conn->ua, 0, 0, 0, "crx"); |
|
queue_set_callback(rx, link_rx_cb, link); |
|
stcp_conn_set_rx_queue(conn, rx); |
|
link->tx_queue = queue_new(conn->ua, 0, 0, 0, "ctx"); |
|
queue_set_threshold(link->tx_queue, 0, 0); |
|
queue_set_waiter_defer(link->tx_queue, 1); |
|
link->etcp_conn.send_input_q = link->tx_queue; |
|
stcp_conn_set_tx_queue(conn, link->tx_queue); |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "stcp_link: client connected"); |
|
if (link->on_ready_cb) link->on_ready_cb(link, link->ready_arg); |
|
} |
|
|
|
// ====== API ====== |
|
|
|
struct stcp_server *stcp_server_listen(struct stcp_link_config *cfg, uint16_t port, |
|
stcp_server_on_link_cb on_link, void *arg) { |
|
if (!cfg || !cfg->ua || !cfg->my_keys) return NULL; |
|
struct stcp_server *ss = u_calloc(1, sizeof(struct stcp_server)); |
|
if (!ss) return NULL; |
|
ss->cfg = *cfg; |
|
ss->on_link = on_link; |
|
ss->on_link_arg = arg; |
|
ss->srv = stcp_server_create(cfg->ua, port, cfg->my_keys, server_accept_cb, ss); |
|
if (!ss->srv) { u_free(ss); return NULL; } |
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "stcp_server_listen: port=%u", port); |
|
return ss; |
|
} |
|
|
|
void stcp_link_server_destroy(struct stcp_server *ss) { |
|
if (!ss) return; |
|
if (ss->srv) stcp_server_destroy(ss->srv); |
|
u_free(ss); |
|
} |
|
|
|
struct stcp_link *stcp_link_connect(struct stcp_link_config *cfg) { |
|
if (!cfg || !cfg->ua || !cfg->my_keys || !cfg->peer_pubkey || !cfg->remote_addr) |
|
return NULL; |
|
|
|
char addr_str[64]; |
|
struct sockaddr_in *sa = (struct sockaddr_in *)cfg->remote_addr; |
|
inet_ntop(AF_INET, &sa->sin_addr, addr_str, sizeof(addr_str)); |
|
uint16_t port = cfg->remote_port ? cfg->remote_port : ntohs(sa->sin_port); |
|
|
|
struct stcp_link *link = u_calloc(1, sizeof(struct stcp_link)); |
|
if (!link) return NULL; |
|
link->cfg = *cfg; |
|
|
|
// Convert hex pubkey to binary if needed |
|
if (cfg->peer_pubkey_mode) { |
|
struct secure_channel sc_tmp; |
|
sc_init_ctx(&sc_tmp, cfg->my_keys); |
|
if (sc_set_peer_public_key(&sc_tmp, cfg->peer_pubkey, 1) != SC_OK) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "stcp_link_connect: invalid peer pubkey hex"); |
|
u_free(link); return NULL; |
|
} |
|
memcpy(link->peer_pubkey, sc_tmp.peer_public_key, SC_PUBKEY_SIZE); |
|
} else { |
|
memcpy(link->peer_pubkey, cfg->peer_pubkey, SC_PUBKEY_SIZE); |
|
} |
|
|
|
link->cli = stcp_client_connect(cfg->ua, addr_str, port, cfg->my_keys, link->peer_pubkey, |
|
client_ready_cb, link); |
|
if (!link->cli) { u_free(link); return NULL; } |
|
|
|
link->etcp_conn.instance = cfg->inst; |
|
link->etcp_conn.transport_link = (void *)link; |
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "stcp_link_connect: connecting to %s:%u", addr_str, port); |
|
return link; |
|
} |
|
|
|
void stcp_link_close(struct stcp_link *link) { |
|
if (!link) return; |
|
struct ll_queue* tq_reg = link->etcp_conn.transit_queues; |
|
if (tq_reg) { |
|
struct ll_entry* te; |
|
while ((te = queue_data_get(tq_reg)) != NULL) { |
|
struct TRANSIT_QUEUE* tq = (struct TRANSIT_QUEUE*)te; |
|
if (link->etcp_conn.send_input_q) queue_waiter_cancel(link->etcp_conn.send_input_q, &tq->waiter); |
|
struct ll_entry* pe; |
|
while ((pe = queue_data_get(tq->q)) != NULL) { queue_dgram_free(pe); queue_entry_free(pe); } |
|
queue_free(tq->q); |
|
queue_entry_free(&tq->ll); |
|
} |
|
queue_free(tq_reg); |
|
link->etcp_conn.transit_queues = NULL; |
|
link->etcp_conn.send_input_q = NULL; |
|
} |
|
if (link->conn) { |
|
if (link->conn->rx_queue) queue_free(link->conn->rx_queue); |
|
if (link->tx_queue) queue_free(link->tx_queue); |
|
stcp_conn_free(link->conn); |
|
} |
|
if (link->cli) stcp_client_destroy(link->cli); |
|
u_free(link); |
|
} |
|
|
|
int stcp_link_send(struct stcp_link *link, const uint8_t *data, size_t len) { |
|
if (!link || !link->ready) return -1; |
|
struct ll_entry *e = queue_entry_new(0); |
|
if (!e) return -1; |
|
e->dgram = u_malloc(len ? len : 1); |
|
if (!e->dgram) { queue_entry_free(e); return -1; } |
|
if (len) memcpy(e->dgram, data, len); |
|
e->len = (uint16_t)len; |
|
queue_data_put(link->conn->tx_queue, e); |
|
return 0; |
|
} |
|
|
|
int stcp_link_is_ready(struct stcp_link *link) { |
|
return link ? link->ready : 0; |
|
} |
|
|
|
struct ETCP_CONN *stcp_link_get_etcp_conn(struct stcp_link *link) { |
|
return link ? &link->etcp_conn : NULL; |
|
} |
|
|
|
void stcp_link_set_on_ready(struct stcp_link *link, stcp_link_cb cb, void *arg) { |
|
if (!link) return; |
|
link->on_ready_cb = cb; |
|
link->ready_arg = arg; |
|
} |
|
|
|
void stcp_link_set_on_close(struct stcp_link *link, void (*cb)(struct stcp_link *link, int err, void *arg), void *arg) { |
|
if (!link) return; |
|
link->on_close_cb = cb; |
|
link->close_arg = arg; |
|
}
|
|
|