Friday, May 8, 2015

Creating a Simple Golang Network Application, Part 4

At the end of the last blog post port_listen had gained some networking abilities. But one thing stuck out; I'm duplicating code for extracting the remote client's IP. It would be trivial to run that routine once and pass the result to the GrabInput for reuse rather than re-running the code snippet.

First I modified the call to GrabInput in the ListenToPort function so it passes the remote address, like "go GrabInput(conn, strPortToListenOn, strRemoteAddrSansPort)"

The next step is to tell GrabInput about the change. First, the declaration needs to change to

func GrabInput(conn net.Conn, strFromListenerNumber string, strClientIP string) {

The two strRemoteAddr* variables declared at the top of GrabInput can be removed, as can these lines:

strRemoteAddr = conn.RemoteAddr().String()
strRemoteAddrSansPort, _, _ = net.SplitHostPort(strRemoteAddr)

Now for one last change; the call to LogEvent needs to be changed from

LogEvent(strFromListenerNumber + ": Remote IP " + strRemoteAddrSansPort, strMessage)

to this

LogEvent(strFromListenerNumber + ": Remote IP " + strClientIP, strMessage)

While reviewing the source code I noticed that when a client disconnects the application just spits out an EOF with the port number. But what if there are two simultaneous connections on the same port? Which client closed the connection? Also, the EOF isn't very user friendly. Most people don't know what EOF means, and it's very simple to reword the message so non-technical people can understand that the connection was closed.

The EOF comes from the error checking code in GrabInput, at which point there's already a known client IP. I change the error checking to this:


if err != nil {
 if err.Error() == "EOF" {
  LogEvent(strFromListenerNumber, "Client located at " + strClientIP + " has disconnected.")
 } else {
  LogEvent(strFromListenerNumber + ": Remote IP " + strClientIP, err.Error())
 }
 return
}

Now if (well, when) the client disconnects the user gets a friendly disconnect message that includes the IP address. At the same time, if the error isn't a disconnect, the regular error message (plus the IP) will be printed to the log.

While I'm on the topic of making the application a little more friendly for the user, I want to eliminate having to explain to the user how to change kernel and shell limits before running the program. That means running a couple of system commands, launchctl and ulimit. Doing that in Go means adding a few more libraries, os/exec and syscall. I also cleaned up the growing list of imports using a slightly different invocation...


import (
 "fmt"
 "strconv"
 "os"
 "strings"
 "net"
 "os/exec"
 "syscall"
)

Running a couple of commands should be simple enough to execute in the main() function, but to help keep things clean I'm going to put them into a separate function.

In main(), declare a call to the SetupEnvironment() function I'm about to define along with a variable to hold the return value.


var intEnvironmentReady int

intEnvironmentReady = SetupEnvironment()

Right after that I run a check to see what value was returned. If it's not 0 I want it to spit an error and exit. Otherwise, keep going.


if intEnvironmentReady != 0 {
 fmt.Println("Something has gone wrong setting up the system environment, error " + strconv.Itoa(intEnvironmentReady))
 os.Exit(1)
}

There's a couple of values that could be returned from the function, hence the reason for the message formatted as it is. This calls strconv's Itoa (integer to ascii) function to convert intEnvironmentReady's value to a string.

Here's the SetupEnvironment() function definition:


func SetupEnvironment() int {
 
 var rLimit syscall.Rlimit
 
 command := exec.Command("/bin/launchctl", "limit", "maxfiles", "1000000","1000000")
 _,err := command.Output()
 if err != nil {
  return 1
 }
 err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
 if err != nil {
  return 3
 }
 rLimit.Cur = 1400
 err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
 if err != nil {
  return 2
 }
 return 0
}

Okay; first, what I thought would be simple, wasn't. I'll get to that shortly.

This function returns an integer value, so in the declaration there's an added "int" before the curly brace and after the closed parenthesis.

Next I create a variable called rLimit of type syscall.Rlimit; I'll get into that in a moment. Syscall is a library that lets you dig into system calls; they are operating-system low level calls. Suffice it to say for now that Rlimit is related to system resource limits.

The next block is fairly straightforward; command is getting the output of running the external command /bin/launchctl, with the arguments "limit maxfiles 1000000 1000000". It then checks to see if there's an error in the output (the first underscore ignores the output from the command because there isn't supposed to be any) and if err is set, the function returns a 1 (remember, it's looking for a 0 as success back in main()).

There's a problem when it comes to the ulimit. Ulimit is typically set using a shell command of the invoking user; if I just "run" it, the application is running under sudo, which is to say it is not running as the person actually running port_listen. The file limit is altered for "root", so the user is still flooded with too many file open errors.

There is no way I can find to alter the limit using a command executed from the os/exec library. That means falling down one more level deeper...syscall. We'll alter the soft file limit with a system call. Fortunately it's not too hard to read through the code and divine what is happening.

The "err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)" gets the current file limit structure. If I don't have the call to Getrlimit, trying to set it later fails with an invalid argument error. If the attempt has an error, it returns a value of 3 to the caller.

Next I set the limit to the number I have been successfully using, 1400. The .Cur attribute is the current soft limit.

The limit is actually set with the line "err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)" and if err has a value other than nil, the function returns a value of 2 to the caller.

If everything gets set without an issue, the last line is executed returning 0 to the caller.

At this point the application can be run with sudo and it'll now properly set up its environment before opening all the network sockets. It's much easier to explain to even a novice user they must run "sudo ./port_limit" than step them through setting up system and shell limits first!

One thing notably missing in the list of information recorded about connections is when the event(s) occurred. It would be helpful to prepend a timestamp to output.

Go has a library for that; add to the import list "time". I then alter the LogEvent function to read:


func LogEvent(strFrom string, strMessage string) {
 fmt.Println(time.Now().String() + " From port " + strFrom + ": " + strMessage)
}

Now the output is prepended with the time when an event occurs!

Next up, I want to try setting up a directory for storing the logfile. I want to make port_listen as self-contained as possible,  which will help make it easy to use. After mulling over a few options I decided to create a directory in the current working directory. This, of course, operates under the assumption that it will be run from a location owned by the user and they didn't try to install it to an application folder or a system binary folder.

I made the setup changes in the SetupEnvironment function since, I figured, this would be run every time and it sounds like it makes sense to make this part of setting up the environment in which to run this application. Here's the modified function:


func SetupEnvironment() int {

 var strSudoUserUid string
 var strSudoUserGid string
 var intSudoUserUid int
 var intSudoUserGid int  
 var strWorkDir string
 var err error
 var fiDirectory os.FileInfo
 var rLimit syscall.Rlimit

 if os.Getuid() == 0 {
  strSudoUserUid = os.Getenv("SUDO_UID")
  strSudoUserGid = os.Getenv("SUDO_GID")
 } else {
  fmt.Println("Please use sudo to run application (sudo ./port_listen)")
  return 4
 }
 strWorkDir, err = os.Getwd()
 if err != nil {
 fmt.Println("Error getting working directory: " + err.Error())
 }
 strWorkDir = strWorkDir + "/port_listen_logs"
 fiDirectory, err = os.Stat(strWorkDir)
 if err != nil {
  fmt.Println("Logs directory does not exist. Creating directory " + strWorkDir)
  err = os.Mkdir(strWorkDir, 0700)
  if err != nil {
   fmt.Println("Error creating directory; " + err.Error())
   return 5
  }
  intSudoUserUid, _ = strconv.Atoi(strSudoUserUid)
  intSudoUserGid, _ = strconv.Atoi(strSudoUserGid)
  err = os.Chown(strWorkDir, intSudoUserUid, intSudoUserGid)
  if err != nil {
   fmt.Println("Error changing permissions on directory: " + err.Error())
   return 7
  }
 }
 fiDirectory, err = os.Stat(strWorkDir)
 if !fiDirectory.IsDir() {
  fmt.Println(strWorkDir + " is not a directory!")
  return 6
 }
 command := exec.Command("/bin/launchctl", "limit", "maxfiles", "1000000","1000000")
 _ , err = command.Output()
 if err != nil {
  return 1
 }
 err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
 if err != nil {
  return 3
 }
 rLimit.Cur = 1400
 err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
 if err != nil {
  return 2
 }
 return 0
}

It looks a little daunting, but the logic isn't a bad as it looks. In the declarations I added four new variables, two int and two string, in which to store a UID and a GID (User ID and Group ID). It's something that could be simplified in execution but I did it the long way for clarity.

I also added an explicit err of type Error to be defined. That means in the already existing part of the function the first declaration of err with a := can now be changed to just an equal sign (well...not accurate to say it can be changed. It has to be changed or the compiler will throw an error back at you.)

You can also see a string for storing the working directory and another for a "FileInfo" type variable. More on that shortly.

The first bit of added code is asking what the current UserID is, with a comparison checking if it's 0. 0 is, of course, root; the application should be running as root in order to monitor privileged ports below 1,024. But I also need to know who invoked sudo for reasons I'll show shortly. Unfortunately any application running under Sudo will be convinced it's root.

After poking around online for a bit I discovered that Sudo sets three environment variables specifically for this purpose; I need to use two of them. They're called SUDO_UID and SUDO GID.

The os.Getuid(), if it equals "root", will then grab the strings returned by the two environment variables.

This also serves a second purpose. If the current userid isn't 0, then the user forgot to invoke the application with Sudo. So the else statement will inform the user they must use sudo, and then exit the program by returning a status of 4 to main()...since it's a non-zero return status, the application will exit.

The next question is, where is the application running from? I suppose I could just create a new directory in dot notation, but to be safe, I decided to work with explicit paths rather than relative ones. The call to os.Getwd assigns a string with the current working directory to the strWorkDir variable. If it fails, the application prints an error and returns a non-zero status to main().

Now that I have that, it's trivial to add on the directory I want to use for my logs. I just append "/port_listen_logs" to the working directory variable. Now that variable holds the location where I want to store the logs...and the directory that must first exist in order to create those logs there.

That's where the fiDirectory comes in. os.Stat, fed the working directory variable, checks if the directory (or file) exists, and if it does it returns an interface with some methods for getting information about that file or directory. If the directory doesn't exist, it spits back an error.

That's what the following if statement checks; if an error is returned, the directory doesn't exist. The application then tells the user that the directory doesn't exist (along with the full path, for useful feedback purposes) and proceeds to try creating it.

The directory is created using the call to os.Mkdir, taking the working directory string and a permissions string as arguments (and here I set it to the owner having full control.) If this fails for some reason, it throws an error message to the user and again spits a non-zero status back to main().

The next block of magic is the reason I needed to know the userid and groupid of the person that invoked the application (hidden behind Sudo.) The directory is being created by an application running as root, meaning the directory it created is owned by and given permissions to root. This section fixes it so the directory is owned by the user.

That needs to be done by the os.Chown() function, which takes integers for the user and group ID's. I take the strSudoUserUid and Gid and use strconv.Atoi to convert them from strings to integers, assigning them over to intSudoUserUid and Gid. Then plug them into os.Chown with the directory whose ownership I want to change and the two ID's and everything should be set; the only thing left to do is check if an error is thrown and if so spit the error to the user along with a nonzero return to main().

New milestone complete...the application now creates a directory in which to save logs in the current working directory of the application, and it detects if you forgot to run it under Sudo.

If the call to isDir on that FileInfo interface is false, then for reasons unknown there's a file called port_listen_logs in the current directory. This should, in theory, be a waste of space, but I figured it's a simple check for a weird error condition and checking for it is stupid simple...better off doing it.

Here's where I'll end part 4!

Wednesday, May 6, 2015

Creating a Simple Golang Network Application, Part 3

In the final version of the utility I want port_listen to keep running until the user tells it to quit. A simple way to do that is to wait for a keyword to be entered.

First I need to import a couple of additional libraries.


import "os"
import "strings"

Next in my opening block of variable creations in the top of main() I add a control variable using "var strUserInput string" between the declaration of i and the channel, then, still inside main():


for {
 fmt.Scanf("%s", &strUserInput)
 strUserInput = strings.ToLower(strUserInput)
 if strUserInput == "quit" {
  os.Exit(0)
 }
}

The "for" creates an infinite loop...at this point you can kind of tell infinite loops can be handy. fmt.Scanf is looking for using input, which is stored in strUserInput; it's passed by reference because of the way arguments are passed in Go functions. Next I use the ToLower function from the strings library to lowercase the entered text before the "if" statement performs a comparison.

I thought of the lowercase transformation after I wrote the function to initially look for the word "Quit"; I capitalized it out of habit. When I ran the application and tried using "quit" it of course wouldn't work. Then I realized my mistake. Whoopsie!

Transforming the string entered by the user to all lowercase was a quick and easy way to change the recognized keyword from one to several variations of the word "quit." When it's that easy...just do it.

The last two lines compares the strUserInput contents to the word "quit" and if it matches call the operating system's "Exit" routine passing the exit code status of 0 (which normally means the application is exiting normally.)

At this point I felt the mini-application was shaping up nicely. Next task to hammer on: the network code.

ListenToPort() was the launching point for the port monitoring, so it was the first to get a revamp.

In the initial source I had written:


func ListenToPort(uint16Port uint16, chanOutGoing chan<- string) {
 var strDiagMessage string

 strDiagMessage = "Listening to port " + strconv.FormatInt(int64(uint16Port),10)
 LogEvent(strconv.FormatInt(int64(uint16Port),10), strDiagMessage)
 chanOutGoing <-"done " + strconv.FormatInt(int64(uint16Port),10)
}

Glancing over the function you can see it does little more than acknowledge that it's listening to a port (obviously it wasn't, but it was a placeholder for that code) by printing a string for the user followed by a quick blurb back to main() through a channel.

