k4/src/net_hi.c

212 lines
4.4 KiB
C

#include"net_hi.h"
#include<stddef.h>
#include"enet.h"
#include<stdlib.h>
#include"stoon.h"
#include"net_server.h"
#include"net_client.h"
#include<math.h>
#include"k3.h"
/*
* Handles networking on a higher-level, combining hole punching with
* basic client & server interface
* */
static void *ReqPeercodeUD;
static void(*ReqPeercodeCB)(void*, struct StoonPeercode *peercode);
static struct Stoon stoon;
static bool inited = false;
static bool isHost;
typedef struct {
ENetAddress pv4;
ENetAddress pv6;
ENetAddress lv4;
ENetAddress lv6;
} ToPunch;
static ToPunch *topunch = NULL;
static size_t topunchCount = 0;
static double Timeout = 0;
static bool connected = false;
static uint16_t servport = 0;
bool net_hi_request_peercode(void *ud, void(*callback)(void*, struct StoonPeercode *peercode)) {
if(inited) return false;
ReqPeercodeUD = ud;
ReqPeercodeCB = callback;
for(size_t port = 25000; port <= 26656; port++) {
stoon = stoon_init("stun.easybell.de", 3478, port);
if(stoon.fd4 >= 0 || stoon.fd6 >= 0) {
servport = port;
break;
}
}
Timeout = 0;
return true;
}
bool net_hi_setup(bool host) {
if(inited) return false;
stoon_kill(&stoon);
inited = true;
isHost = host;
if(host) {
net_server_init(servport);
} else {
net_client_init();
}
return true;
}
void net_hi_update(double now) {
if(now <= Timeout) return;
if(ReqPeercodeCB) {
if(stoon_req(&stoon)) {
if(stoon_listen(&stoon)) {
ReqPeercodeCB(ReqPeercodeUD, &stoon.peercode);
ReqPeercodeCB = NULL;
} else {
k3Log(k3_INFO, "stoon_listen failure");
Timeout = now + 1;
}
} else {
k3Log(k3_INFO, "stoon_req failure");
Timeout = now + 1;
}
} else if(!inited) {
stoon_keepalive(&stoon);
Timeout = now + 1;
} else {
if(isHost) {
ENetHost *h = net_server_get_enethost();
ENetBuffer buf = {.data = "Punch!", .dataLength = 6};
for(size_t tpi = 0; tpi < topunchCount; tpi++) {
ToPunch *tp = &topunch[tpi];
if(tp->lv6.port) enet_socket_send(h->socket, &tp->lv6, &buf, 1);
if(tp->lv4.port) enet_socket_send(h->socket, &tp->lv4, &buf, 1);
if(tp->pv6.port) enet_socket_send(h->socket, &tp->pv6, &buf, 1);
if(tp->pv4.port) enet_socket_send(h->socket, &tp->pv4, &buf, 1);
}
}
Timeout = now + 1;
}
}
static bool stoonpeer_to_enet4(uint8_t *publicV4, uint8_t *publicP4, ENetAddress *enet) {
memset(enet, 0, sizeof(*enet));
if(*(uint32_t*) publicV4 == 0 || *(uint16_t*) publicP4 == 0) {
return false;
}
uint8_t *addr8 = (uint8_t*) &enet->host;
memset(addr8, 0, 16);
addr8[10] = 0xFF;
addr8[11] = 0xFF;
addr8[12] = publicV4[0];
addr8[13] = publicV4[1];
addr8[14] = publicV4[2];
addr8[15] = publicV4[3];
enet->port = *(uint16_t*) publicP4;
return true;
}
static bool stoonpeer_to_enet6(uint8_t *publicV6, uint8_t *publicP6, ENetAddress *enet) {
memset(enet, 0, sizeof(*enet));
if(*(uint32_t*) publicV6 == 0 || *(uint16_t*) publicP6 == 0) {
return false;
}
uint8_t *addr8 = (uint8_t*) &enet->host;
memcpy(addr8, publicV6, 16);
enet->port = *(uint16_t*) publicP6;
return true;
}
void net_hi_add_punch(struct StoonPeercode *peercode) {
topunch = realloc(topunch, sizeof(*topunch) * (++topunchCount));
ToPunch *tp = &topunch[topunchCount - 1];
stoonpeer_to_enet4(peercode->publicV4, peercode->publicP4, &tp->pv4);
stoonpeer_to_enet6(peercode->publicV6, peercode->publicP6, &tp->pv6);
stoonpeer_to_enet4(peercode->localV4, peercode->localP4, &tp->lv4);
stoonpeer_to_enet6(peercode->localV6, peercode->localP6, &tp->lv6);
}
bool net_hi_connect(struct StoonPeercode *peercode) {
if(!inited) return false;
if(connected) return false;
ENetAddress pv4, pv6;
stoonpeer_to_enet4(peercode->publicV4, peercode->publicP4, &pv4);
stoonpeer_to_enet6(peercode->publicV6, peercode->publicP6, &pv6);
ENetAddress lv4, lv6;
stoonpeer_to_enet4(peercode->localV4, peercode->localP4, &lv4);
stoonpeer_to_enet6(peercode->localV6, peercode->localP6, &lv6);
if(lv6.port && net_client_connect(lv6)) {
k3Log(k3_INFO, "Found local IPv6");
return true;
}
if(lv4.port && net_client_connect(lv4)) {
k3Log(k3_INFO, "Found local IPv4");
return true;
}
if(pv6.port && net_client_connect(pv6)) {
k3Log(k3_INFO, "Found remote IPv6");
return true;
}
if(pv4.port && net_client_connect(pv4)) {
k3Log(k3_INFO, "Found remote IPv4");
return true;
}
k3Log(k3_INFO, "Failed to establish connection");
return false;
}