Friday, May 15, 2015

Creating a Simple Golang Network Application, Part 7

Parts 1 through 6 chronicled my process in making a relatively simple network-listener. Part 7 is the point where I review some notes as a kind of "post mortem" of lessons learned and potential issues.

I'll start off by saying that technically I don't think anything is "wrong" with this application. It does what it's supposed to do, and it scratched the itch it was supposed to scratch.

What is a potential problem with port_listen?

In a previous post I said that the developer who glanced at the source code said there were two potential issues; one was the passing of errors instead of numerical errors, and the second one I said I'd get into later (but later is now.)

LogEvent() is called from the goroutines monitoring ports. There is nothing serializing these calls, so there is a remote chance that two ports could try calling, and thus writing to, the log file at the same time leading to a race condition.

I had worried that this was a possibility before the developer had pointed it out, so I had tried testing for a race condition on a system that was probed for security vulnerabilities by a remote testing system. In the code if there was an error trying to open the file port_listen should have exited, and it didn't during the test. That doesn't mean the race condition isn't possible, since this was tested on a pretty fast system; if the application were run from a thumbdrive, for example, it's possible that a high load will cause the race condition and exit the program.

The "GoLang Way" to fix the potential race condition is to rewrite the flow of logic so that the goroutines monitoring connections would pass messages via a channel back to a central calling function, and that function would serialize the calls to LogEvent(). It's an ideal use case for channels.

After some testing and thinking about the situation, I decided to keep things as they are. It was working as-is, and if there was evidence of a race condition later I would be able to fix it.

What did I do incorrect with the development process?

I didn't use version control. Initially this was supposed to be a small application and it ended up a little larger than intended...so why bother with version control?

I should have used it anyway. I went through a few stages of altering items where I ended up copying a ".bak" file of the working source code first. If I had used version control, I could have easily rolled changes back. I also documented what I was doing with the notes for the blog post, and zero in-code comments...I could have kept track of items using commit messages to the version control.

I also should have written a functional spec...I sort of mapped out what I wanted the program to do when I started, but I didn't formalize it or model the application formally.

What did I learn from this application?

First, I didn't know that having a program grab a small number of ports in the 700 range would kill IP printing (LPD) on a Mac client. That was highly unexpected.

Second, I used to have a series of downloaded MP3's with lessons on presenting to groups. I copied them to a local directory and created a way to share them to my own machine as an RSS feed, then configured iTunes to read the feed and copy them to my iPhone like any other podcast. Basically I turned my own computer into a podcast server.

I had finished with the podcasts but using this tool I was reminded the iTunes was still trying to pull new files when it connected to the local machine and dumped some header information to the port.

Third, I learned that Flash will try to connect to the local machine to look for some kind of system policy file. I didn't know you could control Flash player with a policy file. But you can, and it will connect to the localhost through a TCP connection to query the existence of said file. Here's what was found in the log:
2015-04-30 08:24:49.174877175 -0400 EDT From port 843:: ::1 is localhost
2015-04-30 08:24:49.175084657 -0400 EDT From port 843: Remote IP ::1:: <policy-file-request/>
2015-04-30 08:24:52.182099935 -0400 EDT From port 843:: Client located at ::1 has disconnected.

What could have been done differently?

It's said that a program is never really done. If I had unlimited time, I think there are plenty of things that could be tweaked; off the top of my head:

Command line flags for specifying port ranges to monitor
Command line flags for specifying port ranges to exclude
Ability to monitor UDP packets
Ability to dump information to the terminal AND the logfile, or just one or the other
Ability to run as a daemon (oh, that would be an interesting sort of pain)
Change the code so it can monitor Linux systems as well as Macs
Ability to package the logs as a zipfile for easy transfer and "clean up" on client systems
A timer that automatically kills the application after X minutes monitoring
Reverse-lookup of connecting IP's to see what DNS thinks is connecting to the machine
A keystroke shows the last couple of log entries
A dot or other character at the command line that appears whenever there's a log entry, just to indicate that something happened

I'm sure there are others I could think of if I wanted to turn this into a full-on application for general use. I'm also sure that I could spend a month tinkering and trying to perfect it rather than moving on to other things...long after having used this for the intended use case.

Perhaps someday I'll revisit the code and add some other features. Nothing says I can't, after all.

This is just one person's process for creating a simple application. Perhaps it'll be useful to someone; if you have suggestions or links to other sites offering a peek behind the scenes of the development process by other developers, leave a comment!

No comments:

Post a Comment