The revamped code was a wee bit more complicated.


func ListenToPort(uint16Port uint16, chanOutGoing chan<- string) {

 var strPortToListenOn string
 var strRemoteAddr string
 var strRemoteAddrSansPort string
 
 strPortToListenOn = strconv.FormatInt(int64(uint16Port),10)
 
 netListenFor, err := net.Listen("tcp",":" + strPortToListenOn)
 if err != nil {
  LogEvent(strPortToListenOn, err.Error())
  chanOutGoing <-"done" + strconv.FormatInt(int64(uint16Port),10)
  return
 }
 chanOutGoing <-"done " + strconv.FormatInt(int64(uint16Port),10) 
 defer netListenFor.Close()
 
 for {
 
  conn, err := netListenFor.Accept()
  if err != nil {
   LogEvent(strPortToListenOn, err.Error())
  }

  strRemoteAddr = conn.RemoteAddr().String()
  strRemoteAddrSansPort, _, _ = net.SplitHostPort(strRemoteAddr)
  LogEvent(strPortToListenOn, "Connection attempt made from " + strRemoteAddrSansPort)

  go GrabInput(conn, strPortToListenOn)
 }
 
}

The diagnostic string declaration is gone and in its place are string variables for the port to listen on and handle the remote address. I'm certain there's a way to shorten the code or refactor it so I didn't need to break out the remote address bit into multiple strings, but I felt this was a little more explicit in what operations were being performed.

