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

// 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;
}