2009-08-07 12:15:42 +00:00
# include "announce.h"
# include <time.h>
# include <stdio.h>
# include <stdlib.h>
# include <assert.h>
# include <unistd.h>
# include <string.h>
# include <avahi-client/client.h>
# include <avahi-client/lookup.h>
# include <avahi-client/publish.h>
# include <avahi-common/alternative.h>
# include <avahi-common/error.h>
# include <avahi-common/malloc.h>
# include <avahi-common/thread-watch.h>
# define SERVICE_TYPE "_sdlbomber._udp"
static AvahiClient * client = NULL ;
static AvahiThreadedPoll * threaded_poll = NULL ;
static AvahiEntryGroup * group = NULL ;
static char * name = NULL ;
static uint16_t port = 0 ;
2009-08-08 19:45:49 +00:00
static uint32_t version = 0 ;
2009-08-07 12:15:42 +00:00
static volatile int all_for_now ;
gamelistentry gamelistentries [ 10 ] ;
int gamelistsize = 0 ;
static void myerror ( AvahiClient * c , const char * s ) {
fprintf ( stderr , " Error in: %s \n (%s) " , s , avahi_strerror ( avahi_client_errno ( client ) ) ) ;
}
static void create_services ( AvahiClient * c ) ;
static void entry_group_callback ( AvahiEntryGroup * g , AvahiEntryGroupState state , AVAHI_GCC_UNUSED void * userdata ) {
group = g ;
switch ( state ) {
case AVAHI_ENTRY_GROUP_ESTABLISHED :
break ;
case AVAHI_ENTRY_GROUP_COLLISION :
{
char * n = avahi_alternative_service_name ( name ) ;
avahi_free ( name ) ;
name = n ;
}
/* And recreate the services */
avahi_entry_group_reset ( group ) ;
create_services ( avahi_entry_group_get_client ( g ) ) ;
break ;
case AVAHI_ENTRY_GROUP_FAILURE :
fprintf ( stderr , " Entry group failure: %s \n " , avahi_strerror ( avahi_client_errno ( avahi_entry_group_get_client ( g ) ) ) ) ;
avahi_threaded_poll_quit ( threaded_poll ) ;
break ;
case AVAHI_ENTRY_GROUP_UNCOMMITED :
case AVAHI_ENTRY_GROUP_REGISTERING :
break ;
}
}
static void create_services ( AvahiClient * c ) {
int ret ;
if ( ! group ) {
if ( ! ( group = avahi_entry_group_new ( c , entry_group_callback , NULL ) ) ) {
myerror ( c , " avahi_entry_group_new " ) ;
goto fail ;
}
}
again :
if ( avahi_entry_group_is_empty ( group ) ) {
2009-08-08 19:45:49 +00:00
char buf_version [ 128 ] ;
2009-08-07 12:15:42 +00:00
snprintf ( buf_version , sizeof ( buf_version ) , " version=%X " , ( unsigned int ) version ) ;
2009-08-08 19:45:49 +00:00
if ( ( ret = avahi_entry_group_add_service ( group , AVAHI_IF_UNSPEC , AVAHI_PROTO_UNSPEC , 0 , name , SERVICE_TYPE , NULL , NULL , port , buf_version , NULL ) ) < 0 ) {
2009-08-07 12:15:42 +00:00
if ( ret = = AVAHI_ERR_COLLISION )
goto collision ;
fprintf ( stderr , " Failed to add " SERVICE_TYPE " : %s \n " , avahi_strerror ( ret ) ) ;
goto fail ;
}
if ( ( ret = avahi_entry_group_commit ( group ) ) < 0 ) {
fprintf ( stderr , " Failed to commit entry group: %s \n " , avahi_strerror ( ret ) ) ;
goto fail ;
}
}
return ;
collision :
{
char * n = avahi_alternative_service_name ( name ) ;
avahi_free ( name ) ;
name = n ;
}
avahi_entry_group_reset ( group ) ;
goto again ;
fail :
avahi_threaded_poll_quit ( threaded_poll ) ;
}
static void client_callback ( AvahiClient * c , AvahiClientState state , void * userdata ) {
client = c ;
switch ( state ) {
case AVAHI_CLIENT_S_RUNNING :
if ( port ! = 0 ) create_services ( c ) ;
break ;
case AVAHI_CLIENT_FAILURE :
myerror ( c , " client failure " ) ;
avahi_threaded_poll_quit ( threaded_poll ) ;
break ;
case AVAHI_CLIENT_S_COLLISION :
case AVAHI_CLIENT_S_REGISTERING :
if ( group ) {
avahi_entry_group_reset ( group ) ;
}
break ;
case AVAHI_CLIENT_CONNECTING :
break ;
}
}
2009-08-08 19:45:49 +00:00
int registergame ( const char * playername , uint16_t p , const unsigned char v [ 4 ] ) {
2009-08-07 12:15:42 +00:00
if ( name ) avahi_free ( name ) ;
name = avahi_strdup ( playername ) ;
port = p ;
memcpy ( & version , v , 4 ) ;
version = htonl ( version ) ;
avahi_threaded_poll_lock ( threaded_poll ) ;
create_services ( client ) ;
avahi_threaded_poll_unlock ( threaded_poll ) ;
return 1 ;
}
void unregistergame ( ) {
port = 0 ;
avahi_threaded_poll_lock ( threaded_poll ) ;
if ( group ) avahi_entry_group_reset ( group ) ;
group = NULL ;
avahi_threaded_poll_unlock ( threaded_poll ) ;
}
static void resolve_callback ( AvahiServiceResolver * r , AvahiIfIndex interface , AvahiProtocol protocol , AvahiResolverEvent event ,
const char * name , const char * type , const char * domain , const char * host_name , const AvahiAddress * address ,
uint16_t port , AvahiStringList * txt , AvahiLookupResultFlags flags , void * userdata ) {
int i ;
uint32_t want_version = * ( uint32_t * ) userdata ;
assert ( r ) ;
if ( protocol ! = AVAHI_PROTO_INET ) goto done ; /* ignore non IPv4 for now */
if ( gamelistsize > = GAMELIST_MAXSIZE ) goto done ;
/* Called whenever a service has been resolved successfully or timed out */
switch ( event ) {
case AVAHI_RESOLVER_FAILURE :
fprintf ( stderr , " (Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s \n " , name , type , domain , avahi_strerror ( avahi_client_errno ( avahi_service_resolver_get_client ( r ) ) ) ) ;
break ;
case AVAHI_RESOLVER_FOUND : {
gamelistentry * ge ;
2009-08-08 19:45:49 +00:00
unsigned int version ;
int have_version = 0 ;
2009-08-07 12:15:42 +00:00
AvahiStringList * psl ;
for ( psl = txt ; psl ; psl = psl - > next ) {
2009-08-08 19:45:49 +00:00
if ( 0 = = strncmp ( " version= " , ( const char * ) psl - > text , 8 ) ) {
2009-08-07 12:15:42 +00:00
sscanf ( ( const char * ) psl - > text , " version=%X " , & version ) ;
if ( version ! = want_version ) goto done ; /* version mismatch */
have_version = 1 ;
}
}
2009-08-08 19:45:49 +00:00
if ( ! have_version ) goto done ;
2009-08-07 12:15:42 +00:00
i = gamelistsize + + ;
ge = & gamelistentries [ i ] ;
memset ( ge , 0 , sizeof ( * ge ) ) ;
ge - > netname . sin_addr . s_addr = address - > data . ipv4 . address ;
ge - > netname . sin_family = AF_INET ;
ge - > netname . sin_port = port ;
ge - > name = avahi_strdup ( name ) ;
}
}
done :
avahi_service_resolver_free ( r ) ;
}
static void browse_callback ( AvahiServiceBrowser * b , AvahiIfIndex interface , AvahiProtocol protocol , AvahiBrowserEvent event ,
const char * name , const char * type , const char * domain , AvahiLookupResultFlags flags , void * userdata ) {
assert ( b ) ;
/* Called whenever a new services becomes available on the LAN or is removed from the LAN */
switch ( event ) {
case AVAHI_BROWSER_FAILURE :
fprintf ( stderr , " (Browser) %s \n " , avahi_strerror ( avahi_client_errno ( client ) ) ) ;
avahi_threaded_poll_quit ( threaded_poll ) ;
return ;
case AVAHI_BROWSER_NEW :
/* fprintf(stderr, "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain); */
/* We ignore the returned resolver object. In the callback
function we free it . If the server is terminated before
the callback function is called the server will free
the resolver for us . */
if ( ! ( avahi_service_resolver_new ( client , interface , protocol , name , type , domain , AVAHI_PROTO_UNSPEC , 0 , resolve_callback , userdata ) ) )
fprintf ( stderr , " Failed to resolve service '%s': %s \n " , name , avahi_strerror ( avahi_client_errno ( client ) ) ) ;
break ;
case AVAHI_BROWSER_REMOVE :
break ;
case AVAHI_BROWSER_ALL_FOR_NOW :
all_for_now = 1 ;
break ;
case AVAHI_BROWSER_CACHE_EXHAUSTED :
all_for_now = 1 ;
break ;
}
}
static void freefoundgames ( ) {
int i ;
for ( i = 0 ; i < gamelistsize ; i + + ) {
avahi_free ( gamelistentries [ i ] . name ) ;
}
gamelistsize = 0 ;
}
2009-08-08 19:45:49 +00:00
int searchgames ( const unsigned char version [ 4 ] ) {
2009-08-07 12:15:42 +00:00
int i ;
AvahiServiceBrowser * sb = NULL ;
uint32_t gameversion ;
memcpy ( & gameversion , version , 4 ) ;
gameversion = htonl ( gameversion ) ;
freefoundgames ( ) ;
avahi_threaded_poll_lock ( threaded_poll ) ;
all_for_now = 0 ;
if ( NULL = = ( sb = avahi_service_browser_new ( client , AVAHI_IF_UNSPEC , AVAHI_PROTO_UNSPEC , SERVICE_TYPE , NULL , 0 , browse_callback , & gameversion ) ) ) {
fprintf ( stderr , " Failed to create service browser: %s \n " , avahi_strerror ( avahi_client_errno ( client ) ) ) ;
avahi_threaded_poll_unlock ( threaded_poll ) ;
return 0 ;
}
avahi_threaded_poll_unlock ( threaded_poll ) ;
for ( i = 0 ; i < 10 ; i + + ) {
usleep ( 200000 ) ;
avahi_threaded_poll_lock ( threaded_poll ) ;
if ( all_for_now ) {
avahi_service_browser_free ( sb ) ;
avahi_threaded_poll_unlock ( threaded_poll ) ;
return 1 ;
}
avahi_threaded_poll_unlock ( threaded_poll ) ;
}
avahi_threaded_poll_lock ( threaded_poll ) ;
avahi_service_browser_free ( sb ) ;
avahi_threaded_poll_unlock ( threaded_poll ) ;
return 1 ;
}
int initannouncer ( ) {
if ( ! ( threaded_poll = avahi_threaded_poll_new ( ) ) ) {
fprintf ( stderr , " avahi_threaded_poll_new failed \n " ) ;
return 0 ;
}
if ( ! ( client = avahi_client_new ( avahi_threaded_poll_get ( threaded_poll ) , 0 , client_callback , NULL , NULL ) ) ) {
fprintf ( stderr , " avahi_client_new failed \n " ) ;
avahi_threaded_poll_free ( threaded_poll ) ;
threaded_poll = NULL ;
return 0 ;
}
if ( avahi_threaded_poll_start ( threaded_poll ) < 0 ) {
fprintf ( stderr , " avahi_threaded_poll_start failed \n " ) ;
avahi_client_free ( client ) ;
avahi_threaded_poll_free ( threaded_poll ) ;
client = NULL ;
threaded_poll = NULL ;
return 0 ;
}
return 1 ;
}
void freeannouncer ( ) {
freefoundgames ( ) ;
avahi_threaded_poll_stop ( threaded_poll ) ;
if ( client ) avahi_client_free ( client ) ;
if ( threaded_poll ) avahi_threaded_poll_free ( threaded_poll ) ;
client = NULL ;
threaded_poll = NULL ;
avahi_free ( name ) ;
name = NULL ;
}
#if 0
int openmatcher ( )
{
struct hostent * hostptr ;
if ( matcheropened ) return 1 ;
hostptr = gethostbyname ( mname ) ;
if ( ! hostptr )
{
hostptr = gethostbyaddr ( mname , strlen ( mname ) , AF_INET ) ;
if ( ! hostptr )
return 0 ;
}
memset ( & matchername , 0 , sizeof ( matchername ) ) ;
matchername . sin_family = AF_INET ;
matchername . sin_port = htons ( PORT ) ;
memcpy ( & matchername . sin_addr , hostptr - > h_addr , hostptr - > h_length ) ;
matcheropened = 1 ;
return 1 ;
}
int registergame ( )
{
long now ;
int size ;
long lastreg ;
if ( ! openmatcher ( ) ) return 0 ;
pulseoff ( ) ;
now = longtime ( ) ;
lastreg = now - 1 ;
while ( longtime ( ) - now < 10 )
{
if ( longtime ( ) - lastreg > = 1 )
{
lastreg = longtime ( ) ;
putmsg ( & matchername , regpacket , REGISTERLEN ) ;
}
size = getmsg ( 1000 ) ;
if ( size < REGISTERLEN + 1 ) continue ;
if ( mesg [ 0 ] ! = PKT_ACK ) continue ;
if ( memcmp ( regpacket , mesg + 1 , REGISTERLEN ) ) continue ;
return 1 ;
}
return 0 ;
}
int unregistergame ( )
{
long now ;
int size ;
if ( ! openmatcher ( ) ) return 0 ;
pulseoff ( ) ;
now = longtime ( ) ;
clearreg ( ) ;
while ( longtime ( ) - now < 10 )
{
putmsg ( & matchername , regpacket , REGISTERLEN ) ;
size = getmsg ( 1000 ) ;
if ( size < REGISTERLEN + 1 ) continue ;
if ( mesg [ 0 ] ! = PKT_ACK ) continue ;
if ( memcmp ( regpacket , mesg + 1 , REGISTERLEN ) ) continue ;
return 1 ;
}
return 0 ;
}
# endif