-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Includes concurrency, concurrency controls (number of agents and rate limit), and timing, with some performance counters; inludes port-to-service mapping for open ports; includes progress bar; and includes unit tests.
- Loading branch information
Webb Scales
committed
Oct 11, 2016
1 parent
841cd11
commit a945fcf
Showing
13 changed files
with
1,381 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,46 @@ | ||
# webbscan | ||
Port scanner, written in golang | ||
|
||
This is intended to be a simple(ish) port scanner tool, written as a | ||
demonstration of my programming abilities and as an exercise in learning the | ||
Go programming language. | ||
|
||
The source code is divided up in the several packages, nominally to maximize | ||
reusability of each module: | ||
|
||
portserv - a simple library for translating TCP and UDP port numbers into | ||
service names, using /etc/services | ||
progbar - a simple ASCII-text-based "progress bar" which gives the user | ||
feedback during the portscan, visually displaying the progress of | ||
the scan, as well as a "spining" effect showing that probes are | ||
actively being sent. | ||
tcpProbe - a simple library for probing sockets. | ||
vdiag - a handy package for producing diagnositic output. | ||
workflow - a library for administering task execution, supporting concurrent | ||
as well as rate-limited dispatching. | ||
webbscan - the port scanner tool. | ||
|
||
|
||
Highlights of the code include: | ||
- understanding of various Go syntax, constructions, and usage, including | ||
arrays, maps, slices, ranges, structures, methods, strings, | ||
initialization, interfaces, enumerations (using iota), bytes-Buffers, | ||
Writers, command line flag support, goroutines, channels, timers, | ||
functions-as-parameters, and closures | ||
- regular-expression-based parsing | ||
- concurrency and synchronization | ||
- Go unit testing support, test-case objects, use of interfaces for mocking | ||
functions | ||
|
||
|
||
The tool provides several command-line switches which control its execution: | ||
|
||
-agents (default 8): the number of concurrent probes | ||
-host (default "127.0.0.1"): the target host to probe | ||
-protocol (default "tcp"): Protocol ("tcp" or "udp") | ||
-rate (default unlimited): the maximum number of probes to be sent per | ||
second (0: unlimited) | ||
-verbose (default none): The level of verbosity for diagnostic messages | ||
(-v is a shorthand for "level 2") | ||
|
||
In addition to the tool source code, the source includes unit tests for | ||
(nearly) all functions. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
// Package portserv provides interfaces which translate TCP and UDP ports into | ||
// service names. | ||
package portserv | ||
|
||
import ( | ||
"fmt" | ||
"io/ioutil" | ||
"regexp" | ||
"strconv" | ||
) | ||
|
||
var tcpServices map[int]string = make(map[int]string) | ||
var udpServices map[int]string = make(map[int]string) | ||
|
||
func init() { | ||
re := regexp.MustCompile(`\n(\S+)\s+(\d+)/(tcp|udp)`) | ||
text, err := ioutil.ReadFile("/etc/services") | ||
if err != nil { | ||
panic(err) | ||
} | ||
matches := re.FindAllStringSubmatch(string(text), -1) | ||
if matches == nil { | ||
panic("Failed to parse services file") | ||
} | ||
|
||
for _, v := range matches { | ||
port, err := strconv.Atoi(v[2]) | ||
if err != nil { | ||
panic(fmt.Sprintf("regex match returned non-numeric number: \"%s\"", v[1])) | ||
} | ||
switch v[3] { | ||
case "tcp": | ||
tcpServices[port] = v[1] | ||
case "udp": | ||
udpServices[port] = v[1] | ||
default: | ||
panic(fmt.Sprintf("Unexpected protocol value: \"%s\"", | ||
v[3])) | ||
} | ||
} | ||
} | ||
|
||
func Tcp(port int) string { return tcpServices[port] } | ||
func Udp(port int) string { return udpServices[port] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// Unit tests for package portserv. | ||
package portserv | ||
|
||
import ( | ||
"net" | ||
"testing" | ||
) | ||
|
||
func check(t *testing.T, network string, getService func(int) string) { | ||
var checked int | ||
for i := 1; i <= 65535; i++ { | ||
service := getService(i) | ||
if service != "" { | ||
port, err := net.LookupPort(network, service) | ||
if err != nil { | ||
t.Errorf("Port %d(%s): error looking up port for returned service \"%s\": %v\n", i, network, service, err) | ||
} else if port != i { | ||
t.Errorf("Port %d(%s): got mismatched port (%d) for returned service \"%s\".\n", i, network, port, service) | ||
} | ||
checked++ | ||
} | ||
} | ||
|
||
if checked == 0 { | ||
t.Errorf("No services returned for %s ports.\n", network) | ||
} else { | ||
t.Logf("Confirmed matches for %d %s ports/services.\n", | ||
checked, network) | ||
} | ||
} | ||
|
||
func TestTcp(t *testing.T) { | ||
check(t, "tcp", Tcp) | ||
} | ||
|
||
func TestUdp(t *testing.T) { | ||
check(t, "udp", Udp) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
// Package progbar provides a simple ASCII progress bar | ||
// | ||
// After creating the bar with New(), the bar can be painted on the screen (or | ||
// repainted again) using Paint(), it can be updated using Update(), and it | ||
// can be finished with Done(). The function Spin() can be used to show | ||
// intermediate activity by causing a spinning effect at the end of the bar. | ||
package progbar | ||
|
||
import ( | ||
"io" | ||
"strings" | ||
) | ||
|
||
// The magic strings which, when printed in sequence, make the spinner appear | ||
// to spin. | ||
var spinStrs []string = []string{"-\b", "\\\b", "|\b", "/\b"} | ||
|
||
// The Bar structure represents the parameters and state of the progress bar. | ||
type Bar struct { | ||
// The width of the bar on the screen in columns. | ||
width int | ||
// The total size of the bar in "progress units". | ||
total int | ||
// The current amount of progress in the bar in "progress units". | ||
current int | ||
// The current orientation of the end-of-bar "spinner". | ||
curSpin int | ||
// The Writer used to display the bar. | ||
w io.Writer | ||
} | ||
|
||
// New creates a new bar which will grow to the specified width as the number | ||
// of "progress units" approaches the specified size. The specified Writer is | ||
// used to display the bar. | ||
func New(width int, size int, w io.Writer) *Bar { | ||
if width <= 0 || size <= 0 || w == nil { | ||
return nil | ||
} | ||
return &Bar{width: width, total: size, w: w} | ||
} | ||
|
||
// Paint displays an empty progress bar on the screen with vertical bars | ||
// marking the beginning and end, fills the bar up to the current progress | ||
// point, and leaves the cursor at the next column. | ||
func (b *Bar) Paint() { | ||
s := []string{ | ||
strings.Repeat(" ", b.width+1), // Move the cursor to the right | ||
"|\r|", // Last bar, return, first bar | ||
strings.Repeat("=", b.current*b.width/b.total), // Any progress | ||
} | ||
io.WriteString(b.w, strings.Join(s, "")) | ||
} | ||
|
||
// Update advances the bar by one "progress unit" and, if appropriate, adds a | ||
// character to the bar on the screen. | ||
func (b *Bar) Update() { | ||
b.current++ | ||
if b.current%(b.total/b.width) == 0 { | ||
io.WriteString(b.w, "=") | ||
} | ||
} | ||
|
||
// Done erases the bar. | ||
func (b *Bar) Done() { | ||
b.current = b.total // For completeness | ||
|
||
s := []string{ | ||
"\r", // Return the cursor to the beginning of the line | ||
strings.Repeat(" ", b.width+2), // Clear the whole line | ||
"\r", // Like it was never there | ||
} | ||
io.WriteString(b.w, strings.Join(s, "")) | ||
} | ||
|
||
// Spin a little "wheel" at the end of the progress bar to indicate | ||
// intermediate activity. | ||
func (b *Bar) Spin() { | ||
b.curSpin = (b.curSpin + 1) & 0x3 | ||
io.WriteString(b.w, spinStrs[b.curSpin]) | ||
} |
Oops, something went wrong.