diff --git a/src/Http_Worker.c b/src/Http_Worker.c index 8df38b9f4..aa5b27b56 100644 --- a/src/Http_Worker.c +++ b/src/Http_Worker.c @@ -516,6 +516,8 @@ static cc_result HttpConnection_Open(struct HttpConnection* conn, const struct H cc_string host, port; cc_uint16 portNum; cc_result res; + cc_sockaddr addrs[SOCKET_MAX_ADDRS]; + int numAddrs; /* address can be either "host" or "host:port" */ String_UNSAFE_Separate(&url->address, ':', &host, &port); @@ -523,9 +525,12 @@ static cc_result HttpConnection_Open(struct HttpConnection* conn, const struct H portNum = url->https ? 443 : 80; } - conn->socket = 0; + conn->socket = -1; conn->sslCtx = NULL; - if ((res = Socket_Connect(&conn->socket, &host, portNum, false))) return res; + if ((res = Socket_ParseAddress(&host, portNum, addrs, &numAddrs))) return res; + + /* TODO multi addresses support */ + if ((res = Socket_Connect(&conn->socket, &addrs[0], false))) return res; conn->valid = true; if (!url->https) return 0; @@ -550,9 +555,9 @@ static void HttpConnection_Close(struct HttpConnection* conn) { conn->sslCtx = NULL; } - if (conn->socket) { /* Closing socket 0 will crash on GC/Wii */ + if (conn->socket != -1) { Socket_Close(conn->socket); - conn->socket = 0; + conn->socket = -1; } conn->valid = false; } diff --git a/src/LScreens.c b/src/LScreens.c index fa10f5d52..4a2ba4717 100644 --- a/src/LScreens.c +++ b/src/LScreens.c @@ -494,6 +494,8 @@ static void DirectConnectScreen_StartClient(void* w) { cc_string ip, port; cc_uint16 raw_port; + cc_sockaddr addrs[SOCKET_MAX_ADDRS]; + int numAddrs; int index = String_LastIndexOf(addr, ':'); if (index == 0 || index == addr->length - 1) { @@ -512,7 +514,7 @@ static void DirectConnectScreen_StartClient(void* w) { if (!user->length) { LLabel_SetConst(status, "&cUsername required"); return; } - if (!Socket_ValidAddress(&ip)) { + if (Socket_ParseAddress(&ip, 0, addrs, &numAddrs)) { LLabel_SetConst(status, "&cInvalid ip"); return; } if (!Convert_ParseUInt16(&port, &raw_port)) { diff --git a/src/Platform.h b/src/Platform.h index facef07a6..952e15e61 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -242,22 +242,31 @@ CC_API void Waitable_WaitFor(void* handle, cc_uint32 milliseconds); /* Calls SysFonts_Register on each font that is available on this platform. */ void Platform_LoadSysFonts(void); +#define CC_SOCKETADDR_MAXSIZE 512 +#define SOCKET_MAX_ADDRS 5 + +typedef struct cc_sockaddr_ { + int size; /* Actual size of the raw socket address */ + cc_uint8 data[CC_SOCKETADDR_MAXSIZE]; /* Raw socket address (e.g. sockaddr_in) */ +} cc_sockaddr; + /* Checks if the given socket is currently readable (i.e. has data available to read) */ /* NOTE: A closed socket is also considered readable */ cc_result Socket_CheckReadable(cc_socket s, cc_bool* readable); /* Checks if the given socket is currently writable (i.e. has finished connecting) */ cc_result Socket_CheckWritable(cc_socket s, cc_bool* writable); -/* Returns non-zero if the given address is valid for a socket to connect to */ -int Socket_ValidAddress(const cc_string* address); +/* If the input represents an IP address, then parses the input into a single IP address */ +/* Otherwise, attempts to resolve the input via DNS into one or more IP addresses */ +cc_result Socket_ParseAddress(const cc_string* address, int port, cc_sockaddr* addrs, int* numAddrs); -/* Allocates a new socket and then begins connecting to the given address:port. */ -cc_result Socket_Connect(cc_socket* s, const cc_string* address, int port, cc_bool nonblocking); -/* Attempts to read data from the given socket. */ +/* Allocates a new socket and then begins connecting to the given address */ +cc_result Socket_Connect(cc_socket* s, cc_sockaddr* addr, cc_bool nonblocking); +/* Attempts to read data from the given socket */ /* NOTE: A closed socket may set modified to 0, but still return 'success' (i.e. 0) */ cc_result Socket_Read(cc_socket s, cc_uint8* data, cc_uint32 count, cc_uint32* modified); -/* Attempts to write data to the given socket. */ +/* Attempts to write data to the given socket */ cc_result Socket_Write(cc_socket s, const cc_uint8* data, cc_uint32 count, cc_uint32* modified); -/* Attempts to close the given socket. */ +/* Attempts to close the given socket */ void Socket_Close(cc_socket s); #ifdef CC_BUILD_MOBILE diff --git a/src/Platform_Web.c b/src/Platform_Web.c index 544e71612..d596b62f3 100644 --- a/src/Platform_Web.c +++ b/src/Platform_Web.c @@ -250,18 +250,26 @@ void Platform_LoadSysFonts(void) { } *---------------------------------------------------------Socket----------------------------------------------------------* *#########################################################################################################################*/ extern void interop_InitSockets(void); -int Socket_ValidAddress(const cc_string* address) { return true; } + +cc_result Socket_ParseAddress(const cc_string* address, int port, cc_sockaddr* addrs, int* numAddrs) { + int len = String_EncodeUtf8(addrs[0].data, address); + /* TODO can this ever happen */ + if (len >= CC_SOCKETADDR_MAXSIZE) Logger_Abort("Overrun in Socket_ParseAddress"); + + addrs[0].size = port; + *numAddrs = 1; + return 0; +} extern int interop_SocketCreate(void); -extern int interop_SocketConnect(int sock, const char* addr, int port); -cc_result Socket_Connect(cc_socket* s, const cc_string* address, int port, cc_bool nonblocking) { - char addr[NATIVE_STR_LEN]; +extern int interop_SocketConnect(int sock, const cc_uint8* host, int port); +cc_result Socket_Connect(cc_socket* s, cc_sockaddr* addr, cc_bool nonblocking) { int res; - String_EncodeUtf8(addr, address); *s = interop_SocketCreate(); + /* size is used to store port number instead */ /* returned result is negative for error */ - res = -interop_SocketConnect(*s, addr, port); + res = -interop_SocketConnect(*s, addr->data, addr->size); /* error returned when invalid address provided */ if (res == _EHOSTUNREACH) return ERR_INVALID_ARGUMENT; diff --git a/src/Platform_Windows.c b/src/Platform_Windows.c index e4f26fefc..e5f5858f8 100644 --- a/src/Platform_Windows.c +++ b/src/Platform_Windows.c @@ -395,6 +395,9 @@ void Platform_LoadSysFonts(void) { /*########################################################################################################################* *---------------------------------------------------------Socket----------------------------------------------------------* *#########################################################################################################################*/ +/* Sanity check to ensure cc_sockaddr struct is large enough to contain all socket addresses supported by this platform */ +static char sockaddr_size_check[sizeof(SOCKADDR_STORAGE) < CC_SOCKETADDR_MAXSIZE ? 1 : -1]; + static int (WSAAPI *_WSAStartup)(WORD versionRequested, LPWSADATA wsaData); static int (WSAAPI *_WSACleanup)(void); static int (WSAAPI *_WSAGetLastError)(void); @@ -456,12 +459,16 @@ static void LoadWinsockFuncs(void) { if (!_WSAStringToAddressW) _WSAStringToAddressW = FallbackParseAddress; } -static int ParseHost(void* dst, char* host, int port) { - SOCKADDR_IN* addr4 = (SOCKADDR_IN*)dst; +static cc_result ParseHost(char* host, int port, cc_sockaddr* addrs, int* numAddrs) { struct hostent* res; cc_result wsa_res; + cc_sockaddr* dst_addr; + SOCKADDR_IN* addr4; + char* src_addr; + int i; res = _gethostbyname(host); + if (!res) { wsa_res = _WSAGetLastError(); @@ -472,53 +479,60 @@ static int ParseHost(void* dst, char* host, int port) { /* per MSDN, should only be getting AF_INET returned from this */ if (res->h_addrtype != AF_INET) return ERR_INVALID_ARGUMENT; + for (i = 0; i < SOCKET_MAX_ADDRS; i++) + { + src_addr = res->h_addr_list[i]; + if (!src_addr) break; + + dst_addr = &addrs[i]; + dst_addr->size = sizeof(SOCKADDR_IN); + + addr4 = (SOCKADDR_IN*)dst_addr->data; + addr4->sin_family = AF_INET; + addr4->sin_port = _htons(port); + addr4->sin_addr = *(IN_ADDR*)src_addr; + } + + *numAddrs = i; /* Must have at least one IPv4 address */ - if (!res->h_addr_list[0]) return ERR_INVALID_ARGUMENT; - - addr4->sin_family = AF_INET; - addr4->sin_port = _htons(port); - addr4->sin_addr = *(IN_ADDR*)res->h_addr_list[0]; - return 0; + return i == 0 ? ERR_INVALID_ARGUMENT : 0; } -static int Socket_ParseAddress(void* dst, INT* size, const cc_string* address, int port) { - SOCKADDR_IN* addr4 = (SOCKADDR_IN*)dst; - SOCKADDR_IN6* addr6 = (SOCKADDR_IN6*)dst; - cc_winstring addr; - Platform_EncodeString(&addr, address); +cc_result Socket_ParseAddress(const cc_string* address, int port, cc_sockaddr* addrs, int* numAddrs) { + SOCKADDR_IN* addr4 = (SOCKADDR_IN* )addrs[0].data; + SOCKADDR_IN6* addr6 = (SOCKADDR_IN6*)addrs[0].data; + cc_winstring str; + INT size; - *size = sizeof(*addr4); - if (!_WSAStringToAddressW(addr.uni, AF_INET, NULL, addr4, size)) { + *numAddrs = 0; + Platform_EncodeString(&str, address); + + size = sizeof(*addr4); + if (!_WSAStringToAddressW(str.uni, AF_INET, NULL, addr4, &size)) { addr4->sin_port = _htons(port); + + addrs[0].size = size; + *numAddrs = 1; return 0; } - *size = sizeof(*addr6); - if (!_WSAStringToAddressW(addr.uni, AF_INET6, NULL, addr6, size)) { + size = sizeof(*addr6); + if (!_WSAStringToAddressW(str.uni, AF_INET6, NULL, addr6, &size)) { addr6->sin6_port = _htons(port); + + addrs[0].size = size; + *numAddrs = 1; return 0; } - *size = sizeof(*addr4); - return ParseHost(dst, addr.ansi, port); + return ParseHost(str.ansi, port, addrs, numAddrs); } -int Socket_ValidAddress(const cc_string* address) { - SOCKADDR_STORAGE addr; - INT addrSize; - return Socket_ParseAddress(&addr, &addrSize, address, 0) == 0; -} - -cc_result Socket_Connect(cc_socket* s, const cc_string* address, int port, cc_bool nonblocking) { - SOCKADDR_STORAGE addr; +cc_result Socket_Connect(cc_socket* s, cc_sockaddr* addr, cc_bool nonblocking) { + SOCKADDR* raw_addr = (SOCKADDR*)addr->data; cc_result res; - INT addrSize; - *s = -1; - res = Socket_ParseAddress(&addr, &addrSize, address, port); - if (res) return res; - - *s = _socket(addr.ss_family, SOCK_STREAM, IPPROTO_TCP); + *s = _socket(raw_addr->sa_family, SOCK_STREAM, IPPROTO_TCP); if (*s == -1) return _WSAGetLastError(); if (nonblocking) { @@ -526,7 +540,7 @@ cc_result Socket_Connect(cc_socket* s, const cc_string* address, int port, cc_bo _ioctlsocket(*s, FIONBIO, &blockingMode); } - res = _connect(*s, (SOCKADDR*)&addr, addrSize); + res = _connect(*s, raw_addr, addr->size); return res == -1 ? _WSAGetLastError() : 0; } diff --git a/src/Server.c b/src/Server.c index 4de493917..bb6c7ba87 100644 --- a/src/Server.c +++ b/src/Server.c @@ -277,7 +277,10 @@ static void MPConnection_TickConnect(void) { } static void MPConnection_BeginConnect(void) { + static const cc_string invalid_reason = String_FromConst("Invalid IP address"); cc_string title; char titleBuffer[STRING_SIZE]; + cc_sockaddr addrs[SOCKET_MAX_ADDRS]; + int numAddrs; cc_result res; String_InitArray(title, titleBuffer); @@ -289,10 +292,16 @@ static void MPConnection_BeginConnect(void) { Blocks.CanPlace[BLOCK_STILL_WATER] = false; Blocks.CanDelete[BLOCK_STILL_WATER] = false; Blocks.CanPlace[BLOCK_BEDROCK] = false; Blocks.CanDelete[BLOCK_BEDROCK] = false; - res = Socket_Connect(&net_socket, &Server.Address, Server.Port, true); + res = Socket_ParseAddress(&Server.Address, Server.Port, addrs, &numAddrs); if (res == ERR_INVALID_ARGUMENT) { - static const cc_string reason = String_FromConst("Invalid IP address"); - MPConnection_Fail(&reason); + MPConnection_Fail(&invalid_reason); return; + } else if (res) { + MPConnection_FailConnect(res); return; + } + + res = Socket_Connect(&net_socket, &addrs[0], true); + if (res == ERR_INVALID_ARGUMENT) { + MPConnection_Fail(&invalid_reason); } else if (res && res != ReturnCode_SocketInProgess && res != ReturnCode_SocketWouldBlock) { MPConnection_FailConnect(res); } else {