summaryrefslogtreecommitdiff
path: root/src/net_win.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/net_win.h')
-rw-r--r--src/net_win.h401
1 files changed, 401 insertions, 0 deletions
diff --git a/src/net_win.h b/src/net_win.h
new file mode 100644
index 0000000..5204780
--- /dev/null
+++ b/src/net_win.h
@@ -0,0 +1,401 @@
+/*
+Copyright (C) 2012 skuller.net
+
+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 2
+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.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+*/
+
+//
+// net_win.h -- Windows sockets wrapper
+//
+
+static const struct {
+ int err;
+ const char *msg;
+} wsa_error_table[] = {
+ { S_OK, "Success" },
+ { WSAEINTR, "Interrupted function call" },
+ { WSAEBADF, "File handle is not valid" },
+ { WSAEACCES, "Permission denied" },
+ { WSAEFAULT, "Bad address" },
+ { WSAEINVAL, "Invalid argument" },
+ { WSAEMFILE, "Too many open files" },
+ { WSAEWOULDBLOCK, "Resource temporarily unavailable" },
+ { WSAEINPROGRESS, "Operation now in progress" },
+ { WSAEALREADY, "Operation already in progress" },
+ { WSAENOTSOCK, "Socket operation on nonsocket" },
+ { WSAEDESTADDRREQ, "Destination address required" },
+ { WSAEMSGSIZE, "Message too long" },
+ { WSAEPROTOTYPE, "Protocol wrong type for socket" },
+ { WSAENOPROTOOPT, "Bad protocol option" },
+ { WSAEPROTONOSUPPORT, "Protocol not supported" },
+ { WSAESOCKTNOSUPPORT, "Socket type not supported" },
+ { WSAEOPNOTSUPP, "Operation not supported" },
+ { WSAEPFNOSUPPORT, "Protocol family not supported" },
+ { WSAEAFNOSUPPORT, "Address family not supported by protocol family" },
+ { WSAEADDRINUSE, "Address already in use" },
+ { WSAEADDRNOTAVAIL, "Cannot assign requested address" },
+ { WSAENETDOWN, "Network is down" },
+ { WSAENETUNREACH, "Network is unreachable" },
+ { WSAENETRESET, "Network dropped connection on reset" },
+ { WSAECONNABORTED, "Software caused connection abort" },
+ { WSAECONNRESET, "Connection reset by peer" },
+ { WSAENOBUFS, "No buffer space available" },
+ { WSAEISCONN, "Socket is already connected" },
+ { WSAENOTCONN, "Socket is not connected" },
+ { WSAESHUTDOWN, "Cannot send after socket shutdown" },
+ { WSAETOOMANYREFS, "Too many references" },
+ { WSAETIMEDOUT, "Connection timed out" },
+ { WSAECONNREFUSED, "Connection refused" },
+ { WSAELOOP, "Cannot translate name" },
+ { WSAENAMETOOLONG, "Name too long" },
+ { WSAEHOSTDOWN, "Host is down" },
+ { WSAEHOSTUNREACH, "No route to host" },
+ { WSAENOTEMPTY, "Directory not empty" },
+ { WSAEPROCLIM, "Too many processes" },
+ { WSAEUSERS, "User quota exceeded" },
+ { WSAEDQUOT, "Disk quota exceeded" },
+ { WSAESTALE, "Stale file handle reference" },
+ { WSAEREMOTE, "Item is remote" },
+ { WSASYSNOTREADY, "Network subsystem is unavailable" },
+ { WSAVERNOTSUPPORTED, "Winsock.dll version out of range" },
+ { WSANOTINITIALISED, "Successful WSAStartup not yet performed" },
+ { WSAEDISCON, "Graceful shutdown in progress" },
+ { WSAENOMORE, "No more results" },
+ { WSAECANCELLED, "Call has been canceled" },
+ { WSAEINVALIDPROCTABLE, "Procedure call table is invalid" },
+ { WSAEINVALIDPROVIDER, "Service provider is invalid" },
+ { WSAEPROVIDERFAILEDINIT, "Service provider failed to initialize" },
+ { WSASYSCALLFAILURE, "System call failure" },
+ { WSASERVICE_NOT_FOUND, "Service not found" },
+ { WSATYPE_NOT_FOUND, "Class type not found" },
+ { WSA_E_NO_MORE, "No more results" },
+ { WSA_E_CANCELLED, "Call was canceled" },
+ { WSAEREFUSED, "Database query was refused" },
+ { WSAHOST_NOT_FOUND, "Host not found" },
+ { WSATRY_AGAIN, "Nonauthoritative host not found" },
+ { WSANO_RECOVERY, "This is a nonrecoverable error" },
+ { WSANO_DATA, "Valid name, no data record of requested type" },
+ { -1, "Unknown error" }
+};
+
+static const char *os_error_string(int err)
+{
+ int i;
+
+ for (i = 0; wsa_error_table[i].err != -1; i++) {
+ if (wsa_error_table[i].err == err)
+ break;
+ }
+
+ return wsa_error_table[i].msg;
+}
+
+static ssize_t os_udp_recv(netsrc_t sock, void *data,
+ size_t len, netadr_t *from)
+{
+ struct sockaddr_in addr;
+ int addrlen;
+ int ret;
+ int tries;
+
+ for (tries = 0; tries < MAX_ERROR_RETRIES; tries++) {
+ memset(&addr, 0, sizeof(addr));
+ addrlen = sizeof(addr);
+ ret = recvfrom(udp_sockets[sock], data, len, 0,
+ (struct sockaddr *)&addr, &addrlen);
+
+ NET_SockadrToNetadr(&addr, from);
+
+ if (ret != SOCKET_ERROR)
+ return ret;
+
+ net_error = WSAGetLastError();
+
+ // wouldblock is silent
+ if (net_error == WSAEWOULDBLOCK)
+ return NET_AGAIN;
+
+#if USE_ICMP
+ if (net_error == WSAECONNRESET || net_error == WSAENETRESET) {
+ // winsock has already provided us with
+ // a valid address from ICMP error packet
+ NET_ErrorEvent(sock, from, net_error, 0);
+ continue;
+ }
+#endif
+
+ break;
+ }
+
+ return NET_ERROR;
+}
+
+static ssize_t os_udp_send(netsrc_t sock, const void *data,
+ size_t len, const netadr_t *to)
+{
+ struct sockaddr_in addr;
+ int ret;
+
+ NET_NetadrToSockadr(to, &addr);
+
+ ret = sendto(udp_sockets[sock], data, len, 0,
+ (struct sockaddr *)&addr, sizeof(addr));
+
+ if (ret != SOCKET_ERROR)
+ return ret;
+
+ net_error = WSAGetLastError();
+
+ // wouldblock is silent
+ if (net_error == WSAEWOULDBLOCK || net_error == WSAEINTR)
+ return NET_AGAIN;
+
+ // some PPP links do not allow broadcasts
+ if (net_error == WSAEADDRNOTAVAIL && to->type == NA_BROADCAST)
+ return NET_AGAIN;
+
+ return NET_ERROR;
+}
+
+static neterr_t os_get_error(void)
+{
+ net_error = WSAGetLastError();
+ if (net_error == WSAEWOULDBLOCK)
+ return NET_AGAIN;
+
+ return NET_ERROR;
+}
+
+static ssize_t os_recv(qsocket_t sock, void *data, size_t len, int flags)
+{
+ int ret = recv(sock, data, len, flags);
+
+ if (ret == SOCKET_ERROR)
+ return os_get_error();
+
+ return ret;
+}
+
+static ssize_t os_send(qsocket_t sock, const void *data, size_t len, int flags)
+{
+ int ret = send(sock, data, len, flags);
+
+ if (ret == SOCKET_ERROR)
+ return os_get_error();
+
+ return ret;
+}
+
+static neterr_t os_listen(qsocket_t sock, int backlog)
+{
+ if (listen(sock, backlog) == SOCKET_ERROR) {
+ net_error = WSAGetLastError();
+ return NET_ERROR;
+ }
+
+ return NET_OK;
+}
+
+static neterr_t os_accept(qsocket_t sock, qsocket_t *newsock, netadr_t *from)
+{
+ struct sockaddr_in addr;
+ int addrlen;
+ int s;
+
+ memset(&addr, 0, sizeof(addr));
+ addrlen = sizeof(addr);
+ s = accept(sock, (struct sockaddr *)&addr, &addrlen);
+
+ NET_SockadrToNetadr(&addr, from);
+
+ if (s == INVALID_SOCKET) {
+ *newsock = -1;
+ return os_get_error();
+ }
+
+ *newsock = s;
+ return NET_OK;
+}
+
+static neterr_t os_connect(qsocket_t sock, const netadr_t *to)
+{
+ struct sockaddr_in addr;
+
+ NET_NetadrToSockadr(to, &addr);
+
+ if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR) {
+ net_error = WSAGetLastError();
+ if (net_error == WSAEWOULDBLOCK)
+ return NET_OK;
+
+ return NET_ERROR;
+ }
+
+ return NET_OK;
+}
+
+static neterr_t os_make_nonblock(qsocket_t sock, int val)
+{
+ u_long _val = val;
+
+ if (ioctlsocket(sock, FIONBIO, &_val) == SOCKET_ERROR) {
+ net_error = WSAGetLastError();
+ return NET_ERROR;
+ }
+
+ return NET_OK;
+}
+
+static neterr_t os_setsockopt(qsocket_t sock, int level, int name, int val)
+{
+ u_long _val = val;
+
+ if (setsockopt(sock, level, name, (char *)&_val, sizeof(_val)) == SOCKET_ERROR) {
+ net_error = WSAGetLastError();
+ return NET_ERROR;
+ }
+
+ return NET_OK;
+}
+
+static neterr_t os_getsockopt(qsocket_t sock, int level, int name, int *val)
+{
+ u_long _val;
+ int optlen = sizeof(_val);
+
+ if (getsockopt(sock, level, name, (char *)&_val, &optlen) == SOCKET_ERROR) {
+ net_error = WSAGetLastError();
+ return NET_ERROR;
+ }
+
+ *val = _val;
+ return NET_OK;
+}
+
+static neterr_t os_bind(qsocket_t sock, const struct sockaddr *addr, size_t addrlen)
+{
+ if (bind(sock, addr, addrlen) == SOCKET_ERROR) {
+ net_error = WSAGetLastError();
+ return NET_ERROR;
+ }
+
+ return NET_OK;
+}
+
+static neterr_t os_getsockname(qsocket_t sock, netadr_t *name)
+{
+ struct sockaddr_in addr;
+ int addrlen;
+
+ memset(&addr, 0, sizeof(addr));
+ addrlen = sizeof(addr);
+ if (getsockname(sock, (struct sockaddr *)&addr, &addrlen) == SOCKET_ERROR) {
+ net_error = WSAGetLastError();
+ return NET_ERROR;
+ }
+
+ NET_SockadrToNetadr(&addr, name);
+ return NET_OK;
+}
+
+static void os_closesocket(qsocket_t sock)
+{
+ closesocket(sock);
+}
+
+static qsocket_t os_socket(int domain, int type, int protocol)
+{
+ SOCKET s = socket(domain, type, protocol);
+
+ if (s == INVALID_SOCKET) {
+ net_error = WSAGetLastError();
+ return -1;
+ }
+
+ return s;
+}
+
+static ioentry_t *os_add_io(qsocket_t fd)
+{
+ ioentry_t *e;
+ int i;
+
+ for (i = 0, e = io_entries; i < io_numfds; i++, e++) {
+ if (!e->inuse)
+ break;
+ }
+
+ if (i == io_numfds) {
+ if (++io_numfds > FD_SETSIZE)
+ Com_Error(ERR_FATAL, "%s: no more space for fd: %d", __func__, fd);
+ }
+
+ e->fd = fd;
+ return e;
+}
+
+static ioentry_t *os_get_io(qsocket_t fd)
+{
+ ioentry_t *e;
+ int i;
+
+ for (i = 0, e = io_entries; i < io_numfds; i++, e++) {
+ if (!e->inuse)
+ continue;
+ if (e->fd == fd)
+ return e;
+ }
+
+ Com_Error(ERR_FATAL, "%s: fd not found: %d", __func__, fd);
+ return NULL;
+}
+
+static qsocket_t os_get_fd(ioentry_t *e)
+{
+ return e->fd;
+}
+
+static int os_select(int nfds, fd_set *rfds, fd_set *wfds,
+ fd_set *efds, struct timeval *tv)
+{
+ int ret = select(nfds, rfds, wfds, efds, tv);
+
+ if (ret == SOCKET_ERROR)
+ net_error = WSAGetLastError();
+
+ return ret;
+}
+
+static void os_net_init(void)
+{
+ WSADATA ws;
+ int ret;
+
+ ret = WSAStartup(MAKEWORD(1, 1), &ws);
+ if (ret) {
+ Com_Error(ERR_FATAL, "Winsock initialization failed: %s (%d)",
+ os_error_string(ret), ret);
+ }
+
+ Com_DPrintf("Winsock initialized\n");
+}
+
+static void os_net_shutdown(void)
+{
+ WSACleanup();
+}
+