Skip to content

Commit

Permalink
Update function that writes to file to also parse the XML
Browse files Browse the repository at this point in the history
  • Loading branch information
elivlo committed Apr 13, 2023
1 parent a96b94e commit de17478
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 36 deletions.
60 changes: 29 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Most pentest tools are currently written using Python and not Go, because it is
- [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.
- [x] Run a nmap scan asynchronously.
- [x] Scan progress may be piped.
- [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.
Expand All @@ -63,44 +63,42 @@ import (
)

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(
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)
}

var result nmap.Run
var warnings []string
err = scanner.Run(&result, &warnings)
if len(warnings) > 0 {
log.Printf("run finished with warnings: %s\n", warnings) // Warnings are non-critical errors from nmap.
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
}
// 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])
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)
}
}
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)
fmt.Printf("Nmap done: %d hosts up scanned in %.2f seconds\n", len(result.Hosts), result.Stats.Finished.Elapsed)
}
```

Expand Down
9 changes: 6 additions & 3 deletions nmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,7 @@ func (s *Scanner) Progress(liveProgress chan float32) *Scanner {
}

// ToFile enables the Scanner to write the nmap XML output to a given path.
// Nmap will write the normal CLI output to stdout.
// This option will not parse the nmap output to the struct *Run. You may parse it yourself after the run.
// Nmap will write the normal CLI output to stdout. The XML is parsed from file after the scan is finished.
func (s *Scanner) ToFile(file string) *Scanner {
s.toFile = &file
return s
Expand Down Expand Up @@ -251,7 +250,11 @@ func (s *Scanner) processNmapResult(result *Run, warnings *[]string, stdout, std

// Parse nmap xml output. Usually nmap always returns valid XML, even if there is a scan error.
// Potentially available warnings are returned too, but probably not the reason for a broken XML.
err = Parse(stdout.Bytes(), result)
if s.toFile != nil {
err = result.FromFile(*s.toFile)
} else {
err = Parse(stdout.Bytes(), result)
}
if err != nil {
*warnings = append(*warnings, err.Error()) // Append parsing error to warnings for those who are interested.
return ErrParseOutput
Expand Down
11 changes: 9 additions & 2 deletions xml.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ func (r Run) ToReader() io.Reader {
return bytes.NewReader(r.rawXML)
}

func (r *Run) FromFile(filename string) error {
readFile, err := os.ReadFile(filename)
if err != nil {
return err
}
return Parse(readFile, r)
}

// ScanInfo represents the scan information.
type ScanInfo struct {
NumServices int `xml:"numservices,attr" json:"num_services"`
Expand Down Expand Up @@ -432,8 +440,7 @@ func (t *Timestamp) UnmarshalXMLAttr(attr xml.Attr) (err error) {
return t.ParseTime(attr.Value)
}

// Parse takes a byte array of nmap xml data and unmarshals it into a
// Run struct.
// Parse takes a byte array of nmap xml data and unmarshal it into a Run struct.
func Parse(content []byte, result *Run) error {
result.rawXML = content

Expand Down

0 comments on commit de17478

Please sign in to comment.