From ad541c67761b8aa6da1566802c347cac4018d0c2 Mon Sep 17 00:00:00 2001 From: Dirk Engling Date: Mon, 2 Nov 2026 20:50:24 +0100 Subject: Move the build sources into src/ --- src/ot_livesync.c | 241 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100644 src/ot_livesync.c (limited to 'src/ot_livesync.c') diff --git a/src/ot_livesync.c b/src/ot_livesync.c new file mode 100644 index 0000000..083fe1a --- /dev/null +++ b/src/ot_livesync.c @@ -0,0 +1,241 @@ +/* This software was written by Dirk Engling + It is considered beerware. Prost. Skol. Cheers or whatever. */ + +/* System */ +#include +#include +#include +#include +#include +#include +#include + +/* Opentracker */ +#include "ot_accesslist.h" +#include "ot_ip.h" +#include "ot_socket.h" +#include "ot_livesync.h" +#include "ot_mutex.h" +#include "ot_stats.h" +#include "trackerlogic.h" + +#ifdef WANT_SYNC_LIVE + +ot_ip6 groupip_1 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 224, 0, 23, 5}; + +#define LIVESYNC_INCOMING_BUFFSIZE (256 * 256) + +#define LIVESYNC_OUTGOING_BUFFSIZE_PEERS 1480 +#define LIVESYNC_OUTGOING_WATERMARK_PEERS (sizeof(ot_peer) + sizeof(ot_hash)) + +#define LIVESYNC_MAXDELAY 15 /* seconds */ + +enum { OT_SYNC_PEER4, OT_SYNC_PEER6 }; + +/* Forward declaration */ +static void *livesync_worker(void *args); + +/* For incoming packets */ +static int g_socket_in = -1; + +/* For outgoing packets */ +static int g_socket_out = -1; + +static pthread_mutex_t g_outbuf_mutex = PTHREAD_MUTEX_INITIALIZER; +typedef struct { + uint8_t data[LIVESYNC_OUTGOING_BUFFSIZE_PEERS]; + size_t fill; + ot_time next_packet_time; +} sync_buffer; + +static sync_buffer g_v6_buf; +static sync_buffer g_v4_buf; + +static pthread_t thread_id; +static int thread_started; +static _Atomic int thread_left; +void livesync_init(void) { + uint32_t packet_type6 = htonl(OT_SYNC_PEER6); + uint32_t packet_type4 = htonl(OT_SYNC_PEER4); + + if (g_socket_in == -1) + exerr("No socket address for live sync specified."); + + /* Prepare outgoing peers buffer */ + memcpy(g_v6_buf.data, &g_tracker_id, sizeof(g_tracker_id)); + memcpy(g_v4_buf.data, &g_tracker_id, sizeof(g_tracker_id)); + + memcpy(g_v6_buf.data + sizeof(g_tracker_id), &packet_type6, sizeof(packet_type6)); + memcpy(g_v4_buf.data + sizeof(g_tracker_id), &packet_type4, sizeof(packet_type4)); + + g_v6_buf.fill = sizeof(g_tracker_id) + sizeof(uint32_t); + g_v4_buf.fill = sizeof(g_tracker_id) + sizeof(uint32_t); + + g_v6_buf.next_packet_time = time_future_seconds(LIVESYNC_MAXDELAY); + g_v4_buf.next_packet_time = time_future_seconds(LIVESYNC_MAXDELAY); + + thread_started = !pthread_create(&thread_id, NULL, livesync_worker, NULL); +} + +void livesync_deinit(void) { + if (thread_started) { + /* Repeats so it interrupts a read already under way */ + while (!atomic_load(&thread_left)) { + pthread_kill(thread_id, OT_SIGNAL_WAKE); + usleep(1000); + } + pthread_join(thread_id, NULL); + thread_started = 0; + } + + if (g_socket_in != -1) + close(g_socket_in); + if (g_socket_out != -1) + close(g_socket_out); +} + +void livesync_bind_mcast(ot_ip6 ip, uint16_t port) { + if (!ip_is_v4mapped(ip)) + exerr("v6 mcast support not yet available."); + + if (g_socket_in != -1) + exerr("Error: Livesync listen ip specified twice."); + + if ((g_socket_in = socket_mcast_join(groupip_1, ip, port)) == -1) + exerr("Error: Cant bind live sync incoming socket."); + + if ((g_socket_out = socket_mcast_bind(ip, port, 0)) == -1) + exerr("Error: Cant bind live sync outgoing socket."); +} + +/* Caller MUST hold g_outbuf_mutex. Returns with g_outbuf_mutex unlocked */ +static void livesync_issue_peersync(sync_buffer *buf) { + char mycopy[LIVESYNC_OUTGOING_BUFFSIZE_PEERS]; + size_t fill = buf->fill; + + memcpy(mycopy, buf->data, fill); + buf->fill = sizeof(g_tracker_id) + sizeof(uint32_t); + buf->next_packet_time = time_future_seconds(LIVESYNC_MAXDELAY); + + /* From now this thread has a local copy of the buffer and + has modified the protected element */ + pthread_mutex_unlock(&g_outbuf_mutex); + + socket_mcast_send(g_socket_out, mycopy, fill, groupip_1, LIVESYNC_PORT); +} + +static void livesync_handle_peersync(struct ot_workstruct *ws, size_t peer_size) { + size_t off = sizeof(g_tracker_id) + sizeof(uint32_t); + + /* Prepare peer record for v4mapped expansion */ + if (peer_size == OT_PEER_SIZE4) + memcpy(&ws->peer, ip_v4mapped_prefix, sizeof(ip_v4mapped_prefix)); + + /* Now basic sanity checks have been done on the live sync packet + We might add more testing and logging. */ + while ((ssize_t)(off + sizeof(ot_hash) + peer_size) <= ws->request_size) { + char *peer_ip = ws->request + off + sizeof(ot_hash); + if (peer_size == OT_PEER_SIZE4) + memcpy(&ws->peer[0] + sizeof(ip_v4mapped_prefix), peer_ip, peer_size); + else + memcpy(&ws->peer, peer_ip, peer_size); + ws->hash = (ot_hash *)(ws->request + off); + + if (!g_opentracker_running) + return; + + if (OT_PEERFLAG(ws->peer) & PEER_FLAG_STOPPED) + remove_peer_from_torrent(FLAG_MCA, ws); + else + add_peer_to_torrent_and_return_peers(FLAG_MCA, ws, /* amount = */ 0); + + off += sizeof(ot_hash) + peer_size; + } + + stats_issue_event(ws, EVENT_SYNC, 0, (ws->request_size - sizeof(g_tracker_id) - sizeof(uint32_t)) / ((ssize_t)sizeof(ot_hash) + peer_size)); +} + +/* Tickle the live sync module from time to time, so no events get + stuck when there's not enough traffic to fill udp packets fast + enough */ +void livesync_ticker(void) { + /* livesync_issue_peersync sets g_next_packet_time */ + pthread_mutex_lock(&g_outbuf_mutex); + if (time_now_seconds() > g_v6_buf.next_packet_time && g_v6_buf.fill > sizeof(g_tracker_id) + sizeof(uint32_t)) + livesync_issue_peersync(&g_v6_buf); + else + pthread_mutex_unlock(&g_outbuf_mutex); + + pthread_mutex_lock(&g_outbuf_mutex); + if (time_now_seconds() > g_v4_buf.next_packet_time && g_v4_buf.fill > sizeof(g_tracker_id) + sizeof(uint32_t)) + livesync_issue_peersync(&g_v4_buf); + else + pthread_mutex_unlock(&g_outbuf_mutex); +} + +/* Inform live sync about whats going on. */ +void livesync_tell(struct ot_workstruct *ws) { + size_t peer_size; /* initialized in next line */ + ot_peer *peer_src = peer_from_peer6(&ws->peer, &peer_size); + sync_buffer *dest_buf = peer_size == OT_PEER_SIZE6 ? &g_v6_buf : &g_v4_buf; + + pthread_mutex_lock(&g_outbuf_mutex); + + memcpy(dest_buf->data + dest_buf->fill, ws->hash, sizeof(ot_hash)); + dest_buf->fill += sizeof(ot_hash); + + memcpy(dest_buf->data + dest_buf->fill, peer_src, peer_size); + dest_buf->fill += peer_size; + + if (dest_buf->fill >= LIVESYNC_OUTGOING_BUFFSIZE_PEERS - LIVESYNC_OUTGOING_WATERMARK_PEERS) + livesync_issue_peersync(dest_buf); + else + pthread_mutex_unlock(&g_outbuf_mutex); +} + +static void *livesync_worker(void *args) { + struct ot_workstruct ws; + ot_ip6 in_ip; + uint16_t in_port; + + (void)args; + + /* Initialize our "thread local storage" */ + ws.inbuf = ws.request = malloc(LIVESYNC_INCOMING_BUFFSIZE); + ws.outbuf = ws.reply = 0; + + while (g_opentracker_running) { + uint32_t packet_type; + + ws.request_size = socket_recv(g_socket_in, ws.inbuf, LIVESYNC_INCOMING_BUFFSIZE, in_ip, &in_port); + + /* Expect at least tracker id and packet type */ + if (ws.request_size <= (ssize_t)(sizeof(g_tracker_id) + sizeof(uint32_t))) + continue; + if (!accesslist_is_blessed(in_ip, OT_PERMISSION_MAY_LIVESYNC)) + continue; + if (!memcmp(ws.inbuf, &g_tracker_id, sizeof(g_tracker_id))) { + /* TODO: log packet coming from ourselves */ + continue; + } + + memcpy(&packet_type, ws.inbuf + sizeof(g_tracker_id), sizeof(packet_type)); + + switch (ntohl(packet_type)) { + case OT_SYNC_PEER6: + livesync_handle_peersync(&ws, OT_PEER_SIZE6); + break; + case OT_SYNC_PEER4: + livesync_handle_peersync(&ws, OT_PEER_SIZE4); + break; + default: + break; + } + } + + free(ws.inbuf); + atomic_store(&thread_left, 1); + return NULL; +} + +#endif -- cgit v1.3