Merge branch 'master' into next

This commit is contained in:
Sally Coolatta 2020-11-05 16:15:42 -05:00
commit 1d8608c00d
15 changed files with 453 additions and 119 deletions

View file

@ -364,8 +364,9 @@ if(${SRB2_CONFIG_HAVE_DISCORDRPC})
if(${DISCORDRPC_FOUND}) if(${DISCORDRPC_FOUND})
set(SRB2_HAVE_DISCORDRPC ON) set(SRB2_HAVE_DISCORDRPC ON)
add_definitions(-DHAVE_DISCORDRPC) add_definitions(-DHAVE_DISCORDRPC)
set(SRB2_DISCORDRPC_SOURCES discord.c) add_definitions(-DUSE_STUN)
set(SRB2_DISCORDRPC_HEADERS discord.h) set(SRB2_DISCORDRPC_SOURCES discord.c stun.c)
set(SRB2_DISCORDRPC_HEADERS discord.h stun.h)
prepend_sources(SRB2_DISCORDRPC_SOURCES) prepend_sources(SRB2_DISCORDRPC_SOURCES)
prepend_sources(SRB2_DISCORDRPC_HEADERS) prepend_sources(SRB2_DISCORDRPC_HEADERS)
source_group("Discord Rich Presence" FILES ${SRB2_DISCORDRPC_SOURCES} ${SRB2_DISCORDRPC_HEADERS}) source_group("Discord Rich Presence" FILES ${SRB2_DISCORDRPC_SOURCES} ${SRB2_DISCORDRPC_HEADERS})

View file

@ -440,8 +440,8 @@ endif
ifdef HAVE_DISCORDRPC ifdef HAVE_DISCORDRPC
LIBS+=-ldiscord-rpc LIBS+=-ldiscord-rpc
CFLAGS+=-DHAVE_DISCORDRPC CFLAGS+=-DHAVE_DISCORDRPC -DUSE_STUN
OBJS+=$(OBJDIR)/discord.o OBJS+=$(OBJDIR)/discord.o $(OBJDIR)/stun.o
endif endif
ifndef NO_LUA ifndef NO_LUA

View file

@ -19,6 +19,7 @@
#include "lualib.h" #include "lualib.h"
#include "../i_system.h" #include "../i_system.h"
#include "../doomdef.h" #include "../doomdef.h"
#include "../d_main.h"
#include "../m_misc.h" #include "../m_misc.h"
@ -190,7 +191,7 @@ static int io_open (lua_State *L) {
return pushresult(L,0,filename); return pushresult(L,0,filename);
} }
destFilename = va("luafiles"PATHSEP"%s", filename); destFilename = va("%s"PATHSEP"luafiles"PATHSEP"%s", srb2home, filename);
// Make directories as needed // Make directories as needed
splitter = destFilename; splitter = destFilename;

View file

