Files
DOOM-iOS/code/iphone/.svn/text-base/iphone_net.c.svn-base
2012-01-31 16:40:40 -06:00

493 lines
14 KiB
Plaintext

/*
* iphone_net.c
* doom
*
* Created by John Carmack on 7/8/09.
* Copyright 2009 id Software. All rights reserved.
*
*/
/*
Deal with all the DNS / bonjour service discovery and resolution
*/
#include "../doomiphone.h"
#include <dns_sd.h>
#include <netdb.h> // for gethostbyname
#include <net/if.h> // for if_nameindex()
DNSServiceRef browseRef;
DNSServiceRef clientServiceRef;
DNSServiceRef serviceRef;
boolean serviceRefValid;
typedef struct {
int interfaceIndex;
char browseName[1024];
char browseRegtype[1024];
char browseDomain[1024];
} service_t;
boolean localServer;
// we can find services on both WiFi and Bluetooth interfaces
#define MAX_SERVICE_INTEFACES 4
service_t serviceInterfaces[MAX_SERVICE_INTEFACES];
boolean gotServerAddress;
struct sockaddr resolvedServerAddress;
static const char *serviceName = "_DoomServer._udp.";
void DNSServiceRegisterReplyCallback (
DNSServiceRef sdRef,
DNSServiceFlags flags,
DNSServiceErrorType errorCode,
const char *name,
const char *regtype,
const char *domain,
void *context ) {
if ( errorCode == kDNSServiceErr_NoError ) {
localServer = true;
} else {
localServer = false;
}
}
boolean RegisterGameService() {
DNSServiceErrorType err = DNSServiceRegister(
&serviceRef,
kDNSServiceFlagsNoAutoRename, // we want a conflict error
0, // all interfaces
"iPhone Doom Classic",
serviceName,
NULL, // domain
NULL, // host
htons( DOOM_PORT ),
0, // txtLen
NULL, // txtRecord
DNSServiceRegisterReplyCallback,
NULL // context
);
if ( err != kDNSServiceErr_NoError ) {
printf( "DNSServiceRegister error\n" );
} else {
// block until we get a response, process it, and run the callback
err = DNSServiceProcessResult( serviceRef );
if ( err != kDNSServiceErr_NoError ) {
printf( "DNSServiceProcessResult error\n" );
}
}
return localServer;
}
void TerminateGameService() {
if ( localServer ) {
localServer = false;
}
DNSServiceRefDeallocate( serviceRef );
memset( serviceInterfaces, 0, sizeof( serviceInterfaces ) );
}
void DNSServiceQueryRecordReplyCallback (
DNSServiceRef DNSServiceRef,
DNSServiceFlags flags,
uint32_t interfaceIndex,
DNSServiceErrorType errorCode,
const char *fullname,
uint16_t rrtype,
uint16_t rrclass,
uint16_t rdlen,
const void *rdata,
uint32_t ttl,
void *context ) {
assert( rdlen == 4 );
const byte *ip = (const byte *)rdata;
char interfaceName[IF_NAMESIZE];
if_indextoname( interfaceIndex, interfaceName );
printf( "DNSServiceQueryRecordReplyCallback: %s, interface[%i] = %s, [%i] = %i.%i.%i.%i\n",
fullname, interfaceIndex, interfaceName, rdlen, ip[0], ip[1], ip[2], ip[3] );
ReportNetworkInterfaces();
memset( &resolvedServerAddress, 0, sizeof( resolvedServerAddress ) );
struct sockaddr_in *sin = (struct sockaddr_in *)&resolvedServerAddress;
sin->sin_len = sizeof( resolvedServerAddress );
sin->sin_family = AF_INET;
sin->sin_port = htons( DOOM_PORT );
memcpy( &sin->sin_addr, ip, 4 );
gotServerAddress = true;
}
DNSServiceFlags callbackFlags;
void DNSServiceResolveReplyCallback (
DNSServiceRef sdRef,
DNSServiceFlags flags,
uint32_t interfaceIndex,
DNSServiceErrorType errorCode,
const char *fullname,
const char *hosttarget,
uint16_t port,
uint16_t txtLen,
const unsigned char *txtRecord,
void *context ) {
char interfaceName[IF_NAMESIZE];
if_indextoname( interfaceIndex, interfaceName );
printf( "Resolve: interfaceIndex [%i]=%s : %s @ %s\n", interfaceIndex, interfaceName, fullname, hosttarget );
callbackFlags = flags;
#if 0
struct hostent * host = gethostbyname( hosttarget );
if ( host ) {
printf( "h_name: %s\n", host->h_name );
if ( host->h_aliases ) { // this can be NULL
for ( char **list = host->h_aliases ; *list ; list++ ) {
printf( "h_alias: %s\n", *list );
}
}
printf( "h_addrtype: %i\n", host->h_addrtype );
printf( "h_length: %i\n", host->h_length );
if ( !host->h_addr_list ) { // I doubt this would ever be NULL...
return;
}
for ( char **list = host->h_addr_list ; *list ; list++ ) {
printf( "addr: %i.%i.%i.%i\n", ((byte *)*list)[0], ((byte *)*list)[1], ((byte *)*list)[2], ((byte *)*list)[3] );
}
memset( &resolvedServerAddress, 0, sizeof( resolvedServerAddress ) );
resolvedServerAddress.sin_len = sizeof( resolvedServerAddress );
resolvedServerAddress.sin_family = host->h_addrtype;
resolvedServerAddress.sin_port = htons( DOOM_PORT );
assert( host->h_length == 4 );
memcpy( &resolvedServerAddress.sin_addr, *host->h_addr_list, host->h_length );
gotServerAddress = true;
}
#else
DNSServiceRef queryRef;
// look up the name for this host
DNSServiceErrorType err = DNSServiceQueryRecord (
&queryRef,
kDNSServiceFlagsForceMulticast,
interfaceIndex,
hosttarget,
kDNSServiceType_A, // we want the host address
kDNSServiceClass_IN,
DNSServiceQueryRecordReplyCallback,
NULL /* may be NULL */
);
if ( err != kDNSServiceErr_NoError ) {
printf( "DNSServiceQueryRecord error\n" );
} else {
// block until we get a response, process it, and run the callback
err = DNSServiceProcessResult( queryRef );
if ( err != kDNSServiceErr_NoError ) {
printf( "DNSServiceProcessResult error\n" );
}
DNSServiceRefDeallocate( queryRef );
}
#endif
}
boolean NetworkServerAvailable() {
for ( int i = 0 ; i < MAX_SERVICE_INTEFACES ; i++ ) {
if ( serviceInterfaces[i].interfaceIndex != 0 ) {
return true;
}
}
return false;
}
// returns "WiFi", "BlueTooth", or "" for display on the
// main menu multiplayer icon
const char *NetworkServerTransport() {
int count = 0;
for ( int i = 0 ; i < MAX_SERVICE_INTEFACES ; i++ ) {
if ( serviceInterfaces[i].interfaceIndex != 0 ) {
count++;
}
}
static char str[1024];
str[0] = 0;
for ( int i = 0 ; i < MAX_SERVICE_INTEFACES ; i++ ) {
int index = serviceInterfaces[i].interfaceIndex;
if ( index == 0 ) {
continue;
}
if ( str[0] ) {
strcat( str, "+" );
}
if ( index == -1 ) {
strcat( str, "BT-NEW" );
} else if ( index == 1 ) {
strcat( str, "LOOP" ); // we should never see this!
} else if ( index == 2 ) {
strcat( str, "WiFi" );
} else {
strcat( str, "BT-EST" );
}
}
return str;
}
boolean ResolveNetworkServer( struct sockaddr *addr ) {
if ( !NetworkServerAvailable() ) {
return false;
}
gotServerAddress = false;
DNSServiceRef resolveRef;
// An unconnected bluetooth service will report an interfaceIndex of -1, so if
// we have a wifi link with an interfaceIndex > 0, use that
// explicitly.
service_t *service = NULL;
for ( int i = 0 ; i < MAX_SERVICE_INTEFACES ; i++ ) {
if ( serviceInterfaces[i].interfaceIndex > 0 ) {
service = &serviceInterfaces[i];
char interfaceName[IF_NAMESIZE];
if_indextoname( service->interfaceIndex, interfaceName );
printf( "explicitly using resolving server on interface %i = %s\n", service->interfaceIndex, interfaceName );
break;
}
}
if ( !service ) {
// settle for the unconnected bluetooth service
for ( int i = 0 ; i < MAX_SERVICE_INTEFACES ; i++ ) {
if ( serviceInterfaces[i].interfaceIndex != 0 ) {
service = &serviceInterfaces[i];
break;
}
}
if ( !service ) {
printf( "No serviceInterface current.\n" );
return false;
}
}
// look up the name for this service
DNSServiceErrorType err = DNSServiceResolve (
&resolveRef,
kDNSServiceFlagsForceMulticast, // always on local link
service->interfaceIndex > 0 ? service->interfaceIndex : 0, // don't use -1 for bluetooth
service->browseName,
service->browseRegtype,
service->browseDomain,
DNSServiceResolveReplyCallback,
NULL /* context */
);
if ( err != kDNSServiceErr_NoError ) {
printf( "DNSServiceResolve error\n" );
} else {
// We can get two callbacks when both wifi and bluetooth are enabled
callbackFlags = 0;
do {
err = DNSServiceProcessResult( resolveRef );
if ( err != kDNSServiceErr_NoError ) {
printf( "DNSServiceProcessResult error\n" );
}
} while ( callbackFlags & kDNSServiceFlagsMoreComing );
DNSServiceRefDeallocate( resolveRef );
}
if ( gotServerAddress ) {
*addr = resolvedServerAddress;
return true;
}
return false;
}
void DNSServiceBrowseReplyCallback(
DNSServiceRef sdRef,
DNSServiceFlags flags,
uint32_t interfaceIndex,
DNSServiceErrorType errorCode,
const char *serviceName,
const char *regtype,
const char *replyDomain,
void *context ) {
printf( "DNSServiceBrowseReplyCallback %s: interface:%i name:%s regtype:%s domain:%s\n",
(flags & kDNSServiceFlagsAdd) ? "ADD" : "REMOVE",
interfaceIndex, serviceName, regtype, replyDomain );
if ( flags & kDNSServiceFlagsAdd ) {
// add it to the list
if ( interfaceIndex == 1 ) {
printf( "Not adding service on loopback interface.\n" );
} else {
for ( int i = 0 ; i < MAX_SERVICE_INTEFACES ; i++ ) {
service_t *service = &serviceInterfaces[i];
if ( service->interfaceIndex == 0 ) {
strncpy( service->browseName, serviceName, sizeof( service->browseName ) -1 );
strncpy( service->browseRegtype, regtype, sizeof( service->browseRegtype ) -1 );
strncpy( service->browseDomain, replyDomain, sizeof( service->browseDomain ) -1 );
service->interfaceIndex = interfaceIndex;
break;
}
}
}
} else {
// remove it from the list
for ( int i = 0 ; i < MAX_SERVICE_INTEFACES ; i++ ) {
if ( serviceInterfaces[i].interfaceIndex == interfaceIndex ) {
serviceInterfaces[i].interfaceIndex = 0;
}
}
}
}
void ProcessDNSMessages() {
static boolean initialized;
if ( !initialized ) {
initialized = true;
DNSServiceErrorType err = DNSServiceBrowse (
&browseRef,
0, /* flags */
0, /* interface */
serviceName,
NULL, /* domain */
DNSServiceBrowseReplyCallback,
NULL /* context */
);
if ( err != kDNSServiceErr_NoError ) {
printf( "DNSServiceBrowse error\n" );
return;
}
}
// poll the socket for updates
int socket = DNSServiceRefSockFD( browseRef );
if ( socket <= 0 ) {
return;
}
fd_set set;
FD_ZERO( &set );
FD_SET( socket, &set );
struct timeval tv;
memset( &tv, 0, sizeof( tv ) );
if ( select( socket+1, &set, NULL, NULL, &tv ) > 0 ) {
DNSServiceProcessResult( browseRef );
}
}
void ReportNetworkInterfaces() {
struct ifaddrs *ifap;
printf( "getifaddrs():\n" );
if ( getifaddrs( &ifap ) == -1 ) {
perror( "getifaddrs(): " );
} else {
for ( struct ifaddrs *ifa = ifap ; ifa ; ifa = ifa->ifa_next ) {
struct sockaddr_in *ina = (struct sockaddr_in *)ifa->ifa_addr;
if ( ina->sin_family == AF_INET ) {
byte *ip = (byte *)&ina->sin_addr;
// struct ifa_data *data = (struct ifa_data *)ifa->ifa_data;
printf( "ifa_name: %s ifa_flags: %i sa_family: %i=AF_INET ip: %i.%i.%i.%i\n", ifa->ifa_name, ifa->ifa_flags,
ina->sin_family, ip[0], ip[1], ip[2], ip[3] );
} else if ( ina->sin_family == AF_LINK ) {
struct if_data *data = (struct if_data *)ifa->ifa_data;
printf( "ifa_name: %s ifa_flags: %i sa_family: %i=AF_LINK ifi_ipackets: %i\n", ifa->ifa_name, ifa->ifa_flags,
ina->sin_family, data->ifi_ipackets );
} else {
printf( "ifa_name: %s ifa_flags: %i sa_family: %i=???\n", ifa->ifa_name, ifa->ifa_flags,
ina->sin_family );
}
}
freeifaddrs( ifap );
}
printf( "if_nameindex():\n" );
struct if_nameindex *ifnames = if_nameindex();
if ( !ifnames ) {
perror( "if_ameindex():" );
} else {
for ( int i = 0 ; ifnames[i].if_index != 0 ; i++ ) {
printf( "%i : %s\n", ifnames[i].if_index, ifnames[i].if_name );
}
if_freenameindex( ifnames );
}
}
int InterfaceIndexForAddress( struct sockaddr_in *adr ) {
return 0;
}
struct sockaddr_in AddressForInterfaceIndex( int interfaceIndex ) {
struct sockaddr_in addr;
addr.sin_len = sizeof( addr );
addr.sin_family = AF_INET;
if ( interfaceIndex == 0 ) {
addr.sin_addr.s_addr = INADDR_ANY;
} else {
}
return addr;
}
int UDPSocket( int interfaceIndex, int portnum ) {
int udpSocket = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
if ( udpSocket == -1 ) {
Com_Printf( "UDP socket failed: %s\n", strerror( errno ) );
return -1;
}
struct sockaddr_in addr = AddressForInterfaceIndex( interfaceIndex );
addr.sin_port = htons( portnum );
if ( bind( udpSocket, (struct sockaddr *)&addr, sizeof( addr ) ) == -1 ) {
Com_Printf( "UDP bind failed: %s\n", strerror( errno ) );
close( udpSocket );
return -1;
}
#if 0
// enable broadcast
int on = 1;
if ( setsockopt( udpSocket, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on) ) == -1 ) {
Com_Printf( "UDP setsockopt failed: %s\n", strerror( errno ) );
close( udpSocket );
return -1;
}
#endif
#if 0
// set the type-of-service, in hopes that the link level drivers use it
// to stop buffering huge amounts of data when there are line errors
int tos = 0x10; /* IPTOS_LOWDELAY; */ /* see <netinet/in.h> */
if ( setsockopt( udpSocket, IPPROTO_IP, IP_TOS, &tos, sizeof(tos) ) == -1 ) {
Com_Printf( "setsockopt IP_TOS failed: %s\n", strerror( errno ) );
}
#endif
// enable non-blocking IO
if ( fcntl( udpSocket, F_SETFL, O_NONBLOCK ) == -1 ) {
Com_Printf( "UDP fcntl failed: %s\n", strerror( errno ) );
close( udpSocket );
return -1;
}
return udpSocket;
}