mirror of
https://github.com/wiidev/usbloadergx.git
synced 2024-12-23 10:21:55 +01:00
9e79c9d99b
* code cleanup
264 lines
7.9 KiB
C
264 lines
7.9 KiB
C
#include "http.h"
|
|
#include "../svnrev.h"
|
|
|
|
extern char incommingIP[50];
|
|
|
|
/**
|
|
* Emptyblock is a statically defined variable for functions to return if they are unable
|
|
* to complete a request
|
|
*/
|
|
const struct block emptyblock = {0, NULL};
|
|
|
|
//The maximum amount of bytes to send per net_write() call
|
|
//#define NET_BUFFER_SIZE 1024
|
|
#define NET_BUFFER_SIZE 3600
|
|
|
|
// Write our message to the server
|
|
static s32 send_message( s32 server, char *msg )
|
|
{
|
|
s32 bytes_transferred = 0;
|
|
s32 remaining = strlen( msg );
|
|
while ( remaining )
|
|
{
|
|
if ( ( bytes_transferred = net_write( server, msg, remaining > NET_BUFFER_SIZE ? NET_BUFFER_SIZE : remaining ) ) > 0 )
|
|
{
|
|
remaining -= bytes_transferred;
|
|
usleep ( 20 * 1000 );
|
|
}
|
|
else if ( bytes_transferred < 0 )
|
|
{
|
|
return bytes_transferred;
|
|
}
|
|
else
|
|
{
|
|
return -ENODATA;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Connect to a remote server via TCP on a specified port
|
|
*
|
|
* @param u32 ip address of the server to connect to
|
|
* @param u32 the port to connect to on the server
|
|
* @return s32 The connection to the server (negative number if connection could not be established)
|
|
*/
|
|
static s32 server_connect( u32 ipaddress, u32 socket_port )
|
|
{
|
|
//Initialize socket
|
|
s32 connection = net_socket( AF_INET, SOCK_STREAM, IPPROTO_IP );
|
|
if ( connection < 0 ) return connection;
|
|
|
|
struct sockaddr_in connect_addr;
|
|
memset( &connect_addr, 0, sizeof( connect_addr ) );
|
|
connect_addr.sin_family = AF_INET;
|
|
connect_addr.sin_port = socket_port;
|
|
connect_addr.sin_addr.s_addr = ipaddress;
|
|
|
|
sprintf( incommingIP, "%s", inet_ntoa( connect_addr.sin_addr ) );
|
|
|
|
//Attemt to open the socket
|
|
if ( net_connect( connection, ( struct sockaddr* )&connect_addr, sizeof( connect_addr ) ) == -1 )
|
|
{
|
|
net_close( connection );
|
|
return -1;
|
|
}
|
|
return connection;
|
|
}
|
|
|
|
//The amount of memory in bytes reserved initially to store the HTTP response in
|
|
//Be careful in increasing this number, reading from a socket on the Wii
|
|
//will fail if you request more than 20k or so
|
|
#define HTTP_BUFFER_SIZE 1024 * 5
|
|
|
|
//The amount of memory the buffer should expanded with if the buffer is full
|
|
#define HTTP_BUFFER_GROWTH 1024 * 5
|
|
|
|
/**
|
|
* This function reads all the data from a connection into a buffer which it returns.
|
|
* It will return an empty buffer if something doesn't go as planned
|
|
*
|
|
* @param s32 connection The connection identifier to suck the response out of
|
|
* @return block A 'block' struct (see http.h) in which the buffer is located
|
|
*/
|
|
struct block read_message( s32 connection )
|
|
{
|
|
//Create a block of memory to put in the response
|
|
struct block buffer;
|
|
buffer.data = malloc( HTTP_BUFFER_SIZE );
|
|
buffer.size = HTTP_BUFFER_SIZE;
|
|
|
|
if ( buffer.data == NULL )
|
|
{
|
|
return emptyblock;
|
|
}
|
|
|
|
//The offset variable always points to the first byte of memory that is free in the buffer
|
|
u32 offset = 0;
|
|
|
|
while ( 1 )
|
|
{
|
|
//Fill the buffer with a new batch of bytes from the connection,
|
|
//starting from where we left of in the buffer till the end of the buffer
|
|
s32 bytes_read = net_read( connection, buffer.data + offset, buffer.size - offset );
|
|
|
|
//Anything below 0 is an error in the connection
|
|
if ( bytes_read < 0 )
|
|
{
|
|
//printf("Connection error from net_read() Errorcode: %i\n", bytes_read);
|
|
return emptyblock;
|
|
}
|
|
|
|
//No more bytes were read into the buffer,
|
|
//we assume this means the HTTP response is done
|
|
if ( bytes_read == 0 )
|
|
{
|
|
break;
|
|
}
|
|
|
|
offset += bytes_read;
|
|
|
|
//Check if we have enough buffer left over,
|
|
//if not expand it with an additional HTTP_BUFFER_GROWTH worth of bytes
|
|
if ( offset >= buffer.size )
|
|
{
|
|
buffer.size += HTTP_BUFFER_GROWTH;
|
|
buffer.data = realloc( buffer.data, buffer.size );
|
|
|
|
if ( buffer.data == NULL )
|
|
{
|
|
return emptyblock;
|
|
}
|
|
}
|
|
}
|
|
|
|
//At the end of above loop offset should be precisely the amount of bytes that were read from the connection
|
|
buffer.size = offset;
|
|
|
|
//Shrink the size of the buffer so the data fits exactly in it
|
|
buffer.data = realloc( buffer.data, buffer.size );
|
|
|
|
return buffer;
|
|
}
|
|
|
|
/**
|
|
* Downloads the contents of a URL to memory
|
|
* This method is not threadsafe (because networking is not threadsafe on the Wii)
|
|
*/
|
|
struct block downloadfile( const char *url )
|
|
{
|
|
//Check if the url starts with "http://", if not it is not considered a valid url
|
|
if ( strncmp( url, "http://", strlen( "http://" ) ) != 0 )
|
|
{
|
|
//printf("URL '%s' doesn't start with 'http://'\n", url);
|
|
return emptyblock;
|
|
}
|
|
|
|
//Locate the path part of the url by searching for '/' past "http://"
|
|
char *path = strchr( url + strlen( "http://" ), '/' );
|
|
|
|
//At the very least the url has to end with '/', ending with just a domain is invalid
|
|
if ( path == NULL )
|
|
{
|
|
//printf("URL '%s' has no PATH part\n", url);
|
|
return emptyblock;
|
|
}
|
|
|
|
//Extract the domain part out of the url
|
|
int domainlength = path - url - strlen( "http://" );
|
|
|
|
if ( domainlength == 0 )
|
|
{
|
|
//printf("No domain part in URL '%s'\n", url);
|
|
return emptyblock;
|
|
}
|
|
|
|
char domain[domainlength + 1];
|
|
strlcpy( domain, url + strlen( "http://" ), domainlength + 1 );
|
|
|
|
//Parsing of the URL is done, start making an actual connection
|
|
u32 ipaddress = getipbynamecached( domain );
|
|
|
|
if ( ipaddress == 0 )
|
|
{
|
|
//printf("\ndomain %s could not be resolved", domain);
|
|
return emptyblock;
|
|
}
|
|
|
|
|
|
s32 connection = server_connect( ipaddress, 80 );
|
|
|
|
if ( connection < 0 )
|
|
{
|
|
//printf("Error establishing connection");
|
|
return emptyblock;
|
|
}
|
|
|
|
//Form a nice request header to send to the webserver
|
|
char* headerformat = "GET %s HTTP/1.0\r\nHost: %s\r\nReferer: %s\r\nUser-Agent: USBLoaderGX r%s\r\n\r\n";;
|
|
char header[strlen( headerformat ) + strlen( path ) + strlen( domain ) + strlen( domain )];
|
|
sprintf( header, headerformat, path, domain, domain, GetRev() );
|
|
|
|
//Do the request and get the response
|
|
send_message( connection, header );
|
|
struct block response = read_message( connection );
|
|
net_close( connection );
|
|
|
|
//Search for the 4-character sequence \r\n\r\n in the response which signals the start of the http payload (file)
|
|
unsigned char *filestart = NULL;
|
|
u32 filesize = 0;
|
|
int i;
|
|
for ( i = 3; i < response.size; i++ )
|
|
{
|
|
if ( response.data[i] == '\n' &&
|
|
response.data[i-1] == '\r' &&
|
|
response.data[i-2] == '\n' &&
|
|
response.data[i-3] == '\r' )
|
|
{
|
|
filestart = response.data + i + 1;
|
|
filesize = response.size - i - 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( filestart == NULL )
|
|
{
|
|
//printf("HTTP Response was without a file\n");
|
|
free( response.data );
|
|
return emptyblock;
|
|
}
|
|
|
|
//Copy the file part of the response into a new memoryblock to return
|
|
struct block file;
|
|
file.data = malloc( filesize );
|
|
file.size = filesize;
|
|
|
|
if ( file.data == NULL )
|
|
{
|
|
//printf("No more memory to copy file from HTTP response\n");
|
|
free( response.data );
|
|
return emptyblock;
|
|
}
|
|
|
|
memcpy( file.data, filestart, filesize );
|
|
|
|
//Dispose of the original response
|
|
free( response.data );
|
|
|
|
return file;
|
|
}
|
|
|
|
s32 GetConnection( char * domain )
|
|
{
|
|
|
|
u32 ipaddress = getipbynamecached( domain );
|
|
if ( ipaddress == 0 )
|
|
{
|
|
return -1;
|
|
}
|
|
s32 connection = server_connect( ipaddress, 80 );
|
|
return connection;
|
|
|
|
}
|