@ -2263,7 +2263,10 @@ static boolean CL_ServerConnectionSearchTicker(tic_t *asksent)
cl_mode = CL_CHECKFILES; cl_mode = CL_CHECKFILES;
} }
else else
{
cl_mode = CL_ASKJOIN; // files need not be checked for the server. cl_mode = CL_ASKJOIN; // files need not be checked for the server.
*asksent = 0;
}
return true; return true;
} }
@ -3469,6 +3472,76 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
} }
} }
#ifdef HAVE_CURL
/** Add a login for HTTP downloads. If the
* user/password is missing, remove it.
*
* \sa Command_list_http_logins
*/
static void Command_set_http_login (void)
{
HTTP_login *login;
HTTP_login **prev_next;
if (COM_Argc() < 2)
{
CONS_Printf(
"set_http_login <URL> [user:password]: Set or remove a login to "
"authenticate HTTP downloads.\n"
);
return;
}
login = CURLGetLogin(COM_Argv(1), &prev_next);
if (COM_Argc() == 2)
{
if (login)
{
(*prev_next) = login->next;
CONS_Printf("Login for '%s' removed.\n", login->url);
Z_Free(login);
}
}
else
{
if (login)
Z_Free(login->auth);
else
{
login = ZZ_Alloc(sizeof *login);
login->url = Z_StrDup(COM_Argv(1));
}
login->auth = Z_StrDup(COM_Argv(2));
login->next = curl_logins;
curl_logins = login;
}
}
/** List logins for HTTP downloads.
*
* \sa Command_set_http_login
*/
static void Command_list_http_logins (void)
{
HTTP_login *login;
for (
login = curl_logins;
login;
login = login->next
){
CONS_Printf(
"'%s' -> '%s'\n",
login->url,
login->auth
);
}
}
#endif/*HAVE_CURL*/
static CV_PossibleValue_t netticbuffer_cons_t[] = {{0, "MIN"}, {3, "MAX"}, {0, NULL}}; static CV_PossibleValue_t netticbuffer_cons_t[] = {{0, "MIN"}, {3, "MAX"}, {0, NULL}};
consvar_t cv_netticbuffer = {"netticbuffer", "1", CV_SAVE, netticbuffer_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_netticbuffer = {"netticbuffer", "1", CV_SAVE, netticbuffer_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
@ -3537,6 +3610,10 @@ void D_ClientServerInit(void)
COM_AddCommand("reloadbans", Command_ReloadBan); COM_AddCommand("reloadbans", Command_ReloadBan);
COM_AddCommand("connect", Command_connect); COM_AddCommand("connect", Command_connect);
COM_AddCommand("nodes", Command_Nodes); COM_AddCommand("nodes", Command_Nodes);
#ifdef HAVE_CURL
COM_AddCommand("set_http_login", Command_set_http_login);
COM_AddCommand("list_http_logins", Command_list_http_logins);
#endif
#ifdef PACKETDROP #ifdef PACKETDROP
COM_AddCommand("drop", Command_Drop); COM_AddCommand("drop", Command_Drop);
COM_AddCommand("droprate", Command_Droprate); COM_AddCommand("droprate", Command_Droprate);

View file

@ -502,6 +502,7 @@ extern INT32 mapchangepending;
// Points inside doomcom // Points inside doomcom
extern doomdata_t *netbuffer; extern doomdata_t *netbuffer;
extern consvar_t cv_stunserver;
extern consvar_t cv_httpsource; extern consvar_t cv_httpsource;
extern consvar_t cv_showjoinaddress; extern consvar_t cv_showjoinaddress;
extern consvar_t cv_playbackspeed; extern consvar_t cv_playbackspeed;

View file

@ -714,6 +714,10 @@ void D_RegisterServerCommands(void)
CV_RegisterVar(&cv_dummyconsvar); CV_RegisterVar(&cv_dummyconsvar);
#ifdef USE_STUN
CV_RegisterVar(&cv_stunserver);
#endif
CV_RegisterVar(&cv_discordinvites); CV_RegisterVar(&cv_discordinvites);
RegisterNetXCmd(XD_DISCORD, Got_DiscordInfo); RegisterNetXCmd(XD_DISCORD, Got_DiscordInfo);
} }

View file

