diff --git a/engine/common/net_http.c b/engine/common/net_http.c new file mode 100644 index 00000000..8926a88a --- /dev/null +++ b/engine/common/net_http.c @@ -0,0 +1,916 @@ +/* +net_http.c - HTTP client implementation +Copyright (C) 2024 mittorn +Copyright (C) 2024 Alibek Omarov + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "common.h" +#include "client.h" // ConnectionProgress +#include "netchan.h" +#include "xash3d_mathlib.h" +#include "ipv6text.h" +#include "net_ws_private.h" + +/* +================================================= + +HTTP downloader + +================================================= +*/ + +#define MAX_HTTP_BUFFER_SIZE (BIT( 13 )) + +typedef struct httpserver_s +{ + char host[256]; + int port; + char path[MAX_SYSPATH]; + qboolean needfree; + struct httpserver_s *next; + +} httpserver_t; + +enum connectionstate +{ + HTTP_QUEUE = 0, + HTTP_OPENED, + HTTP_SOCKET, + HTTP_NS_RESOLVED, + HTTP_CONNECTED, + HTTP_REQUEST, + HTTP_REQUEST_SENT, + HTTP_RESPONSE_RECEIVED, + HTTP_FREE +}; + +typedef struct httpfile_s +{ + struct httpfile_s *next; + httpserver_t *server; + char path[MAX_SYSPATH]; + file_t *file; + int socket; + int size; + int downloaded; + int lastchecksize; + float checktime; + float blocktime; + int id; + enum connectionstate state; + qboolean process; + resource_t *resource; + + string query_backup; + + // query or response + char buf[MAX_HTTP_BUFFER_SIZE+1]; + int header_size, query_length, bytes_sent; +} httpfile_t; + +static struct http_static_s +{ + // file and server lists + httpfile_t *first_file, *last_file; + httpserver_t *first_server, *last_server; +} http; + + +static CVAR_DEFINE_AUTO( http_useragent, "", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "User-Agent string" ); +static CVAR_DEFINE_AUTO( http_autoremove, "1", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "remove broken files" ); +static CVAR_DEFINE_AUTO( http_timeout, "45", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "timeout for http downloader" ); +static CVAR_DEFINE_AUTO( http_maxconnections, "2", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "maximum http connection number" ); +static CVAR_DEFINE_AUTO( http_show_request_header, "0", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "show headers of the HTTP request" ); + +/* +======================== +HTTP_ClearCustomServers +======================== +*/ +void HTTP_ClearCustomServers( void ) +{ + if( http.first_file ) + return; // may be referenced + + while( http.first_server && http.first_server->needfree ) + { + httpserver_t *tmp = http.first_server; + + http.first_server = http.first_server->next; + Mem_Free( tmp ); + } +} + +/* +============== +HTTP_FreeFile + +Skip to next server/file +============== +*/ +static void HTTP_FreeFile( httpfile_t *file, qboolean error ) +{ + char incname[256]; + + // Allways close file and socket + if( file->file ) + FS_Close( file->file ); + + file->file = NULL; + + if( file->socket != -1 ) + closesocket( file->socket ); + + file->socket = -1; + + Q_snprintf( incname, sizeof( incname ), DEFAULT_DOWNLOADED_DIRECTORY "%s.incomplete", file->path ); + if( error ) + { + // Switch to next fastdl server if present + if( file->server && ( file->state > HTTP_QUEUE ) && ( file->state != HTTP_FREE )) + { + file->server = file->server->next; + file->state = HTTP_QUEUE; // Reset download state, HTTP_Run() will open file again + return; + } + + // Called because there was no servers to download, free file now + if( http_autoremove.value == 1 ) // remove broken file + FS_Delete( incname ); + else // autoremove disabled, keep file + Con_Printf( "cannot download %s from any server. " + "You may remove %s now\n", file->path, incname ); // Warn about trash file + +#if !XASH_DEDICATED + if( file->process ) + { + if( file->resource ) + { + char buf[1024]; + sizebuf_t msg; + + MSG_Init( &msg, "DlFile", buf, sizeof( buf )); + MSG_BeginClientCmd( &msg, clc_stringcmd ); + MSG_WriteStringf( &msg, "dlfile %s", file->path ); + + Netchan_CreateFragments( &cls.netchan, &msg ); + Netchan_FragSend( &cls.netchan ); + } + else + { + CL_ProcessFile( false, file->path ); // Process file, increase counter + } + } +#endif // !XASH_DEDICATED + } + else + { + // Success, rename and process file + char name[256]; + + Q_snprintf( name, sizeof( name ), DEFAULT_DOWNLOADED_DIRECTORY "%s", file->path ); + FS_Rename( incname, name ); + +#if !XASH_DEDICATED + if( file->process ) + CL_ProcessFile( true, name ); + else +#endif + { + Con_Printf( "successfully downloaded %s, processing disabled!\n", name ); + } + } + + file->state = HTTP_FREE; +} + +/* +=================== +HTTP_AutoClean + +remove files with HTTP_FREE state from list +=================== +*/ +static void HTTP_AutoClean( void ) +{ + httpfile_t *curfile, *prevfile = 0; + + // clean all files marked to free + for( curfile = http.first_file; curfile; curfile = curfile->next ) + { + if( curfile->state != HTTP_FREE ) + { + prevfile = curfile; + continue; + } + + if( curfile == http.first_file ) + { + http.first_file = http.first_file->next; + Mem_Free( curfile ); + curfile = http.first_file; + if( !curfile ) + break; + continue; + } + + if( prevfile ) + prevfile->next = curfile->next; + Mem_Free( curfile ); + curfile = prevfile; + if( !curfile ) + break; + } + http.last_file = prevfile; +} + +/* +=================== +HTTP_ProcessStream + +process incoming data +=================== +*/ +static qboolean HTTP_ProcessStream( httpfile_t *curfile ) +{ + char buf[sizeof( curfile->buf )]; + char *begin = 0; + int res; + + if( curfile->header_size >= sizeof( buf )) + { + Con_Reportf( S_ERROR "Header too big, the size is %d\n", curfile->header_size ); + HTTP_FreeFile( curfile, true ); + return false; + } + + while( ( res = recv( curfile->socket, buf, sizeof( buf ) - curfile->header_size, 0 )) > 0) // if we got there, we are receiving data + { + curfile->blocktime = 0; + + if( curfile->state < HTTP_RESPONSE_RECEIVED ) // Response still not received + { + memcpy( curfile->buf + curfile->header_size, buf, res ); + curfile->buf[curfile->header_size + res] = 0; + begin = Q_strstr( curfile->buf, "\r\n\r\n" ); + + if( begin ) // Got full header + { + int cutheadersize = begin - curfile->buf + 4; // after that begin of data + char *content_length_line; + + if( !Q_strstr( curfile->buf, "200 OK" )) + { + *begin = 0; // cut string to print out response + begin = Q_strchr( curfile->buf, '\r' ); + + if( !begin ) begin = Q_strchr( curfile->buf, '\n' ); + if( begin ) + *begin = 0; + + Con_Printf( S_ERROR "%s: bad response: %s\n", curfile->path, curfile->buf ); + + if( http_show_request_header.value ) + Con_Printf( "Request headers: %s", curfile->query_backup ); + HTTP_FreeFile( curfile, true ); + return false; + } + + // print size + content_length_line = Q_stristr( curfile->buf, "Content-Length: " ); + if( content_length_line ) + { + int size; + + content_length_line += sizeof( "Content-Length: " ) - 1; + size = Q_atoi( content_length_line ); + + Con_Reportf( "HTTP: Got 200 OK! File size is %d\n", size ); + + if( ( curfile->size != -1 ) && ( curfile->size != size )) // check size if specified, not used + Con_Reportf( S_WARN "Server reports wrong file size for %s!\n", curfile->path ); + + curfile->size = size; + curfile->header_size = 0; + } + + if( curfile->size == -1 ) + { + // Usually fastdl's reports file size if link is correct + Con_Printf( S_ERROR "file size is unknown, refusing download!\n" ); + HTTP_FreeFile( curfile, true ); + return false; + } + + curfile->state = HTTP_RESPONSE_RECEIVED; // got response, let's start download + begin += 4; + + // Write remaining message part + if( res - cutheadersize - curfile->header_size > 0 ) + { + int ret = FS_Write( curfile->file, begin, res - cutheadersize - curfile->header_size ); + + if( ret != res - cutheadersize - curfile->header_size ) // could not write file + { + // close it and go to next + Con_Printf( S_ERROR "write failed for %s!\n", curfile->path ); + HTTP_FreeFile( curfile, true ); + return false; + } + curfile->downloaded += ret; + } + } + else + curfile->header_size += res; + } + else if( res > 0 ) + { + // data download + int ret = FS_Write( curfile->file, buf, res ); + + if ( ret != res ) + { + // close it and go to next + Con_Printf( S_ERROR "write failed for %s!\n", curfile->path ); + curfile->state = HTTP_FREE; + HTTP_FreeFile( curfile, true ); + return false; + } + + curfile->downloaded += ret; + curfile->lastchecksize += ret; + + // as after it will run in same frame + if( curfile->checktime > 5 ) + { + float speed = (float)curfile->lastchecksize / ( 5.0f * 1024 ); + + curfile->checktime = 0; + Con_Reportf( "download speed %f KB/s\n", speed ); + curfile->lastchecksize = 0; + } + } + } + curfile->checktime += host.frametime; + + return true; +} + +/* +============== +HTTP_Run + +Download next file block of each active file +Call every frame +============== +*/ +void HTTP_Run( void ) +{ + httpfile_t *curfile; + int iActiveCount = 0; + int iProgressCount = 0; + float flProgress = 0; + qboolean fResolving = false; + + for( curfile = http.first_file; curfile; curfile = curfile->next ) + { + struct sockaddr_storage addr; + + if( curfile->state == HTTP_FREE ) + continue; + + if( curfile->state == HTTP_QUEUE ) + { + char name[MAX_SYSPATH]; + + if( iActiveCount > http_maxconnections.value ) + continue; + + if( !curfile->server ) + { + Con_Printf( S_ERROR "no servers to download %s!\n", curfile->path ); + HTTP_FreeFile( curfile, true ); + break; + } + + Con_Reportf( "HTTP: Starting download %s from %s\n", curfile->path, curfile->server->host ); + Q_snprintf( name, sizeof( name ), DEFAULT_DOWNLOADED_DIRECTORY "%s.incomplete", curfile->path ); + + curfile->file = FS_Open( name, "wb", true ); + + if( !curfile->file ) + { + Con_Printf( S_ERROR "HTTP: cannot open %s!\n", name ); + HTTP_FreeFile( curfile, true ); + break; + } + + curfile->state = HTTP_OPENED; + curfile->blocktime = 0; + curfile->downloaded = 0; + curfile->lastchecksize = 0; + curfile->checktime = 0; + } + + iActiveCount++; + + if( curfile->state < HTTP_SOCKET ) // Socket is not created + { + dword mode; + + curfile->socket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); + + // Now set non-blocking mode + // You may skip this if not supported by system, + // but download will lock engine, maybe you will need to add manual returns + mode = 1; + ioctlsocket( curfile->socket, FIONBIO, (void*)&mode ); +#if XASH_LINUX + // SOCK_NONBLOCK is not portable, so use fcntl + fcntl( curfile->socket, F_SETFL, fcntl( curfile->socket, F_GETFL, 0 ) | O_NONBLOCK ); +#endif + curfile->state = HTTP_SOCKET; + } + + if( curfile->state < HTTP_NS_RESOLVED ) + { + net_gai_state_t res; + char hostport[MAX_VA_STRING]; + + if( fResolving ) + continue; + + Q_snprintf( hostport, sizeof( hostport ), "%s:%d", curfile->server->host, curfile->server->port ); + + res = NET_StringToSockaddr( hostport, &addr, true, AF_INET ); + + if( res == NET_EAI_AGAIN ) + { + fResolving = true; + continue; + } + + if( res == NET_EAI_NONAME ) + { + Con_Printf( S_ERROR "failed to resolve server address for %s!\n", curfile->server->host ); + HTTP_FreeFile( curfile, true ); // Cannot connect + break; + } + curfile->state = HTTP_NS_RESOLVED; + } + + if( curfile->state < HTTP_CONNECTED ) // Connection not enstabilished + { + int res = connect( curfile->socket, (struct sockaddr*)&addr, NET_SockAddrLen( &addr ) ); + + if( res ) + { + if( WSAGetLastError() == WSAEINPROGRESS || WSAGetLastError() == WSAEWOULDBLOCK ) // Should give EWOOLDBLOCK if try recv too soon + curfile->state = HTTP_CONNECTED; + else + { + Con_Printf( S_ERROR "cannot connect to server: %s\n", NET_ErrorString( )); + HTTP_FreeFile( curfile, true ); // Cannot connect + break; + } + continue; // skip to next file + } + curfile->state = HTTP_CONNECTED; + } + + if( curfile->state < HTTP_REQUEST ) // Request not formatted + { + string useragent; + + if( !COM_CheckStringEmpty( http_useragent.string ) || !Q_strcmp( http_useragent.string, "xash3d" )) + { + Q_snprintf( useragent, sizeof( useragent ), "%s/%s (%s-%s; build %d; %s)", + XASH_ENGINE_NAME, XASH_VERSION, Q_buildos( ), Q_buildarch( ), Q_buildnum( ), Q_buildcommit( )); + } + else + { + Q_strncpy( useragent, http_useragent.string, sizeof( useragent )); + } + + curfile->query_length = Q_snprintf( curfile->buf, sizeof( curfile->buf ), + "GET %s%s HTTP/1.0\r\n" + "Host: %s:%d\r\n" + "User-Agent: %s\r\n" + "Accept: */*\r\n\r\n", curfile->server->path, + curfile->path, curfile->server->host, curfile->server->port, useragent ); + Q_strncpy( curfile->query_backup, curfile->buf, sizeof( curfile->query_backup )); + curfile->header_size = 0; + curfile->bytes_sent = 0; + curfile->state = HTTP_REQUEST; + } + + if( curfile->state < HTTP_REQUEST_SENT ) // Request not sent + { + qboolean wait = false; + + while( curfile->bytes_sent < curfile->query_length ) + { + int res = send( curfile->socket, curfile->buf + curfile->bytes_sent, curfile->query_length - curfile->bytes_sent, 0 ); + + if( res < 0 ) + { + if( WSAGetLastError() != WSAEWOULDBLOCK && WSAGetLastError() != WSAENOTCONN ) + { + Con_Printf( S_ERROR "failed to send request: %s\n", NET_ErrorString( )); + HTTP_FreeFile( curfile, true ); + wait = true; + break; + } + // blocking while waiting connection + // increase counter when blocking + curfile->blocktime += host.frametime; + wait = true; + + if( curfile->blocktime > http_timeout.value ) + { + Con_Printf( S_ERROR "timeout on request send:\n%s\n", curfile->buf ); + HTTP_FreeFile( curfile, true ); + break; + } + break; + } + else + { + curfile->bytes_sent += res; + curfile->blocktime = 0; + } + } + + if( wait ) + continue; + + Con_Reportf( "HTTP: Request sent!\n"); + memset( curfile->buf, 0, sizeof( curfile->buf )); + curfile->state = HTTP_REQUEST_SENT; + } + + if( !HTTP_ProcessStream( curfile )) + break; + + if( curfile->size > 0 ) + { + flProgress += (float)curfile->downloaded / curfile->size; + iProgressCount++; + } + + if( curfile->size > 0 && curfile->downloaded >= curfile->size ) + { + HTTP_FreeFile( curfile, false ); // success + break; + } + else if(( WSAGetLastError( ) != WSAEWOULDBLOCK ) && ( WSAGetLastError( ) != WSAEINPROGRESS )) + Con_Reportf( "problem downloading %s:\n%s\n", curfile->path, NET_ErrorString( )); + else + curfile->blocktime += host.frametime; + + if( curfile->blocktime > http_timeout.value ) + { + Con_Printf( S_ERROR "timeout on receiving data!\n"); + HTTP_FreeFile( curfile, true ); + break; + } + } + + // update progress + if( !Host_IsDedicated() && iProgressCount != 0 ) + Cvar_SetValue( "scr_download", flProgress/iProgressCount * 100 ); + + HTTP_AutoClean(); +} + +/* +=================== +HTTP_AddDownload + +Add new download to end of queue +=================== +*/ +void HTTP_AddDownload( const char *path, int size, qboolean process, resource_t *res ) +{ + httpfile_t *httpfile = Z_Calloc( sizeof( httpfile_t )); + + Con_Reportf( "File %s queued to download\n", path ); + + httpfile->resource = res; + httpfile->size = size; + httpfile->downloaded = 0; + httpfile->socket = -1; + Q_strncpy ( httpfile->path, path, sizeof( httpfile->path )); + + if( http.last_file ) + { + // Add next to last download + httpfile->id = http.last_file->id + 1; + http.last_file->next= httpfile; + http.last_file = httpfile; + } + else + { + // It will be the only download + httpfile->id = 0; + http.last_file = http.first_file = httpfile; + } + + httpfile->file = NULL; + httpfile->next = NULL; + httpfile->state = HTTP_QUEUE; + httpfile->server = http.first_server; + httpfile->process = process; +} + +/* +=============== +HTTP_Download_f + +Console wrapper +=============== +*/ +static void HTTP_Download_f( void ) +{ + if( Cmd_Argc() < 2 ) + { + Con_Printf( S_USAGE "download \n"); + return; + } + + HTTP_AddDownload( Cmd_Argv( 1 ), -1, false, NULL ); +} + +/* +============== +HTTP_ParseURL +============== +*/ +static httpserver_t *HTTP_ParseURL( const char *url ) +{ + httpserver_t *server; + int i; + + url = Q_strstr( url, "http://" ); + + if( !url ) + return NULL; + + url += 7; + server = Z_Calloc( sizeof( httpserver_t )); + i = 0; + + while( *url && ( *url != ':' ) && ( *url != '/' ) && ( *url != '\r' ) && ( *url != '\n' )) + { + if( i > sizeof( server->host )) + return NULL; + + server->host[i++] = *url++; + } + + server->host[i] = 0; + + if( *url == ':' ) + { + server->port = Q_atoi( ++url ); + + while( *url && ( *url != '/' ) && ( *url != '\r' ) && ( *url != '\n' )) + url++; + } + else + server->port = 80; + + i = 0; + + while( *url && ( *url != '\r' ) && ( *url != '\n' )) + { + if( i > sizeof( server->path ) - 1 ) + return NULL; + + server->path[i++] = *url++; + } + + if( i == 0 || server->path[i-1] != '/' ) + server->path[i++] = '/'; + server->path[i] = 0; + server->next = NULL; + server->needfree = false; + + return server; +} + +/* +======================= +HTTP_AddCustomServer +======================= +*/ +void HTTP_AddCustomServer( const char *url ) +{ + httpserver_t *server = HTTP_ParseURL( url ); + + if( !server ) + { + Con_Printf( S_ERROR "\"%s\" is not valid url!\n", url ); + return; + } + + server->needfree = true; + server->next = http.first_server; + http.first_server = server; +} + +/* +======================= +HTTP_AddCustomServer_f +======================= +*/ +static void HTTP_AddCustomServer_f( void ) +{ + if( Cmd_Argc() == 2 ) + { + HTTP_AddCustomServer( Cmd_Argv( 1 )); + } + else + { + Con_Printf( S_USAGE "http_addcustomserver \n" ); + } +} + +/* +============ +HTTP_Clear_f + +Clear all queue +============ +*/ +static void HTTP_Clear_f( void ) +{ + http.last_file = NULL; + + while( http.first_file ) + { + httpfile_t *file = http.first_file; + + http.first_file = http.first_file->next; + + if( file->file ) + FS_Close( file->file ); + + if( file->socket != -1 ) + closesocket( file->socket ); + + Mem_Free( file ); + } +} + +/* +============== +HTTP_Cancel_f + +Stop current download, skip to next file +============== +*/ +static void HTTP_Cancel_f( void ) +{ + if( !http.first_file ) + return; + + http.first_file->state = HTTP_FREE; + HTTP_FreeFile( http.first_file, true ); +} + +/* +============= +HTTP_Skip_f + +Stop current download, skip to next server +============= +*/ +static void HTTP_Skip_f( void ) +{ + if( http.first_file ) + HTTP_FreeFile( http.first_file, true ); +} + +/* +============= +HTTP_List_f + +Print all pending downloads to console +============= +*/ +static void HTTP_List_f( void ) +{ + httpfile_t *file = http.first_file; + + while( file ) + { + if ( file->server ) + Con_Printf ( "\t%d %d http://%s:%d/%s%s %d\n", file->id, file->state, + file->server->host, file->server->port, file->server->path, + file->path, file->downloaded ); + else + Con_Printf ( "\t%d %d (no server) %s\n", file->id, file->state, file->path ); + + file = file->next; + } +} + +/* +================ +HTTP_ResetProcessState + +When connected to new server, all old files should not increase counter +================ +*/ +void HTTP_ResetProcessState( void ) +{ + httpfile_t *file = http.first_file; + + while( file ) + { + file->process = false; + file = file->next; + } +} + +/* +============= +HTTP_Init +============= +*/ +void HTTP_Init( void ) +{ + char *serverfile, *line, token[1024]; + + http.last_server = NULL; + + http.first_file = http.last_file = NULL; + + Cmd_AddRestrictedCommand( "http_download", HTTP_Download_f, "add file to download queue" ); + Cmd_AddRestrictedCommand( "http_skip", HTTP_Skip_f, "skip current download server" ); + Cmd_AddRestrictedCommand( "http_cancel", HTTP_Cancel_f, "cancel current download" ); + Cmd_AddRestrictedCommand( "http_clear", HTTP_Clear_f, "cancel all downloads" ); + Cmd_AddRestrictedCommand( "http_list", HTTP_List_f, "list all queued downloads" ); + Cmd_AddCommand( "http_addcustomserver", HTTP_AddCustomServer_f, "add custom fastdl server"); + + Cvar_RegisterVariable( &http_useragent ); + Cvar_RegisterVariable( &http_autoremove ); + Cvar_RegisterVariable( &http_timeout ); + Cvar_RegisterVariable( &http_maxconnections ); + Cvar_RegisterVariable( &http_show_request_header ); + + // Read servers from fastdl.txt + line = serverfile = (char *)FS_LoadFile( "fastdl.txt", 0, false ); + + if( serverfile ) + { + while(( line = COM_ParseFile( line, token, sizeof( token )))) + { + httpserver_t *server = HTTP_ParseURL( token ); + + if( !server ) + continue; + + if( !http.last_server ) + http.last_server = http.first_server = server; + else + { + http.last_server->next = server; + http.last_server = server; + } + } + + Mem_Free( serverfile ); + } +} + +/* +==================== +HTTP_Shutdown +==================== +*/ +void HTTP_Shutdown( void ) +{ + HTTP_Clear_f(); + + while( http.first_server ) + { + httpserver_t *tmp = http.first_server; + + http.first_server = http.first_server->next; + Mem_Free( tmp ); + } + + http.last_server = NULL; +} diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 81427705..51b7e559 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -18,17 +18,7 @@ GNU General Public License for more details. #include "netchan.h" #include "xash3d_mathlib.h" #include "ipv6text.h" - -#if XASH_NO_NETWORK -#include "platform/stub/net_stub.h" -#elif XASH_WIN32 -#include "platform/win32/net.h" -#elif XASH_PSVITA -#include "platform/psvita/net_psvita.h" -static const struct in6_addr in6addr_any; -#else -#include "platform/posix/net.h" -#endif +#include "net_ws_private.h" #if XASH_SDL == 2 #include @@ -141,85 +131,6 @@ static CVAR_DEFINE_AUTO( net6_address, "0", FCVAR_PRIVILEGED|FCVAR_READ_ONLY, "c static void NET_ClearLagData( qboolean bClient, qboolean bServer ); -/* -==================== -NET_ErrorString -==================== -*/ -static const char *NET_ErrorString( void ) -{ -#if XASH_WIN32 - int err = WSANOTINITIALISED; - - if( net.initialized ) - err = WSAGetLastError(); - - switch( err ) - { - case WSAEINTR: return "WSAEINTR"; - case WSAEBADF: return "WSAEBADF"; - case WSAEACCES: return "WSAEACCES"; - case WSAEFAULT: return "WSAEFAULT"; - case WSAEINVAL: return "WSAEINVAL"; - case WSAEMFILE: return "WSAEMFILE"; - case WSAEWOULDBLOCK: return "WSAEWOULDBLOCK"; - case WSAEINPROGRESS: return "WSAEINPROGRESS"; - case WSAEALREADY: return "WSAEALREADY"; - case WSAENOTSOCK: return "WSAENOTSOCK"; - case WSAEDESTADDRREQ: return "WSAEDESTADDRREQ"; - case WSAEMSGSIZE: return "WSAEMSGSIZE"; - case WSAEPROTOTYPE: return "WSAEPROTOTYPE"; - case WSAENOPROTOOPT: return "WSAENOPROTOOPT"; - case WSAEPROTONOSUPPORT: return "WSAEPROTONOSUPPORT"; - case WSAESOCKTNOSUPPORT: return "WSAESOCKTNOSUPPORT"; - case WSAEOPNOTSUPP: return "WSAEOPNOTSUPP"; - case WSAEPFNOSUPPORT: return "WSAEPFNOSUPPORT"; - case WSAEAFNOSUPPORT: return "WSAEAFNOSUPPORT"; - case WSAEADDRINUSE: return "WSAEADDRINUSE"; - case WSAEADDRNOTAVAIL: return "WSAEADDRNOTAVAIL"; - case WSAENETDOWN: return "WSAENETDOWN"; - case WSAENETUNREACH: return "WSAENETUNREACH"; - case WSAENETRESET: return "WSAENETRESET"; - case WSAECONNABORTED: return "WSWSAECONNABORTEDAEINTR"; - case WSAECONNRESET: return "WSAECONNRESET"; - case WSAENOBUFS: return "WSAENOBUFS"; - case WSAEISCONN: return "WSAEISCONN"; - case WSAENOTCONN: return "WSAENOTCONN"; - case WSAESHUTDOWN: return "WSAESHUTDOWN"; - case WSAETOOMANYREFS: return "WSAETOOMANYREFS"; - case WSAETIMEDOUT: return "WSAETIMEDOUT"; - case WSAECONNREFUSED: return "WSAECONNREFUSED"; - case WSAELOOP: return "WSAELOOP"; - case WSAENAMETOOLONG: return "WSAENAMETOOLONG"; - case WSAEHOSTDOWN: return "WSAEHOSTDOWN"; - case WSAEDISCON: return "WSAEDISCON"; - case WSASYSNOTREADY: return "WSASYSNOTREADY"; - case WSAVERNOTSUPPORTED: return "WSAVERNOTSUPPORTED"; - case WSANOTINITIALISED: return "WSANOTINITIALISED"; - case WSAHOST_NOT_FOUND: return "WSAHOST_NOT_FOUND"; - case WSATRY_AGAIN: return "WSATRY_AGAIN"; - case WSANO_RECOVERY: return "WSANO_RECOVERY"; - case WSANO_DATA: return "WSANO_DATA"; - default: return "NO ERROR"; - } -#else - return strerror( errno ); -#endif -} - -static inline socklen_t NET_SockAddrLen( const struct sockaddr_storage *addr ) -{ - switch ( addr->ss_family ) - { - case AF_INET: - return sizeof( struct sockaddr_in ); - case AF_INET6: - return sizeof( struct sockaddr_in6 ); - default: - return sizeof( *addr ); // what the fuck is this? - } -} - static inline qboolean NET_IsSocketError( int retval ) { #if XASH_WIN32 || XASH_DOS4GW @@ -491,7 +402,7 @@ idnewt:28000 192.246.40.70:28000 ============= */ -static net_gai_state_t NET_StringToSockaddr( const char *s, struct sockaddr_storage *sadr, qboolean nonblocking, int family ) +net_gai_state_t NET_StringToSockaddr( const char *s, struct sockaddr_storage *sadr, qboolean nonblocking, int family ) { int ret = 0, port; char *colon; @@ -2210,896 +2121,3 @@ void NET_Shutdown( void ) } -/* -================================================= - -HTTP downloader - -================================================= -*/ - -#define MAX_HTTP_BUFFER_SIZE (BIT( 13 )) - -typedef struct httpserver_s -{ - char host[256]; - int port; - char path[MAX_SYSPATH]; - qboolean needfree; - struct httpserver_s *next; - -} httpserver_t; - -enum connectionstate -{ - HTTP_QUEUE = 0, - HTTP_OPENED, - HTTP_SOCKET, - HTTP_NS_RESOLVED, - HTTP_CONNECTED, - HTTP_REQUEST, - HTTP_REQUEST_SENT, - HTTP_RESPONSE_RECEIVED, - HTTP_FREE -}; - -typedef struct httpfile_s -{ - struct httpfile_s *next; - httpserver_t *server; - char path[MAX_SYSPATH]; - file_t *file; - int socket; - int size; - int downloaded; - int lastchecksize; - float checktime; - float blocktime; - int id; - enum connectionstate state; - qboolean process; - resource_t *resource; - - string query_backup; - - // query or response - char buf[MAX_HTTP_BUFFER_SIZE+1]; - int header_size, query_length, bytes_sent; -} httpfile_t; - -static struct http_static_s -{ - // file and server lists - httpfile_t *first_file, *last_file; - httpserver_t *first_server, *last_server; -} http; - - -static CVAR_DEFINE_AUTO( http_useragent, "", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "User-Agent string" ); -static CVAR_DEFINE_AUTO( http_autoremove, "1", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "remove broken files" ); -static CVAR_DEFINE_AUTO( http_timeout, "45", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "timeout for http downloader" ); -static CVAR_DEFINE_AUTO( http_maxconnections, "2", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "maximum http connection number" ); -static CVAR_DEFINE_AUTO( http_show_request_header, "0", FCVAR_ARCHIVE | FCVAR_PRIVILEGED, "show headers of the HTTP request" ); - -/* -======================== -HTTP_ClearCustomServers -======================== -*/ -void HTTP_ClearCustomServers( void ) -{ - if( http.first_file ) - return; // may be referenced - - while( http.first_server && http.first_server->needfree ) - { - httpserver_t *tmp = http.first_server; - - http.first_server = http.first_server->next; - Mem_Free( tmp ); - } -} - -/* -============== -HTTP_FreeFile - -Skip to next server/file -============== -*/ -static void HTTP_FreeFile( httpfile_t *file, qboolean error ) -{ - char incname[256]; - - // Allways close file and socket - if( file->file ) - FS_Close( file->file ); - - file->file = NULL; - - if( file->socket != -1 ) - closesocket( file->socket ); - - file->socket = -1; - - Q_snprintf( incname, sizeof( incname ), DEFAULT_DOWNLOADED_DIRECTORY "%s.incomplete", file->path ); - if( error ) - { - // Switch to next fastdl server if present - if( file->server && ( file->state > HTTP_QUEUE ) && ( file->state != HTTP_FREE )) - { - file->server = file->server->next; - file->state = HTTP_QUEUE; // Reset download state, HTTP_Run() will open file again - return; - } - - // Called because there was no servers to download, free file now - if( http_autoremove.value == 1 ) // remove broken file - FS_Delete( incname ); - else // autoremove disabled, keep file - Con_Printf( "cannot download %s from any server. " - "You may remove %s now\n", file->path, incname ); // Warn about trash file - -#if !XASH_DEDICATED - if( file->process ) - { - if( file->resource ) - { - char buf[1024]; - sizebuf_t msg; - - MSG_Init( &msg, "DlFile", buf, sizeof( buf )); - MSG_BeginClientCmd( &msg, clc_stringcmd ); - MSG_WriteStringf( &msg, "dlfile %s", file->path ); - - Netchan_CreateFragments( &cls.netchan, &msg ); - Netchan_FragSend( &cls.netchan ); - } - else - { - CL_ProcessFile( false, file->path ); // Process file, increase counter - } - } -#endif // !XASH_DEDICATED - } - else - { - // Success, rename and process file - char name[256]; - - Q_snprintf( name, sizeof( name ), DEFAULT_DOWNLOADED_DIRECTORY "%s", file->path ); - FS_Rename( incname, name ); - -#if !XASH_DEDICATED - if( file->process ) - CL_ProcessFile( true, name ); - else -#endif - { - Con_Printf( "successfully downloaded %s, processing disabled!\n", name ); - } - } - - file->state = HTTP_FREE; -} - -/* -=================== -HTTP_AutoClean - -remove files with HTTP_FREE state from list -=================== -*/ -static void HTTP_AutoClean( void ) -{ - httpfile_t *curfile, *prevfile = 0; - - // clean all files marked to free - for( curfile = http.first_file; curfile; curfile = curfile->next ) - { - if( curfile->state != HTTP_FREE ) - { - prevfile = curfile; - continue; - } - - if( curfile == http.first_file ) - { - http.first_file = http.first_file->next; - Mem_Free( curfile ); - curfile = http.first_file; - if( !curfile ) - break; - continue; - } - - if( prevfile ) - prevfile->next = curfile->next; - Mem_Free( curfile ); - curfile = prevfile; - if( !curfile ) - break; - } - http.last_file = prevfile; -} - -/* -=================== -HTTP_ProcessStream - -process incoming data -=================== -*/ -static qboolean HTTP_ProcessStream( httpfile_t *curfile ) -{ - char buf[sizeof( curfile->buf )]; - char *begin = 0; - int res; - - if( curfile->header_size >= sizeof( buf )) - { - Con_Reportf( S_ERROR "Header too big, the size is %d\n", curfile->header_size ); - HTTP_FreeFile( curfile, true ); - return false; - } - - while( ( res = recv( curfile->socket, buf, sizeof( buf ) - curfile->header_size, 0 )) > 0) // if we got there, we are receiving data - { - curfile->blocktime = 0; - - if( curfile->state < HTTP_RESPONSE_RECEIVED ) // Response still not received - { - memcpy( curfile->buf + curfile->header_size, buf, res ); - curfile->buf[curfile->header_size + res] = 0; - begin = Q_strstr( curfile->buf, "\r\n\r\n" ); - - if( begin ) // Got full header - { - int cutheadersize = begin - curfile->buf + 4; // after that begin of data - char *content_length_line; - - if( !Q_strstr( curfile->buf, "200 OK" )) - { - *begin = 0; // cut string to print out response - begin = Q_strchr( curfile->buf, '\r' ); - - if( !begin ) begin = Q_strchr( curfile->buf, '\n' ); - if( begin ) - *begin = 0; - - Con_Printf( S_ERROR "%s: bad response: %s\n", curfile->path, curfile->buf ); - - if( http_show_request_header.value ) - Con_Printf( "Request headers: %s", curfile->query_backup ); - HTTP_FreeFile( curfile, true ); - return false; - } - - // print size - content_length_line = Q_stristr( curfile->buf, "Content-Length: " ); - if( content_length_line ) - { - int size; - - content_length_line += sizeof( "Content-Length: " ) - 1; - size = Q_atoi( content_length_line ); - - Con_Reportf( "HTTP: Got 200 OK! File size is %d\n", size ); - - if( ( curfile->size != -1 ) && ( curfile->size != size )) // check size if specified, not used - Con_Reportf( S_WARN "Server reports wrong file size for %s!\n", curfile->path ); - - curfile->size = size; - curfile->header_size = 0; - } - - if( curfile->size == -1 ) - { - // Usually fastdl's reports file size if link is correct - Con_Printf( S_ERROR "file size is unknown, refusing download!\n" ); - HTTP_FreeFile( curfile, true ); - return false; - } - - curfile->state = HTTP_RESPONSE_RECEIVED; // got response, let's start download - begin += 4; - - // Write remaining message part - if( res - cutheadersize - curfile->header_size > 0 ) - { - int ret = FS_Write( curfile->file, begin, res - cutheadersize - curfile->header_size ); - - if( ret != res - cutheadersize - curfile->header_size ) // could not write file - { - // close it and go to next - Con_Printf( S_ERROR "write failed for %s!\n", curfile->path ); - HTTP_FreeFile( curfile, true ); - return false; - } - curfile->downloaded += ret; - } - } - else - curfile->header_size += res; - } - else if( res > 0 ) - { - // data download - int ret = FS_Write( curfile->file, buf, res ); - - if ( ret != res ) - { - // close it and go to next - Con_Printf( S_ERROR "write failed for %s!\n", curfile->path ); - curfile->state = HTTP_FREE; - HTTP_FreeFile( curfile, true ); - return false; - } - - curfile->downloaded += ret; - curfile->lastchecksize += ret; - - // as after it will run in same frame - if( curfile->checktime > 5 ) - { - float speed = (float)curfile->lastchecksize / ( 5.0f * 1024 ); - - curfile->checktime = 0; - Con_Reportf( "download speed %f KB/s\n", speed ); - curfile->lastchecksize = 0; - } - } - } - curfile->checktime += host.frametime; - - return true; -} - -/* -============== -HTTP_Run - -Download next file block of each active file -Call every frame -============== -*/ -void HTTP_Run( void ) -{ - httpfile_t *curfile; - int iActiveCount = 0; - int iProgressCount = 0; - float flProgress = 0; - qboolean fResolving = false; - - for( curfile = http.first_file; curfile; curfile = curfile->next ) - { - struct sockaddr_storage addr; - - if( curfile->state == HTTP_FREE ) - continue; - - if( curfile->state == HTTP_QUEUE ) - { - char name[MAX_SYSPATH]; - - if( iActiveCount > http_maxconnections.value ) - continue; - - if( !curfile->server ) - { - Con_Printf( S_ERROR "no servers to download %s!\n", curfile->path ); - HTTP_FreeFile( curfile, true ); - break; - } - - Con_Reportf( "HTTP: Starting download %s from %s\n", curfile->path, curfile->server->host ); - Q_snprintf( name, sizeof( name ), DEFAULT_DOWNLOADED_DIRECTORY "%s.incomplete", curfile->path ); - - curfile->file = FS_Open( name, "wb", true ); - - if( !curfile->file ) - { - Con_Printf( S_ERROR "HTTP: cannot open %s!\n", name ); - HTTP_FreeFile( curfile, true ); - break; - } - - curfile->state = HTTP_OPENED; - curfile->blocktime = 0; - curfile->downloaded = 0; - curfile->lastchecksize = 0; - curfile->checktime = 0; - } - - iActiveCount++; - - if( curfile->state < HTTP_SOCKET ) // Socket is not created - { - dword mode; - - curfile->socket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); - - // Now set non-blocking mode - // You may skip this if not supported by system, - // but download will lock engine, maybe you will need to add manual returns - mode = 1; - ioctlsocket( curfile->socket, FIONBIO, (void*)&mode ); -#if XASH_LINUX - // SOCK_NONBLOCK is not portable, so use fcntl - fcntl( curfile->socket, F_SETFL, fcntl( curfile->socket, F_GETFL, 0 ) | O_NONBLOCK ); -#endif - curfile->state = HTTP_SOCKET; - } - - if( curfile->state < HTTP_NS_RESOLVED ) - { - net_gai_state_t res; - char hostport[MAX_VA_STRING]; - - if( fResolving ) - continue; - - Q_snprintf( hostport, sizeof( hostport ), "%s:%d", curfile->server->host, curfile->server->port ); - - res = NET_StringToSockaddr( hostport, &addr, true, AF_INET ); - - if( res == NET_EAI_AGAIN ) - { - fResolving = true; - continue; - } - - if( res == NET_EAI_NONAME ) - { - Con_Printf( S_ERROR "failed to resolve server address for %s!\n", curfile->server->host ); - HTTP_FreeFile( curfile, true ); // Cannot connect - break; - } - curfile->state = HTTP_NS_RESOLVED; - } - - if( curfile->state < HTTP_CONNECTED ) // Connection not enstabilished - { - int res = connect( curfile->socket, (struct sockaddr*)&addr, NET_SockAddrLen( &addr ) ); - - if( res ) - { - if( WSAGetLastError() == WSAEINPROGRESS || WSAGetLastError() == WSAEWOULDBLOCK ) // Should give EWOOLDBLOCK if try recv too soon - curfile->state = HTTP_CONNECTED; - else - { - Con_Printf( S_ERROR "cannot connect to server: %s\n", NET_ErrorString( )); - HTTP_FreeFile( curfile, true ); // Cannot connect - break; - } - continue; // skip to next file - } - curfile->state = HTTP_CONNECTED; - } - - if( curfile->state < HTTP_REQUEST ) // Request not formatted - { - string useragent; - - if( !COM_CheckStringEmpty( http_useragent.string ) || !Q_strcmp( http_useragent.string, "xash3d" )) - { - Q_snprintf( useragent, sizeof( useragent ), "%s/%s (%s-%s; build %d; %s)", - XASH_ENGINE_NAME, XASH_VERSION, Q_buildos( ), Q_buildarch( ), Q_buildnum( ), Q_buildcommit( )); - } - else - { - Q_strncpy( useragent, http_useragent.string, sizeof( useragent )); - } - - curfile->query_length = Q_snprintf( curfile->buf, sizeof( curfile->buf ), - "GET %s%s HTTP/1.0\r\n" - "Host: %s:%d\r\n" - "User-Agent: %s\r\n" - "Accept: */*\r\n\r\n", curfile->server->path, - curfile->path, curfile->server->host, curfile->server->port, useragent ); - Q_strncpy( curfile->query_backup, curfile->buf, sizeof( curfile->query_backup )); - curfile->header_size = 0; - curfile->bytes_sent = 0; - curfile->state = HTTP_REQUEST; - } - - if( curfile->state < HTTP_REQUEST_SENT ) // Request not sent - { - qboolean wait = false; - - while( curfile->bytes_sent < curfile->query_length ) - { - int res = send( curfile->socket, curfile->buf + curfile->bytes_sent, curfile->query_length - curfile->bytes_sent, 0 ); - - if( res < 0 ) - { - if( WSAGetLastError() != WSAEWOULDBLOCK && WSAGetLastError() != WSAENOTCONN ) - { - Con_Printf( S_ERROR "failed to send request: %s\n", NET_ErrorString( )); - HTTP_FreeFile( curfile, true ); - wait = true; - break; - } - // blocking while waiting connection - // increase counter when blocking - curfile->blocktime += host.frametime; - wait = true; - - if( curfile->blocktime > http_timeout.value ) - { - Con_Printf( S_ERROR "timeout on request send:\n%s\n", curfile->buf ); - HTTP_FreeFile( curfile, true ); - break; - } - break; - } - else - { - curfile->bytes_sent += res; - curfile->blocktime = 0; - } - } - - if( wait ) - continue; - - Con_Reportf( "HTTP: Request sent!\n"); - memset( curfile->buf, 0, sizeof( curfile->buf )); - curfile->state = HTTP_REQUEST_SENT; - } - - if( !HTTP_ProcessStream( curfile )) - break; - - if( curfile->size > 0 ) - { - flProgress += (float)curfile->downloaded / curfile->size; - iProgressCount++; - } - - if( curfile->size > 0 && curfile->downloaded >= curfile->size ) - { - HTTP_FreeFile( curfile, false ); // success - break; - } - else if(( WSAGetLastError( ) != WSAEWOULDBLOCK ) && ( WSAGetLastError( ) != WSAEINPROGRESS )) - Con_Reportf( "problem downloading %s:\n%s\n", curfile->path, NET_ErrorString( )); - else - curfile->blocktime += host.frametime; - - if( curfile->blocktime > http_timeout.value ) - { - Con_Printf( S_ERROR "timeout on receiving data!\n"); - HTTP_FreeFile( curfile, true ); - break; - } - } - - // update progress - if( !Host_IsDedicated() && iProgressCount != 0 ) - Cvar_SetValue( "scr_download", flProgress/iProgressCount * 100 ); - - HTTP_AutoClean(); -} - -/* -=================== -HTTP_AddDownload - -Add new download to end of queue -=================== -*/ -void HTTP_AddDownload( const char *path, int size, qboolean process, resource_t *res ) -{ - httpfile_t *httpfile = Z_Calloc( sizeof( httpfile_t )); - - Con_Reportf( "File %s queued to download\n", path ); - - httpfile->resource = res; - httpfile->size = size; - httpfile->downloaded = 0; - httpfile->socket = -1; - Q_strncpy ( httpfile->path, path, sizeof( httpfile->path )); - - if( http.last_file ) - { - // Add next to last download - httpfile->id = http.last_file->id + 1; - http.last_file->next= httpfile; - http.last_file = httpfile; - } - else - { - // It will be the only download - httpfile->id = 0; - http.last_file = http.first_file = httpfile; - } - - httpfile->file = NULL; - httpfile->next = NULL; - httpfile->state = HTTP_QUEUE; - httpfile->server = http.first_server; - httpfile->process = process; -} - -/* -=============== -HTTP_Download_f - -Console wrapper -=============== -*/ -static void HTTP_Download_f( void ) -{ - if( Cmd_Argc() < 2 ) - { - Con_Printf( S_USAGE "download \n"); - return; - } - - HTTP_AddDownload( Cmd_Argv( 1 ), -1, false, NULL ); -} - -/* -============== -HTTP_ParseURL -============== -*/ -static httpserver_t *HTTP_ParseURL( const char *url ) -{ - httpserver_t *server; - int i; - - url = Q_strstr( url, "http://" ); - - if( !url ) - return NULL; - - url += 7; - server = Z_Calloc( sizeof( httpserver_t )); - i = 0; - - while( *url && ( *url != ':' ) && ( *url != '/' ) && ( *url != '\r' ) && ( *url != '\n' )) - { - if( i > sizeof( server->host )) - return NULL; - - server->host[i++] = *url++; - } - - server->host[i] = 0; - - if( *url == ':' ) - { - server->port = Q_atoi( ++url ); - - while( *url && ( *url != '/' ) && ( *url != '\r' ) && ( *url != '\n' )) - url++; - } - else - server->port = 80; - - i = 0; - - while( *url && ( *url != '\r' ) && ( *url != '\n' )) - { - if( i > sizeof( server->path ) - 1 ) - return NULL; - - server->path[i++] = *url++; - } - - if( i == 0 || server->path[i-1] != '/' ) - server->path[i++] = '/'; - server->path[i] = 0; - server->next = NULL; - server->needfree = false; - - return server; -} - -/* -======================= -HTTP_AddCustomServer -======================= -*/ -void HTTP_AddCustomServer( const char *url ) -{ - httpserver_t *server = HTTP_ParseURL( url ); - - if( !server ) - { - Con_Printf( S_ERROR "\"%s\" is not valid url!\n", url ); - return; - } - - server->needfree = true; - server->next = http.first_server; - http.first_server = server; -} - -/* -======================= -HTTP_AddCustomServer_f -======================= -*/ -static void HTTP_AddCustomServer_f( void ) -{ - if( Cmd_Argc() == 2 ) - { - HTTP_AddCustomServer( Cmd_Argv( 1 )); - } - else - { - Con_Printf( S_USAGE "http_addcustomserver \n" ); - } -} - -/* -============ -HTTP_Clear_f - -Clear all queue -============ -*/ -static void HTTP_Clear_f( void ) -{ - http.last_file = NULL; - - while( http.first_file ) - { - httpfile_t *file = http.first_file; - - http.first_file = http.first_file->next; - - if( file->file ) - FS_Close( file->file ); - - if( file->socket != -1 ) - closesocket( file->socket ); - - Mem_Free( file ); - } -} - -/* -============== -HTTP_Cancel_f - -Stop current download, skip to next file -============== -*/ -static void HTTP_Cancel_f( void ) -{ - if( !http.first_file ) - return; - - http.first_file->state = HTTP_FREE; - HTTP_FreeFile( http.first_file, true ); -} - -/* -============= -HTTP_Skip_f - -Stop current download, skip to next server -============= -*/ -static void HTTP_Skip_f( void ) -{ - if( http.first_file ) - HTTP_FreeFile( http.first_file, true ); -} - -/* -============= -HTTP_List_f - -Print all pending downloads to console -============= -*/ -static void HTTP_List_f( void ) -{ - httpfile_t *file = http.first_file; - - while( file ) - { - if ( file->server ) - Con_Printf ( "\t%d %d http://%s:%d/%s%s %d\n", file->id, file->state, - file->server->host, file->server->port, file->server->path, - file->path, file->downloaded ); - else - Con_Printf ( "\t%d %d (no server) %s\n", file->id, file->state, file->path ); - - file = file->next; - } -} - -/* -================ -HTTP_ResetProcessState - -When connected to new server, all old files should not increase counter -================ -*/ -void HTTP_ResetProcessState( void ) -{ - httpfile_t *file = http.first_file; - - while( file ) - { - file->process = false; - file = file->next; - } -} - -/* -============= -HTTP_Init -============= -*/ -void HTTP_Init( void ) -{ - char *serverfile, *line, token[1024]; - - http.last_server = NULL; - - http.first_file = http.last_file = NULL; - - Cmd_AddRestrictedCommand( "http_download", HTTP_Download_f, "add file to download queue" ); - Cmd_AddRestrictedCommand( "http_skip", HTTP_Skip_f, "skip current download server" ); - Cmd_AddRestrictedCommand( "http_cancel", HTTP_Cancel_f, "cancel current download" ); - Cmd_AddRestrictedCommand( "http_clear", HTTP_Clear_f, "cancel all downloads" ); - Cmd_AddRestrictedCommand( "http_list", HTTP_List_f, "list all queued downloads" ); - Cmd_AddCommand( "http_addcustomserver", HTTP_AddCustomServer_f, "add custom fastdl server"); - - Cvar_RegisterVariable( &http_useragent ); - Cvar_RegisterVariable( &http_autoremove ); - Cvar_RegisterVariable( &http_timeout ); - Cvar_RegisterVariable( &http_maxconnections ); - Cvar_RegisterVariable( &http_show_request_header ); - - // Read servers from fastdl.txt - line = serverfile = (char *)FS_LoadFile( "fastdl.txt", 0, false ); - - if( serverfile ) - { - while(( line = COM_ParseFile( line, token, sizeof( token )))) - { - httpserver_t *server = HTTP_ParseURL( token ); - - if( !server ) - continue; - - if( !http.last_server ) - http.last_server = http.first_server = server; - else - { - http.last_server->next = server; - http.last_server = server; - } - } - - Mem_Free( serverfile ); - } -} - -/* -==================== -HTTP_Shutdown -==================== -*/ -void HTTP_Shutdown( void ) -{ - HTTP_Clear_f(); - - while( http.first_server ) - { - httpserver_t *tmp = http.first_server; - - http.first_server = http.first_server->next; - Mem_Free( tmp ); - } - - http.last_server = NULL; -} diff --git a/engine/common/net_ws_private.h b/engine/common/net_ws_private.h new file mode 100644 index 00000000..16ad622f --- /dev/null +++ b/engine/common/net_ws_private.h @@ -0,0 +1,103 @@ +/* +net_ws_private.h - networking private definitions +Copyright (C) 2024 Alibek Omarov + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "net_ws.h" +#if XASH_NO_NETWORK +#include "platform/stub/net_stub.h" +#elif XASH_WIN32 +#include "platform/win32/net.h" +#elif XASH_PSVITA +#include "platform/psvita/net_psvita.h" +static const struct in6_addr in6addr_any; +#else +#include "platform/posix/net.h" +#endif + + +// +// net_ws.c +// +static inline const char *NET_ErrorString( void ) +{ +#if XASH_WIN32 + int err = WSAGetLastError(); + switch( err ) + { + case WSAEINTR: return "WSAEINTR"; + case WSAEBADF: return "WSAEBADF"; + case WSAEACCES: return "WSAEACCES"; + case WSAEFAULT: return "WSAEFAULT"; + case WSAEINVAL: return "WSAEINVAL"; + case WSAEMFILE: return "WSAEMFILE"; + case WSAEWOULDBLOCK: return "WSAEWOULDBLOCK"; + case WSAEINPROGRESS: return "WSAEINPROGRESS"; + case WSAEALREADY: return "WSAEALREADY"; + case WSAENOTSOCK: return "WSAENOTSOCK"; + case WSAEDESTADDRREQ: return "WSAEDESTADDRREQ"; + case WSAEMSGSIZE: return "WSAEMSGSIZE"; + case WSAEPROTOTYPE: return "WSAEPROTOTYPE"; + case WSAENOPROTOOPT: return "WSAENOPROTOOPT"; + case WSAEPROTONOSUPPORT: return "WSAEPROTONOSUPPORT"; + case WSAESOCKTNOSUPPORT: return "WSAESOCKTNOSUPPORT"; + case WSAEOPNOTSUPP: return "WSAEOPNOTSUPP"; + case WSAEPFNOSUPPORT: return "WSAEPFNOSUPPORT"; + case WSAEAFNOSUPPORT: return "WSAEAFNOSUPPORT"; + case WSAEADDRINUSE: return "WSAEADDRINUSE"; + case WSAEADDRNOTAVAIL: return "WSAEADDRNOTAVAIL"; + case WSAENETDOWN: return "WSAENETDOWN"; + case WSAENETUNREACH: return "WSAENETUNREACH"; + case WSAENETRESET: return "WSAENETRESET"; + case WSAECONNABORTED: return "WSWSAECONNABORTEDAEINTR"; + case WSAECONNRESET: return "WSAECONNRESET"; + case WSAENOBUFS: return "WSAENOBUFS"; + case WSAEISCONN: return "WSAEISCONN"; + case WSAENOTCONN: return "WSAENOTCONN"; + case WSAESHUTDOWN: return "WSAESHUTDOWN"; + case WSAETOOMANYREFS: return "WSAETOOMANYREFS"; + case WSAETIMEDOUT: return "WSAETIMEDOUT"; + case WSAECONNREFUSED: return "WSAECONNREFUSED"; + case WSAELOOP: return "WSAELOOP"; + case WSAENAMETOOLONG: return "WSAENAMETOOLONG"; + case WSAEHOSTDOWN: return "WSAEHOSTDOWN"; + case WSAEDISCON: return "WSAEDISCON"; + case WSASYSNOTREADY: return "WSASYSNOTREADY"; + case WSAVERNOTSUPPORTED: return "WSAVERNOTSUPPORTED"; + case WSANOTINITIALISED: return "WSANOTINITIALISED"; + case WSAHOST_NOT_FOUND: return "WSAHOST_NOT_FOUND"; + case WSATRY_AGAIN: return "WSATRY_AGAIN"; + case WSANO_RECOVERY: return "WSANO_RECOVERY"; + case WSANO_DATA: return "WSANO_DATA"; + } + + return "NO ERROR"; +#else + return strerror( errno ); +#endif +} + +static inline socklen_t NET_SockAddrLen( const struct sockaddr_storage *addr ) +{ + switch ( addr->ss_family ) + { + case AF_INET: + return sizeof( struct sockaddr_in ); + case AF_INET6: + return sizeof( struct sockaddr_in6 ); + default: + return sizeof( *addr ); // what the fuck is this? + } +} + +net_gai_state_t NET_StringToSockaddr( const char *s, struct sockaddr_storage *sadr, qboolean nonblocking, int family );