From 6e1f784b41629e88ab441f500538d45ec807e2e2 Mon Sep 17 00:00:00 2001 From: Preston Marshall Date: Mon, 11 Apr 2011 12:09:46 -0500 Subject: [PATCH] Updated to 0.03 - bugfix release: broken with IPv6 (Mac troubles with iTunes). You must install IO::Socket::INET6 for Perl to fix this - Debian/Ubuntu users, this is lib-io-socket-inet6-perl. --- INSTALL | 18 ++++++++++------- README | 16 +++++++++++++-- hairtunes.c | 40 +++++++++++++++++++++++++++++++------ shairport.pl | 56 +++++++++++++++++++++++++++++++++++++++++++--------- 4 files changed, 106 insertions(+), 24 deletions(-) diff --git a/INSTALL b/INSTALL index 9a9016a0..62ca8b3f 100644 --- a/INSTALL +++ b/INSTALL @@ -1,11 +1,15 @@ Type 'make' to build the packet decoder, 'hairtunes'. + You need the following installed: + openssl + libao + avahi (avahi-daemon running and avahi-publish-service on path) + Perl -openssl -libao -avahi (avahi-daemon running and avahi-publish-service on path) -Perl +Debian/Ubuntu users need: + libssl-dev libcrypt-openssl-rsa-perl libao2 libao-dev2 libio-socket-inet6-perl -Perl modules (install from CPAN): -HTTP::Message -Crypt::OpenSSL::RSA +Perl modules (install from CPAN if needed): + HTTP::Message + Crypt::OpenSSL::RSA + IO::Socket::INET6 diff --git a/README b/README index b481c5ad..dadddbba 100644 --- a/README +++ b/README @@ -1,7 +1,7 @@ -Shairport v0.01 +Shairport v0.03 ============== James Laird -April 5, 2011 +April 11, 2011 What it is ---------- @@ -31,4 +31,16 @@ Thanks to everyone who has worked to reverse engineer the RAOP protocol - after finding the keys, everything else was pretty much trivial. Thanks also to Apple for obfuscating the private key in the ROM image, using a scheme that made the deobfuscation code itself stand out like a flare. +Thanks to Ten Thousand Free Men and their Families for having a computer and stuff. Thanks to wtbw. + +Changelog +--------- +0.01 April 5, 2011 + initial release +0.02 April 11, 2011 + bugfix: libao compatibility +0.03 April 11, 2011 + bugfix: ipv6 didn't work - + IO::Socket::INET6 is required too + diff --git a/hairtunes.c b/hairtunes.c index aa5f08ea..5dbad18f 100644 --- a/hairtunes.c +++ b/hairtunes.c @@ -318,7 +318,12 @@ void buffer_put_packet(seq_t seqno, char *data, int len) { } static int rtp_sockets[2]; // data, control -struct sockaddr_in rtp_client; +#ifdef AF_INET6 + struct sockaddr_in6 rtp_client; +#else + struct sockaddr_in rtp_client; +#endif + void *rtp_thread_func(void *arg) { socklen_t si_len = sizeof(rtp_client); char packet[MAX_PACKET]; @@ -374,33 +379,55 @@ void rtp_request_resend(seq_t first, seq_t last) { *(unsigned short *)(req+4) = htons(first); // missed seqnum *(unsigned short *)(req+6) = htons(last-first+1); // count +#ifdef AF_INET6 + rtp_client.sin6_port = htons(controlport); +#else rtp_client.sin_port = htons(controlport); +#endif sendto(rtp_sockets[1], req, sizeof(req), 0, (struct sockaddr *)&rtp_client, sizeof(struct sockaddr_in)); } int init_rtp(void) { +#ifdef AF_INET6 + struct sockaddr_in6 si; + int type = AF_INET6; + short *sin_port = &si.sin6_port; +#else struct sockaddr_in si; + int type = AF_INET; + short *sin_port = &si.sin_port; +#endif int sock, csock; // data and control (we treat the streams the same here) - sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + sock = socket(type, SOCK_DGRAM, IPPROTO_UDP); if (sock==-1) die("Can't create socket!"); memset(&si, 0, sizeof(si)); +#ifdef AF_INET6 + si.sin6_family = AF_INET6; + #ifdef SIN6_LEN + si.sin6_len = sizeof(si); + #endif + si.sin6_addr = in6addr_any; + si.sin6_flowinfo = 0; +#else si.sin_family = AF_INET; + si.sin_len = sizeof(si); si.sin_addr.s_addr = htonl(INADDR_ANY); +#endif unsigned short port = 6000 - 3; do { port += 3; - si.sin_port = htons(port); + *sin_port = htons(port); } while (bind(sock, (struct sockaddr*)&si, sizeof(si))==-1); - csock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + csock = socket(type, SOCK_DGRAM, IPPROTO_UDP); if (csock==-1) die("Can't create socket!"); - si.sin_port = htons(port + 1); + *sin_port = htons(port + 1); if (bind(csock, (struct sockaddr*)&si, sizeof(si))==-1) die("can't bind control socket"); @@ -635,11 +662,12 @@ int init_output(void) { int driver = ao_default_driver_id(); ao_sample_format fmt; + memset(&fmt, 0, sizeof(fmt)); + fmt.bits = 16; fmt.rate = sampling_rate; fmt.channels = 2; fmt.byte_format = AO_FMT_LITTLE; - fmt.matrix = 0; ao_device *dev = ao_open_live(driver, &fmt, 0); diff --git a/shairport.pl b/shairport.pl index d2d4a4ac..48f62ebd 100755 --- a/shairport.pl +++ b/shairport.pl @@ -4,7 +4,7 @@ # AP name - as will be shown in iTunes' menu # example: # my $apname = "SteePort"; -my $apname = "HairPort $$ on " . `hostname`; +my $apname = "ShairPort $$ on " . `hostname`; # password - required to connect # for no password, set: @@ -45,7 +45,7 @@ use IPC::Open2; use Crypt::OpenSSL::RSA; use Digest::MD5 qw/md5_hex/; use POSIX ":sys_wait_h"; - +eval "use IO::Socket::INET6;"; chomp $apname; @@ -78,7 +78,7 @@ $SIG{CHLD} = \&REAP; my %conns; -$SIG{TERM} = $SIG{INT} = $SIG{__DIE__} = sub { +$SIG{TERM} = $SIG{INT} = sub { map { eval { kill $_->{decoder_pid} } } keys %conns; kill 9, $avahi_publish; exit(0); @@ -87,12 +87,43 @@ $SIG{TERM} = $SIG{INT} = $SIG{__DIE__} = sub { my $airport_pem = join '', ; my $rsa = Crypt::OpenSSL::RSA->new_private_key($airport_pem) || die; -my $listen = new IO::Socket::INET(Listen => 1, - LocalPort => 5000, - ReuseAddr => 1, - Proto => 'tcp'); +my $listen; +{ + eval { + $listen = new IO::Socket::INET6(Listen => 1, + Domain => AF_INET6, + LocalPort => 5000, + ReuseAddr => 1, + Proto => 'tcp'); + }; + if ($@) { + print "**************************************\n\n", + "* IO::Socket::INET6 not present! *\n", + "* Install this if iTunes won't play. *\n", + "**************************************\n\n"; + + $listen = new IO::Socket::INET(Listen => 1, + LocalPort => 5000, + ReuseAddr => 1, + Proto => 'tcp'); + } +} die "Can't listen on port 5000: $!" unless $listen; +sub ip6bin { + my $ip = shift; + $ip =~ /((.*)::)?(.+)/; + my @left = split /:/, $2; + my @right = split /:/, $3; + my @mid; + my $pad = 8 - ($#left + $#right + 2); + if ($pad > 0) { + @mid = (0) x $pad; + } + + pack('S>*', map { hex } (@left, @mid, @right)); +} + my $sel = new IO::Select($listen); print "listening...\n"; @@ -102,7 +133,8 @@ while (1) { foreach $fh (@waiting) { if ($fh==$listen) { my $new = $listen->accept; - print "new connection\n"; + printf "new connection from %s\n", $new->sockhost; + $sel->add($new); $new->blocking(0); $conns{$new} = {fh => $fh}; @@ -184,7 +216,13 @@ sub conn_handle_request { if (my $chall = $req->header('Apple-Challenge')) { my $data = decode_base64($chall); - $data .= join '', map { chr } split(/\./, $fh->sockhost); + my $ip = $fh->sockhost; + if ($ip =~ /((\d+\.){3}\d+)$/) { # IPv4 + $data .= join '', map { chr } split(/\./, $1); + } else { + $data .= ip6bin($ip); + } + $data .= join '', map { chr } @hw_addr; $data .= chr(0) x (0x20-length($data));