Extend network traversal to local addresses

This commit is contained in:
mid 2025-06-29 20:47:14 +03:00
parent e126db573a
commit 355df2ee44
9 changed files with 222 additions and 132 deletions

View File

@ -2224,9 +2224,9 @@ static int dagame_set_texture_reduction(lua_State *L) {
}
int PeercodeHandler = LUA_NOREF;
static void get_peercode_callback(void *ud, const char *peercode_bin) {
char peercode[STOON_CONN_INFO_SIZE * 2 + 1] = {};
for(int i = 0; i < STOON_CONN_INFO_SIZE; i++) {
static void get_peercode_callback(void *ud, struct StoonPeercode *peercode_bin) {
char peercode[sizeof(*peercode_bin) * 2 + 1] = {};
for(int i = 0; i < sizeof(*peercode_bin); i++) {
snprintf(peercode + i * 2, 3, "%02x", ((uint8_t*) peercode_bin)[i]);
}
@ -2296,11 +2296,11 @@ static int get_peercode_bin(const char *peercode, char *peercode_bin) {
if(!peercode) return 0;
if(!peercode_bin) return 0;
if(strlen(peercode) < 2 * STOON_CONN_INFO_SIZE) {
if(strlen(peercode) < 2 * sizeof(struct StoonPeercode)) {
return 0;
}
for(int i = 0; i < STOON_CONN_INFO_SIZE; i++) {
for(int i = 0; i < sizeof(struct StoonPeercode); i++) {
int b = 0;
if(peercode[i * 2 + 0] >= '0' && peercode[i * 2 + 0] <= '9') {
@ -2328,8 +2328,8 @@ static int dagame_net_host(lua_State *L) {
}
static int dagame_net_join(lua_State *L) {
char peercode[STOON_CONN_INFO_SIZE];
if(!get_peercode_bin(lua_tostring(L, 1), peercode)) {
struct StoonPeercode peercode;
if(!get_peercode_bin(lua_tostring(L, 1), &peercode)) {
lua_pushliteral(L, "Invalid peercode");
lua_error(L);
return 0;
@ -2337,20 +2337,20 @@ static int dagame_net_join(lua_State *L) {
net_hi_setup(false);
lua_pushboolean(L, net_hi_connect(peercode));
lua_pushboolean(L, net_hi_connect(&peercode));
return 1;
}
static int dagame_net_punch(lua_State *L) {
char peercode[STOON_CONN_INFO_SIZE];
if(!get_peercode_bin(lua_tostring(L, 1), peercode)) {
struct StoonPeercode peercode;
if(!get_peercode_bin(lua_tostring(L, 1), &peercode)) {
lua_pushliteral(L, "Invalid peercode");
lua_error(L);
return 0;
}
net_hi_add_punch(peercode);
net_hi_add_punch(&peercode);
return 0;
}

View File

@ -26,11 +26,7 @@ void net_client_init() {
Game.isAuthority = 0;
}
bool net_client_connect(const char *addr, uint16_t port) {
ENetAddress eaddr;
enet_address_set_host(&eaddr, addr);
eaddr.port = port;
bool net_client_connect(ENetAddress eaddr) {
peer = enet_host_connect(host, &eaddr, 1, 0);
ENetEvent ev;

View File

@ -1,7 +1,9 @@
#pragma once
#include"enet.h"
void net_client_init();
bool net_client_connect(const char *addr, uint16_t port);
bool net_client_connect(ENetAddress addr);
void net_client_receive();
void net_client_update();
void net_client_dejitter();

View File

@ -7,9 +7,15 @@
#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*, const char *peercode);
static void(*ReqPeercodeCB)(void*, struct StoonPeercode *peercode);
static struct Stoon stoon;
@ -17,11 +23,11 @@ static bool inited = false;
static bool isHost;
typedef struct {
#define MASK_IPv4 1
#define MASK_IPv6 2
int mask;
ENetAddress v4;
ENetAddress v6;
ENetAddress pv4;
ENetAddress pv6;
ENetAddress lv4;
ENetAddress lv6;
} ToPunch;
static ToPunch *topunch = NULL;
static size_t topunchCount = 0;
@ -30,13 +36,22 @@ static double Timeout = 0;
static bool connected = false;
bool net_hi_request_peercode(void *ud, void(*callback)(void*, const char *peercode)) {
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;
stoon = stoon_init("stun.easybell.de", 3478, 26656);
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;
@ -52,7 +67,7 @@ bool net_hi_setup(bool host) {
isHost = host;
if(host) {
net_server_init();
net_server_init(servport);
} else {
net_client_init();
}
@ -67,16 +82,17 @@ void net_hi_update(double now) {
if(stoon_req(&stoon)) {
if(stoon_listen(&stoon)) {
uint8_t peercode[STOON_CONN_INFO_SIZE] = {};
stoon_serialize(&stoon, peercode);
ReqPeercodeCB(ReqPeercodeUD, peercode);
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;
}
@ -97,11 +113,11 @@ void net_hi_update(double now) {
for(size_t tpi = 0; tpi < topunchCount; tpi++) {
ToPunch *tp = &topunch[tpi];
if(tp->mask & MASK_IPv4) {
enet_socket_send(h->socket, &tp->v4, &buf, 1);
} else if(tp->mask & MASK_IPv6) {
enet_socket_send(h->socket, &tp->v6, &buf, 1);
}
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);
}
}
@ -111,57 +127,85 @@ void net_hi_update(double now) {
}
}
static void stoonpeer_to_enets(const char *peercode, ENetAddress *v4, ENetAddress *v6, int *mask) {
uint8_t *addr8 = (uint8_t*) &v4->host;
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] = peercode[0];
addr8[13] = peercode[1];
addr8[14] = peercode[2];
addr8[15] = peercode[3];
v4->port = ntohs(*(uint16_t*) &peercode[4]);
addr8[12] = publicV4[0];
addr8[13] = publicV4[1];
addr8[14] = publicV4[2];
addr8[15] = publicV4[3];
enet->port = *(uint16_t*) publicP4;
memcpy(&v6->host, peercode + 6, 16);
v6->port = ntohs(*(uint16_t*) &peercode[22]);
*mask = 0;
if(v4->port) {
*mask |= MASK_IPv4;
}
if(v6->port) {
*mask |= MASK_IPv6;
}
return true;
}
void net_hi_add_punch(const char *peercode) {
int mask;
ENetAddress v4 = {}, v6 = {};
stoonpeer_to_enets(peercode, &v4, &v6, &mask);
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[topunchCount - 1] = (ToPunch) {
.v4 = v4,
.v6 = v6,
.mask = mask,
};
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(const char *peercode) {
bool net_hi_connect(struct StoonPeercode *peercode) {
if(!inited) return false;
if(connected) return false;
char ip[64];
int port;
if(*(uint16_t*) (peercode + 22) == 0) {
inet_ntop(AF_INET, peercode, ip, sizeof(ip));
port = ntohs(*(uint16_t*) (peercode + 4));
} else {
inet_ntop(AF_INET6, peercode + 6, ip, sizeof(ip));
port = ntohs(*(uint16_t*) (peercode + 22));
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;
}
return net_client_connect(ip, port);
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;
}

View File

@ -2,10 +2,12 @@
#include<stdbool.h>
bool net_hi_request_peercode(void *ud, void(*callback)(void*, const char *peercode));
struct StoonPeercode;
bool net_hi_request_peercode(void *ud, void(*callback)(void*, struct StoonPeercode *peercode));
bool net_hi_setup(bool host);
void net_hi_update(double now);
void net_hi_add_punch(const char *peercode);
void net_hi_add_punch(struct StoonPeercode *peercode);
bool net_hi_connect(const char *peercode);
bool net_hi_connect(struct StoonPeercode *peercode);

View File

@ -18,8 +18,8 @@ struct Conn {
#define MAX_PLAYERS 64
static struct Conn conns[MAX_PLAYERS];
void net_server_init() {
host = enet_host_create(&(ENetAddress) {.host = ENET_HOST_ANY, .port = 26656}, 16, 1, 0, 0);
void net_server_init(uint16_t port) {
host = enet_host_create(&(ENetAddress) {.host = ENET_HOST_ANY, .port = port}, 16, 1, 0, 0);
Game.isMultiplayer = 1;
Game.isAuthority = 1;

View File

@ -5,7 +5,7 @@
struct _ENetPeer;
struct bstr;
void net_server_init();
void net_server_init(uint16_t port);
void net_server_receive();
void net_server_update();

View File

@ -17,6 +17,7 @@
#include<string.h>
#include<stdio.h>
#include<errno.h>
#include<ifaddrs.h>
#define STUN_BINDING_REQUEST 0x0001
#define STUN_BINDING_INDICATION 0x1100
@ -43,7 +44,7 @@ struct StunMsg {
uint8_t id[12];
};
static int stoon_init_mini(struct addrinfo *serv, uint16_t myport) {
static int stoon_init_mini(struct Stoon *this, struct addrinfo *serv, uint16_t myport) {
#ifdef _WIN32
errno = 0;
int fd = socket(serv->ai_family, serv->ai_socktype, serv->ai_protocol);
@ -59,16 +60,71 @@ static int stoon_init_mini(struct addrinfo *serv, uint16_t myport) {
char p[6];
sprintf(p, "%u", myport);
struct addrinfo *myaddr;
if(getaddrinfo(NULL, p, &(struct addrinfo) {.ai_family = serv->ai_family, .ai_socktype = SOCK_DGRAM, .ai_protocol = IPPROTO_UDP, .ai_flags = AI_PASSIVE}, &myaddr)) {
struct addrinfo *myaddrinfo;
if(getaddrinfo(NULL, p, &(struct addrinfo) {.ai_family = serv->ai_family, .ai_socktype = SOCK_DGRAM, .ai_protocol = IPPROTO_UDP, .ai_flags = AI_PASSIVE}, &myaddrinfo)) {
close(fd);
return -1;
}
if(bind(fd, myaddr->ai_addr, myaddr->ai_addrlen)) {
if(bind(fd, myaddrinfo->ai_addr, myaddrinfo->ai_addrlen)) {
close(fd);
return -2;
}
freeaddrinfo(myaddr);
freeaddrinfo(myaddrinfo);
#ifdef _WIN32
IP_ADAPTER_ADDRESSES *addrs = malloc(1024 * 64);
if(GetAdaptersAddresses(serv->ai_family, GAA_FLAG_SKIP_MULTICAST, NULL, &addrs) == NO_ERROR) {
for(IP_ADAPTER_ADDRESSES *ifa = addrs; ifa; ifa = ifa->Next) {
if(serv->ai_family == AF_INET6) {
uint8_t *addr = (void*) &((struct sockaddr_in6*) ifa->FirstUnicastAddress->Address.lpSockaddr)->sin6_addr;
if(addr[0] == 0 && addr[1] == 0 && addr[2] == 0 && addr[3] == 0 && addr[4] == 0 && addr[5] == 0 && addr[6] == 0 && addr[7] == 0 && addr[8] == 0 && addr[9] == 0 && addr[10] == 0 && addr[11] == 0 && addr[12] == 0 && addr[13] == 0 && addr[14] == 0 && (addr[15] == 0 || addr[15] == 1)) {
continue;
}
memcpy(this->peercode.localV6, addr, 16);
memcpy(this->peercode.localP6, &myport, 2);
} else {
uint8_t *addr = (void*) &((struct sockaddr_in*) ifa->FirstUnicastAddress->Address.lpSockaddr)->sin_addr;
if(addr[0] == 0 && addr[1] == 0 && addr[2] == 0 && addr[3] == 0 || addr[0] == 127 && addr[1] == 0 && addr[2] == 0 && addr[3] == 1) {
continue;
}
memcpy(this->peercode.localV4, addr, 4);
memcpy(this->peercode.localP4, &myport, 2);
}
}
}
free(addrs);
#else
struct ifaddrs *ifaddr;
if(getifaddrs(&ifaddr) >= 0) {
for(struct ifaddrs *ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
if(ifa->ifa_addr->sa_family == serv->ai_family) {
if(serv->ai_family == AF_INET6) {
uint8_t *addr = (void*) &((struct sockaddr_in6*) ifa->ifa_addr)->sin6_addr;
if(addr[0] == 0 && addr[1] == 0 && addr[2] == 0 && addr[3] == 0 && addr[4] == 0 && addr[5] == 0 && addr[6] == 0 && addr[7] == 0 && addr[8] == 0 && addr[9] == 0 && addr[10] == 0 && addr[11] == 0 && addr[12] == 0 && addr[13] == 0 && addr[14] == 0 && (addr[15] == 0 || addr[15] == 1)) {
continue;
}
memcpy(this->peercode.localV6, addr, 16);
memcpy(this->peercode.localP6, &myport, 2);
} else {
uint8_t *addr = (void*) &((struct sockaddr_in*) ifa->ifa_addr)->sin_addr;
if(addr[0] == 0 && addr[1] == 0 && addr[2] == 0 && addr[3] == 0 || addr[0] == 127 && addr[1] == 0 && addr[2] == 0 && addr[3] == 1) {
continue;
}
memcpy(this->peercode.localV4, addr, 4);
memcpy(this->peercode.localP4, &myport, 2);
}
break;
}
}
freeifaddrs(ifaddr);
}
#endif
return fd;
}
@ -92,18 +148,16 @@ struct Stoon stoon_init(const char *stunhost, uint16_t stunport, uint16_t myport
}
}
struct Stoon ret;
struct Stoon ret = {};
memset(&ret.my4, 0, sizeof(ret.my4));
if(v4) {
memcpy(&ret.stu4, v4->ai_addr, v4->ai_addrlen);
ret.fd4 = stoon_init_mini(v4, myport);
ret.fd4 = stoon_init_mini(&ret, v4, myport);
} else ret.fd4 = -1;
memset(&ret.my6, 0, sizeof(ret.my6));
if(v6) {
memcpy(&ret.stu6, v6->ai_addr, v6->ai_addrlen);
ret.fd6 = stoon_init_mini(v6, myport);
ret.fd6 = stoon_init_mini(&ret, v6, myport);
} else ret.fd6 = -1;
ret.poonchstage = 0;
@ -121,12 +175,21 @@ static int stoon_req_mini(struct Stoon *this, int *fd, struct sockaddr *serv, in
if(errno == ENETUNREACH) {
close(*fd);
*fd = -1;
return false;
}
}
return true;
}
int stoon_req(struct Stoon *this) {
if(this->fd4 >= 0) stoon_req_mini(this, &this->fd4, (struct sockaddr*) &this->stu4, sizeof(struct sockaddr_in));
if(this->fd6 >= 0) stoon_req_mini(this, &this->fd6, (struct sockaddr*) &this->stu6, sizeof(struct sockaddr_in6));
if(this->fd4 < 0 && this->fd6 < 0) {
return false;
}
return true;
}
static int stoon_listen_mini(struct Stoon *this, int fd, struct sockaddr *serv, int servlen) {
@ -161,11 +224,10 @@ static int stoon_listen_mini(struct Stoon *this, int fd, struct sockaddr *serv,
uint16_t netfam = ntohs(((uint16_t*) d)[2]);
uint16_t publicPort = ntohs(((uint16_t*) d)[3]) ^ (STUN_MAGIC >> 16);
if(netfam == STUN_NETFAM_IPV4) {
uint32_t publicIp = ntohl(((uint32_t*) d)[2]) ^ STUN_MAGIC;
uint32_t publicIp = htonl(ntohl(((uint32_t*) d)[2]) ^ STUN_MAGIC);
this->my4.sin_family = AF_INET;
memcpy(&this->my4.sin_addr, &publicIp, 4);
this->my4.sin_port = htons(publicPort);
memcpy(this->peercode.publicV4, &publicIp, 4);
memcpy(this->peercode.publicP4, &publicPort, 2);
return 1;
} else if(netfam == STUN_NETFAM_IPV6) {
@ -175,9 +237,8 @@ static int stoon_listen_mini(struct Stoon *this, int fd, struct sockaddr *serv,
publicIp[2] = ((uint32_t*) d)[4] ^ ((uint32_t*) res.base.id)[1];
publicIp[3] = ((uint32_t*) d)[5] ^ ((uint32_t*) res.base.id)[2];
this->my6.sin6_family = AF_INET6;
memcpy(&this->my6.sin6_addr, &publicIp, 16);
this->my6.sin6_port = htons(publicPort);
memcpy(this->peercode.publicV6, &publicIp, 16);
memcpy(this->peercode.publicP6, &publicPort, 2);
return 1;
} else {
@ -188,12 +249,12 @@ static int stoon_listen_mini(struct Stoon *this, int fd, struct sockaddr *serv,
d += 4 + attribLen;
}
return 0;
return false;
}
int stoon_listen(struct Stoon *this) {
if(this->fd4 >= 0) if(stoon_listen_mini(this, this->fd4, (struct sockaddr*) &this->stu4, sizeof(struct sockaddr_in)) < 0) return 0;
if(this->fd6 >= 0) if(stoon_listen_mini(this, this->fd6, (struct sockaddr*) &this->stu6, sizeof(struct sockaddr_in6)) < 0) return 0;
return 1;
return true;
}
void stoon_keepalive(struct Stoon *this) {
@ -217,31 +278,7 @@ void stoon_kill(struct Stoon *this) {
this->fd6 = -1;
}
int stoon_v4_available(struct Stoon *this) {
return this->fd4 >= 0;
}
int stoon_v6_available(struct Stoon *this) {
return this->fd6 >= 0;
}
void stoon_serialize(struct Stoon *this, uint8_t ret[static STOON_CONN_INFO_SIZE]) {
if(this->fd4 >= 0) {
memcpy(ret + 0, &this->my4.sin_addr, 4);
memcpy(ret + 4, &this->my4.sin_port, 2);
} else {
memset(ret, 0, 6);
}
if(this->fd6 >= 0) {
memcpy(ret + 6, &this->my6.sin6_addr, 16);
memcpy(ret + 22, &this->my6.sin6_port, 2);
} else {
memset(ret + 6, 0, 18);
}
}
int stoon_poonch(struct Stoon *this, const uint8_t peerdata[static STOON_CONN_INFO_SIZE]) {
/*int stoon_poonch(struct Stoon *this, const uint8_t peerdata[static STOON_CONN_INFO_SIZE]) {
struct {
uint32_t addr4;
uint16_t port4;
@ -303,7 +340,7 @@ int stoon_poonch(struct Stoon *this, const uint8_t peerdata[static STOON_CONN_IN
}
return STOON_POONCH_NO_KNOCK;
}
}*/
#ifdef STOON_STANDALONE
int main() {

View File

@ -6,6 +6,7 @@
#include<stddef.h>
#include<stdint.h>
#include<stdbool.h>
#ifdef _WIN32
#include<winsock2.h>
@ -16,8 +17,6 @@
/* Standalone hole punching libary (IPv4 & IPv6 compatible) */
#define STOON_CONN_INFO_SIZE (4 + 2 + 16 + 2)
#define POONCH_MAGIC 0x504F4348
#define POONCH_STAGE_KNOCKING 0
#define POONCH_STAGE_ACK 1
@ -26,16 +25,30 @@ struct Poonch {
uint32_t stage;
};
struct StoonPeercode {
uint8_t publicV4[4];
uint8_t publicP4[2];
uint8_t publicV6[16];
uint8_t publicP6[2];
uint8_t localV4[4];
uint8_t localP4[2];
uint8_t localV6[16];
uint8_t localP6[2];
};
struct Stoon {
struct sockaddr_in stu4;
struct sockaddr_in my4;
int fd4;
struct sockaddr_in6 stu6;
struct sockaddr_in6 my6;
int fd6;
uint8_t poonchstage;
struct StoonPeercode peercode;
};
// stunhost: Self-explanatory
@ -53,12 +66,8 @@ void stoon_keepalive(struct Stoon*);
void stoon_kill(struct Stoon*);
int stoon_v4_available(struct Stoon*);
int stoon_v6_available(struct Stoon*);
void stoon_serialize(struct Stoon*, uint8_t ret[static STOON_CONN_INFO_SIZE]);
#define STOON_POONCH_INCOMPATIBLE_NETFAMS (-1)
/*#define STOON_POONCH_INCOMPATIBLE_NETFAMS (-1)
#define STOON_POONCH_NO_KNOCK 0
#define STOON_POONCH_ACKED 1
int stoon_poonch(struct Stoon*, const uint8_t peerdata[static STOON_CONN_INFO_SIZE]);
*/