summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2012-03-01 05:07:31 (GMT)
committerH. Peter Anvin <hpa@zytor.com>2012-03-01 05:07:31 (GMT)
commit59432e836fb2e1f2943fcc30e51e9fc6866a39f5 (patch)
tree5b121c6487ca75f3e7bc0fe6637a37a3d7b0d23e
parent6d349aebe9db752359f485ba07edeec937491c18 (diff)
downloadgrv-59432e836fb2e1f2943fcc30e51e9fc6866a39f5.zip
grv-59432e836fb2e1f2943fcc30e51e9fc6866a39f5.tar.gz
grv-59432e836fb2e1f2943fcc30e51e9fc6866a39f5.tar.bz2
grv-59432e836fb2e1f2943fcc30e51e9fc6866a39f5.tar.xz
netopen: Support IPv6; abstract out some common code
If AF_INET6 is defined, compile with getaddrinfo() so we can use IPv6 to talk to the high score server.
-rw-r--r--netopen.c102
1 files changed, 83 insertions, 19 deletions
diff --git a/netopen.c b/netopen.c
index e664363..4599384 100644
--- a/netopen.c
+++ b/netopen.c
@@ -1,7 +1,7 @@
/*
* netopen.c
*
- * Open an IPv4 TCP network connection to the specified hostname and port.
+ * Open a TCP network connection to the specified hostname and port.
* This might need some porting work to work on Win32.
*/
@@ -168,9 +168,6 @@ netcon_t fopen_network(const char *name, int port, int is_write)
if ( !nc )
return NULL;
- if ( !winsock_started )
- start_winsock();
-
/* FIX: We should use getaddrinfo() instead */
he = gethostbyname(name);
if ( !he || he->h_addrtype != AF_INET )
@@ -232,22 +229,83 @@ int fclose_network(netcon_t nc)
#include <sys/socket.h>
#include <netinet/in.h>
-FILE *fopen_network(const char *name, int port, int is_write)
+/* Winsuck-compatibility */
+typedef int SOCKET;
+#define INVALID_SOCKET (-1)
+#define start_winsock() do { } while (0)
+
+static netcon_t sock_to_netcon(int sock, int is_write)
+{
+ FILE *f = fdopen(sock, is_write ? "w" : "r");
+ if ( !f ) {
+ close(sock);
+ return NULL;
+ }
+
+ return f;
+}
+
+#endif
+
+#ifdef AF_INET6
+
+static SOCKET open_socket(const char *name, int port)
+{
+ SOCKET sock;
+ struct addrinfo *ai, *ap;
+ struct addrinfo hints;
+ char port_str[32];
+
+ start_winsock();
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_ADDRCONFIG;
+
+ sprintf(port_str, "%d", port);
+ if (getaddrinfo(name, port_str, &hints, &ai))
+ return INVALID_SOCKET;
+
+ sock = INVALID_SOCKET;
+
+ for (ap = ai; ap; ap = ap->ai_next) {
+ sock = socket(ap->ai_family, ap->ai_socktype, ap->ai_protocol);
+ if ( sock == INVALID_SOCKET )
+ continue;
+
+ if ( !connect(sock, ap->ai_addr, ap->ai_addrlen) )
+ break;
+
+ close(sock);
+ sock = INVALID_SOCKET;
+ }
+
+ freeaddrinfo(ai);
+
+ return sock;
+}
+
+#else
+
+/* Legacy IPv4-only API */
+
+static SOCKET open_socket(const char *name, int port)
{
struct hostent *he;
- int sock;
- FILE *f;
+ SOCKET sock;
struct in_addr **haddr;
struct sockaddr_in si;
- /* FIX: We should use getaddrinfo() instead */
+ start_winsock();
+
he = gethostbyname(name);
if ( !he || he->h_addrtype != AF_INET )
- return NULL;
+ return INVALID_SOCKET;
sock = socket(PF_INET, SOCK_STREAM, 0);
- if ( sock < 0 )
- return NULL;
+ if ( sock == INVALID_SOCKET )
+ return INVALID_SOCKET;
for ( haddr = (struct in_addr **)he->h_addr_list ; *haddr ; haddr++ ) {
memset(&si, 0, sizeof si);
@@ -260,16 +318,22 @@ FILE *fopen_network(const char *name, int port, int is_write)
}
if ( !*haddr ) {
close(sock);
- return NULL;
- }
-
- f = fdopen(sock, is_write ? "w" : "r");
- if ( !f ) {
- close(sock);
- return NULL;
+ return INVALID_SOCKET;
}
- return f;
+ return sock;
}
#endif
+
+netcon_t fopen_network(const char *name, int port, int is_write)
+{
+ SOCKET sock;
+
+ sock = open_socket(name, port);
+ if (sock == INVALID_SOCKET)
+ return NULL;
+
+ return sock_to_netcon(sock, is_write);
+}
+