diff options
Diffstat (limited to 'src/net_win.h')
-rw-r--r-- | src/net_win.h | 401 |
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(); +} + |