The strPortToListenOn variable, as you can tell from my naming convention as well as the declaration, is a string. The next line converts the port...passed in the argument list as uint16Port meaning it's an unsigned 16 bit int...into a string by calling strconv's FormatInt function. Because that function needs a 64 bit integer I have to cast uint16Port as a 64 bit integer and tell it to use base 10 (that's the second argument passed to FormatInt) in the conversion function.

I know, it's strange and seems wasteful but I didn't see a straightforward 16 bit conversion function in the documentation so I just cast it as 64 bit integer.

Next in the execution flow is the network listener. netListenFor is a listener returned by net.Listen with the type tcp and the port assigned with the value contained in strPortToListenOn. To be more accurate, it listens on ":<strPortToListenOn>" as the empty part prefacing the colon means the listener should listen on all local network interfaces. That way multiple interfaces, like wireless and wired ethernet, can be monitored.

Following the attempt to create a listener the value of err is evaluated to see if something went wrong. Go is very big on error checking; it's idiomatic to check immediately after function calls to reduce bugs. If an error is encountered (meaning there's a value in err, making it something other than nil) the code calls LogEvent (gotta tell the user something went wrong...) with the port number (so we know which go routine had the problem) and the error message. It then executes "return", effectively breaking out of the go routine. Most common reason something would "error" here is another process is already listening on that port; the return means that goroutine stops running, but the program will keep executing.

