/* Copyright (C) 1999-2006 Id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant 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. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ //=========================================================================== // // Name: l_net_wins.c // Function: WinSock // Programmer: MrElusive // Last update: TTimo: cross-platform version, l_net library // Tab Size: 2 // Notes: //=========================================================================== #if defined(POSIX) || defined(__linux__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) //#include #include #include #include #include "l_net.h" #include "l_net_wins.h" #include #include #include #include #include #include #include #include #include #include #include const int SOCKET_ERROR = -1; const int INVALID_SOCKET = -1; #define WinError WinPrint #define ioctlsocket ioctl #define closesocket close int WSAGetLastError(){ return errno; } /* typedef struct tag_error_struct { int errnum; LPSTR errstr; } ERROR_STRUCT; */ typedef struct tag_error_struct { int errnum; const char *errstr; } ERROR_STRUCT; #define NET_NAMELEN 64 static char my_tcpip_address[NET_NAMELEN]; const int DEFAULTnet_hostport = 26000; const int MAXHOSTNAMELEN = 256; static int net_acceptsocket = -1; // socket for fielding new connections static int net_controlsocket; static int net_hostport; // udp port number for acceptsocket static int net_broadcastsocket = 0; //static qboolean ifbcastinit = qfalse; //static struct sockaddr_s broadcastaddr; static struct sockaddr_s broadcastaddr; static unsigned long myAddr; ERROR_STRUCT errlist[] = { {EACCES,"EACCES - The address is protected, user is not root"}, {EAGAIN,"EAGAIN - Operation on non-blocking socket that cannot return immediatly"}, {EBADF, "EBADF - sockfd is not a valid descriptor"}, {EFAULT, "EFAULT - The parameter is not in a writable part of the user address space"}, {EINVAL,"EINVAL - The socket is already bound to an address"}, {ENOBUFS,"ENOBUFS - not enough memory"}, {ENOMEM, "ENOMEM - not enough memory"}, {ENOTCONN, "ENOTCONN - not connected"}, {ENOTSOCK,"ENOTSOCK - Argument is file descriptor not a socket"}, {EOPNOTSUPP,"ENOTSUPP - The referenced socket is not of type SOCK_STREAM"}, {EPERM, "EPERM - Firewall rules forbid connection"}, {-1, NULL} }; //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== const char *WINS_ErrorMessage( int error ){ int search = 0; if ( !error ) { return "No error occurred"; } for ( search = 0; errlist[search].errstr; search++ ) { if ( error == errlist[search].errnum ) { return errlist[search].errstr; } } //end for return "Unknown error"; } //end of the function WINS_ErrorMessage //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int WINS_Init( void ){ int i; struct hostent *local; char buff[MAXHOSTNAMELEN]; struct sockaddr_s addr; char *p; /* linux doesn't have anything to initialize for the net "Windows .. built for the internet .. the internet .. built with unix" */ #if 0 WORD wVersionRequested; wVersionRequested = MAKEWORD( 2, 2 ); r = WSAStartup( wVersionRequested, &winsockdata ); if ( r ) { WinPrint( "Winsock initialization failed.\n" ); return -1; } #endif /* i = COM_CheckParm ("-udpport"); if (i == 0)*/ net_hostport = DEFAULTnet_hostport; /* else if (i < com_argc-1) net_hostport = Q_atoi (com_argv[i+1]); else Sys_Error ("WINS_Init: you must specify a number after -udpport"); */ // determine my name & address gethostname( buff, MAXHOSTNAMELEN ); local = gethostbyname( buff ); if(local && local->h_addr_list && local->h_addr_list[0]) myAddr = *(int *)local->h_addr_list[0]; else myAddr = inet_addr("127.0.0.1"); // if the quake hostname isn't set, set it to the machine name // if (Q_strcmp(hostname.string, "UNNAMED") == 0) { // see if it's a text IP address (well, close enough) for ( p = buff; *p; p++ ) if ( ( *p < '0' || *p > '9' ) && *p != '.' ) { break; } // if it is a real name, strip off the domain; we only want the host if ( *p ) { for ( i = 0; i < 15; i++ ) if ( buff[i] == '.' ) { break; } buff[i] = 0; } // Cvar_Set ("hostname", buff); } //++timo WTF is that net_controlsocket? it's sole purpose is to retrieve the local IP? if ( ( net_controlsocket = WINS_OpenSocket( 0 ) ) == SOCKET_ERROR ) { WinError( "WINS_Init: Unable to open control socket\n" ); } ( (struct sockaddr_in *)&broadcastaddr )->sin_family = AF_INET; ( (struct sockaddr_in *)&broadcastaddr )->sin_addr.s_addr = INADDR_BROADCAST; ( (struct sockaddr_in *)&broadcastaddr )->sin_port = htons( (u_short)net_hostport ); WINS_GetSocketAddr( net_controlsocket, &addr ); strcpy( my_tcpip_address, WINS_AddrToString( &addr ) ); p = strrchr( my_tcpip_address, ':' ); if ( p ) { *p = 0; } WinPrint( "Winsock Initialized\n" ); return net_controlsocket; } //end of the function WINS_Init //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== char *WINS_MyAddress( void ){ return my_tcpip_address; } //end of the function WINS_MyAddress //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void WINS_Shutdown( void ){ //WINS_Listen(0); WINS_CloseSocket( net_controlsocket ); // WSACleanup(); // //WinPrint("Winsock Shutdown\n"); } //end of the function WINS_Shutdown //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== /* void WINS_Listen(int state) { // enable listening if (state) { if (net_acceptsocket != -1) return; if ((net_acceptsocket = WINS_OpenSocket (net_hostport)) == -1) WinError ("WINS_Listen: Unable to open accept socket\n"); return; } // disable listening if (net_acceptsocket == -1) return; WINS_CloseSocket (net_acceptsocket); net_acceptsocket = -1; } //end of the function WINS_Listen*/ //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int WINS_OpenSocket( int port ){ int newsocket; struct sockaddr_in address; u_long _true = 1; if ( ( newsocket = socket( PF_INET, SOCK_DGRAM, IPPROTO_UDP ) ) == SOCKET_ERROR ) { WinPrint( "WINS_OpenSocket: %s\n", WINS_ErrorMessage( WSAGetLastError() ) ); return -1; } //end if if ( ioctlsocket( newsocket, FIONBIO, &_true ) == SOCKET_ERROR ) { WinPrint( "WINS_OpenSocket: %s\n", WINS_ErrorMessage( WSAGetLastError() ) ); closesocket( newsocket ); return -1; } //end if memset( (char *) &address, 0, sizeof( address ) ); address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons( (u_short)port ); if ( bind( newsocket, (void *)&address, sizeof( address ) ) == -1 ) { WinPrint( "WINS_OpenSocket: %s\n", WINS_ErrorMessage( WSAGetLastError() ) ); closesocket( newsocket ); return -1; } //end if return newsocket; } //end of the function WINS_OpenSocket //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int WINS_OpenReliableSocket( int port ){ int newsocket; struct sockaddr_in address; qboolean _true = 0xFFFFFFFF; //IPPROTO_TCP // if ( ( newsocket = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ) { WinPrint( "WINS_OpenReliableSocket: %s\n", WINS_ErrorMessage( WSAGetLastError() ) ); return -1; } //end if memset( (char *) &address, 0, sizeof( address ) ); address.sin_family = AF_INET; address.sin_addr.s_addr = htonl( INADDR_ANY ); address.sin_port = htons( (u_short)port ); if ( bind( newsocket, (void *)&address, sizeof( address ) ) == -1 ) { WinPrint( "WINS_OpenReliableSocket: %s\n", WINS_ErrorMessage( WSAGetLastError() ) ); closesocket( newsocket ); return -1; } //end if // if ( setsockopt( newsocket, IPPROTO_TCP, TCP_NODELAY, (void *) &_true, sizeof( int ) ) == -1 ) { WinPrint( "WINS_OpenReliableSocket: %s\n", WINS_ErrorMessage( WSAGetLastError() ) ); WinPrint( "setsockopt error\n" ); } //end if return newsocket; } //end of the function WINS_OpenReliableSocket //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int WINS_Listen( int socket ){ u_long _true = 1; if ( ioctlsocket( socket, FIONBIO, &_true ) == -1 ) { WinPrint( "WINS_Listen: %s\n", WINS_ErrorMessage( WSAGetLastError() ) ); return -1; } //end if if ( listen( socket, SOMAXCONN ) == SOCKET_ERROR ) { WinPrint( "WINS_Listen: %s\n", WINS_ErrorMessage( WSAGetLastError() ) ); return -1; } //end if return 0; } //end of the function WINS_Listen //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int WINS_Accept( int socket, struct sockaddr_s *addr ){ socklen_t addrlen = sizeof( struct sockaddr_s ); int newsocket; qboolean _true = 1; newsocket = accept( socket, (struct sockaddr *)addr, &addrlen ); if ( newsocket == INVALID_SOCKET ) { if ( errno == EAGAIN ) { return -1; } WinPrint( "WINS_Accept: %s\n", WINS_ErrorMessage( WSAGetLastError() ) ); return -1; } //end if // if ( setsockopt( newsocket, IPPROTO_TCP, TCP_NODELAY, (void *) &_true, sizeof( int ) ) == SOCKET_ERROR ) { WinPrint( "WINS_Accept: %s\n", WINS_ErrorMessage( WSAGetLastError() ) ); WinPrint( "setsockopt error\n" ); } //end if return newsocket; } //end of the function WINS_Accept //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int WINS_CloseSocket( int socket ){ /* if (socket == net_broadcastsocket) net_broadcastsocket = 0; */ // shutdown(socket, SD_SEND); if ( closesocket( socket ) == SOCKET_ERROR ) { WinPrint( "WINS_CloseSocket: %s\n", WINS_ErrorMessage( WSAGetLastError() ) ); return SOCKET_ERROR; } //end if return 0; } //end of the function WINS_CloseSocket //=========================================================================== // this lets you type only as much of the net address as required, using // the local network components to fill in the rest // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== static int PartialIPAddress( char *in, struct sockaddr_s *hostaddr ){ char buff[256]; char *b; int addr; int num; int mask; buff[0] = '.'; b = buff; strcpy( buff + 1, in ); if ( buff[1] == '.' ) { b++; } addr = 0; mask = -1; while ( *b == '.' ) { num = 0; if ( *++b < '0' || *b > '9' ) { return -1; } while ( !( *b < '0' || *b > '9' ) ) num = num * 10 + *( b++ ) - '0'; mask <<= 8; addr = ( addr << 8 ) + num; } hostaddr->sa_family = AF_INET; ( (struct sockaddr_in *)hostaddr )->sin_port = htons( (u_short)net_hostport ); ( (struct sockaddr_in *)hostaddr )->sin_addr.s_addr = ( myAddr & htonl( mask ) ) | htonl( addr ); return 0; } //end of the function PartialIPAddress //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int WINS_Connect( int socket, struct sockaddr_s *addr ){ int ret; u_long _true2 = 0xFFFFFFFF; ret = connect( socket, (struct sockaddr *)addr, sizeof( struct sockaddr_s ) ); if ( ret == SOCKET_ERROR ) { WinPrint( "WINS_Connect: %s\n", WINS_ErrorMessage( WSAGetLastError() ) ); return -1; } //end if if ( ioctlsocket( socket, FIONBIO, &_true2 ) == -1 ) { WinPrint( "WINS_Connect: %s\n", WINS_ErrorMessage( WSAGetLastError() ) ); return -1; } //end if return 0; } //end of the function WINS_Connect //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int WINS_CheckNewConnections( void ){ char buf[4]; if ( net_acceptsocket == -1 ) { return -1; } if ( recvfrom( net_acceptsocket, buf, 4, MSG_PEEK, NULL, NULL ) > 0 ) { return net_acceptsocket; } return -1; } //end of the function WINS_CheckNewConnections //=========================================================================== // returns the number of bytes read // 0 if no bytes available // -1 on failure // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int WINS_Read( int socket, byte *buf, int len, struct sockaddr_s *addr ){ socklen_t addrlen = sizeof( struct sockaddr_s ); int ret; if ( addr ) { ret = recvfrom( socket, buf, len, 0, (struct sockaddr *)addr, &addrlen ); if ( ret == -1 ) { // errno = WSAGetLastError(); if ( errno == EAGAIN || errno == ENOTCONN ) { return 0; } } //end if } //end if else { ret = recv( socket, buf, len, 0 ); // if there's no data on the socket ret == -1 and errno == EAGAIN // MSDN states that if ret == 0 the socket has been closed // man recv doesn't say anything if ( ret == 0 ) { return -1; } if ( ret == SOCKET_ERROR ) { // errno = WSAGetLastError(); if ( errno == EAGAIN || errno == ENOTCONN ) { return 0; } } //end if } //end else if ( ret == SOCKET_ERROR ) { WinPrint( "WINS_Read: %s\n", WINS_ErrorMessage( WSAGetLastError() ) ); } //end if return ret; } //end of the function WINS_Read //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int WINS_MakeSocketBroadcastCapable( int socket ){ int i = 1; // make this socket broadcast capable if ( setsockopt( socket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof( i ) ) < 0 ) { return -1; } net_broadcastsocket = socket; return 0; } //end of the function WINS_MakeSocketBroadcastCapable //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int WINS_Broadcast( int socket, byte *buf, int len ){ int ret; if ( socket != net_broadcastsocket ) { if ( net_broadcastsocket != 0 ) { WinError( "Attempted to use multiple broadcasts sockets\n" ); } ret = WINS_MakeSocketBroadcastCapable( socket ); if ( ret == -1 ) { WinPrint( "Unable to make socket broadcast capable\n" ); return ret; } } return WINS_Write( socket, buf, len, &broadcastaddr ); } //end of the function WINS_Broadcast //=========================================================================== // returns qtrue on success or qfalse on failure // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int WINS_Write( int socket, byte *buf, int len, struct sockaddr_s *addr ){ int ret, written; ret = 0; if ( addr ) { written = 0; while ( written < len ) { ret = sendto( socket, &buf[written], len - written, 0, (struct sockaddr *)addr, sizeof( struct sockaddr_s ) ); if ( ret == SOCKET_ERROR ) { if ( WSAGetLastError() != EAGAIN ) { return qfalse; } //++timo FIXME: what is this used for? // Sleep(1000); } //end if else { written += ret; } } } //end if else { written = 0; while ( written < len ) { ret = send( socket, buf, len, 0 ); if ( ret == SOCKET_ERROR ) { if ( WSAGetLastError() != EAGAIN ) { return qfalse; } //++timo FIXME: what is this used for? // Sleep(1000); } //end if else { written += ret; } } } //end else if ( ret == SOCKET_ERROR ) { WinPrint( "WINS_Write: %s\n", WINS_ErrorMessage( WSAGetLastError() ) ); } //end if return ( ret == len ); } //end of the function WINS_Write //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== char *WINS_AddrToString( struct sockaddr_s *addr ){ static char buffer[22]; int haddr; haddr = ntohl( ( (struct sockaddr_in *)addr )->sin_addr.s_addr ); sprintf( buffer, "%d.%d.%d.%d:%d", ( haddr >> 24 ) & 0xff, ( haddr >> 16 ) & 0xff, ( haddr >> 8 ) & 0xff, haddr & 0xff, ntohs( ( (struct sockaddr_in *)addr )->sin_port ) ); return buffer; } //end of the function WINS_AddrToString //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int WINS_StringToAddr( char *string, struct sockaddr_s *addr ){ int ha1, ha2, ha3, ha4, hp; int ipaddr; sscanf( string, "%d.%d.%d.%d:%d", &ha1, &ha2, &ha3, &ha4, &hp ); ipaddr = ( ha1 << 24 ) | ( ha2 << 16 ) | ( ha3 << 8 ) | ha4; addr->sa_family = AF_INET; ( (struct sockaddr_in *)addr )->sin_addr.s_addr = htonl( ipaddr ); ( (struct sockaddr_in *)addr )->sin_port = htons( (u_short)hp ); return 0; } //end of the function WINS_StringToAddr //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int WINS_GetSocketAddr( int socket, struct sockaddr_s *addr ){ socklen_t addrlen = sizeof( struct sockaddr_s ); unsigned int a; memset( addr, 0, sizeof( struct sockaddr_s ) ); getsockname( socket, (struct sockaddr *)addr, &addrlen ); a = ( (struct sockaddr_in *)addr )->sin_addr.s_addr; if ( a == 0 || a == inet_addr( "127.0.0.1" ) ) { ( (struct sockaddr_in *)addr )->sin_addr.s_addr = myAddr; } return 0; } //end of the function WINS_GetSocketAddr //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int WINS_GetNameFromAddr( struct sockaddr_s *addr, char *name ){ struct hostent *hostentry; hostentry = gethostbyaddr( (char *)&( (struct sockaddr_in *)addr )->sin_addr, sizeof( struct in_addr ), AF_INET ); if ( hostentry ) { strncpy( name, (char *)hostentry->h_name, NET_NAMELEN - 1 ); return 0; } strcpy( name, WINS_AddrToString( addr ) ); return 0; } //end of the function WINS_GetNameFromAddr //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int WINS_GetAddrFromName( char *name, struct sockaddr_s *addr ){ struct hostent *hostentry; if ( name[0] >= '0' && name[0] <= '9' ) { return PartialIPAddress( name, addr ); } hostentry = gethostbyname( name ); if ( !hostentry ) { return -1; } addr->sa_family = AF_INET; ( (struct sockaddr_in *)addr )->sin_port = htons( (u_short)net_hostport ); ( (struct sockaddr_in *)addr )->sin_addr.s_addr = *(int *)hostentry->h_addr_list[0]; return 0; } //end of the function WINS_GetAddrFromName //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int WINS_AddrCompare( struct sockaddr_s *addr1, struct sockaddr_s *addr2 ){ if ( addr1->sa_family != addr2->sa_family ) { return -1; } if ( ( (struct sockaddr_in *)addr1 )->sin_addr.s_addr != ( (struct sockaddr_in *)addr2 )->sin_addr.s_addr ) { return -1; } if ( ( (struct sockaddr_in *)addr1 )->sin_port != ( (struct sockaddr_in *)addr2 )->sin_port ) { return 1; } return 0; } //end of the function WINS_AddrCompare //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int WINS_GetSocketPort( struct sockaddr_s *addr ){ return ntohs( ( (struct sockaddr_in *)addr )->sin_port ); } //end of the function WINS_GetSocketPort //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int WINS_SetSocketPort( struct sockaddr_s *addr, int port ){ ( (struct sockaddr_in *)addr )->sin_port = htons( (u_short)port ); return 0; } //end of the function WINS_SetSocketPort #endif