O'Reilly Hacks
oreilly.comO'Reilly NetworkSafari BookshelfConferences Sign In/My Account | View Cart   
Book List Learning Lab PDFs O'Reilly Gear Newsletters Press Room Jobs  


 
Buy the book!
Mac OS X Panther Hacks
By Rael Dornfest, James Duncan Davidson
June 2004
More Info

HACK
#55
Share Your Mac's Net Connection with a Bluetooth Phone
Instead of wasting time and kilobytes on GPRS, use the Bluetooth functionality of your mobile phone to access your Mac's Internet connection
The Code
[Discuss (0) | Link to this hack]

Soon after going Bluetooth with my Nokia 3650 in the summer of 2003, I discovered that one of my favorite programs from my Palm OS days, Avantgo, would not sync between the 3650 and the Mac. Rather, Avantgo is designed to sync via the phone's GPRS connection. I ran the sync once over that connection and quickly blew through the 1MB per month of connectivity that my cellular account provided me. According to the logs, 3.46MB of data had been downloaded to my phone. Because of that one sync, my bill that month was three times what it was supposed to be. I needed to find a better and less expensive way to get the information I wanted on my phone.

At the time, I knew it was possible to set up the 3650 as a wireless modem for my Mac and had even tested that connection. I thought that if an Internet connection works in this direction, it should also be possible to reverse it. I started poring over pages and pages of Google searches, Mac OS X Hints (http://www.macosxhints.com), and every discussion forum I could find to discover a way to do this.

Early in this process, I discovered TechnoHappyMeal's Bluetooth Internet Sharing AppleScripts (http://www.technohappymeal.com/archives/000070.html), but I quickly found that they did not work for my phone. Upon discovering this, I tried three or four more times, howling curses into the night and flinging my 3650 (gently) across the sofa into the pillows. After calming down a bit, I opened up the TechnoHappyMeal scripts in Apple's Script Editor to take a look at the code:

do shell script "sudo /usr/sbin/pppd /dev/tty.Bluetooth-PDA-Sync 115200 \
  noauth local passive proxyarp asyncmap 0 silent persist :10.0.1.201 &" \
  with administrator privileges
do shell script "sudo /usr/sbin/sysctl -w net.inet.ip.forwarding=1"
do shell script "sudo /usr/sbin/natd -same_ports -use_sockets -log \
  -deny_incoming -interface en1"
do shell script "sudo /sbin/ipfw add divert natd ip from any to any via en1"

I then read Peter's TechnoHappyMeal article on how to set up this connection (http://www.technohappymeal.com/archives/000069.html). I tried all the steps in the article without the scripts. Twice. Then I tried the scripts again. Nothing worked.

But according to the comments at TechnoHappyMeal, it was working for some people, so I knew it was possible. So I kept looking. I found two useful things. I first discovered a Mac OS X Hint for sharing a Bluetooth connection out to a Palm (http://www.macosxhints.com/article.php?story=20021103062212288&query=bluetooth). I posted about all this on my site, and Terry Chay emailed me to point me to another bit buried in the comments of another post at Mac OS X Hints (http://www.macosxhints.com/article.php?story=20030616191119129) about creating a consistent Bluetooth link between a phone and an Address Book. Jim Wright commented that he was able to ping between his phone and his Mac, but he couldn't get the DNS to resolve.

When I emailed Jim about this, he said that it had since started working for him, albeit sporadically, but for some reason Avantgo syncing continued to fail. Jim pointed me to an Apple Discussion thread (which has since disappeared) and a post by a JRLove, whom I have never managed to contact. On the Apple Discussion thread, JRLove posted a group of commands to run from the Terminal, and he later posted these commands on the original Mac OS X Hint where Jim had posted.

I entered these commands in order, substituting my phone's MAC address in the first command line and removing -p800, which was necessary only for P800/P900 users. It connected. The Mac and the 3650 both indicated a connection, just like when I ran iSync. I followed instructions on the Apple Discussion thread to fool my 3650 into thinking that the paired connection between the 3650 and my PowerBook was a GPRS access point. Avantgo still wouldn't work. Browsing worked if I typed in the IP address of the site I wanted, but, as Jim had experienced, the DNS was not resolving.

Terry, Jim, and I emailed back and forth for about a week about this issue. Ultimately, Terry noted that sudo named was the command that starts running the DNS on the Mac and he had discovered two ways of forcing it to work: by reloading a web page with a reference to a static IP or by running sudo named again to start up another nameserver daemon.

Remembering the TechnoHappyMeal scripts, I added another line of sudo named to the code and slapped it into some AppleScripts between do shell script and with administrator privileges. I came up with a basic and stupid name that I thought would never conflict with any existing products with actual lawyers behind them, posted the scripts to my web site, and the first raw and rather unpredictable version of Share2Blue2th was born.

When I wrote these scripts, I did not fully understand what all of the command-line code did. I just wanted my Mac and my phone to do something that I knew was possible, and I dug around on the Internet until I found the how-to of it all. Once I found that and got it working, I decided I wanted to automate it, so I packaged the command-line code in an AppleScript wrapper.

In these early phases, because of my ignorance about all the command-line code, I was superstitious about the scripts. Jim discovered that adding pauses between the commands ensured that everything ran properly. I put the additional code in the scripts, and they suddenly started to run much more consistently.

Continued communication with Jim Wright and Terry Chay, help from the readers of my web site, and conversations with people like Jon in the MacScripter BBS AppleScript Forum (http://bbs.applescript.net/viewtopic.php?p=26388) have enabled me to refine the scripts over time and have helped me come to understand what is going on with most of the code. The Share2Blue2th scripts are less my own accomplishment than they are a testament to the collaborative power of the Internet and the Mac community.

The Code

First, let's look at the commands as you can type them manually into the Terminal application. All these commands must be run as an administrator, thus the sudo before each command. This is necessary because the commands use bits and pieces of your system that iSync has access to, but which you normally (and rightfully) don't have permission to use. You will be prompted for your administrator password after entering the first command.

Here are the commands in order, with my commentary:

sudo sh -c \"cd ~; nohup \
  /System/Library/SyncServices/SymbianConduit.bundle/Contents/Resources/¬ mRouter \
  -a 00:00:00:00:00 -t 180 -btt 180 -p -v >~/mrouter.log 2>&1 &\"

This is one long string, used to invoke mRouter. As you can see, mRouter is part of the SymbianConduit.bundle within System/Library/SyncServices. iSync uses this little program to establish connections with Symbian-based devices. If you type the complete path to mRouter in the Terminal, you will receive a list of valid options.

The first option is -aaddr, which specifies the 48-bit device address of the target. For our purposes, this is the MAC address for the paired device with which you want to share your computer's Internet connection. I've substituted 00:00:00:00:00 for the MAC address; you'll need to replace this with your device's MAC address (which is listed as the Device Address of your paired device in System Preferences → Bluetooth → Devices).

Next, the -ttime option specifies the timeout, in seconds, for the mRouter connection, and -btttime specifies the Bluetooth timeout. I have set both of them to 180 to give everything plenty of room. The -p switch tells mRouter not to use a pipe and is necessary if you run mRouter as standalone code. Next, for compatibility if you are using a P800 or P900 phone, you will need to add the -p800 switch. Finally, -v specifies the verbose switch. Running this command from the command line establishes a Bluetooth connection between your device and your Mac.

These variables are specific to the version of mRouter bundled in Mac OS X 10.3.2 and later running iSync 1.4. The code is slightly different if you are running an earlier version of iSync. Try the following command instead:

sudo sh -c \"cd ~; nohup \
  /System/Library/SyncServices/SymbianConduit.bundle/Contents/Resources/¬ mRouter \
  -p -a 00:00:00:00:00 -vv -t 180 >~/mrouter.log 2>&1 &\"

Now we can do something with this connection:

sudo /usr/sbin/sysctl -w net.inet.ip.forwarding=1

Unlike mRouter, sysctl actually has a manpage, so launch the Terminal and type man sysctl if you want to find out all about it. Basically, we are invoking sysctl to change the kernel's default IP forwarding to 1 (which, in the world of ones and zeros that the kernel speaks, translates into "turn on Internet Sharing, please"), effectively making your Mac a gateway.

Next, we need to start the NAT daemon:

sudo /usr/sbin/natd -interface en0 -use_sockets -same_ports -dynamic ¬
-clamp_mss

This command invokes natd, the Network Address Translation daemon (type man natd to read its manpage). Basically, natd does what its name implies: it translates the network address. What does that mean? Well, the command tells natd to use the Ethernet connection (-interface en0) to share over Bluetooth. If you want to share your AirPort connection instead, substitute en1 for en0. If you are connected via a USB modem that uses a PPP connection, you have to use the -nat switch to ppp(8), rather than -interface en0 (I've never tested this, so let me know if it works!). The -dynamic command ensures that if your Mac's IP address changes, that change will be translated for the shared connection as well.

Now that the NAT daemon is running, we must divert traffic to it:

sudo /sbin/ipfw add divert natd ip from any to any via en0

ipfw is the IP firewall and traffic shaper control program (type man ipfw in the Terminal to read up on it). This command adds a rule to ipfw, diverting all packets that pass through natd from any IP number to any IP number via the defined interface port (en1 for AirPort and en0 for Ethernet).

The following command runs named, which is a Domain Name System (DNS) server (type man named in the Terminal for more information):

sudo named

Run this command twice to force DNS resolution over the connection. If, after running named twice, you are still unable to connect to a web site without typing in the IP address for the site, wait a few seconds and run named again. In the early, superstitious days of using these commands, I used to run sudo named four or five times in a row just to be sure I would connect, but that was overkill.

Running these commands, substituting your MAC address for 00:00:00:00:00 and using the interface appropriate for your purposes, should share your Mac's Internet connection to your Bluetooth paired device. In order for your device to realize that this connection is available for Internet, you will need to trick it into accessing the Bluetooth paired connection with your Mac as an Internet access point. You can do this by setting up an Internet access point with the same name as your Mac's shared Bluetooth name normally appears on your device's pairing. The configuration will differ depending upon your device.

On the Nokia 3650, navigate to Connections → Bluetooth, turn on Bluetooth, and make sure your phone and your Mac are paired, if you haven't already done so.

Next, navigate to Tools → Settings and choose the Connection settings tab. When you click on Access Points, a screen should display your available access points. Select Options: New Access Point and Use Default settings. Change the connection name to the name of your computer as it appears in the paired device list on your phone. Change the Access Point name to the same name. Leave everything else set to the defaults.

Navigate back to the Connection settings and select GPRS. Make sure "GPRS connection" is set to "When available," and make sure that the access point you just created is selected under Access Point.

After successfully establishing this Internet sharing, you need to know how to kill this connection. Again, from the Terminal, type the following command to kill the DNS routing over the shared connection:

sudo killall named

Next, use this command to clear the natd firewall settings that you defined through ipfw when you established the connection:

sudo /sbin/ipfw -f flush

This command kills the Network Address Translation over the shared connection:

sudo killall natd

This command switches IP forwarding back to 0, tuning it off:

sudo /usr/sbin/sysctl -w net.inet.ip.forwarding=0

This command ends the mRouter connection between your device and your computer:

sudo killall mRouter

Finally, this optional command removes the mRouter log from your home folder:

sudo rm -f ~/mrouter*

If you are curious, or if you are troubleshooting the connection, you might want to delay erasing this file. Navigate to your Home folder and double-click mrouter.log to read a log of all that has happened during your connection.

As you can see, this is a lot of information to type into the Terminal each time you want to share your Internet to your Bluetooth-enabled device. The next step to making this a real hack is to automate the process, and the easiest way to do this is via AppleScript. Here is the final code for the script:

--Open this script in a new Script Editor window. 
property your_phones_MAC : missing value
property userchoice : "Ethernet"

--set the MAC address once: 
if your_phones_MAC = missing value then
    set your_phones_MAC to text returned of \
    (display dialog "Enter your phone's MAC address:" & return \
    & "(This can be found under System Preferences-->Bluetooth-->Devices \
    under your phone's paired name. Make sure you use colons!)" & return \
    & return \
    & "e.g., 00:00:00:00:00" default answer "" buttons {"Cancel", "OK"} \
    default button 2 with icon 1)
end if

--remember the last port chosen and default to that button: 
set userchoice to button returned of (display dialog "Select port to share:" \ buttons 
{"Cancel", "AirPort", "Ethernet"} default button userchoice with \
icon 1)
if userchoice = "AirPort" then
    set thePort to "en1"
else
    set thePort to "en0"
end if

do shell script "sh -c \"cd ~; nohup \ 
  /System/Library/SyncServices/SymbianConduit.bundle/Contents/\
Resources/mRouter \
  -a " & your_phones_MAC & " -t 180 -btt 180 -p -v ¬
>~/mrouter.log 2>&1 &\"" 
with administrator privileges
do shell script "sleep 10"
do shell script "sudo /usr/sbin/sysctl -w net.inet.ip.forwarding=1"
do shell script "sleep 2"
do shell script "sudo /usr/sbin/natd -interface " & thePort & \
" -use_sockets -same_ports -dynamic -clamp_mss"
do shell script "sleep 2"
do shell script "sudo /sbin/ipfw add divert natd ip from any to any via " \
& thePort
do shell script "sleep 2"
do shell script "sudo named"
do shell script "sudo named"

The first section of this script checks to see if your device's MAC address has previously been defined. If it is missing, the script displays the dialog box shown in , prompting you to enter your phone's MAC address and telling you where you can find this information.

Figure 1. Prompting for a MAC address

After this value is defined, it is saved as the variable your_phones_MAC, which is substituted in the first bit of command-line code regarding mRouter. As a result, this variable is saved in the script, so this dialog box will not pop up on subsequent launches of the script.

Before I added this portion to most recent release of the scripts, users had to open the scripts in Script Editor (found in /Applications/AppleScript/) and manually edit the first bit of command-line code with the correct MAC address. I tried various methods of grabbing the MAC address from the system, but this always broke mRouter, because the system displays the MAC address with dashes rather than with colons and mRouter thought each dash was followed by a command that it did not understand.

The next section of the script uses the display dialog function to launch a dialog box (see ) that asks you to choose the port you want to share over Bluetooth: Airport or Ethernet.

Figure 2. Prompting for a port

The dialog defaults to the port that was selected the last time the script ran. If you choose AirPort, the thePort variable is set to en1; if you choose Ethernet, the thePort variable is set to en0. Selecting Cancel ends the script.

The remainder of the script runs the previously discussed command-line commands by wrapping them in quotation marks following do shellscript. As you can see, the first command lacks sudo but is followed by withadministrator privileges, which amounts to the same thing.

Since you are running these commands through an AppleScript, rather than directly from the command line, a third and final dialog box will launch, prompting you for your administrator password. The values you input in the previous dialog boxes are substituted for the variables thePort and your_phones_MAC within the commands. Notice that sleep commands have been added between the different commands. This ensures that each command has time to execute before the script kicks out the next command.

In the Share2Blue2th package, all the scripts are included both as standalone script applications and as regular scripts that can be placed in the Scripts folder (~/Library/Scripts/) and launched via the Script Menu (/Applications/Apple Script/Install Script Menu). The package consists of four scripts: Share Internet Over Bluetooth, Share Ethernet Over Bluetooth, Share AirPort Over Bluetooth, and Kill Net Over Bluetooth. The only difference between the Share Internet Over Bluetooth and the Ethernet and AirPort scripts is that the latter two simply default to their specific ports, without launching the port dialog that prompts the user's input. Here's the code for the Kill Net Over Bluetooth script:

do shell script "killall named" with administrator privileges
do shell script "sudo /sbin/ipfw -f flush"
do shell script "sudo killall natd"
do shell script "sudo /usr/sbin/sysctl -w net.inet.ip.forwarding=0"
do shell script "sudo killall mRouter"
do shell script "sleep 15"
do shell script "sudo rm -f ~/mrouter*"

As you can see, the Kill Net Over Bluetooth script is simply the command code to kill the connection, wrapped in quotation marks following do shellscript. Again, the first command lacks sudo but is followed by withadministrator privileges. Since you are running these commands through an AppleScript, a dialog box will prompt you for your administrator password.

Hacking the Hack

These scripts share your Mac's Internet connection over Bluetooth with any paired Bluetooth device. The only problematic part of the equation is getting your device to see this connection as a viable source for Internet access.

I wrote these scripts specifically for use with a Nokia 3650, but numerous readers of my site have reported being able to use them with the Nokia 7650, 3660, 3620, and N-Gage, Sony Ericsson P800 and P900, and the Palm Tungsten T3. A Salling Clicker v.2.1 (http://homepage.mac.com/jonassalling/Shareware/Clicker/) plug-in version of the scripts is also available from my site (http://3650anda12inch.blogspot.com).

One person reported using the scripts to share an Internet connection between two Macs, and another between a Mac and a PC. I've not tried any of these combinations myself and have no idea how they managed it, but give it a shot. Play around with the code and contact me about anything you discover. I'll mention it on my site. Happy hacking!

C. K. Sample III

The Code

--Open this script in a new Script Editor window. 
property your_phones_MAC : missing value
property userchoice : "Ethernet"

--set the MAC address once: 
if your_phones_MAC = missing value then
    set your_phones_MAC to text returned of \
    (display dialog "Enter your phone's MAC address:" & return \
    & "(This can be found under System Preferences-->Bluetooth-->Devices \
    under your phone's paired name. Make sure you use colons!)" & return \
    & return \
    & "e.g., 00:00:00:00:00" default answer "" buttons {"Cancel", "OK"} \
    default button 2 with icon 1)
end if

--remember the last port chosen and default to that button: 
set userchoice to button returned of (display dialog "Select port to share:" \ buttons 
{"Cancel", "AirPort", "Ethernet"} default button userchoice with \
icon 1)
if userchoice = "AirPort" then
    set thePort to "en1"
else
    set thePort to "en0"
end if

do shell script "sh -c \"cd ~; nohup \ 
  /System/Library/SyncServices/SymbianConduit.bundle/Contents/\
Resources/mRouter \
  -a " & your_phones_MAC & " -t 180 -btt 180 -p -v ¬
>~/mrouter.log 2>&1 &\"" 
with administrator privileges
do shell script "sleep 10"
do shell script "sudo /usr/sbin/sysctl -w net.inet.ip.forwarding=1"
do shell script "sleep 2"
do shell script "sudo /usr/sbin/natd -interface " & thePort & \
" -use_sockets -same_ports -dynamic -clamp_mss"
do shell script "sleep 2"
do shell script "sudo /sbin/ipfw add divert natd ip from any to any via " \
& thePort
do shell script "sleep 2"
do shell script "sudo named"
do shell script "sudo named"


O'Reilly Home | Privacy Policy

© 2007 O'Reilly Media, Inc.
Website: | Customer Service: | Book issues:

All trademarks and registered trademarks appearing on oreilly.com are the property of their respective owners.