There is one line of code that is repeated in both the error block and immediately outside the error block, and that's a "done" string sent to the channel. This is still part of my proto-framework; in main() the channel is being monitored to tally that all the ports are being monitored (or trying to be monitored.)

In the previous form the same thing happened, only each ListenToPort goroutine only made one call. This revamped version also makes one channel communication but it's listed twice because if the attempt to listen fails the second channel communication never happens; if the attempt to listen succeeds the err check never runs. If even one error occurred and that second channel reply wasn't there the tally in main() will never reach the full count and the application would get "stuck" in an infinite loop waiting for the missing, errored connections to report back.

The deferred Close() function means the application should keep the port listener open until the function returns. This lets the listener get handed off and get cleaned up automatically when the function is finished doing what needs to be done (and reduces bugs introduced when the programmer forgets to close handles/connections! Defer is handy when dealing with sockets and files!)

The next for{} block is the bit that handles actual connections. A connection is listened for and the accept of the connection is returned as a connection type to the variable "conn", along with an err message if there's an error. Again, if there's an error, it's evaluated by checking whether err is nil and if it isn't a message is sent with the port number (thus identifying the problem port "process") and the contents of the error message.

Then I wanted the IP of the machine that was making the connection, because knowing the (supposed) origin of the connection attempt would be great for troubleshooting ("What's connecting to me?"). I started by assigning strRemoteAddr the return value of conn.RemoteAddr().String(); it was very handy that RemoteAddr has the ability to return a string value!

