Skip to content

Commit

Permalink
Merge pull request #108 from Ullaakut/dev/v3
Browse files Browse the repository at this point in the history
Redesign to version v3
  • Loading branch information
elivlo authored Apr 13, 2023
2 parents b2a0803 + 07fef9e commit 0a4c3dc
Show file tree
Hide file tree
Showing 43 changed files with 3,298 additions and 3,363 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ jobs:
uses: actions/checkout@v2

- name: Install Go
uses: actions/setup-go@v2
uses: actions/setup-go@v4
with:
go-version: 1.17
go-version: "1.20.0"

- name: Cache Go modules
uses: actions/cache@v2
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ jobs:
uses: actions/checkout@v2

- name: Install Go
uses: actions/setup-go@v2
uses: actions/setup-go@v4
with:
go-version: 1.17
go-version: "1.20.0"

- name: Cache Go modules
uses: actions/cache@v2
Expand Down Expand Up @@ -60,7 +60,7 @@ jobs:
run: goveralls -coverprofile=c.out -service=github

- name: Install staticcheck
run: go install honnef.co/go/tools/cmd/staticcheck@latest
run: go install honnef.co/go/tools/cmd/staticcheck@v0.4.3

- name: Run staticcheck for possible optimizations
run: staticcheck -tests=false
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
## Golang
vendor/*

### Binaries for programs and plugins
*.exe
Expand Down
96 changes: 50 additions & 46 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
<a href="LICENSE">
<img src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat" />
</a>
<a href="https://pkg.go.dev/github.com/Ullaakut/nmap/v2"><img src="https://pkg.go.dev/badge/github.com/Ullaakut/nmap/v2" alt="PkgGoDev github.com/Ullaakut/nmap/v2"></a>
<a href="https://goreportcard.com/report/github.com/Ullaakut/nmap">
<img src="https://goreportcard.com/badge/github.com/Ullaakut/nmap">
<a href="https://pkg.go.dev/github.com/Ullaakut/nmap/v3"><img src="https://pkg.go.dev/badge/github.com/Ullaakut/nmap/v3" alt="PkgGoDev github.com/Ullaakut/nmap/v3"></a>
<a href="https://goreportcard.com/report/github.com/Ullaakut/nmap/v3">
<img src="https://goreportcard.com/badge/github.com/Ullaakut/nmap/v3">
</a>
<a href="https://github.com/Ullaakut/nmap/actions/workflows/build.yml">
<img src="https://github.com/Ullaakut/nmap/actions/workflows/build.yml/badge.svg">
Expand Down Expand Up @@ -40,13 +40,13 @@ Most pentest tools are currently written using Python and not Go, because it is

- [x] All of `nmap`'s native options.
- [x] Additional [idiomatic go filters](examples/service_detection/main.go#L19) for filtering hosts and ports.
- [x] [Cancellable contexts support](examples/basic_scan/main.go).
- [x] Helpful enums for nmap commands. (time templates, os families, port states, etc.)
- [x] Complete documentation of each option, mostly insipred from nmap's documentation.

## TODO

- [ ] Add asynchronous scan, send scan progress percentage and time estimation through channel
- [x] Run a nmap scan asynchronously.
- [x] Scan progress can be piped through a channel.
- [x] Write the nmap output to a given file while also parsing it to the struct.
- [x] Stream the nmap output to an `io.Writer` interface while also parsing it to the struct.
- [x] Functionality to show local interfaces and routes.

## Simple example

Expand All @@ -59,47 +59,46 @@ import (
"log"
"time"

"github.com/Ullaakut/nmap/v2"
"github.com/Ullaakut/nmap/v3"
)

func main() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()

// Equivalent to `/usr/local/bin/nmap -p 80,443,843 google.com facebook.com youtube.com`,
// with a 5 minute timeout.
scanner, err := nmap.NewScanner(
nmap.WithTargets("google.com", "facebook.com", "youtube.com"),
nmap.WithPorts("80,443,843"),
nmap.WithContext(ctx),
)
if err != nil {
log.Fatalf("unable to create nmap scanner: %v", err)
}

result, warnings, err := scanner.Run()
if err != nil {
log.Fatalf("unable to run nmap scan: %v", err)
}

if warnings != nil {
log.Printf("Warnings: \n %v", warnings)
}

// Use the results to print an example output
for _, host := range result.Hosts {
if len(host.Ports) == 0 || len(host.Addresses) == 0 {
continue
}

fmt.Printf("Host %q:\n", host.Addresses[0])

for _, port := range host.Ports {
fmt.Printf("\tPort %d/%s %s %s\n", port.ID, port.Protocol, port.State, port.Service.Name)
}
}

fmt.Printf("Nmap done: %d hosts up scanned in %3f seconds\n", len(result.Hosts), result.Stats.Finished.Elapsed)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()

// Equivalent to `/usr/local/bin/nmap -p 80,443,843 google.com facebook.com youtube.com`,
// with a 5-minute timeout.
scanner, err := nmap.NewScanner(
ctx,
nmap.WithTargets("google.com", "facebook.com", "youtube.com"),
nmap.WithPorts("80,443,843"),
)
if err != nil {
log.Fatalf("unable to create nmap scanner: %v", err)
}

result, warnings, err := scanner.Run()
if len(*warnings) > 0 {
log.Printf("run finished with warnings: %s\n", *warnings) // Warnings are non-critical errors from nmap.
}
if err != nil {
log.Fatalf("unable to run nmap scan: %v", err)
}

// Use the results to print an example output
for _, host := range result.Hosts {
if len(host.Ports) == 0 || len(host.Addresses) == 0 {
continue
}

fmt.Printf("Host %q:\n", host.Addresses[0])

for _, port := range host.Ports {
fmt.Printf("\tPort %d/%s %s %s\n", port.ID, port.Protocol, port.State, port.Service.Name)
}
}

fmt.Printf("Nmap done: %d hosts up scanned in %.2f seconds\n", len(result.Hosts), result.Stats.Finished.Elapsed)
}
```

Expand Down Expand Up @@ -131,9 +130,14 @@ Nmap done: 3 hosts up scanned in 1.29 seconds

More examples:

- [Basic scan](examples/basic_scan/main.go)
- [Basic scan but asynchronously](examples/basic_scan_async/main.go)
- [Basic scan with nmap progress piped through](examples/basic_scan_progress/main.go)
- [Basic scan with output to a streamer](examples/basic_scan_streamer_interface/main.go)
- [Count hosts for each operating system on a network](examples/count_hosts_by_os/main.go)
- [Service detection](examples/service_detection/main.go)
- [IP address spoofing and decoys](examples/spoof_and_decoys/main.go)
- [List local interfaces](examples/list_interfaces/main.go)

## External resources

Expand Down
11 changes: 7 additions & 4 deletions examples/basic_scan/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,28 @@ import (
"log"
"time"

"github.com/Ullaakut/nmap/v2"
"github.com/Ullaakut/nmap/v3"
)

func main() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()

// Equivalent to `/usr/local/bin/nmap -p 80,443,843 google.com facebook.com youtube.com`,
// with a 5 minute timeout.
// with a 5-minute timeout.
scanner, err := nmap.NewScanner(
ctx,
nmap.WithTargets("google.com", "facebook.com", "youtube.com"),
nmap.WithPorts("80,443,843"),
nmap.WithContext(ctx),
)
if err != nil {
log.Fatalf("unable to create nmap scanner: %v", err)
}

result, _, err := scanner.Run()
result, warnings, err := scanner.Run()
if len(*warnings) > 0 {
log.Printf("run finished with warnings: %s\n", *warnings) // Warnings are non-critical errors from nmap.
}
if err != nil {
log.Fatalf("unable to run nmap scan: %v", err)
}
Expand Down
56 changes: 13 additions & 43 deletions examples/basic_scan_async/main.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,18 @@
package main

import (
"context"
"fmt"
"log"
"strings"

"github.com/Ullaakut/nmap/v2"
"github.com/Ullaakut/nmap/v3"
)

func main() {
var (
resultBytes []byte
errorBytes []byte
)
// Equivalent to `/usr/local/bin/nmap -p 80,443,843 google.com facebook.com youtube.com`,
// with a 5 minute timeout.
// with a 5-minute timeout.
s, err := nmap.NewScanner(
context.Background(),
nmap.WithTargets("google.com", "facebook.com", "youtube.com"),
nmap.WithPorts("80,443,843"),
)
Expand All @@ -24,45 +21,18 @@ func main() {
}

// Executes asynchronously, allowing results to be streamed in real time.
if err := s.RunAsync(); err != nil {
panic(err)
done := make(chan error)
result, warnings, err := s.Async(done).Run()
if err != nil {
log.Fatal(err)
}

// Connect to stdout of scanner.
stdout := s.GetStdout()

// Connect to stderr of scanner.
stderr := s.GetStderr()

// Goroutine to watch for stdout and print to screen. Additionally it stores
// the bytes intoa variable for processiing later.
go func() {
for stdout.Scan() {
fmt.Println(stdout.Text())
resultBytes = append(resultBytes, stdout.Bytes()...)
}
}()

// Goroutine to watch for stderr and print to screen. Additionally it stores
// the bytes intoa variable for processiing later.
go func() {
for stderr.Scan() {
errorBytes = append(errorBytes, stderr.Bytes()...)
}
}()

// Blocks main until the scan has completed.
if err := s.Wait(); err != nil {
panic(err)
}

// Parsing the results into corresponding structs.
result, err := nmap.Parse(resultBytes)

// Parsing the results into the NmapError slice of our nmap Struct.
result.NmapErrors = strings.Split(string(errorBytes), "\n")
if err != nil {
panic(err)
if err := <-done; err != nil {
if len(*warnings) > 0 {
log.Printf("run finished with warnings: %s\n", *warnings) // Warnings are non-critical errors from nmap.
}
log.Fatal(err)
}

// Use the results to print an example output
Expand Down
11 changes: 8 additions & 3 deletions examples/basic_scan_progress/main.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
package main

import (
"context"
"fmt"
"log"

"github.com/Ullaakut/nmap/v2"
"github.com/Ullaakut/nmap/v3"
)

func main() {
scanner, err := nmap.NewScanner(
context.Background(),
nmap.WithTargets("localhost"),
nmap.WithPorts("1-4000"),
nmap.WithPorts("1-10000"),
nmap.WithServiceInfo(),
nmap.WithVerbosity(3),
)
Expand All @@ -27,7 +29,10 @@ func main() {
}
}()

result, _, err := scanner.RunWithProgress(progress)
result, warnings, err := scanner.Progress(progress).Run()
if len(*warnings) > 0 {
log.Printf("run finished with warnings: %s\n", *warnings) // Warnings are non-critical errors from nmap.
}
if err != nil {
log.Fatalf("unable to run nmap scan: %v", err)
}
Expand Down
48 changes: 8 additions & 40 deletions examples/basic_scan_streamer_interface/main.go
Original file line number Diff line number Diff line change
@@ -1,45 +1,17 @@
package main

import (
"context"
"fmt"
"io/ioutil"
"log"
"strings"
"os"

"github.com/Ullaakut/nmap/v2"
"github.com/Ullaakut/nmap/v3"
)

// CustomType is your custom type in code.
// You just have to make it a Streamer.
type CustomType struct {
nmap.Streamer
File string
}

// Write is a function that handles the normal nmap stdout.
func (c *CustomType) Write(d []byte) (int, error) {
lines := string(d)

if strings.Contains(lines, "Stats: ") {
fmt.Print(lines)
}
return len(d), nil
}

// Bytes returns scan result bytes.
func (c *CustomType) Bytes() []byte {
data, err := ioutil.ReadFile(c.File)
if err != nil {
data = append(data, "\ncould not read File"...)
}
return data
}

func main() {
cType := &CustomType{
File: "/tmp/output.xml",
}
scanner, err := nmap.NewScanner(
context.Background(),
nmap.WithTargets("localhost"),
nmap.WithPorts("1-4000"),
nmap.WithServiceInfo(),
Expand All @@ -49,16 +21,12 @@ func main() {
log.Fatalf("unable to create nmap scanner: %v", err)
}

warnings, err := scanner.RunWithStreamer(cType, cType.File)
if err != nil {
log.Fatalf("unable to run nmap scan: %v", err)
result, warnings, err := scanner.Streamer(os.Stdout).Run()
if len(*warnings) > 0 {
log.Printf("run finished with warnings: %s\n", *warnings) // Warnings are non-critical errors from nmap.
}

fmt.Printf("Nmap warnings: %v\n", warnings)

result, err := nmap.Parse(cType.Bytes())
if err != nil {
log.Fatalf("unable to parse nmap output: %v", err)
log.Fatalf("unable to run nmap scan: %v", err)
}

fmt.Printf("Nmap done: %d hosts up scanned in %.2f seconds\n", len(result.Hosts), result.Stats.Finished.Elapsed)
Expand Down
Loading

0 comments on commit 0a4c3dc

Please sign in to comment.