Running a Web Server on a Home Router
Posted: October 15, 2007 Filed under: NEZzen 23 Comments »You know that little blue box sitting in the corner of your home office, providing you with access to the glorious World Wide Web? Yes – I mean your wireless router. Did you know that you can serve web pages directly off of it? I bet you didn’t. Well I’m going to show you how.
Before we get started, however, I would like to remind you that you’re taking a risk (albeit small one if you know what you’re doing) here by voiding your warranty and modifying your router. Make sure you have a spare router just in case you screw up and brick it
First off, I’m going to assume that you’re a self-respecting geek who’s already flashed your Linksys, Buffalo, ASUS, Belkin, D-Link, Motorola, Netgear, or ZyXel router with some variant of homebrew Linux firmware. If not, I suggest you pick up a copy of Tomato, OpenWrt, DD-WRT, or the like and load it up. For this tutorial, I’m going to use Tomato 1.10 build 1188, since it happens to leave a decent amount of free space on the flash to host your own content, and is fairly feature rich.
Once you’ve got your router all configured and running an SSH server, go ahead and connect to it. If you run ps you should notice that there is an instance of httpd already running on port 80! Don’t get yourself excited though – this is the special web server used to admin the router, and it’s pointed at the read-only directory /www. Actually, if you notice, everything on the router is mounted read-only (with the exception of /tmp which is mounted tmpfs).
Unlocking Flash for Writing
Nevertheless, it is easy to unlock a portion of the flash memory for our own use. Point your browser at the internal webserver, click Administration and JFFS2, and then format/enable the journaled filesystem. It will be automatically mounted read-write on /jffs. As for the box about executing a script, tell it to run /jffs/start.sh – I’ll explain why in a minute.
Back in your open shell, change directory over into /jffs and type mkdir web. Ok, so now you’re thinking that we’re just going to point the existing /usr/sbin/httpd at /jffs/web – but you’re wrong! After a quick email with Jon, the creator of Tomato, I found out that the password prompt and default filename are statically compiled into the binary. Can you guess what that means? We get to compile our own special httpd with our own modifications – how exciting!
Compiling the Server
Grab a copy of the Tomato source code along with the official Linksys code. Don’t worry if you don’t have a Linksys router, we’re not compiling anything model-specific – in fact, I’m doing this with a Buffalo WBR2-G54. Extract everything in the way the Tomato README explains, and change to tomato/release/src/router/httpd.
Bring up httpd.c in your favorite *nix code editor (*cough* vim *cough*) and find and remove the line that says sprintf(header, "WWW-Authenticate: Basic realm=\"%s\"", realm);. Do the same for send_error(401, header, NULL);. This will remove the code that would have instructed the browser that it sent an invalid username/password. Next you have to remove the two lines that check the authentication on the router side. In the same file, locate and neutralize the two instances of if (!check_auth(authorization, cl)) return;. Finally, do a search-and-replace for status-overview.asp and replace it with whatever you want to call your default page (I picked index.asp). Save the file and close your editor.
Make sure your distro’s compilers are set up properly and make httpd, or just build the whole entire thing and copy the resulting binary file out of httpd. If you’re using Debian or Ubuntu as your host, you’ll want to apt-get install build-essential as root beforehand.
If something gets messed up and you’re getting build errors, you can just download and uncompress my prebuilt binary to your router.
# cd /jffs
# wget http://192.168.1.51/tomato110-custom-httpd.gz
# mv tomato110-custom-httpd.gz httpd.gz
# gunzip httpd.gz
Creating Boot Scripts
Remember that shell script we referenced a while ago? Now it is time to create it.
# cd /jffs
# cat << EOF > start.sh
#!/bin/ash
cd /jffs/web
/jffs/httpd -p 90
EOF
# chmod +x start.sh
Notice how the script changes into the directory that httpd will be serving up, and specifies port 90 as to not interfere with the internal web server used for configuration. (Don’t worry, we’ll map port 80 later on.)
Finalizing Access
In order to enable access from the outside, you will need to go to poke two holes in the NAT/firewall. First, go to Port Forwarding / Basic and forward external port 80 to internal port 90 on the internal IP assigned to your router. Second, go to Administration / Scripts, click the Firewall tab, and add this line of code to allow direct connections to port 90 on the router.
iptables -A INPUT -p tcp -m tcp --dport 90 -j ACCEPT
You can now reboot your router and do a quick test that your special instance of our new mini web server is running. Assuming the test went well, you can go ahead and fill up the tiny flash partition with some static HTML (remember to name the home page index.asp) for your own use — whatever that may be. Happy hacking
how map port 80 later on???
Read the first paragraph of the last section.
Great but i am lost in the beginning so can we do that step by step tutorial. I couldn’t find that version of tomato. I cant do the compilling. Thank you for the post it is a blind spot for google search so for the common people.
You don’t need to be running the exact version of Tomato as I did when I wrote this post in 2007. The pre-built binary I have available (see link at bottom of Compiling section) works just fine on the latest Tomato variants. Also, it appears the Linksys FTP isn’t working. You can get the GPL code from: http://homesupport.cisco.com/en-us/gplcodecenter
How can one transfer files on the router. ssh for ehample but what is the syntax since wget didnt worked for me. Thank you! sorry for the dummyest question
From your computer just use SCP to copy the file to the router (ex. 192.168.1.1).
$ scp tomato110-custom-httpd.gz 192.168.1.1:/jffs/httpd.gz
Then on the router side, uncompress it.
# gunzip httpd.gz
Don’t forget to set permissions on the file after extracting.
# chmod +x httpd
i dont know what i touched but it works
THANK YOU THANK YOU!!!
the clues were enough for a blind man to pass
Glad you were able to get it working!
how do you open “httpd” file you made?
All you need to do is give it execute permissions with “chmod +x ./httpd” and then run it with “./httpd -p 90″ where 90 can be replaced with the port number you want to bind it to. By default it will serve files from the directory you started it inside of.
Any ideas on how to modify this setup to understand the application/x-ns-proxy-autoconfig mime type? This would be for the purpose of serving up a web proxy automatic detection (wpad) file (wpad.dat/proxy.pac) directly from the router. This httpd binary as well as tomato’s web server don’t seem to be configured to natively handle this mime type.
The web server binary that ships with the source code is a very basic implementation of HTTP and does not support advanced features like this. It is really only meant for the admin interface. You could try editing the source code and compiling your own if you would like; but my suggestion would be to port over a small web server that supports these features from a distribution such as OpenWRT.
That’s what I was thinking. I’ve looked into setting up lighttpd (OpenWRT package) (I looked at a number of others as well) but have already run out of space on the jffs partition getting all the libraries it requires to run. It seems it wouldn’t take very much to add to the tomato httpd source. Could you share the process you used to compile your httpd binary. Perhaps provide a copy of the official Linksys source if you still have it since that link is dead. I thought I had found another copy at the twtomato google code project but I get a bad crc when extracting the tgz from the joined zip file and after fully extracting it some stuff appears to be missing as there are a number of compile errors – the tomato/tools/brcm folder doesn’t exist which contains the mipsel-uclibc-gcc binary which appears to be requisite for compilation.
As stated in a previous comment.
For the Linksys code try using the WRT54GL tarball from: http://homesupport.cisco.com/en-us/gplcodecenter
For the Tomato code get the Tomato_Source tarball from: http://sourceforge.net/projects/tomatofirmware/files/tomato/1_28/
are these asp commands possible to run on the httpd server i got from here or it does not support these? Thank you
PS: Still proud to have my website on the router!!!
Lio, can you run php based website on the router?
i ment these
set FSO = Server.CreateObject(“scripting.FileSystemObject”)
set myFile = fso.CreateTextFile(“/jffs/web/form/xx.txt”, true)
myFile.WriteLine(“my text here”)
myFile.WriteLine(“more text here”)
myFile.Close
No, the router can’t run ASP code – it’s not powerful enough. Maybe rudimentary CGI if you tweak the webserver source.
When I ./httpd -p 80, I get “segmentation error”..
yet when I, service httpd start, it completes.
However, when I, service httpd -p 90 start, it refuses to do anything..
Interesting. Did you compile the httpd yourself? What version of the software is your system using? Linux kernel?
Hi Brian, Do you still have a copy of the httpd? The link is down. thanks
Try this link. (MD5sum = beb610af1a2d98a73d5c276dcb37f71b)
Thanks