@ -128,6 +128,7 @@ static UINT32 curl_origfilesize;
static UINT32 curl_origtotalfilesize; static UINT32 curl_origtotalfilesize;
static char *curl_realname = NULL; static char *curl_realname = NULL;
fileneeded_t *curl_curfile = NULL; fileneeded_t *curl_curfile = NULL;
HTTP_login *curl_logins;
#endif #endif
/** Fills a serverinfo packet with information about wad files loaded. /** Fills a serverinfo packet with information about wad files loaded.
@ -430,10 +431,10 @@ INT32 CL_CheckFiles(void)
for (i = 0; i < fileneedednum; i++) for (i = 0; i < fileneedednum; i++)
{ {
if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_FALLBACK) if (fileneeded[i].status == FS_NOTFOUND || fileneeded[i].status == FS_MD5SUMBAD || fileneeded[i].status == FS_FALLBACK)
downloadrequired = true; downloadrequired = true;
if (fileneeded[i].status == FS_FOUND || fileneeded[i].status == FS_NOTFOUND) if (fileneeded[i].status != FS_OPEN)
filestoload++; filestoload++;
if (fileneeded[i].status != FS_NOTCHECKED) //since we're running this over multiple tics now, its possible for us to come across files checked in previous tics if (fileneeded[i].status != FS_NOTCHECKED) //since we're running this over multiple tics now, its possible for us to come across files checked in previous tics
@ -1082,6 +1083,8 @@ int curlprogress_callback(void *clientp, double dltotal, double dlnow, double ul
void CURLPrepareFile(const char* url, int dfilenum) void CURLPrepareFile(const char* url, int dfilenum)
{ {
HTTP_login *login;
#ifdef PARANOIA #ifdef PARANOIA
if (M_CheckParm("-nodownload")) if (M_CheckParm("-nodownload"))
I_Error("Attempted to download files in -nodownload mode"); I_Error("Attempted to download files in -nodownload mode");
@ -1110,6 +1113,14 @@ void CURLPrepareFile(const char* url, int dfilenum)
curl_easy_setopt(http_handle, CURLOPT_USERAGENT, va("SRB2Kart/v%d.%d", VERSION, SUBVERSION)); // Set user agent as some servers won't accept invalid user agents. curl_easy_setopt(http_handle, CURLOPT_USERAGENT, va("SRB2Kart/v%d.%d", VERSION, SUBVERSION)); // Set user agent as some servers won't accept invalid user agents.
// Authenticate if the user so wishes
login = CURLGetLogin(url, NULL);
if (login)
{
curl_easy_setopt(http_handle, CURLOPT_USERPWD, login->auth);
}
// Follow a redirect request, if sent by the server. // Follow a redirect request, if sent by the server.
curl_easy_setopt(http_handle, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(http_handle, CURLOPT_FOLLOWLOCATION, 1L);
@ -1211,4 +1222,27 @@ void CURLGetFile(void)
curl_global_cleanup(); curl_global_cleanup();
} }
} }
HTTP_login *
CURLGetLogin (const char *url, HTTP_login ***return_prev_next)
{
HTTP_login * login;
HTTP_login ** prev_next;
for (
prev_next = &curl_logins;
( login = (*prev_next));
prev_next = &login->next
){
if (strcmp(login->url, url) == 0)
{
if (return_prev_next)
(*return_prev_next) = prev_next;
return login;
}
}
return NULL;
}
#endif #endif

View file

@ -63,6 +63,16 @@ extern UINT32 totalfilesrequestedsize;
extern boolean curl_failedwebdownload; extern boolean curl_failedwebdownload;
extern boolean curl_running; extern boolean curl_running;
extern INT32 curl_transfers; extern INT32 curl_transfers;
typedef struct HTTP_login HTTP_login;
extern struct HTTP_login
{
char * url;
char * auth;
HTTP_login * next;
}
*curl_logins;
#endif #endif
UINT8 *PutFileNeeded(UINT16 firstfile); UINT8 *PutFileNeeded(UINT16 firstfile);
@ -98,6 +108,7 @@ size_t nameonlylength(const char *s);
#ifdef HAVE_CURL #ifdef HAVE_CURL
void CURLPrepareFile(const char* url, int dfilenum); void CURLPrepareFile(const char* url, int dfilenum);
void CURLGetFile(void); void CURLGetFile(void);
HTTP_login * CURLGetLogin (const char *url, HTTP_login ***return_prev_next);
#endif #endif
#endif // __D_NETFIL__ #endif // __D_NETFIL__

View file

@ -12,9 +12,7 @@
#ifdef HAVE_DISCORDRPC #ifdef HAVE_DISCORDRPC
#ifdef HAVE_CURL #include <time.h>
#include <curl/curl.h>
#endif // HAVE_CURL
#include "i_system.h" #include "i_system.h"
#include "d_clisrv.h" #include "d_clisrv.h"
@ -27,6 +25,8 @@
#include "mserv.h" // cv_advertise #include "mserv.h" // cv_advertise
#include "z_zone.h" #include "z_zone.h"
#include "byteptr.h" #include "byteptr.h"
#include "stun.h"
#include "i_tcp.h" // current_port
#include "discord.h" #include "discord.h"
#include "doomdef.h" #include "doomdef.h"
@ -45,16 +45,7 @@ struct discordInfo_s discordInfo;
discordRequest_t *discordRequestList = NULL; discordRequest_t *discordRequestList = NULL;
#ifdef HAVE_CURL
struct SelfIPbuffer
{
CURL *curl;
char *pointer;
size_t length;
};
static char self_ip[IP_SIZE]; static char self_ip[IP_SIZE];
#endif // HAVE_CURL
/*-------------------------------------------------- /*--------------------------------------------------
static char *DRPC_XORIPString(const char *input) static char *DRPC_XORIPString(const char *input)
@ -335,39 +326,23 @@ void DRPC_Init(void)
DRPC_UpdatePresence(); DRPC_UpdatePresence();
} }
#ifdef HAVE_CURL
/*-------------------------------------------------- /*--------------------------------------------------
static size_t DRPC_WriteServerIP(char *s, size_t size, size_t n, void *userdata) static void DRPC_GotServerIP(UINT32 address)
Writing function for use with curl. Only intended to be used with simple text. Callback triggered by successful STUN response.
Input Arguments:- Input Arguments:-
s - Data to write address - IPv4 address of this machine, in network byte order.
size - Always 1.
n - Length of data
userdata - Passed in from CURLOPT_WRITEDATA, intended to be SelfIPbuffer
Return:- Return:-
Number of bytes wrote in this pass. None
--------------------------------------------------*/ --------------------------------------------------*/
static size_t DRPC_WriteServerIP(char *s, size_t size, size_t n, void *userdata) static void DRPC_GotServerIP(UINT32 address)
{ {
struct SelfIPbuffer *buffer; const unsigned char * p = (const unsigned char *)&address;
size_t newlength; sprintf(self_ip, "%u.%u.%u.%u:%u", p[0], p[1], p[2], p[3], current_port);
DRPC_UpdatePresence();
buffer = userdata;
newlength = buffer->length + size*n;
buffer->pointer = realloc(buffer->pointer, newlength+1);
memcpy(buffer->pointer + buffer->length, s, size*n);
buffer->pointer[newlength] = '\0';
buffer->length = newlength;
return size*n;
} }
#endif // HAVE_CURL
/*-------------------------------------------------- /*--------------------------------------------------
static const char *DRPC_GetServerIP(void) static const char *DRPC_GetServerIP(void)
@ -387,64 +362,21 @@ static const char *DRPC_GetServerIP(void)
{ {
// We're not the server, so we could successfully get the IP! // We're not the server, so we could successfully get the IP!
// No need to do anything else :) // No need to do anything else :)
return address; sprintf(self_ip, "%s:%u", address, current_port);
return self_ip;
} }
} }
#ifdef HAVE_CURL
// This is a little bit goofy, but
// there's practically no good way to get your own public IP address,
// so we've gotta break out curl for this :V
if (!self_ip[0])
{
CURL *curl;
curl_global_init(CURL_GLOBAL_ALL);
curl = curl_easy_init();
if (curl)
{
// The API to get your public IP address from.
// Picked because it's stupid simple and it's been up for a long time.
const char *api = "http://ip4only.me/api/";
struct SelfIPbuffer buffer;
CURLcode success;
buffer.length = 0;
buffer.pointer = malloc(buffer.length+1);
buffer.pointer[0] = '\0';
curl_easy_setopt(curl, CURLOPT_URL, api);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, DRPC_WriteServerIP);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
success = curl_easy_perform(curl);
if (success == CURLE_OK)
{
char *tmp;
tmp = strtok(buffer.pointer, ",");
if (!strcmp(tmp, "IPv4")) // ensure correct type of IP
{
tmp = strtok(NULL, ",");
strncpy(self_ip, tmp, IP_SIZE); // Yay, we have the IP :)
}
}
free(buffer.pointer);
curl_easy_cleanup(curl);
}
curl_global_cleanup();
}
if (self_ip[0]) if (self_ip[0])
{
return self_ip; return self_ip;
}
else else
#endif // HAVE_CURL {
return NULL; // Could not get your IP for whatever reason, so we cannot do Discord invites // There happens to be a good way to get it after all! :D
STUN_bind(DRPC_GotServerIP);
return NULL;
}
} }
/*-------------------------------------------------- /*--------------------------------------------------
@ -510,19 +442,6 @@ void DRPC_UpdatePresence(void)
// Server info // Server info
if (netgame) if (netgame)
{ {
if (cv_advertise.value)
{
discordPresence.state = "Public";
}
else
{
discordPresence.state = "Private";
}
discordPresence.partyId = server_context; // Thanks, whoever gave us Mumble support, for implementing the EXACT thing Discord wanted for this field!
discordPresence.partySize = D_NumPlayers(); // Players in server
discordPresence.partyMax = discordInfo.maxPlayers; // Max players
if (DRPC_InvitesAreAllowed() == true) if (DRPC_InvitesAreAllowed() == true)
{ {
const char *join; const char *join;
@ -536,7 +455,24 @@ void DRPC_UpdatePresence(void)
joinSecretSet = true; joinSecretSet = true;
} }
else
{
return;
}
} }
if (cv_advertise.value)
{
discordPresence.state = "Public";
}
else
{
discordPresence.state = "Private";
}
discordPresence.partyId = server_context; // Thanks, whoever gave us Mumble support, for implementing the EXACT thing Discord wanted for this field!
discordPresence.partySize = D_NumPlayers(); // Players in server
discordPresence.partyMax = discordInfo.maxPlayers; // Max players
} }
else else
{ {

View file

@ -182,6 +182,7 @@ static UINT8 UPNP_support = TRUE;
#include "d_netfil.h" #include "d_netfil.h"
#include "i_tcp.h" #include "i_tcp.h"
#include "m_argv.h" #include "m_argv.h"
#include "stun.h"
#include "doomstat.h" #include "doomstat.h"
@ -612,6 +613,13 @@ static boolean SOCK_Get(void)
(void *)&fromaddress, &fromlen); (void *)&fromaddress, &fromlen);
if (c != ERRSOCKET) if (c != ERRSOCKET)
{ {
#ifdef USE_STUN
if (STUN_got_response(doomcom->data, c))
{
return false;
}
#endif
// find remote node number // find remote node number
for (j = 1; j <= MAXNETNODES; j++) //include LAN for (j = 1; j <= MAXNETNODES; j++) //include LAN
{ {

View file

@ -16,16 +16,12 @@
#include "endian.h" #include "endian.h"
// Endianess handling. #define SWAP_SHORT(x) ((INT16)(\
// WAD files are stored little endian.
#ifdef SRB2_BIG_ENDIAN
#define SHORT(x) ((INT16)(\
(((UINT16)(x) & (UINT16)0x00ffU) << 8) \ (((UINT16)(x) & (UINT16)0x00ffU) << 8) \
| \ | \
(((UINT16)(x) & (UINT16)0xff00U) >> 8))) \ (((UINT16)(x) & (UINT16)0xff00U) >> 8))) \
#define LONG(x) ((INT32)(\ #define SWAP_LONG(x) ((INT32)(\
(((UINT32)(x) & (UINT32)0x000000ffUL) << 24) \ (((UINT32)(x) & (UINT32)0x000000ffUL) << 24) \
| \ | \
(((UINT32)(x) & (UINT32)0x0000ff00UL) << 8) \ (((UINT32)(x) & (UINT32)0x0000ff00UL) << 8) \
@ -34,9 +30,18 @@
| \ | \
(((UINT32)(x) & (UINT32)0xff000000UL) >> 24))) (((UINT32)(x) & (UINT32)0xff000000UL) >> 24)))
// Endianess handling.
// WAD files are stored little endian.
#ifdef SRB2_BIG_ENDIAN
#define SHORT SWAP_SHORT
#define LONG SWAP_LONG
#define MSBF_SHORT(x) ((INT16)(x))
#define MSBF_LONG(x) ((INT32)(x))
#else #else
#define SHORT(x) ((INT16)(x)) #define SHORT(x) ((INT16)(x))
#define LONG(x) ((INT32)(x)) #define LONG(x) ((INT32)(x))
#define MSBF_SHORT SWAP_SHORT
#define MSBF_LONG SWAP_LONG
#endif #endif
#endif #endif

View file

@ -1952,7 +1952,7 @@ static boolean S_PlayMusic(boolean looping, UINT32 fadeinms)
S_InitMusicVolume(); // switch between digi and sequence volume S_InitMusicVolume(); // switch between digi and sequence volume
if (window_notinfocus && !cv_playmusicifunfocused.value) if (window_notinfocus && !cv_playmusicifunfocused.value)
I_PauseSong(); I_SetMusicVolume(0);
return true; return true;
} }
@ -2418,9 +2418,9 @@ static void PlayMusicIfUnfocused_OnChange(void)
if (window_notinfocus) if (window_notinfocus)
{ {
if (cv_playmusicifunfocused.value) if (cv_playmusicifunfocused.value)
I_PauseSong(); I_SetMusicVolume(0);
else else
I_ResumeSong(); S_InitMusicVolume();
} }
} }

View file

@ -625,7 +625,7 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt)
window_notinfocus = false; window_notinfocus = false;
if (!paused) if (!paused)
I_ResumeSong(); //resume it S_InitMusicVolume();
if (cv_gamesounds.value) if (cv_gamesounds.value)
S_EnableSound(); S_EnableSound();
@ -641,7 +641,7 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt)
// Tell game we lost focus, pause music // Tell game we lost focus, pause music
window_notinfocus = true; window_notinfocus = true;
if (!cv_playmusicifunfocused.value) if (!cv_playmusicifunfocused.value)
I_PauseSong(); I_SetMusicVolume(0);
if (!cv_playsoundifunfocused.value) if (!cv_playsoundifunfocused.value)
S_DisableSound(); S_DisableSound();

236
src/stun.c Normal file
View file

@ -0,0 +1,236 @@
// SONIC ROBO BLAST 2 KART
//-----------------------------------------------------------------------------
// Copyright (C) 2020 by James R.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file stun.c
/// \brief RFC 5389 client implementation to fetch external IP address.
/* https://tools.ietf.org/html/rfc5389 */
#if defined (__linux__)
#include <sys/random.h>
#elif defined (_WIN32)
#define _CRT_RAND_S
#elif defined (__APPLE__)
#include <CommonCrypto/CommonRandom.h>
#else
#error "Need CSPRNG."
#endif
#include "doomdef.h"
#include "d_clisrv.h"
#include "command.h"
#include "i_net.h"
#include "stun.h"
/* https://gist.github.com/zziuni/3741933 */
/* I can only trust google to keep their shit up :y */
consvar_t cv_stunserver = {
"stunserver", "stun.l.google.com:19302", CV_SAVE, NULL,
NULL, 0, NULL, NULL, 0, 0, NULL/* C90 moment */
};
static stun_callback_t stun_callback;
/* 18.4 STUN UDP and TCP Port Numbers */
#define STUN_PORT "3478"
/* 6. STUN Message Structure */
#define BIND_REQUEST 0x0001
#define BIND_RESPONSE 0x0101
static const UINT32 MAGIC_COOKIE = MSBF_LONG (0x2112A442);
static char transaction_id[12];
/* 18.2 STUN Attribute Registry */
#define XOR_MAPPED_ADDRESS 0x0020
/* 15.1 MAPPED-ADDRESS */
#define STUN_IPV4 0x01
static SINT8
STUN_node (void)
{
SINT8 node;
char * const colon = strchr(cv_stunserver.zstring, ':');
const char * const host = cv_stunserver.zstring;
const char * const port = &colon[1];
I_Assert(I_NetMakeNodewPort != NULL);
if (colon != NULL)
{
*colon = '\0';
node = I_NetMakeNodewPort(host, port);
*colon = ':';
}
else
{
node = I_NetMakeNodewPort(host, STUN_PORT);
}
return node;
}
static void
csprng
(
void * const buffer,
const size_t size
){
#if defined (_WIN32)
size_t o;
for (o = 0; o < size; o += sizeof (unsigned int))
{
rand_s((unsigned int *)&((char *)buffer)[o]);
}
#elif defined (__linux__)
getrandom(buffer, size, 0U);
#elif defined (__APPLE__)
CCRandomGenerateBytes(buffer, size);
#elif defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
arc4random_buf(buffer, size);
#endif
}
void
STUN_bind (stun_callback_t callback)
{
/* 6. STUN Message Structure */
const UINT16 type = MSBF_SHORT (BIND_REQUEST);
const SINT8 node = STUN_node();
doomcom->remotenode = node;
doomcom->datalength = 20;
csprng(transaction_id, 12U);
memcpy(&doomcom->data[0], &type, 2U);
memset(&doomcom->data[2], 0, 2U);
memcpy(&doomcom->data[4], &MAGIC_COOKIE, 4U);
memcpy(&doomcom->data[8], transaction_id, 12U);
stun_callback = callback;
I_NetSend();
Net_CloseConnection(node);/* will handle response at I_NetGet */
}
static size_t
STUN_xor_mapped_address (const char * const value)
{
const UINT32 xaddr = *(const UINT32 *)&value[4];
const UINT32 addr = xaddr ^ MAGIC_COOKIE;
(*stun_callback)(addr);
return 0U;
}
static size_t
align4 (size_t n)
{
return n + n % 4U;
}
static size_t
STUN_parse_attribute (const char * const attribute)
{
/* 15. STUN Attributes */
const UINT16 type = MSBF_SHORT (*(const UINT16 *)&attribute[0]);
const UINT16 length = MSBF_SHORT (*(const UINT16 *)&attribute[2]);
/* 15.2 XOR-MAPPED-ADDRESS */
if (
type == XOR_MAPPED_ADDRESS &&
length == 8U &&
(unsigned char)attribute[5] == STUN_IPV4
){
return STUN_xor_mapped_address(&attribute[4]);
}
return align4(4U + length);
}
boolean
STUN_got_response
(
const char * const buffer,
const size_t size
){
const char * const end = &buffer[size];
const char * p = &buffer[20];
UINT16 type;
UINT16 length;
/*
Check for STUN response.
Header is 20 bytes.
XOR-MAPPED-ADDRESS attribute is required.
Each attribute has a 2 byte header.
The XOR-MAPPED-ADDRESS attribute also has a 8 byte value.
This totals 10 bytes for the attribute.
*/
if (size < 30U || stun_callback == NULL)
{
return false;
}
/* 6. STUN Message Structure */
if (
*(const UINT32 *)&buffer[4] == MAGIC_COOKIE &&
memcmp(&buffer[8], transaction_id, 12U) == 0
){
type = MSBF_SHORT (*(const UINT16 *)&buffer[0]);
length = MSBF_SHORT (*(const UINT16 *)&buffer[2]);
if (
(type >> 14) == 0U &&
(length & 0x02) == 0U &&
(20U + length) <= size
){
if (type == BIND_RESPONSE)
{
do
{
length = STUN_parse_attribute(p);
if (length == 0U)
{
break;
}
p += length;
}
while (p < end) ;
}
stun_callback = NULL;
return true;
}
}
return false;
}

20
src/stun.h Normal file
View file

@ -0,0 +1,20 @@
// SONIC ROBO BLAST 2 KART
//-----------------------------------------------------------------------------
// Copyright (C) 2020 by James R.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file stun.h
/// \brief RFC 5389 client implementation to fetch external IP address.
#ifndef KART_STUN_H
#define KART_STUN_H
typedef void (*stun_callback_t)(UINT32 address);
void STUN_bind (stun_callback_t);
boolean STUN_got_response (const char * const buffer, const size_t size);
#endif/*KART_STUN_H*/