// ____ _ __ // / __ )____ _____ | | / /___ ___________ // / __ / __ \/ ___/ | | /| / / __ `/ ___/ ___/ // / /_/ / /_/ (__ ) | |/ |/ / /_/ / / (__ ) // /_____/\____/____/ |__/|__/\__,_/_/ /____/ // // A futuristic real-time strategy game. // This file is part of Bos Wars. // /**@name lowlevel.cpp - The network lowlevel. */ // // (c) Copyright 2000-2007 by Lutz Sammer and Jimmy Salmon // // 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; only version 2 of the License. // // 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. // // $Id: lowlevel.cpp 8617 2007-03-15 05:35:34Z jim4 $ //@{ //---------------------------------------------------------------------------- // Includes //---------------------------------------------------------------------------- #include #include #include #include #include #include #ifndef _MSC_VER #include #endif #include "stratagus.h" #include "net_lowlevel.h" #include "network.h" //---------------------------------------------------------------------------- // Declarations //---------------------------------------------------------------------------- #define MAX_LOC_IP 10 #if defined(_MSC_VER) || defined(__MINGW32__) typedef const char *setsockopttype; typedef char *recvfrombuftype; typedef char *recvbuftype; typedef const char *sendtobuftype; typedef const char *sendbuftype; typedef int socklen_t; #else typedef const void *setsockopttype; typedef void *recvfrombuftype; typedef void *recvbuftype; typedef const void *sendtobuftype; typedef const void *sendbuftype; #endif //---------------------------------------------------------------------------- // Variables //---------------------------------------------------------------------------- int NetLastSocket; /// Last socket unsigned long NetLastHost; /// Last host number (net format) int NetLastPort; /// Last port number (net format) unsigned long NetLocalAddrs[MAX_LOC_IP]; /// Local IP-Addrs of this host (net format) //---------------------------------------------------------------------------- // Low level functions //---------------------------------------------------------------------------- #ifdef USE_WINSOCK // { /** ** Hardware dependend network init. */ int NetInit(void) { WSADATA wsaData; // Start up the windows networking if (WSAStartup(MAKEWORD(2, 2), &wsaData)) { fprintf(stderr, "Couldn't initialize Winsock 2\n"); return -1; } return 0; } /** ** Hardware dependend network exit. */ void NetExit(void) { // Clean up windows networking if (WSACleanup() == SOCKET_ERROR) { if (WSAGetLastError() == WSAEINPROGRESS) { WSACancelBlockingCall(); WSACleanup(); } } } /** ** Close an UDP socket port. ** ** @param sockfd Socket fildes */ void NetCloseUDP(Socket sockfd) { closesocket(sockfd); } /** ** Close a TCP socket port. ** ** @param sockfd Socket fildes */ void NetCloseTCP(Socket sockfd) { closesocket(sockfd); } #endif // } !USE_WINSOCK #if !defined(USE_WINSOCK) // { /** ** Hardware dependend network init. */ int NetInit(void) { return 0; } /** ** Hardware dependend network exit. */ void NetExit(void) { } /** ** Close an UDP socket port. ** ** @param sockfd Socket fildes */ void NetCloseUDP(Socket sockfd) { close(sockfd); } /** ** Close a TCP socket port. ** ** @param sockfd Socket fildes */ void NetCloseTCP(Socket sockfd) { close(sockfd); } #endif // } !USE_WINSOCK /** ** Set socket to non-blocking. ** ** @param sockfd Socket ** ** @return 0 for success, -1 for error */ #ifdef USE_WINSOCK int NetSetNonBlocking(Socket sockfd) { unsigned long opt = 1; return ioctlsocket(sockfd, FIONBIO, &opt); } #else int NetSetNonBlocking(Socket sockfd) { int flags = fcntl(sockfd, F_GETFL, 0); return fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); } #endif /** ** Resolve host in name or dotted quad notation. ** ** @param host Host name (f.e. 192.168.0.0 or stratagus.net) */ unsigned long NetResolveHost(const std::string &host) { unsigned long addr; if (!host.empty()) { addr = inet_addr(host.c_str()); // try dot notation if (addr == INADDR_NONE) { struct hostent *he; he = 0; he = gethostbyname(host.c_str()); if (he) { addr = 0; Assert(he->h_length == 4); memcpy(&addr, he->h_addr, he->h_length); } } return addr; } return INADDR_NONE; } /** ** Get IP-addrs of local interfaces from Network file descriptor ** and store them in the NetLocalAddrs array. ** ** @param sock local socket. ** ** @return number of IP-addrs found. */ #ifdef USE_WINSOCK // { // ARI: MS documented this for winsock2, so I finally found it.. // I also found a way for winsock1.1 (= win95), but // that one was too complex to start with.. -> trouble // Lookout for INTRFC.EXE on the MS web site... int NetSocketAddr(const Socket sock) { INTERFACE_INFO localAddr[MAX_LOC_IP]; // Assume there will be no more than MAX_LOC_IP interfaces DWORD bytesReturned; SOCKADDR_IN *pAddrInet; u_long SetFlags; int i; int nif; int wsError; int numLocalAddr; nif = 0; if (sock != (Socket)-1) { wsError = WSAIoctl(sock, SIO_GET_INTERFACE_LIST, NULL, 0, &localAddr, sizeof(localAddr), &bytesReturned, NULL, NULL); if (wsError == SOCKET_ERROR) { DebugPrint("SIOCGIFCONF:WSAIoctl(SIO_GET_INTERFACE_LIST) - errno %d\n" _C_ WSAGetLastError()); } // parse interface information numLocalAddr = (bytesReturned / sizeof(INTERFACE_INFO)); for (i = 0; i < numLocalAddr; ++i) { SetFlags = localAddr[i].iiFlags; if ((SetFlags & IFF_UP) == 0) { continue; } if ((SetFlags & IFF_LOOPBACK)) { continue; } pAddrInet = (SOCKADDR_IN *)&localAddr[i].iiAddress; NetLocalAddrs[nif] = pAddrInet->sin_addr.s_addr; ++nif; if (nif == MAX_LOC_IP) { break; } } } return nif; } #else // } { !USE_WINSOCK #ifdef unix // { // ARI: I knew how to write this for a unix environment, // but am quite certain that porting this can cause you // trouble.. int NetSocketAddr(const Socket sock) { char buf[4096]; char *cp; char *cplim; struct ifconf ifc; struct ifreq ifreq; struct ifreq *ifr; struct sockaddr_in *sap; struct sockaddr_in sa; int i; int nif; nif = 0; if (sock != (Socket)-1) { ifc.ifc_len = sizeof(buf); ifc.ifc_buf = buf; if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) { DebugPrint("SIOCGIFCONF - errno %d\n" _C_ errno); return 0; } // with some inspiration from routed.. ifr = ifc.ifc_req; cplim = buf + ifc.ifc_len; // skip over if's with big ifr_addr's for (cp = buf; cp < cplim; cp += sizeof(ifr->ifr_name) + sizeof(ifr->ifr_ifru)) { ifr = (struct ifreq *)cp; ifreq = *ifr; if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreq) < 0) { DebugPrint("%s: SIOCGIFFLAGS - errno %d\n" _C_ ifr->ifr_name _C_ errno); continue; } if ((ifreq.ifr_flags & IFF_UP) == 0 || ifr->ifr_addr.sa_family == AF_UNSPEC) { continue; } // argh, this'll have to change sometime if (ifr->ifr_addr.sa_family != AF_INET) { continue; } if (ifreq.ifr_flags & IFF_LOOPBACK) { continue; } sap = (struct sockaddr_in *)&ifr->ifr_addr; sa = *sap; NetLocalAddrs[nif] = sap->sin_addr.s_addr; if (ifreq.ifr_flags & IFF_POINTOPOINT) { if (ioctl(sock, SIOCGIFDSTADDR, (char *)&ifreq) < 0) { DebugPrint("%s: SIOCGIFDSTADDR - errno %d\n" _C_ ifr->ifr_name _C_ errno); // failed to obtain dst addr - ignore continue; } if (ifr->ifr_addr.sa_family == AF_UNSPEC) { continue; } } // avoid p-t-p links with common src if (nif) { for (i = 0; i < nif; ++i) { if (sa.sin_addr.s_addr == NetLocalAddrs[i]) { i = -1; break; } } if (i == -1) { continue; } } ++nif; if (nif == MAX_LOC_IP) { break; } } } return nif; } #else // } !unix // Beos?? Mac?? int NetSocketAddr(const Socket sock) { NetLocalAddrs[0] = htonl(0x7f000001); return 1; } #endif #endif // } !USE_WINSOCK /** ** Open an UDP Socket port. ** ** @param port !=0 Port to bind in host notation. ** ** @return If success the socket fildes, -1 otherwise. */ Socket NetOpenUDP(int port) { Socket sockfd; // open the socket sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd == INVALID_SOCKET) { return (Socket)-1; } // bind local port if (port) { struct sockaddr_in sock_addr; memset(&sock_addr, 0, sizeof(sock_addr)); sock_addr.sin_family = AF_INET; sock_addr.sin_addr.s_addr = INADDR_ANY; sock_addr.sin_port = htons(port); // Bind the socket for listening if (bind(sockfd, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) < 0) { fprintf(stderr, "Couldn't bind to local port\n"); NetCloseUDP(sockfd); return (Socket)-1; } NetLastHost = sock_addr.sin_addr.s_addr; NetLastPort = sock_addr.sin_port; } return sockfd; } /** ** Open a TCP socket ** ** @param port Bind socket to a specific port number ** ** @return If success the socket fildes, -1 otherwise */ Socket NetOpenTCP(int port) { Socket sockfd; sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == INVALID_SOCKET) { return (Socket)-1; } // bind local port if (port) { struct sockaddr_in sock_addr; int opt; memset(&sock_addr, 0, sizeof(sock_addr)); sock_addr.sin_family = AF_INET; sock_addr.sin_addr.s_addr = INADDR_ANY; sock_addr.sin_port = htons(port); opt = 1; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (setsockopttype)&opt, sizeof(opt)); if (bind(sockfd,(struct sockaddr *)&sock_addr, sizeof(sock_addr)) < 0) { fprintf(stderr, "Couldn't bind to local port\n"); NetCloseTCP(sockfd); return (Socket)-1; } NetLastHost = sock_addr.sin_addr.s_addr; NetLastPort = sock_addr.sin_port; } NetLastSocket = sockfd; return sockfd; } /** ** Open a TCP connection ** ** @param sockfd An open socket to use ** @param addr Address returned from NetResolveHost ** @param port Port on remote host to connect to ** ** @return 0 if success, -1 if failure */ int NetConnectTCP(Socket sockfd, unsigned long addr, int port) { struct sockaddr_in sa; #ifndef __BEOS__ int opt; opt = 1; setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, (setsockopttype)&opt, sizeof(opt)); opt = 0; setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (setsockopttype)&opt, sizeof(opt)); #endif if (addr == INADDR_NONE) { return -1; } memset(&sa, 0, sizeof(sa)); memcpy(&sa.sin_addr, &addr, sizeof(addr)); sa.sin_family = AF_INET; sa.sin_port = htons(port); if (connect(sockfd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { fprintf(stderr, "connect to %d.%d.%d.%d:%d failed\n", NIPQUAD(ntohl(addr)), port); return -1; } return sockfd; } /** ** Wait for socket ready. ** ** @param sockfd Socket fildes to probe. ** @param timeout Timeout in 1/1000 seconds. ** ** @return 1 if data is available, 0 if not, -1 if failure. */ int NetSocketReady(Socket sockfd, int timeout) { int retval; struct timeval tv; fd_set mask; // Check the file descriptors for available data do { // Set up the mask of file descriptors FD_ZERO(&mask); FD_SET(sockfd, &mask); // Set up the timeout tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; // Data available? retval = select(sockfd + 1, &mask, NULL, NULL, &tv); #ifdef USE_WINSOCK } while (retval == SOCKET_ERROR && WSAGetLastError() == WSAEINTR); #else } while (retval == -1 && errno == EINTR); #endif return retval; } /** ** Wait for socket set ready. ** ** @param set Socket set to probe. ** @param timeout Timeout in 1/1000 seconds. ** ** @return 1 if data is available, 0 if not, -1 if failure. */ int NetSocketSetReady(SocketSet *set, int timeout) { int retval; struct timeval tv; fd_set mask; std::vector::size_type i; // Check the file descriptors for available data do { // Set up the mask of file descriptors FD_ZERO(&mask); for (i = 0; i < set->Sockets.size(); ++i) { FD_SET(set->Sockets[i], &mask); } // Set up the timeout tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) * 1000; // Data available? retval = select(set->MaxSockFD + 1, &mask, NULL, NULL, &tv); #ifdef USE_WINSOCK } while (retval == SOCKET_ERROR && WSAGetLastError() == WSAEINTR); #else } while (retval == -1 && errno == EINTR); #endif for (i = 0; i < set->Sockets.size(); ++i) { set->SocketReady[i] = FD_ISSET(set->Sockets[i], &mask); } return retval; } /** ** Check if a socket in a socket set is ready. ** ** @param set Socket set ** @param socket Socket to check ** ** @return Non-zero if socket is ready */ int NetSocketSetSocketReady(SocketSet *set, Socket socket) { std::vector::size_type i; for (i = 0; i < set->Sockets.size(); ++i) { if (set->Sockets[i] == socket) { return set->SocketReady[i]; } } DebugPrint("Socket not found in socket set\n"); return 0; } /** ** Receive from a UDP socket. ** ** @param sockfd Socket ** @param buf Receive message buffer. ** @param len Receive message buffer length. ** ** @return Number of bytes placed in buffer, or -1 if failure. */ int NetRecvUDP(Socket sockfd, void *buf, int len) { socklen_t n; int l; struct sockaddr_in sock_addr; n = sizeof(struct sockaddr_in); if ((l = recvfrom(sockfd, (recvfrombuftype)buf, len, 0, (struct sockaddr *)&sock_addr, &n)) < 0) { PrintFunction(); fprintf(stdout, "Could not read from UDP socket\n"); return -1; } // Packet check for validness is done higher up, we don't know who should be // sending us packets at this level NetLastHost = sock_addr.sin_addr.s_addr; NetLastPort = sock_addr.sin_port; return l; } /** ** Receive from a TCP socket. ** ** @param sockfd Socket ** @param buf Receive message buffer. ** @param len Receive message buffer length. ** ** @return Number of bytes placed in buffer or -1 if failure. */ int NetRecvTCP(Socket sockfd, void *buf, int len) { int ret; NetLastSocket = sockfd; ret = recv(sockfd, (recvbuftype)buf, len, 0); if (ret > 0) { return ret; } if (ret == 0) { return -1; } #ifdef USE_WINSOCK if (WSAGetLastError() == WSAEWOULDBLOCK) { #else if (errno == EWOULDBLOCK || errno == EAGAIN) { #endif return 0; } return ret; } /** ** Send through a UPD socket to a host:port. ** ** @param sockfd Socket ** @param host Host to send to (network byte order). ** @param port Port of host to send to (network byte order). ** @param buf Send message buffer. ** @param len Send message buffer length. ** ** @return Number of bytes sent. */ int NetSendUDP(Socket sockfd, unsigned long host, int port, const void *buf, int len) { int n; struct sockaddr_in sock_addr; n = sizeof(struct sockaddr_in); sock_addr.sin_addr.s_addr = host; sock_addr.sin_port = port; sock_addr.sin_family = AF_INET; // if (MyRand() % 7) { return 0; } return sendto(sockfd, (sendtobuftype)buf, len, 0, (struct sockaddr *)&sock_addr, n); } /** ** Send through a TCP socket. ** ** @param sockfd Socket ** @param buf Send message buffer. ** @param len Send message buffer length. ** ** @return Number of bytes sent. */ int NetSendTCP(Socket sockfd, const void *buf, int len) { return send(sockfd, (sendbuftype)buf, len, 0); } /** ** Listen for connections on a TCP socket. ** ** @param sockfd Socket ** ** @return 0 for success, -1 for error */ int NetListenTCP(Socket sockfd) { return listen(sockfd, PlayerMax); } /** ** Accept a connection on a TCP socket. ** ** @param sockfd Socket ** ** @return If success the new socket fildes, -1 otherwise. */ Socket NetAcceptTCP(Socket sockfd) { struct sockaddr_in sa; socklen_t len; len = sizeof(struct sockaddr_in); NetLastSocket = accept(sockfd, (struct sockaddr *)&sa, &len); NetLastHost = sa.sin_addr.s_addr; NetLastPort = sa.sin_port; return NetLastSocket; } /** ** Add a socket to a socket set ** ** @param socket Socket to add to the socket set */ void SocketSet::AddSocket(Socket socket) { Sockets.push_back(socket); SocketReady.push_back(0); if (socket > MaxSockFD) { MaxSockFD = socket; } } /** ** Delete a socket from a socket set ** ** @param socket Socket to delete from the socket set */ void SocketSet::DelSocket(Socket socket) { std::vector::iterator i; std::vector::iterator j; for (i = Sockets.begin(), j = SocketReady.begin(); i != Sockets.end(); ++i, ++j) { if (*i == socket) { Sockets.erase(i); SocketReady.erase(j); break; } } if (socket == MaxSockFD) { MaxSockFD = 0; for (i = Sockets.begin(); i != Sockets.end(); ++i) { if (*i > MaxSockFD) { MaxSockFD = *i; } } } } //@}