Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RunAsync() method #6

Closed
4 tasks
Ullaakut opened this issue Jan 20, 2019 · 6 comments · Fixed by #26
Closed
4 tasks

Add RunAsync() method #6

Ullaakut opened this issue Jan 20, 2019 · 6 comments · Fixed by #26
Assignees
Labels
enhancement New feature or request good first issue Good for newcomers

Comments

@Ullaakut
Copy link
Owner

Allowing the users to run nmap asnchronously makes it possible to parse the live nmap output, for example for building a UI on top, or computing a progress bar.

I'm not sure if many people have this use-case though, so I won't prioritize this for now, but I find it really cool.

  • Add a RunAsync() method with the following signature func (s *Scanner) RunAsync() (stdout chan <-[]byte, stderr chan <-[]byte, error)
    • Make this method return one channel for stdout, one for stderr, and an error if the nmap process could not be started.
    • Have goroutine to kill nmap if context is done or times out.
    • Store output in private attribute to expose a nmap.Result structure once the scan is done
@Ullaakut Ullaakut self-assigned this Jan 20, 2019
@Ullaakut Ullaakut added enhancement New feature or request help wanted Extra attention is needed good first issue Good for newcomers labels Jan 31, 2019
@TerminalFi
Copy link
Collaborator

Have you had time to look at this more ? If not, I’ll start looking into how to add this properly. It’s be nice to have.

@Ullaakut
Copy link
Owner Author

Hey @TheSecEng I didn't have time as of now, I've been working on other projects, but if you're interested in picking this one up I'd love to give you any help you need :)

@Ullaakut Ullaakut assigned TerminalFi and unassigned Ullaakut Aug 13, 2019
@Ullaakut Ullaakut removed the help wanted Extra attention is needed label Aug 13, 2019
@TerminalFi
Copy link
Collaborator

@Ullaakut you got it!

@TerminalFi
Copy link
Collaborator

TerminalFi commented Aug 28, 2019

@Ullaakut Thoughts? As I wasn't fully understanding of channels. This would be a solution I propose.

However, if this doesn't meet the full need. I'll need to research further into channels and take time to really understand them.

I think this method avoids some of the pains of using channels, but potentially at the sacrifice of easily putting the results into a private var then exposing it.

But then again, it would require Run() to be modified as well. Those updates being minimal.

// Scanner represents an Nmap scanner.
type Scanner struct {
	Cmd *exec.Cmd

	args       []string
	binaryPath string
	ctx        context.Context

	portFilter func(Port) bool
	hostFilter func(Host) bool

	Stderr, Stdout bufio.Scanner
}
// RunAsync runs nmap asynchronously and returns error.
func (s *Scanner) RunAsync() error {
	// Enable XML output
	s.args = append(s.args, "-oX")

	// Get XML output in stdout instead of writing it in a file
	s.args = append(s.args, "-")
	s.Cmd = exec.Command(s.binaryPath, s.args...)

	stderr, err := s.Cmd.StderrPipe()
	if err != nil {
		return err
	}

	stdout, err := s.Cmd.StdoutPipe()
	if err != nil {
		return err
	}

	s.Stdout = *bufio.NewScanner(stdout)
	s.Stderr = *bufio.NewScanner(stderr)

	if err := s.Cmd.Start(); err != nil {
		return err
	}

	go func() {
		select {
		case <-s.ctx.Done():
			s.Cmd.Process.Kill()
		}
	}()

	return nil
}

Example

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"strings"

	"github.com/Ullaakut/nmap"
)

func main() {

	var resultBytes []byte
	var errorBytes []byte
	s, err := nmap.NewScanner(
		nmap.WithTargets("google.com", "facebook.com", "youtube.co1m"),
		nmap.WithPorts("80,443,843"),
	)

	if err != nil {
		log.Fatalf("unable to create nmap scanner: %v", err)
	}

	if err := s.RunAsync(); err != nil {
		panic(err)
	}

	go func() {
		for s.Stdout.Scan() {
			fmt.Println(s.Stdout.Text())
			resultBytes = append(resultBytes, s.Stdout.Bytes()...)
		}
	}()

	go func() {
		for s.Stderr.Scan() {
			errorBytes = append(errorBytes, s.Stderr.Bytes()...)
		}
	}()

	if err := s.Cmd.Wait(); err != nil {
		panic(err)
	}

	results, err := nmap.Parse(resultBytes)
	results.NmapErrors = strings.Split(string(errorBytes), "\n")
	if err != nil {
		panic(err)
	}

	temp, err := json.MarshalIndent(results, "", "  ")
	if err != nil {
		panic(err)
	}
	fmt.Printf("%s\n", temp)
}

@Ullaakut
Copy link
Owner Author

Ullaakut commented Sep 3, 2019

Hey @TheSecEng!

Sorry about the delay. This looks like a good implementation! Thank you so much!

It seems like a good first step to provide utility to people who might want to use it, but I'm curious how people will end up integrating it into their apps.

Either way, I'd love to see it in the form of a PR and to work on it together!

@TerminalFi
Copy link
Collaborator

Hey @TheSecEng!

Sorry about the delay. This looks like a good implementation! Thank you so much!

It seems like a good first step to provide utility to people who might want to use it, but I'm curious how people will end up integrating it into their apps.

Either way, I'd love to see it in the form of a PR and to work on it together!

I've actually got a use case right now that I will be using this for once its finished. So there is definitely reasons to get this running.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants