? diff ? diff2 Index: acconfig.h =================================================================== RCS file: /cvs/gnome/esound/acconfig.h,v retrieving revision 1.18 diff -c -u -r1.18 acconfig.h --- acconfig.h 23 Apr 2002 10:06:38 -0000 1.18 +++ acconfig.h 11 May 2003 04:22:53 -0000 @@ -16,6 +16,7 @@ #undef WITH_SYMBOL_UNDERSCORE #undef ESDBG +#undef HAVE_IPV6 #undef INADDR_LOOPBACK #undef HAVE_SUN_LEN Index: configure.in =================================================================== RCS file: /cvs/gnome/esound/configure.in,v retrieving revision 1.84 diff -c -u -r1.84 configure.in --- configure.in 16 Aug 2002 14:26:03 -0000 1.84 +++ configure.in 11 May 2003 04:22:53 -0000 @@ -62,6 +62,18 @@ *) AC_DEFINE(HAVE_INET_ATON) esac +dnl Check for IPv6 structures. +AC_ARG_ENABLE(check_ipv6,[ --enable-check-ipv6 enable ipv6 checks [default=yes]], , enable_check_ipv6=yes) + +if test "x$enable_check_ipv6" = "xyes"; then + AC_TRY_COMPILE([ + #include], + [struct in6_addr address; + struct sockaddr_in6 socket_address; + int address_family = AF_INET6;], + [AC_DEFINE(HAVE_IPV6)],,) +fi + dnl Check if INADDR_LOOPBACK exists AC_TRY_COMPILE([ Index: esd.c =================================================================== RCS file: /cvs/gnome/esound/esd.c,v retrieving revision 1.67 diff -c -u -r1.67 esd.c --- esd.c 24 Feb 2003 10:02:57 -0000 1.67 +++ esd.c 11 May 2003 04:22:54 -0000 @@ -55,6 +55,7 @@ int esd_on_autostandby = 0; /* set when auto paused for auto reawaken */ int esd_use_tcpip = 0; /* use tcp/ip sockets instead of unix domain */ +int esd_use_tcpip6 = 0; /* use tcp/IPv6 sockets instead of unix domain */ int esd_terminate = 0; /* terminate after the last client exits */ int esd_public = 0; /* allow connects from hosts other than localhost */ int esd_spawnpid = 0; /* The PID of the process that spawned us (for use by esdlib only) */ @@ -165,7 +166,7 @@ close( client->fd ); client = client->next; } - if (!esd_use_tcpip) + if (!esd_use_tcpip && !esd_use_tcpip6) { unlink(ESD_UNIX_SOCKET_NAME); rmdir(ESD_UNIX_SOCKET_DIR); @@ -320,6 +321,9 @@ /*********************/ /* socket test setup */ struct sockaddr_in socket_addr; +#ifdef HAVE_IPV6 + struct sockaddr_in6 socket_addr6; +#endif struct sockaddr_un socket_unix; int socket_listen = -1; struct linger lin; @@ -330,6 +334,10 @@ /* create the socket, and set for non-blocking */ if (esd_use_tcpip) socket_listen=socket(AF_INET, SOCK_STREAM, 0); +#ifdef HAVE_IPV6 + else if (esd_use_tcpip6) + socket_listen=socket(AF_INET6, SOCK_STREAM, 0); +#endif else { if (safe_mksocketdir()) @@ -413,6 +421,37 @@ exit(1); } } +#ifdef HAVE_IPV6 + else if (esd_use_tcpip6) + { + /* set the listening information */ + memset(&socket_addr6, 0, sizeof(struct sockaddr_in6)); + socket_addr6.sin6_family = AF_INET6; + socket_addr6.sin6_port = htons( port ); + + /* if hostname is set, bind to its first address */ + if (hostname) + { + if (!(resolved=gethostbyname2(hostname, AF_INET6))) + { + herror(programname); + return -1; + } + memcpy(&(socket_addr6.sin6_addr), resolved->h_addr_list[0], resolved->h_length); + } else if (esd_public) + socket_addr6.sin6_addr = in6addr_any; + else + socket_addr6.sin6_addr = in6addr_loopback; + + if ( bind( socket_listen, + (struct sockaddr *) &socket_addr6, + sizeof(struct sockaddr_in6) ) < 0 ) + { + fprintf(stderr,"Unable to bind port %d\n", port ); + exit(1); + } + } +#endif else { mode_t old_umask; @@ -424,7 +463,7 @@ (struct sockaddr *) &socket_unix, SUN_LEN(&socket_unix) ) < 0 ) { fprintf(stderr,"Unable to connect to UNIX socket %s\n", ESD_UNIX_SOCKET_NAME); - if (!esd_use_tcpip) + if (!esd_use_tcpip && !esd_use_tcpip6) { unlink(ESD_UNIX_SOCKET_NAME); rmdir(ESD_UNIX_SOCKET_DIR); @@ -436,7 +475,7 @@ if (listen(socket_listen,16)<0) { fprintf(stderr,"Unable to set socket listen buffer length\n"); - if (!esd_use_tcpip) + if (!esd_use_tcpip && !esd_use_tcpip6) { unlink(ESD_UNIX_SOCKET_NAME); rmdir(ESD_UNIX_SOCKET_DIR); @@ -593,8 +632,13 @@ /* fprintf( stderr, "- disabling startup beeps\n" );*/ } else if ( !strcmp( argv[ arg ], "-unix" ) ) { esd_use_tcpip = 0; + esd_use_tcpip6 = 0; } else if ( !strcmp( argv[ arg ], "-tcp" ) ) { esd_use_tcpip = 1; +#ifdef HAVE_IPV6 + } else if ( !strcmp( argv[ arg ], "-tcp6" ) ) { + esd_use_tcpip6 = 1; +#endif } else if ( !strcmp( argv[ arg ], "-public" ) ) { esd_public = 1; } else if ( !strcmp( argv[ arg ], "-promiscuous" ) ) { @@ -623,6 +667,9 @@ fprintf( stderr, " -as SECS free audio device after SECS of inactivity\n" ); fprintf( stderr, " -unix use unix domain sockets instead of tcp/ip\n" ); fprintf( stderr, " -tcp use tcp/ip sockets instead of unix domain\n" ); +#ifdef HAVE_IPV6 + fprintf( stderr, " -tcp6 use tcp/IPv6 sockets instead of unix domain\n" ); +#endif fprintf( stderr, " -public make tcp/ip access public (other than localhost)\n" ); fprintf( stderr, " -promiscuous start unlocked and owned (disable authenticaton) NOT RECOMMENDED\n" ); fprintf( stderr, " -terminate terminate esd daemone after last client exits\n" ); @@ -647,7 +694,7 @@ listen_socket = open_listen_socket(hostname, esd_port ); if ( listen_socket < 0 ) { fprintf( stderr, "fatal error opening socket\n" ); - if (!esd_use_tcpip) + if (!esd_use_tcpip && !esd_use_tcpip6) { unlink(ESD_UNIX_SOCKET_NAME); rmdir(ESD_UNIX_SOCKET_DIR); @@ -681,7 +728,7 @@ write (esd_spawnfd, &c, 1); } - if (!esd_use_tcpip) { + if (!esd_use_tcpip && !esd_use_tcpip6) { unlink(ESD_UNIX_SOCKET_NAME); rmdir(ESD_UNIX_SOCKET_DIR); } @@ -752,7 +799,7 @@ ESD_AUDIO_STUFF; if ( esd_audio_open() < 0 ) { fprintf(stderr, "Sound device inadequate for Esound. Fatal.\n"); - if (!esd_use_tcpip) + if (!esd_use_tcpip && !esd_use_tcpip6) { unlink(ESD_UNIX_SOCKET_NAME); rmdir(ESD_UNIX_SOCKET_DIR); Index: esdlib.c =================================================================== RCS file: /cvs/gnome/esound/esdlib.c,v retrieving revision 1.81 diff -c -u -r1.81 esdlib.c --- esdlib.c 5 Sep 2002 17:28:38 -0000 1.81 +++ esdlib.c 11 May 2003 04:22:54 -0000 @@ -23,6 +23,11 @@ #include +/* Older resolvers don't have gethostbyname2() */ +#ifndef HAVE_GETHOSTBYNAME2 +#define gethostbyname2(host, family) gethostbyname((host)) +#endif /* HAVE_GETHOSTBYNAME2 */ + /*******************************************************************/ /* prototypes */ int esd_set_socket_buffers( int sock, int src_format, @@ -41,33 +46,6 @@ extern int esd_spawn_wait_ms; void esd_config_read(void); -/*******************************************************************/ -/* alternate implementations */ -#ifndef HAVE_INET_ATON -int inet_aton(const char *cp, struct in_addr *inp) -{ - union { - unsigned int n; - char parts[4]; - } u; - int a=0,b=0,c=0,d=0, i; - - i = sscanf(cp, "%d.%d.%d.%d%*s", &a, &b, &c, &d); - - if(i != 4) - return 0; - - u.parts[0] = a; - u.parts[1] = b; - u.parts[2] = c; - u.parts[3] = d; - - inp->s_addr = u.n; - - return 1; -} -#endif - /** * esd_set_socket_buffers: set buffer lengths on a socket to optimal. * @sock: ESD socket @@ -402,103 +380,275 @@ } /** - * esd_connect_tcpip: make a TCPIP connection to ESD - * @host: Specifies hostname and port to connect to as "hostname:port" - * Both parts are optional, the default being to connect to localhost on - * ESD_DEFAULT_PORT. This default is used if host is NULL. + * esd_connect_tcpip4: Create an IPv4 connection. + * @address: The address of the connection. + * @port: The port of the connection. * - * Attempts to make a connection to ESD using TCPIP. - * Similar to esd_connect_unix(). + * This function attempts to connect to the given address and port. * * Return Value: -1 on error, else a socket number connected to ESD. */ static int -esd_connect_tcpip(const char *host) +esd_connect_tcpip4(struct in_addr address, int port) { - const char *espeaker = NULL; - struct hostent *he; struct sockaddr_in socket_addr; int socket_out = -1; int curstate = 1; - char default_host[] = "127.0.0.1"; - char connect_host[64]; - int port = ESD_DEFAULT_PORT; - unsigned int host_div = 0; - memset (&socket_addr, 0, sizeof (socket_addr)); - memset (&he, 0, sizeof (he)); - /* see if we have a remote speaker to play to */ - espeaker = host; - if ( espeaker && *espeaker ) { - strncpy(connect_host, espeaker, sizeof(connect_host)); - - /* split the espeaker host into host:port */ - host_div = strcspn( connect_host, ":" ); - if(host_div > 0 && host_div < strlen(espeaker)) { - connect_host[ host_div ] = '\0'; - } - else if ( host_div == 0) - strcpy( connect_host, default_host ); - connect_host[sizeof(connect_host) - 1] = '\0'; - - /* Resolving the host name */ - if ( ( he = gethostbyname( connect_host ) ) == NULL ) { - fprintf( stderr, "Can\'t resolve host name \"%s\"!\n", - connect_host); - return(-1); - } - memcpy( (struct in_addr *) &socket_addr.sin_addr, he->h_addr, - sizeof( struct in_addr ) ); - - /* get port */ - if ( host_div < strlen( espeaker ) ) - port = atoi( espeaker + host_div + 1 ); - if ( !port ) - port = ESD_DEFAULT_PORT; - /* printf( "(remote) host is %s : %d\n", connect_host, port ); */ - } else if( !inet_aton( default_host, &socket_addr.sin_addr ) ) { - fprintf( stderr, "couldn't convert %s to inet address\n", - default_host ); - return -1; - } + memcpy( &socket_addr.sin_addr, &address, sizeof( struct in_addr ) ); /* create the socket, and set for non-blocking */ socket_out = socket( AF_INET, SOCK_STREAM, 0 ); if ( socket_out < 0 ) { fprintf(stderr,"Unable to create TCP socket\n"); - goto error_out; + goto error_out4; } /* this was borrowed blindly from the Tcl socket stuff */ if ( fcntl( socket_out, F_SETFD, FD_CLOEXEC ) < 0 ) { fprintf(stderr,"Unable to set socket to non-blocking\n"); - goto error_out; + goto error_out4; } if ( setsockopt( socket_out, SOL_SOCKET, SO_REUSEADDR, &curstate, sizeof(curstate) ) < 0 ) { fprintf(stderr,"Unable to set for a fresh socket\n"); - goto error_out; + goto error_out4; } /* set the connect information */ socket_addr.sin_family = AF_INET; socket_addr.sin_port = htons( port ); - if ( connect( socket_out, - (struct sockaddr *) &socket_addr, - sizeof(struct sockaddr_in) ) < 0 ) - goto error_out; + if ( connect( socket_out, (struct sockaddr *) &socket_addr, sizeof(struct sockaddr_in) ) < 0 ) + { + printf( "Couldn't connect\n"); + goto error_out4; + } + + return socket_out; + + error_out4: + if( socket_out >= 0 ) + close( socket_out ); + return -1; +} + +/** + * esd_connect_tcpip6: Create an IPv6 connection. + * @address: The address of the connection. + * @port: The port of the connection. + * + * This function attempts to connect to the given address and port. + * + * Return Value: -1 on error, else a socket number connected to ESD. + */ +#ifdef HAVE_IPV6 +static int +esd_connect_tcpip6( struct in6_addr address, int port) +{ + struct sockaddr_in6 socket_addr; + int socket_out = -1; + int curstate = 1; + + memcpy( &socket_addr.sin6_addr, &address, sizeof( struct in6_addr ) ); + + /* create the socket, and set for non-blocking */ + socket_out = socket( AF_INET6, SOCK_STREAM, 0 ); + if ( socket_out < 0 ) + { + fprintf(stderr,"Unable to create TCP socket\n"); + goto error_out6; + } + + /* this was borrowed blindly from the Tcl socket stuff */ + if ( fcntl( socket_out, F_SETFD, FD_CLOEXEC ) < 0 ) + { + fprintf(stderr,"Unable to set socket to non-blocking\n"); + goto error_out6; + } + + if ( setsockopt( socket_out, SOL_SOCKET, SO_REUSEADDR, + &curstate, sizeof(curstate) ) < 0 ) + { + fprintf(stderr,"Unable to set for a fresh socket\n"); + goto error_out6; + } + + /* set the connect information */ + socket_addr.sin6_family = AF_INET6; + socket_addr.sin6_port = htons( port ); + + if ( connect( socket_out, (struct sockaddr *) &socket_addr, sizeof(struct sockaddr_in6) ) < 0 ) + { + printf( "Couldn't connect\n"); + goto error_out6; + } return socket_out; - error_out: + error_out6: if( socket_out >= 0 ) close( socket_out ); return -1; +} +#endif + +/** + * esd_connect_tcpip: make a TCPIP connection to ESD + * @host: Specifies hostname and port to connect to as "hostname:port" + * Both parts are optional, the default being to connect to localhost on + * ESD_DEFAULT_PORT. This default is used if host is NULL. + * + * Attempts to make a connection to ESD using TCPIP. + * Similar to esd_connect_unix(). + * + * Return Value: -1 on error, else a socket number connected to ESD. + */ +static int +esd_connect_tcpip(const char *host) +{ + struct in_addr address4; + char default_host4[] = "127.0.0.1"; + int ipv4_hostname= 0; + +#ifdef HAVE_IPV6 + struct in6_addr address6; + char default_host6[] = "::1"; + int ipv6_hostname = 0; +#endif + + int port = ESD_DEFAULT_PORT; + int unkn_hostname = 0; + int use_default = 0; + struct hostent *he; + int curstate = 1; + char connect_host[64]; + int connect_host_length; + unsigned int host_div = 0; + int result = -1; + + /* see if we have a remote speaker to play to */ + + if ( host && *host ) + { + strncpy(connect_host, host, sizeof(connect_host)); + + /* If there is more than one ':' character, then it's an IPv6 + * address. + */ + if (strchr(connect_host, ':') != strrchr (connect_host, ':') ) + { +#ifdef HAVE_IPV6 + if (strchr(connect_host, '[') != NULL) + { + connect_host_length = strlen (connect_host); + host_div = strcspn( connect_host + 1, "]"); + + if (host_div + 1 == connect_host_length) + { + /* No closing bracket */ + fprintf( stderr, "Bad IP address\n" ); + return -1; + } + + strncpy( connect_host, host + 1, host_div ); + connect_host[host_div] = '\0'; + + if( connect_host[host_div + 3] != ':' ) + { + printf ("Bad IP address format. Using default port.\n"); + port = ESD_DEFAULT_PORT; + } + else + port = atoi ( connect_host + host_div + 3 ); + } + else + port = ESD_DEFAULT_PORT; + + ipv6_hostname = 1; +#else + fprintf( stderr, "Sorry, no IPv6 support.\n" ); + return -1; +#endif + } + else + { + host_div = strcspn( connect_host, ":" ); + if( host_div > 0 && host_div < strlen( connect_host )) { + connect_host[ host_div ] = '\0'; + } + else if ( host_div == 0) + strcpy( connect_host, default_host4 ); + + connect_host[sizeof(connect_host) - 1] = '\0'; + + /* get port */ + if ( host_div < strlen( host ) ) + port = atoi( connect_host + host_div + 1 ); + if ( !port ) + port = ESD_DEFAULT_PORT; + + unkn_hostname = 1; + } + + printf( "(remote) host is %s : %d\n", connect_host, port ); + } + else + { + /* espeaker isn't defined, so we'll try the IPv4 defaults first. */ + + use_default = 1; + ipv4_hostname; + strcpy( connect_host, default_host4 ); + port = ESD_DEFAULT_PORT; + } + + /* Okay, we have the correct hostname or IP address in the + * connect_host variable and the correct port. Let's + * try connecting to it! + */ + + if ( ipv4_hostname || unkn_hostname) + { + if ( ( he = gethostbyname2( connect_host, AF_INET ) ) == NULL ) + { + fprintf( stderr, "Can\'t resolve host name \"%s\"!\n", connect_host); + result = -1; + } + else + { + memcpy ( &address4, he->h_addr, sizeof( struct in_addr )); + result = esd_connect_tcpip4 (address4, port); + } + } +#ifdef HAVE_IPV6 + if ( use_default && result == -1 ) + { + /* The IPv4 defaults didn't work, so we'll try IPv6 next. */ + + strcpy( connect_host, default_host6 ); + } + if ( ipv6_hostname || ( result == -1 && unkn_hostname ) ) + { + printf( "Trying IPv6.\n" ); + if ( ( he = gethostbyname2( connect_host, AF_INET6 ) ) == NULL ) + { + fprintf( stderr, "Can\'t resolve host name \"%s\"!\n", connect_host); + result = -1; + } + else + { + memcpy ( &address6, he->h_addr, sizeof( struct in6_addr )); + result = esd_connect_tcpip6 (address6, port); + } + } +#endif + + return result; } /**