/* =========================================================================== Wolfenstein: Enemy Territory GPL Source Code Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. This file is part of the Wolfenstein: Enemy Territory GPL Source Code (“Wolf ET Source Code”). Wolf ET Source Code 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 3 of the License, or (at your option) any later version. Wolf ET Source Code 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 Wolf ET Source Code. If not, see . In addition, the Wolf: ET Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Wolf ET Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #include Q3DEF_BEGIN #include "../client/client.h" #include "mac_local.h" Q3DEF_END /* #ifndef DEDICATED #include "GameRanger SDK/GameRanger.h" #endif */ static qboolean gOTInited; static EndpointRef endpoint = kOTInvalidEndpointRef; #define MAX_IPS 16 static int num_interfaces = 0; static InetInterfaceInfo sys_inetInfo[MAX_IPS]; static byte localIP[MAX_IPS][4]; InetSvcRef inet_services; static TUDErr uderr; OTClientContextPtr clientContext = NULL; void RcvUDErr( EndpointRef inEndpoint ) { memset( &uderr, 0, sizeof( uderr ) ); uderr.addr.maxlen = 0; uderr.opt.maxlen = 0; OTRcvUDErr( inEndpoint, &uderr ); } void HandleOTError( int err, const char *func, EndpointRef inEndpoint ) { int r; static int lastErr; if ( err != lastErr ) { Com_Printf( "%s: error %i\n", func, err ); } // if we don't call OTLook, things wedge r = OTLook( inEndpoint ); if ( err != lastErr ) { Com_DPrintf( "%s: OTLook %i\n", func, r ); } switch ( r ) { case T_UDERR: RcvUDErr( inEndpoint ); if ( err != lastErr ) { Com_DPrintf( "%s: OTRcvUDErr %i\n", func, uderr.error ); } break; default: // Com_Printf( "%s: Unknown OTLook error %i\n", func, r ); break; } lastErr = err; // don't spew tons of messages } /* ================= NotifyProc ================= */ pascal void NotifyProc( void* contextPtr, OTEventCode code, OTResult result, void* cookie ) { switch ( code ) { case T_OPENCOMPLETE: endpoint = (EndpointRef) cookie; break; case T_UDERR: RcvUDErr( (EndpointRef)cookie ); break; } } /* ================= GetFourByteOption ================= */ static OTResult GetFourByteOption( EndpointRef ep, OTXTILevel level, OTXTIName name, UInt32 *value ) { OTResult err; TOption option; TOptMgmt request; TOptMgmt result; /* Set up the option buffer */ option.len = kOTFourByteOptionSize; option.level = level; option.name = name; option.status = 0; option.value[0] = 0; // Ignored because we're getting the value. /* Set up the request parameter for OTOptionManagement to point to the option buffer we just filled out */ request.opt.buf = (UInt8 *) &option; request.opt.len = sizeof( option ); request.flags = T_CURRENT; /* Set up the reply parameter for OTOptionManagement. */ result.opt.buf = (UInt8 *) &option; result.opt.maxlen = sizeof( option ); err = OTOptionManagement( ep, &request, &result ); if ( err == noErr ) { switch ( option.status ) { case T_SUCCESS: case T_READONLY: *value = option.value[0]; break; default: err = option.status; break; } } return ( err ); } /* ================= SetFourByteOption ================= */ static OTResult SetFourByteOption( EndpointRef ep, OTXTILevel level, OTXTIName name, UInt32 value ) { OTResult err; TOption option; TOptMgmt request; TOptMgmt result; /* Set up the option buffer to specify the option and value to set. */ option.len = kOTFourByteOptionSize; option.level = level; option.name = name; option.status = 0; option.value[0] = value; /* Set up request parameter for OTOptionManagement */ request.opt.buf = (UInt8 *) &option; request.opt.len = sizeof( option ); request.flags = T_NEGOTIATE; /* Set up reply parameter for OTOptionManagement. */ result.opt.buf = (UInt8 *) &option; result.opt.maxlen = sizeof( option ); err = OTOptionManagement( ep, &request, &result ); if ( err == noErr ) { if ( option.status != T_SUCCESS ) { err = option.status; } } return ( err ); } /* ================== Sys_ShowIP ================== */ void Sys_ShowIP( void ) { int i; for ( i = 0; i < num_interfaces; i++ ) { Com_Printf( "IP: %i.%i.%i.%i\n", localIP[i][0], localIP[i][1], localIP[i][2], localIP[i][3] ); } } /* ===================== NET_GetLocalAddress ===================== */ void NET_GetLocalAddress( void ) { OSStatus err; int iOT; InetInterfaceInfo inetInfo; num_interfaces = 0; iOT = 0; while ( true ) { err = OTInetGetInterfaceInfo( &inetInfo, iOT ); if ( err ) { break; } if ( inetInfo.fAddress != 0 ) { sys_inetInfo[num_interfaces] = inetInfo; UInt32 IP = ntohl( inetInfo.fAddress ); localIP[num_interfaces][0] = ( (byte*)&IP )[0]; localIP[num_interfaces][1] = ( (byte*)&IP )[1]; localIP[num_interfaces][2] = ( (byte*)&IP )[2]; localIP[num_interfaces][3] = ( (byte*)&IP )[3]; Com_Printf( "LocalAddress: %i.%i.%i.%i\n", localIP[ num_interfaces ][0], localIP[ num_interfaces ][1], localIP[ num_interfaces ][2], localIP[ num_interfaces ][3] ); UInt32 netmask = ntohl( inetInfo.fNetmask ); Com_Printf( "Netmask: %i.%i.%i.%i\n", ( (byte*)&netmask )[0], ( (byte*)&netmask )[1], ( (byte*)&netmask )[2], ( (byte*)&netmask )[3] ); num_interfaces++; } iOT++; } } /* ================== Sys_InitNetworking struct InetAddress { OTAddressType fAddressType; // always AF_INET InetPort fPort; // Port number InetHost fHost; // Host address in net byte order UInt8 fUnused[8]; // Traditional unused bytes }; typedef struct InetAddress InetAddress; ================== */ void Sys_InitNetworking( void ) { cvar_t *ip; int port; OSStatus err; OTConfigurationRef config; TBind bind, bindOut; InetAddress in, out; int i; ip = Cvar_Get( "net_ip", "localhost", CVAR_LATCH ); port = Cvar_Get( "net_port", va( "%i", PORT_SERVER ), CVAR_LATCH )->integer; Com_Printf( "----- Sys_InitNetworking -----\n" ); // init OpenTransport Com_Printf( "... InitOpenTransport()\n" ); if ( (Ptr) kUnresolvedCFragSymbolAddress == (Ptr) InitOpenTransportInContext ) { Com_Error( ERR_FATAL, "OpenTransport extensions not installed" ); return; } err = InitOpenTransportInContext( kInitOTForApplicationMask, NULL /*&clientContext*/ ); if ( err != noErr ) { Com_Printf( "InitOpenTransport() failed\n" ); Com_Printf( "------------------------------\n" ); return; } gOTInited = qtrue; // get an endpoint Com_Printf( "... OTOpenEndpoint()\n" ); config = OTCreateConfiguration( kUDPName ); endpoint = OTOpenEndpointInContext( config, 0, NULL, &err, NULL ); if ( err != noErr ) { endpoint = 0; Com_Printf( "OTOpenEndpoint() failed\n" ); Com_Printf( "------------------------------\n" ); return; } // set non-blocking err = OTSetNonBlocking( endpoint ); // scan for a valid port in our range Com_Printf( "... OTBind()\n" ); for ( i = 0 ; i < 10 ; i++ ) { in.fAddressType = AF_INET; in.fPort = port + i; in.fHost = 0; bind.addr.maxlen = sizeof( in ); bind.addr.len = sizeof( in ); bind.addr.buf = (unsigned char *)∈ bind.qlen = 0; bindOut.addr.maxlen = sizeof( out ); bindOut.addr.len = sizeof( out ); bindOut.addr.buf = (unsigned char *)&out; bindOut.qlen = 0; err = OTBind( endpoint, &bind, &bindOut ); if ( err == noErr ) { Com_Printf( "Opened UDP endpoint at port %i\n", out.fPort ); break; } } if ( err != noErr ) { Com_Printf( "Couldn't bind a local port\n" ); } // get the local address for LAN client detection NET_GetLocalAddress(); // set to allow broadcasts err = SetFourByteOption( endpoint, INET_IP, kIP_BROADCAST, T_YES ); if ( err != noErr ) { Com_Printf( "IP_BROADCAST failed\n" ); } inet_services = OTOpenInternetServicesInContext( kDefaultInternetServicesPath, 0, &err, NULL ); /* #ifndef DEDICATED if (GRIsHostCmd()) GRHostReady(); #endif */ Com_Printf( "------------------------------\n" ); } /* ================== Sys_ShutdownNetworking ================== */ void Sys_ShutdownNetworking( void ) { Com_Printf( "Sys_ShutdownNetworking();\n" ); /* #ifndef DEDICATED if (GRIsHostCmd()) GRHostClosed(); #endif */ if ( endpoint != kOTInvalidEndpointRef ) { OTUnbind( endpoint ); OTCloseProvider( endpoint ); endpoint = kOTInvalidEndpointRef; } if ( inet_services ) { OTCloseProvider( inet_services ); } if ( gOTInited ) { CloseOpenTransportInContext( NULL ); gOTInited = qfalse; } } /* ==================== NET_Shutdown ==================== */ void NET_Shutdown( void ) { } /* ============= Sys_StringToAdr Does NOT parse port numbers ============= */ qboolean Sys_StringToAdr( const char *s, netadr_t *a ) { OSStatus err; InetHostInfo info; err = OTInetStringToAddress( inet_services, (char *)s, &info ); if ( err ) { char name[256]; Com_DPrintf( "Sys_StringToAdr addr failed: %s\n", s ); return qfalse; } a->type = NA_IP; *(int *)a->ip = ntohl( info.addrs[0] ); a->port = 0; return qtrue; } /* ================== Sys_SendPacket ================== */ #define MAX_PACKETLEN 1400 void Sys_SendPacket( int length, const void *data, netadr_t to ) { TUnitData d; InetAddress inAddr; OSStatus err; if ( !endpoint ) { return; } if ( length > MAX_PACKETLEN ) { Com_Error( ERR_DROP, "Sys_SendPacket: length > MAX_PACKETLEN" ); } inAddr.fAddressType = AF_INET; // netadr_t holds port in network order // ?but it seems we have to give it in host order here? inAddr.fPort = ntohs( to.port ); if ( to.type == NA_BROADCAST ) { inAddr.fHost = -1; } else { inAddr.fHost = htonl( *(int*)&to.ip ); } memset( &d, 0, sizeof( d ) ); d.addr.len = sizeof( inAddr ); d.addr.maxlen = sizeof( inAddr ); d.addr.buf = (unsigned char *)&inAddr; d.opt.len = 0; d.opt.maxlen = 0; d.opt.buf = NULL; d.udata.len = length; d.udata.maxlen = length; d.udata.buf = (unsigned char *)data; err = OTSndUData( endpoint, &d ); if ( err ) { HandleOTError( err, "Sys_SendPacket", endpoint ); } } /* ================== Sys_GetPacket Never called by the game logic, just the system event queing ================== */ qboolean Sys_GetPacket( netadr_t *net_from, msg_t *net_message ) { TUnitData d; InetAddress inAddr; OSStatus err; OTFlags flags; if ( !endpoint ) { return qfalse; } inAddr.fAddressType = AF_INET; inAddr.fPort = 0; inAddr.fHost = 0; memset( &d, 0, sizeof( d ) ); d.addr.len = sizeof( inAddr ); d.addr.maxlen = sizeof( inAddr ); d.addr.buf = (unsigned char *)&inAddr; d.opt.len = 0; d.opt.maxlen = 0; d.opt.buf = 0; d.udata.len = net_message->maxsize; d.udata.maxlen = net_message->maxsize; d.udata.buf = net_message->data; err = OTRcvUData( endpoint, &d, &flags ); if ( err ) { if ( err == kOTNoDataErr ) { return qfalse; } HandleOTError( err, "Sys_GetPacket", endpoint ); return qfalse; } net_from->type = NA_IP; // the game wants them in network order, for some reason we're getting them in host order net_from->port = htons( inAddr.fPort ); *(int *)net_from->ip = ntohl( inAddr.fHost ); net_message->cursize = d.udata.len; return qtrue; } /* ================== Sys_IsLANAddress LAN clients will have their rate var ignored ================== */ qboolean Sys_IsLANAddress( netadr_t adr ) { int i; if ( adr.type == NA_LOOPBACK ) { return qtrue; } if ( adr.type == NA_IPX ) { return qtrue; } if ( adr.type != NA_IP ) { return qfalse; } //bani if ( num_interfaces ) { unsigned long *p_ip; unsigned long ip; p_ip = (unsigned long *)&adr.ip[0]; ip = htonl( *p_ip ); for ( i = 0; i < num_interfaces; i++ ) { if ( ( sys_inetInfo[i].fAddress & sys_inetInfo[i].fNetmask ) == ( ip & sys_inetInfo[i].fNetmask ) ) { return qtrue; } } return qfalse; } // choose which comparison to use based on the class of the address being tested // any local adresses of a different class than the address being tested will fail based on the first byte if ( adr.ip[0] == 127 && adr.ip[1] == 0 && adr.ip[2] == 0 && adr.ip[3] == 1 ) { return qtrue; } // Class A if ( ( adr.ip[0] & 0x80 ) == 0x00 ) { for ( i = 0 ; i < num_interfaces ; i++ ) { if ( adr.ip[0] == localIP[i][0] ) { return qtrue; } } // the RFC1918 class a block will pass the above test return qfalse; } // Class B if ( ( adr.ip[0] & 0xc0 ) == 0x80 ) { for ( i = 0 ; i < num_interfaces ; i++ ) { if ( adr.ip[0] == localIP[i][0] && adr.ip[1] == localIP[i][1] ) { return qtrue; } // also check against the RFC1918 class b blocks if ( adr.ip[0] == 172 && localIP[i][0] == 172 && ( adr.ip[1] & 0xf0 ) == 16 && ( localIP[i][1] & 0xf0 ) == 16 ) { return qtrue; } } return qfalse; } // Class C for ( i = 0 ; i < num_interfaces ; i++ ) { if ( adr.ip[0] == localIP[i][0] && adr.ip[1] == localIP[i][1] && adr.ip[2] == localIP[i][2] ) { return qtrue; } // also check against the RFC1918 class c blocks if ( adr.ip[0] == 192 && localIP[i][0] == 192 && adr.ip[1] == 168 && localIP[i][1] == 168 ) { return qtrue; } } return qfalse; } void NET_Init( void ) { } void NET_Sleep( int i ) { } extern "C" int WSACleanup( void ) { return 0; }