The problem is that this contains the IP octals along with the random port to which the connection is handed off (A connection may initiate on a known port, but then often gets handed off to an unprivileged random port in the high range). I wasn't interested in that; I just wanted the IP address. So I next assigned the strRemoteAddrSansPort the first return value (the other two going to the _ (underscore) meaning we're not interested in the return values there...toss'em!) of the net library's SplitHostPort function. At that point I can send a log message containing the port number (the goroutine's "id", so to speak...see the pattern?) and a string saying there has been a connection attempt from <IP value>.

At this point the flow of control is handed off to a new function called "GrabInput" with the connection (called conn) and the strPortToListenOn as arguments. The for{} loop then continues a new iteration (remember, the go keyword says GrabInput should spin off to do its thing asynchronously.) That means if a connection is made on port 123, GrabInput will grab that connection to do its thing and a new Listen is connected to port 123 to wait for a new connection. If multiple attempts are made to hit port 123 they'll all get handled; if I didn't do this, connections would be serialized.

Quick story; I experimented with a web server (very very very basic version) in Go using the online tutorials. I discovered that if you didn't properly handle the incoming connections as described in the previous paragraphs, what happens is you can connect and get a web page, but until the web browser was done and closed the socket nothing else could get a web page. That first page serve was snappy, though.

Back to the topic at hand...ListenToPort is done! Now on to GrabInput(), a new addition to the program. Here's that function block:


func GrabInput(conn net.Conn, strFromListenerNumber string) {

 var strMessage string
 var strRemoteAddr string
 var strRemoteAddrSansPort string
 
 bufIncoming := make([]byte, 1024)

 strRemoteAddr = conn.RemoteAddr().String()
 strRemoteAddrSansPort, _, _ = net.SplitHostPort(strRemoteAddr)
 
 for {
  bytesRead, err := conn.Read(bufIncoming)
  if err != nil {
   LogEvent(strFromListenerNumber, err.Error())
   return
  }
 
  strMessage = string(bufIncoming[0:bytesRead-1])
  LogEvent(strFromListenerNumber + ": Remote IP " + strRemoteAddrSansPort, strMessage)
 } 
}

GrabInput takes the network connection and port being listened on (the former as a connection type and the latter as a string type) as arguments. In the beginning I declare three variables, basically duplicating some of the code from ListenToPort, creating string variables for strMessage, strRemoteAddr, and strRemote AddrSansPort.

Next comes the heart of the function; a buffer is created called bufIncoming with a length of 1024 bytes. Pedantically this is a buffer in how it's being used...it's using syntax to create a Go slice, of type byte, with a length of 1,024 and returns the slice to bufIncoming. Basically a roughly one kilobyte "buffer", for practical purposes. (Note: I need to get this clarified, as I may be mixing up what is happening here...I know the effect of what is happening, but is it a slice? An array? Pedantically not a buffer?)

The next couple of lines duplicates what was done previously in ListenToPort; it extracts a string with the IP address of the connection.

The first line of the for{} block reads the information coming into the network connection (conn) using the Read function (conn.Read) and puts the contents into bufIncoming while simultaneously getting the number of bytes being used (the code creates and assigns the returned integer...the count of elements in the slice used...into byteRead; you can see the aggregation of creation of the variable and the assignment being performed because of the use of the colon and equal sign.

At this point you have a count of the number of bytes used in the slice and the actual information that was sent to the socket contained in bufIncoming. And an error message, if one occurred, assigned to err. If there is no data in the buffer, the information read is EOF (end of file.)

As is typical in Go applications the next bit is the checking for an error...if err is not nil, send the error message (in this program it's the port number as a string...again...along with the error message itself) sent to the LogEvent function.

The next bit takes the strMessage variable and assigns the contents from bufIncoming. This is done by using the string function to pull the contents from bufIncoming, reading from item 0 to one short of the length of the information. Why? Lengths and counting starting at 0 must be accounted for or you'll get an out of range error (and in Go, a crash.)

That message is then once again passed to LogEvent with the port number and a message containing the remote IP address and the contents of what the remote client sent.

At this point the bulk of the networking portion is completed; there are a few aesthetic changes made to clean up a little. For example, the original LogEvent said:


fmt.Println("From " + strFrom + ": " + strMessage)

But now it says


fmt.Println("From port " + strFrom + ": " + strMessage)

...so I didn't have to keep including "port " with every call to LogEvent. I also removed the line in main() that printed a diagnostic message whenever a ListenToPort process sent a "done" message through the channel; it just printed a thousand-number counter. The iterations are still counted, it just doesn't announce it now.

I added a "Waiting for Quit" line in main() so the user would have a prompt that the program could be exited by typing Quit, making the application slightly more user-friendly.

I used "go build" to create a new binary and then ran a test of the latest version; this uncovered a new set of complications...

First, ports below 1024 are considered privileged in Unix, and only a privileged user can open those ports for monitoring. The previous test versions of the code would just run. Now it only runs if executed using sudo. Well...that's simple enough to fix. Use sudo to invoke port_listen.

The next item I bumped into were limits built into the shell and kernel for user processes. A certain number of ports would open for monitoring before a slew of "too many open files" would pour into the terminal, which I believe is triggered because Unix treats the open ports as file handles. The way around it was to run the following two commands:

sudo launchctl limit maxfiles 1000000 1000000
ulimit -n 1400

Then I could listen to all the ports. I haven't tested yet if multiple interfaces would surpass the limit, but I don't think so since with just the wired ethernet connection the application is listening to 2,048 ports (the wired IP address ports plus all the localhost ports,) surpassing the 1400 limit imposed by the shell after modification.

These changes are temporary; a new shell or reboot will have the previous shell limits imposed again. To get around them permanently would mean some alterations to configuration files or automating some method of altering these limits when executing port_listen...

So where do we stand with the state of the application? Port_listen now launches and listens all ports 1 through 1024 and prints to the standard output the content sent by remote connections, and exits when the user types "quit" and hits enter. Here's the source code:


package main

import "fmt"
import "strconv"
import "os"
import "strings"
import "net"

func main() {
// Opening so many ports brings a "too many open files" error. The way around it in
// OS X: sudo launchctl limit maxfiles 1000000 1000000; ulimit -n 1400; sudo ./port_listen
 var i uint16
 var strUserInput string
 chanCommLine := make(chan string)
 
 for i = 1; i < 1025; i++ {
  go ListenToPort(i, chanCommLine)
 }
 i = 0
 for {
  strChanData:=<-chanCommLine
  if strChanData != "" {
   i++
  }
  if i == 1024 {
   fmt.Println("All channels accounted for")
   break
  }
 }
 for {
  fmt.Println("Waiting for Quit...")
  fmt.Scanf("%s", &strUserInput)
  strUserInput = strings.ToLower(strUserInput)
  if strUserInput == "quit" {
   os.Exit(0)
  }
 }
}

func ListenToPort(uint16Port uint16, chanOutGoing chan<- string) {

 var strPortToListenOn string
 var strRemoteAddr string
 var strRemoteAddrSansPort string
 
 strPortToListenOn = strconv.FormatInt(int64(uint16Port),10)
 
 netListenFor, err := net.Listen("tcp",":" + strPortToListenOn)
 if err != nil {
  LogEvent(strPortToListenOn, err.Error())
  chanOutGoing <-"done" + strconv.FormatInt(int64(uint16Port),10)
  return
 }
 chanOutGoing <-"done" + strconv.FormatInt(int64(uint16Port),10) 
 defer netListenFor.Close()
 for {
  conn, err := netListenFor.Accept()
  if err != nil {
   LogEvent(strPortToListenOn, err.Error())
  }
  strRemoteAddr = conn.RemoteAddr().String()
  strRemoteAddrSansPort, _, _ = net.SplitHostPort(strRemoteAddr)
  LogEvent(strPortToListenOn, "Connection attempt made from " + strRemoteAddrSansPort)

  go GrabInput(conn, strPortToListenOn)
 } 
}
func GrabInput(conn net.Conn, strFromListenerNumber string) {
 var strMessage string
 var strRemoteAddr string
 var strRemoteAddrSansPort string
 
 bufIncoming := make([]byte, 1024)

 strRemoteAddr = conn.RemoteAddr().String()
 strRemoteAddrSansPort, _, _ = net.SplitHostPort(strRemoteAddr)
 
 for {
  bytesRead, err := conn.Read(bufIncoming)
  if err != nil {
   LogEvent(strFromListenerNumber, err.Error())
   return
  }
  strMessage = string(bufIncoming[0:bytesRead-1])
  LogEvent(strFromListenerNumber + ": Remote IP " + strRemoteAddrSansPort, strMessage)
 } 
}
func LogEvent(strFrom string, strMessage string) {
 fmt.Println("From port " + strFrom + ": " + strMessage)
}

And here I'll end part 3!

Monday, May 4, 2015

Creating a Simple Golang Network Application, Part 2


Next I turned my thoughts to logging. If the processes are going to "log" things (eventually to a text file) I should probably create something to stub out that function.

Is that even the right word, stub? I used to think it was a placeholder so other parts of the application had something to call that could be fleshed out later. Apparently looking into the term I ended up discovering that it has a more specific meaning...and I'm not sure what it is called to create a function that does something, but the intention is to come back later to add or enhance the functionality; maybe the definition still fits.

But for my purposes here that doesn't matter. The point is, I wanted to have a separate, modular function call to do the work of printing stuff to the screen or dumping stuff to a text file.

So I created this:


func LogEvent(strFrom string, strMessage string) {

 fmt.Println("From " + strFrom + ": " + strMessage)
 
}

The function is called LogEvent(), and takes two string arguments. The strFrom is so the caller can insert the process (remember, asynchronous goroutines...) and strMessage is the message. This simplified form is going pretty much retain the previous incarnation's output, but under the hood I can greatly reduce duplicated code.

Also notice that I started using a naming convention of strNameOfFunction. I usually preface my variables with their type so I can keep track of them. The only time I didn't usually do that is using simple one-off single letter counters, like the "i" in the initial loop contained in main().

It started bugging me that my ListenToPort function didn't follow my variable naming convention. I had to rework it to use the logging function anyway, so I fixed it.


func ListenToPort(uint16Port uint16) {

 var strDiagMessage string

 strDiagMessage = "Listening to port " + strconv.FormatInt(int64(uint16Port),10)
 LogEvent(strconv.FormatInt(int64(uint16Port),10), strDiagMessage)
 
}

I created a string variable because, as I alluded previously, integers aren't strings. This was an easy way to make it clear that I'm converting something to a string and storing it in a nice little package to send off to another function. strDiagMessage is declared, then it's set to the diagnostic message for my testing and shuffled into LogEvent. I could have created another variable to store the name of the "port" it's listening on, but...meh.

The next item I started thinking about was communicating from the goroutines back to the calling process. Because the goroutines are asynchronous, doing anything that requires a check of the status of an individual goroutine requires some kind of communication channel to exchange data and "sync up" information. At this point I wanted to validate that the goroutines were running properly and could give a nod back to main() that those processes were ready.

This approach also meant I could get rid of the clunky timer. The timer is just a guesstimate of how long it will take all the goroutines to run and is rather sloppy. Time to modify some source code...

First, I won't need timers, so I remove the 'import "time"' line along with the time.Sleep and the associated "done" line as well as the comments referring to those lines. I use comments to explain what I'm doing for later reference mainly because I have the memory of a sieve, but I'm documenting everything here so for a short program like this I figure it's not really needed (and highly redundant when I'm elaborating in exposition.)

Next I create a channel. It's a Go method of intra-communication among asynchronous code; I'll spare you the details beyond the idea that if you want to pass data among routines, it's a handy data chute to slide information. And to be completely honest, channels seem a little tricky to get the hang of...

In my block of declarations, I create a channel called chanCommLine with:


chanCommLine := make(chan string)

Next I want to collect data from my goroutines. I change the definition of ListenToPort so it takes an additional argument:


func ListenToPort(uint16Port uint16, chanOutGoing chan<- string) {

Then I add a line to send both a "done" along with the "port number" (at this point, port number is a name-only thing...it's the port the particular routine is supposed to handle, not what it's actually doing. Don't get confused. Practically speaking it's an identifier of which goroutine is passing the message.)


 chanOutGoing <-"done " + strconv.FormatInt(int64(uint16Port),10)

The message sent into the channel is getting passed back to the main() function where something is listening for input from the channel conveyor belt. Of course something has to pass that channel to ListenToPort, so in main() where it's invoked I change the call to:


  go ListenToPort(i, chanCommLine)

Now I have to do something with that information. I add the following after all the go routines are spawned:


 i = 0

 for {

  strChanData:=<-chanCommLine
  if strChanData != "" {
   i++
   fmt.Println("Input detected from channel, loop " + strconv.FormatInt(int64(i),10) + " " + strChanData)
  }

  if i == 1024 {
   fmt.Println("Channels accounted for")
   break
  }
 }


What's happening here?

First I took the iterator from the previous loop and reused it, setting it to 0.

Next I create an infinite loop with "for" and inside create a string variable strChanData that is set to the data coming out of chanCommLine. Then, if the value isn't empty, another loop runs incrementing i and outputting a line telling the user that information was found from loop number something and at the end printing the data that came out of the channel.

When i hits 1024, the program prints that all the channels are accounted for and then "break" exits the infinite loop.

Notice this is comparing for 1024 while the loop spawning the goroutines counts to 1025? That's because of the point at which the comparison is being made. The loop creating the goroutines starts at 1, and once it's compared at the beginning of the final loop it will equal 1025 and not run again, leaving 1024 iterations. In this loop it starts at 0, runs a series of steps, then compares the state of the variable; if it's 1024 at the end of the last run, it stops. Rearranging the logic a bit could make it all consistent with one value (1024 or 1025) but I wanted the port counting to start at 1 and really, this isn't all that strange to figure out once you see the logic. I would wager that loop comparisons can be a huge source of difficulties and logic errors for new programmers, though.

How did I verify I have it running with the proper loop counts? I did a quick build ("go build" from the src directory) and ran my application with a "|grep 102" to see what popped out:

Input detected from channel, loop 102 done 874
From 1020: Listening to port 1020
From 1021: Listening to port 1021
From 1022: Listening to port 1022
From 1023: Listening to port 1023
From 1024: Listening to port 1024
Input detected from channel, loop 248 done 1020
Input detected from channel, loop 249 done 1021
Input detected from channel, loop 250 done 1022
Input detected from channel, loop 251 done 1023
Input detected from channel, loop 252 done 1024
From 102: Listening to port 102
Input detected from channel, loop 354 done 102
Input detected from channel, loop 1020 done 769
Input detected from channel, loop 1021 done 770
Input detected from channel, loop 1022 done 771
Input detected from channel, loop 1023 done 772
Input detected from channel, loop 1024 done 902

You can see the highest count is 1024. If the goroutines hit 1025, the lines starting with "From" would list a 1025, and if the channel loop hit 1025 then a line starting with "Input" would have a loop 1025 listed.

One important thing to note is that you can't treat the channel as if it were a variable. When something reads from the channel it is "emptied." In one version of the code I had tried to use the channel in a conditional check then output the contents in two separate statements; the number of loop iterations dropped to 512, only half of what was supposed to run. Why?

It happened because I was reading once as a conditional check, and in a second statement tried dumping the contents of the channel. What actually happened was it emptied when I checked the channel to evaluate for a condition then when the channel was used to print the contents it was getting the next item put into the channel, not the item used to check the condition.

On the bright side at least it was kind of obvious when the symptom was cutting the loop iterations in half...

A minor fix and voila! Communication from the goroutines to the caller! If you recall the earliest version of the program had a 2-second delay built in. This method of having channels tell main() that the goprocesses are all ready and listening means there should be a drastic drop in "do nothing" time. How long does the current version take to run?

I ran port_listen (the name of the application) on my somewhat old Mac running at 2.4 GHz on a Core 2 Duo processor using the "time" utility:

real 0m0.152s
user 0m0.010s
sys 0m0.010s

I'd say that's a bit of an improvement!

Here's what the source looks like at this point:


package main

import "fmt"
import "strconv"

func main() {

 var i uint16
 chanCommLine := make(chan string)
 
 for i = 1; i < 1025; i++ {
  go ListenToPort(i, chanCommLine)
 }

 i = 0

 for {
  strChanData:=<-chanCommLine
  if strChanData != "" {
   i++
   fmt.Println("Input detected from channel, loop " + strconv.FormatInt(int64(i),10) + " " + strChanData)
  }
  if i == 1024 {
   fmt.Println("Channels accounted for")
   break
  }
 }
}

func ListenToPort(uint16Port uint16, chanOutGoing chan<- string) {
 var strDiagMessage string

 strDiagMessage = "Listening to port " + strconv.FormatInt(int64(uint16Port),10)
 LogEvent(strconv.FormatInt(int64(uint16Port),10), strDiagMessage)
 chanOutGoing <-"done " + strconv.FormatInt(int64(uint16Port),10)
}

func LogEvent(strFrom string, strMessage string) {
 fmt.Println("From " + strFrom + ": " + strMessage)
}



This concludes part two!

Friday, May 1, 2015

Creating a Simple Golang Network Application, Part 1

Many moons ago I worked in a place that had an "incident" involving a network worm. It would hop from system to system, leaving behind itself as a payload before trying to find another machine to try logging into and repeating the spreading process.

I was reminded of this incident while troubleshooting a network issue for someone. What if something were on this user's network, poking around for access? How would she know?

What if I created a small application she could run on her Mac that would act as a kind of dumb honeypot; accepting connections and logging what the remote machine sent?

That's what set me on the path of using Go to create a simple program to do just that. I was vaguely familiar with the language and the resulting Go executable is statically linked; I can just send her the executable and it'll run, no need to make sure a set of libraries or framework was up to date before the program would run properly. And Go is also pretty fast, far faster than Python or Ruby.

I decided to chronicle the process of creating this application; perhaps it would be useful to a novice programmer wondering if anyone else created small programs with a similar process. I know there are times I would have found that kind of information useful. So...I'm leaving this here.

The first step was figuring out what I wanted the program to do. This was going to be a simple, small application. I wanted it to be easy enough that a kind of non-technical person could easily be talked through the steps of running the application. I wanted the program to listen to all the service ports, the ones commonly probed for vulnerabilities, and accept TCP connections and then log whatever was sent to those ports (passwords or commands, for example) to a text file.

I started off by making a simple "skeleton" of the application, modeling certain behaviors I could use as the bones of the program.


package main

import "fmt"
import "strconv"
import "time"

func main() {

 var i uint16
 
 for i = 1; i < 1025; i++ {

  go ListenToPort(i)
  
 }

 // This sleep and print is just a debugging/testing thing. The for loop using 
 // "go ListenToPort" is asynchronous...so we have to wait or it just finishes.
 time.Sleep(2 * time.Second)
 fmt.Println("Done!\n")
 
}

func ListenToPort(port uint16) {

 fmt.Println("Listening to port " + strconv.FormatInt(int64(port), 10))
 
}

Package main -> this tells the compiler this is the "main" application file with the main() function, instead of a package that is treated like a library to be compiled in the workspace pkg directory.

Import -> fmt, strconv and time are built-in libraries to the language. Fmt handles string handling and formatting, strconv handles converting strings to and from other types, and time lets us play with sleep.

main -> This function consists of just a for loop to run from 1 to 1024 and increment by one each time. Each iteration of the loop spawns a goroutine call to ListenToPort with the current iterator value, which later will be used to listen to the network ports. For now it just models the calling behavior validating that my looping logic will work in the earliest stages.

The "go" keyword means "this is to be run as a goroutine without returning a value or even waiting for any feedback." The application will spit out 1,024 asynchronous routines like cards shooting from the hands of a magician playing 1,024 card pickup before finishing the loop. The timing of these routines, separately scheduled, will finish at unpredictable moments later and may not even finish in order.

time.Sleep(2 * time.Second) and the fmt.Println-> Remember how I said the "go" keyword spawns go processes without regard for returning values or waiting for things to complete? Yeah, without this "let's wait" thing the system will almost immediately spit out "Done!" This means the application will finish before the goroutines have a chance to do anything.

This sleeping pause just gives the application some time to show some output. This is a quick-and-dirty thing...don't judge me! I'm simply forcing main() to give some breathing room to validate my loop logic.

The Main() function is closed up, and I then define a placeholder for a useful ListenToPort function with ListenToPort(port uint16); func means this is a function, ListenToPort is the name of that function, and "port" is the name of the variable I want to work with and uint16 means unsigned 16 bit integer since that should be enough to hold the number of ports I could want to iterate through.

The Println call is kind of complicated for the purposes of a simple test taking shape, but basically it is saying "Print 'Listening to port" and append the port number passed to me." Because it's an integer, I have to convert the uint16 into a string or a type error will be spat out by the compiler.

There doesn't seem to be a straightforward way of changing the variable into a string but FormatInt will do the conversion if the variable is a 64 bit int; so I cast port to a 64 bit integer using int64. The 10 is because FormatInt takes a "base number" argument and I'm using base 10 counting.

This source code compiles and spits out numbers...almost in order...to the command prompt, and after three seconds pass it says, "Done!"

Pretty simple, eh?

I'll end part one here...