Works with iTunes 10 and Lion

After (mostly unsuccessful) Googling for how to stream iTunes over SSH, I finally tracked down the difficult bits myself and put together a four three-line script:

#!/bin/sh
dns-sd -P "Home iTunes" _daap._tcp local 3689 localhost.local. 
  127.0.0.1 "Arbitrary text record" & 
trap "kill $!" 0 1 2 15
ssh -C -N -L 3689:localhost:3689 myusername@blahblahblah.dyndns.org

Update: I’ve cut this down to three lines using trap

Update: You can do this with iPhoto too.

Update: Works with OS X 10.7 Lion.

itunes-sharing

Multicast DNS (mDNS) and DNS Service Discovery (DNS-SD) Test Tool

We need to trick iTunes into thinking our home computer is on our local network, because if you’re sitting in Starbucks, then your home computer is not on your local network (and if it is, then you need to address your caffeine addiction). For that we use a tool quietly sitting at /usr/bin/dns-sd that provides a command line interface to the DNS-SD libraries which is normally accessible to programmers via /usr/include/dns_sd.h. Like all good libraries, it helps to have a command line reference tool for scripters, and dns-sd works great for us.

iTunes uses Multicast DNS (mDNS) and DNS Service Discovery (DNS-SD) to announce its presence and to look for other iTunes instances. Apple calls this technology Bonjour (formerly Rendezvous), but it is also known as Zero Configuration Networking (www.zeroconf.org).

The dns-sd man page only gives a few of the many options available. Typing dns-sd at the command line shows you more options but without much explanation.

$ dns-sd
dns-sd -E                  (Enumerate recommended registration domains)
dns-sd -F                      (Enumerate recommended browsing domains)
dns-sd -B        <Type> <Domain>        (Browse for services instances)
dns-sd -L <Name> <Type> <Domain>           (Look up a service instance)
dns-sd -R <Name> <Type> <Domain> <Port> [<TXT>...] (Register a service)
dns-sd -P <Name> <Type> <Domain> <Port> <Host> <IP> [<TXT>...]  (Proxy)
dns-sd -Z        <Type> <Domain>   (Output results in Zone File format)
dns-sd -Q <FQDN> <rrtype> <rrclass> (Generic query for any record type)
dns-sd -C <FQDN> <rrtype> <rrclass>   (Query; reconfirming each result)
dns-sd -X udp/tcp/udptcp <IntPort> <ExtPort> <TTL>   (NAT Port Mapping)
dns-sd -G v4/v6/v4v6 <Hostname>  (Get address information for hostname)
dns-sd -V    (Get version of currently running daemon / system service)
dns-sd -A                      (Test Adding/Updating/Deleting a record)
dns-sd -U                                  (Test updating a TXT record)
dns-sd -N                             (Test adding a large NULL record)
dns-sd -T                            (Test creating a large TXT record)
dns-sd -M      (Test creating a registration with multiple TXT records)
dns-sd -I   (Test registering and then immediately updating TXT record)
dns-sd -S                 (Test multiple operations on a shared socket)

We’ll use the Proxy option -P to indicate that the service we’re advertising will be rerouted, in our case through an SSH tunnel.

dns-sd -P <Name> <Type> <Domain> <Port> <Host> <IP> [<TXT>...]  (Proxy)

Looking at the relevant line in our script we see how we match up the fields.

dns-sd -P "Home iTunes" _daap._tcp local 3689 localhost.local. 
  127.0.0.1 "Arbitrary text record" &
PID=$!

Note: Some people need “localhost” and others need “localhost.local.” for this to work. Not sure what’s different about people’s systems. Try both. Updated 1 Sept 2010

The Name parameter will show up in iTunes as the name of the iTunes library we’re sharing. It doesn’t have to be the actual name of the library, so we’ll just use Home iTunes as a convenient placeholder.

Port 3689 is the port used by iTunes for sharing music.

The “Arbitrary text record” is just that—an arbitrary chunk of text that’s required for dns-sd to properly process the command.

The next line of our command, PID=$! saves the process id of the command we just executed into a variable $PID which we’ll need in order to neatly shutdown the proxy when our SSH tunnel closes.

The SSH Tunnel

We use SSH to connect to our home machine and forward connections from our remote machine’s port 3689 (in Starbucks) to our home machine’s port 3689, where you left iTunes up and running before you left this morning (right?).

ssh -C -N -L 3689:localhost:3689 myusername@blahblahblah.dyndns.org

The -C option turns on compression, which may not do much good for streaming MP3’s but a) it doesn’t hurt, and processors are much, much faster than your bandwidth, and b) don’t forget that you’ll be receiving a description of your home iTunes library, which is probably a rather sizable XML file.

The -N option says we’re not executing an SSH command but rather just sitting here port forwarding until the connection is broken (like when you leave Starbucks).

The -L “listen” option is covered extensively in tutorials all over the Internet, but in short it says, “listen for local connections to port 3689 and forward them to the other end and try connecting to localhost on its port 3689.”

The myusername@blahblahblah.dyndns.org portion will be dependent on how you connect to your home computer over SSH. Again, that’s covered in detail all over the Internet. If your remote computer is secured, you might consider using SSH keys to allow automatic login. Then you can put this script in ~/Library/Scripts and launch it from the global scripts menu, if you have that activated.

Cleanup

When the SSH connection eventually closes, such as when you close your laptop or disconnect from the network, the ssh command will exit, and kill $PID will run which closes down the dns-sd proxy, so you won’t see it in iTunes anymore.

Packaging

I find it helpful to put this script in ~/Library/Scripts so that I can execute from my global Scripts menu in my menubar, but it’s up to you how you want to launch it. Good luck.