Software, technology, sysadmin war stories, and more. Feed
Monday, June 13, 2011

Network printing on the cheap in DOS

As previously mentioned in another post, I went to a school which had a Unix box and a bunch of scraggly PCs running NCSA telnet. This particular operation had no budget to speak of for computers or network stuff, so we had to make do with donations and a lot of volunteer time.

Teachers started running WordPerfect on these PCs and wanted a way to print, but there was no possibility of getting that many printers. Obviously we needed some kind of network printing solution, but how would that work? We didn't have any kind of "real" network software like Netware, and couldn't afford one anyway. The Unix box was out of the question since it was locked away, and it was too far from most users anyway.

They had no budget, but they did have me as a student, and that's where my involvement with this problem began. I had just started teaching myself how to send raw packets of my own design directly over the Ethernet using DOS packet drivers, and had written a dumb little pseudo-chat program using broadcasts. It seemed possible to turn that into some kind of client-server printing mechanism, so I proposed it and they agreed. I started an "independent study" class to do exactly that.

It's been a long time since this happened, but I went back and analyzed my old source code to figure out how it really worked. First of all, there was no client-side configuration. The last thing I wanted was yet another reason to go around to classrooms to update things. So, when the client came up, it would just fire off a ServerPing packet to the broadcast address.

Every instance of the server would then respond with a ServerAck packet to say, "yep, here I am, I'm a print server". The client would wait a second or two to collect all of them, and then it would take the just-entered username and password and try it against each server it had heard. Each server would respond with an ack or a nack to those attempts, and now the client knew who had accepted it. In the event of an ack, that response contained a "login cookie", and the client would keep that around for later use.

Now, with a list of servers which were willing to chat, the client could query each of those for its list of printers, and then it built a list. This list was then presented to the user, who could pick a target printer. Choosing one would generate a request to print to that server for that printer, and assuming that got a positive response, a file transfer would then begin: build a request to send a file, get the ACK, then enter a loop: push a block, wait for ACK, repeat. Streaming? What's that?

Anyway, after this finishes, it then asks the print server to actually process the job -- that is, make it print. It passes in some args which control what winds up on the cover sheet: recycled paper Y/N, two-sided copies Y/N, staple Y/N -- these are instructions for the folks in the on-campus print shop.

Finally, it reminds the user that their job will be waiting at whatever location they chose before, sends logout packets, and exits back to the DOS prompt.

There are some other fun things about this design. First, client and server were the same program -- the same .exe file. It was the ultimate in code reuse. Different combinations of things would run depending on whether it found the magic "make me a server" switch on its command line. Next, the server was actually able to handle multiple clients at the same time, just by round-robining between them. At one point in its loop, it would also push a few bytes at the printer from whatever job was on top, so that kept working, too. Threads? Processes? This is DOS! We had none of those things.

Finally, the best part, in my opinion: the auto-updating code. Since the server and client were the same program and they had version numbers, it was possible for the client to see if it was out of date. If it did, the server would push a copy of itself back to the client using the same file transfer protocol normally used for submitting jobs.

I originally wrote this to avoid a bunch of work during development: drop out of the Turbo Pascal (!) IDE on the dev box, start NCSA telnet (which had a FTP server), FTP over from the client box, get the file, quit both, go back to TP, start the server, start the client. Ugh. This let the client auto-update and I could run it in a looping batch file. Win.

Epilogue: I found an old copy of this source code years later after moving on to Linux-based systems. I wanted to see if it would still work, so I dug through my box of junk and found an old NIC which had a packet driver, cajoled another machine into running DOS and stood up the server. It seemed happy, but without a client, how could I test things? I decided to flip things around... make my DOS box a client... and... wrote a server on the Linux box!

10 years later, my original DOS net.exe talked to a supremely evil hack job on Linux which used raw sockets to hear and build its own packets. It actually worked, too.