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.
173 lines
6.9 KiB
173 lines
6.9 KiB
// stcp.c — shared stcp_conn lifecycle + frame encrypt/decrypt + pending queue + send |
|
#include "stcp.h" |
|
#include "../lib/ll_queue.h" |
|
#include "../lib/mem.h" |
|
#include "../lib/debug_config.h" |
|
#include "../lib/platform_compat.h" |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <errno.h> |
|
|
|
void stcp_conn_set_tx_queue(struct stcp_conn *c, struct ll_queue *q) { |
|
if (!c || !q) return; |
|
c->tx_queue = q; |
|
if (c->tx_cb) queue_set_callback(q, c->tx_cb, c); |
|
} |
|
|
|
void stcp_conn_set_rx_queue(struct stcp_conn *c, struct ll_queue *q) { |
|
if (!c || !q) return; |
|
c->rx_queue = q; |
|
} |
|
|
|
void stcp_conn_set_on_close(struct stcp_conn *c, void (*cb)(struct stcp_conn *conn, int err, void *arg), void *arg) { |
|
if (!c || !cb) return; |
|
c->on_close = cb; |
|
c->close_arg = arg; |
|
} |
|
|
|
void stcp_conn_free(struct stcp_conn *c) { |
|
if (!c) return; |
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "stcp_conn_free: c=%p sock=%d sock_id=%p recv_buf=%p state=%d allocated=%d", |
|
(void*)c, (int)c->sock, (void*)c->socket_id, (void*)c->recv_buf, (int)c->state, (int)c->allocated); |
|
if (c->socket_id) { uasync_remove_socket_t(c->ua, c->sock); c->socket_id = NULL; } |
|
if (c->sock != SOCKET_INVALID) { socket_close_wrapper(c->sock); c->sock = SOCKET_INVALID; } |
|
if (c->recv_buf) { u_free(c->recv_buf); c->recv_buf = NULL; } |
|
if (c->send_buf) { u_free(c->send_buf); c->send_buf = NULL; } |
|
stcp_pending_clear(c); |
|
sc_stream_cleanup(&c->stream_send); |
|
sc_stream_cleanup(&c->stream_recv); |
|
if (c->allocated) u_free(c); |
|
} |
|
|
|
int stcp_frame_encrypt(struct stcp_conn *c, const uint8_t *data, size_t data_len, uint8_t *output, size_t *output_len) { |
|
uint32_t crc = crc32_calc(data, data_len); |
|
output[0] = (uint8_t)(data_len >> 0); |
|
output[1] = (uint8_t)(data_len >> 8); |
|
if (data_len) memcpy(output + 2, data, data_len); |
|
output[2 + data_len + 0] = (uint8_t)(crc >> 0); |
|
output[2 + data_len + 1] = (uint8_t)(crc >> 8); |
|
output[2 + data_len + 2] = (uint8_t)(crc >> 16); |
|
output[2 + data_len + 3] = (uint8_t)(crc >> 24); |
|
size_t enc_len = 2 + data_len + 4; |
|
if (sc_stream_xor(&c->stream_send, output + 2, data_len + 4) != SC_OK) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "stcp_frame_encrypt stream_xor failed len=%zu", data_len); |
|
return -1; |
|
} |
|
*output_len = enc_len; |
|
return 0; |
|
} |
|
|
|
int stcp_frame_decrypt(uint8_t *data, size_t len, struct sc_stream_state *stream, size_t *out_len) { |
|
if (len < SC_CRC32_SIZE) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "stcp_frame_decrypt too short len=%zu", len); return -1; } |
|
if (sc_stream_xor(stream, data, len) != SC_OK) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "stcp_frame_decrypt stream_xor failed len=%zu", len); |
|
return -1; |
|
} |
|
size_t data_len = len - SC_CRC32_SIZE; |
|
uint32_t recv_crc = ((uint32_t)data[data_len]) | ((uint32_t)data[data_len + 1] << 8) | |
|
((uint32_t)data[data_len + 2] << 16) | ((uint32_t)data[data_len + 3] << 24); |
|
uint32_t calc_crc = crc32_calc(data, data_len); |
|
if (recv_crc != calc_crc) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "stcp_frame_decrypt CRC mismatch recv=%08x calc=%08x", recv_crc, calc_crc); |
|
return -1; |
|
} |
|
*out_len = data_len; |
|
return 0; |
|
} |
|
|
|
void stcp_pending_queue(struct stcp_conn *c, const uint8_t *data, size_t len) { |
|
if (!c) return; |
|
struct pending_entry *pe = u_malloc(sizeof(*pe)); |
|
if (!pe) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "stcp_pending_queue malloc entry failed"); return; } |
|
if (len && data) { pe->data = u_malloc(len); if (!pe->data) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "stcp_pending_queue malloc data(%zu) failed", len); u_free(pe); return; } memcpy(pe->data, data, len); } |
|
else { pe->data = NULL; } |
|
pe->len = len; |
|
pe->next = NULL; |
|
if (c->pending_tail) c->pending_tail->next = pe; |
|
else c->pending_head = pe; |
|
c->pending_tail = pe; |
|
} |
|
|
|
void stcp_pending_clear(struct stcp_conn *c) { |
|
if (!c) return; |
|
while (c->pending_head) { |
|
struct pending_entry *pe = c->pending_head; |
|
c->pending_head = pe->next; |
|
if (pe->data) u_free(pe->data); |
|
u_free(pe); |
|
} |
|
c->pending_tail = NULL; |
|
} |
|
|
|
int stcp_try_send(struct stcp_conn *c, const uint8_t *data, size_t len) { |
|
if (c->sock == SOCKET_INVALID) return -1; |
|
if (c->send_buf) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "stcp_try_send: send_buf already busy"); |
|
return -1; |
|
} |
|
ssize_t sent = send(c->sock, data, len, 0); |
|
if (sent < 0) { |
|
int err = socket_get_error(); |
|
if (err == ERR_AGAIN || err == ERR_WOULDBLOCK) { |
|
c->send_buf = (uint8_t *)data; c->send_len = len; c->send_offset = 0; |
|
uasync_set_socket_write(c->ua, c->socket_id, 1); |
|
return 1; |
|
} |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "stcp_try_send send failed err=%d", err); |
|
return -1; |
|
} |
|
if ((size_t)sent < len) { |
|
c->send_buf = (uint8_t *)data; c->send_len = len; c->send_offset = (size_t)sent; |
|
uasync_set_socket_write(c->ua, c->socket_id, 1); |
|
return 1; |
|
} |
|
u_free((uint8_t *)data); |
|
return 0; |
|
} |
|
|
|
void stcp_write_cb(socket_t sock, void *arg) { |
|
struct stcp_conn *c = (struct stcp_conn *)arg; |
|
if (!c->send_buf) { uasync_set_socket_write(c->ua, c->socket_id, 0); return; } |
|
ssize_t sent = send(sock, c->send_buf + c->send_offset, c->send_len - c->send_offset, 0); |
|
if (sent < 0) { |
|
int err = socket_get_error(); |
|
if (err == ERR_AGAIN || err == ERR_WOULDBLOCK) return; |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "stcp_write_cb send failed err=%d", err); |
|
if (c->on_write_error) c->on_write_error(c, err); |
|
return; |
|
} |
|
c->send_offset += (size_t)sent; |
|
if (c->send_offset >= c->send_len) { |
|
u_free(c->send_buf); c->send_buf = NULL; c->send_len = 0; c->send_offset = 0; |
|
uasync_set_socket_write(c->ua, c->socket_id, 0); |
|
stcp_flush_pending(c); |
|
} |
|
} |
|
|
|
void stcp_flush_pending(struct stcp_conn *c) { |
|
if (!c) return; |
|
while (!c->send_buf && c->pending_head) { |
|
struct pending_entry *pe = c->pending_head; |
|
c->pending_head = pe->next; |
|
if (!c->pending_head) c->pending_tail = NULL; |
|
|
|
size_t enc_len; |
|
uint8_t *enc = u_malloc(2 + pe->len + SC_CRC32_SIZE); |
|
if (!enc) { DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "stcp_flush_pending malloc enc failed len=%zu", pe->len); if (pe->data) u_free(pe->data); u_free(pe); continue; } |
|
if (stcp_frame_encrypt(c, pe->data, pe->len, enc, &enc_len)) { |
|
DEBUG_ERROR(DEBUG_CATEGORY_ETCP, "stcp_flush_pending encrypt failed"); u_free(enc); if (pe->data) u_free(pe->data); u_free(pe); |
|
if (c->on_write_error) c->on_write_error(c, ECANCELED); |
|
return; |
|
} |
|
if (pe->data) u_free(pe->data); |
|
u_free(pe); |
|
|
|
int r = stcp_try_send(c, enc, enc_len); |
|
if (r < 0) { |
|
u_free(enc); |
|
if (c->on_write_error) c->on_write_error(c, ECANCELED); |
|
return; |
|
} |
|
if (r > 0) return; |
|
} |
|
}
|
|
|