182 lines
6.0 KiB
C
182 lines
6.0 KiB
C
#include "server_udp_hub.h"
|
|
|
|
#include <unistd.h>
|
|
|
|
typedef struct udp_peer_entry {
|
|
struct udp_peer_entry *next;
|
|
char peer_id[OMNI_MAX_PEER_ID];
|
|
struct sockaddr_storage addr;
|
|
socklen_t addr_len;
|
|
} udp_peer_entry_t;
|
|
|
|
struct udp_hub {
|
|
udp_conn_t *conn;
|
|
pthread_rwlock_t lock;
|
|
udp_peer_entry_t *peers;
|
|
};
|
|
|
|
static int udp_addr_equal(const struct sockaddr_storage *a, socklen_t a_len, const struct sockaddr_storage *b, socklen_t b_len) {
|
|
return a_len == b_len && memcmp(a, b, a_len) == 0;
|
|
}
|
|
|
|
static udp_peer_entry_t *udp_hub_find_by_id(udp_hub_t *hub, const char *peer_id) {
|
|
udp_peer_entry_t *entry;
|
|
for (entry = hub->peers; entry != NULL; entry = entry->next) {
|
|
if (strcmp(entry->peer_id, peer_id) == 0) {
|
|
return entry;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static udp_peer_entry_t *udp_hub_find_by_addr(udp_hub_t *hub, const struct sockaddr_storage *addr, socklen_t addr_len) {
|
|
udp_peer_entry_t *entry;
|
|
for (entry = hub->peers; entry != NULL; entry = entry->next) {
|
|
if (udp_addr_equal(&entry->addr, entry->addr_len, addr, addr_len)) {
|
|
return entry;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int udp_hub_send_error(udp_hub_t *hub, const struct sockaddr_storage *addr, socklen_t addr_len, const char *to, const char *message) {
|
|
message_t msg;
|
|
protocol_message_init(&msg);
|
|
msg.type = MSG_TYPE_ERROR;
|
|
msg.id = 0;
|
|
snprintf(msg.from, sizeof(msg.from), "%s", SERVER_PEER_ID);
|
|
snprintf(msg.to, sizeof(msg.to), "%s", to == NULL || to[0] == '\0' ? "unknown" : to);
|
|
msg.body = (uint8_t *) omni_strdup(message);
|
|
msg.body_len = msg.body == NULL ? 0 : strlen((const char *) msg.body);
|
|
if (msg.body == NULL) {
|
|
return -1;
|
|
}
|
|
if (udp_conn_send_to(hub->conn, &msg, (const struct sockaddr *) addr, addr_len) != 0) {
|
|
protocol_message_clear(&msg);
|
|
return -1;
|
|
}
|
|
protocol_message_clear(&msg);
|
|
return 0;
|
|
}
|
|
|
|
udp_hub_t *udp_hub_open(const char *listen_addr, latency_logger_t *logger, tx_timestamp_debug_logger_t *debug_logger, int enable_timestamping) {
|
|
udp_hub_t *hub = (udp_hub_t *) calloc(1, sizeof(*hub));
|
|
if (hub == NULL) {
|
|
return NULL;
|
|
}
|
|
hub->conn = udp_conn_bind(listen_addr, NULL, enable_timestamping, logger, OMNI_NODE_ROLE_SERVER, "hub", debug_logger);
|
|
if (hub->conn == NULL) {
|
|
free(hub);
|
|
return NULL;
|
|
}
|
|
pthread_rwlock_init(&hub->lock, NULL);
|
|
return hub;
|
|
}
|
|
|
|
int udp_hub_serve(udp_hub_t *hub) {
|
|
message_t msg;
|
|
struct sockaddr_storage addr;
|
|
socklen_t addr_len;
|
|
udp_peer_entry_t *sender;
|
|
udp_peer_entry_t *target;
|
|
udp_peer_entry_t *entry;
|
|
|
|
if (hub == NULL) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
protocol_message_init(&msg);
|
|
for (;;) {
|
|
protocol_message_clear(&msg);
|
|
if (udp_conn_receive(hub->conn, &msg, &addr, &addr_len) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (msg.type == MSG_TYPE_REGISTER) {
|
|
pthread_rwlock_wrlock(&hub->lock);
|
|
entry = udp_hub_find_by_id(hub, msg.from);
|
|
if (entry == NULL) {
|
|
entry = (udp_peer_entry_t *) calloc(1, sizeof(*entry));
|
|
if (entry == NULL) {
|
|
pthread_rwlock_unlock(&hub->lock);
|
|
protocol_message_clear(&msg);
|
|
return -1;
|
|
}
|
|
snprintf(entry->peer_id, sizeof(entry->peer_id), "%s", msg.from);
|
|
entry->next = hub->peers;
|
|
hub->peers = entry;
|
|
}
|
|
memcpy(&entry->addr, &addr, sizeof(addr));
|
|
entry->addr_len = addr_len;
|
|
pthread_rwlock_unlock(&hub->lock);
|
|
continue;
|
|
}
|
|
if (msg.type != MSG_TYPE_TEXT && msg.type != MSG_TYPE_FILE && msg.type != MSG_TYPE_BINARY) {
|
|
if (msg.type == MSG_TYPE_ERROR) {
|
|
udp_hub_send_error(hub, &addr, addr_len, msg.from, "peers cannot send error messages");
|
|
} else {
|
|
char *error_text = omni_strdup_printf("unsupported message type: %s", protocol_message_type_name(msg.type));
|
|
if (error_text != NULL) {
|
|
udp_hub_send_error(hub, &addr, addr_len, msg.from, error_text);
|
|
free(error_text);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
pthread_rwlock_rdlock(&hub->lock);
|
|
sender = udp_hub_find_by_addr(hub, &addr, addr_len);
|
|
if (sender == NULL) {
|
|
pthread_rwlock_unlock(&hub->lock);
|
|
udp_hub_send_error(hub, &addr, addr_len, msg.from, "not registered; send register first");
|
|
continue;
|
|
}
|
|
snprintf(msg.from, sizeof(msg.from), "%s", sender->peer_id);
|
|
target = udp_hub_find_by_id(hub, msg.to);
|
|
if (target == NULL) {
|
|
char *error_text;
|
|
pthread_rwlock_unlock(&hub->lock);
|
|
error_text = omni_strdup_printf("unknown target: %s", msg.to);
|
|
if (error_text != NULL) {
|
|
udp_hub_send_error(hub, &addr, addr_len, sender->peer_id, error_text);
|
|
free(error_text);
|
|
}
|
|
continue;
|
|
}
|
|
if (udp_conn_send_to(hub->conn, &msg, (const struct sockaddr *) &target->addr, target->addr_len) != 0) {
|
|
char *error_text;
|
|
pthread_rwlock_unlock(&hub->lock);
|
|
error_text = omni_strdup_printf("failed to forward to %s", msg.to);
|
|
if (error_text != NULL) {
|
|
udp_hub_send_error(hub, &addr, addr_len, sender->peer_id, error_text);
|
|
free(error_text);
|
|
}
|
|
continue;
|
|
}
|
|
pthread_rwlock_unlock(&hub->lock);
|
|
}
|
|
}
|
|
|
|
int udp_hub_close(udp_hub_t *hub) {
|
|
if (hub == NULL) {
|
|
return 0;
|
|
}
|
|
return udp_conn_close(hub->conn);
|
|
}
|
|
|
|
void udp_hub_free(udp_hub_t *hub) {
|
|
udp_peer_entry_t *entry;
|
|
udp_peer_entry_t *next;
|
|
if (hub == NULL) {
|
|
return;
|
|
}
|
|
udp_conn_free(hub->conn);
|
|
for (entry = hub->peers; entry != NULL; entry = next) {
|
|
next = entry->next;
|
|
free(entry);
|
|
}
|
|
pthread_rwlock_destroy(&hub->lock);
|
|
free(hub);
|
|
}
|