Skip to content

Commit

Permalink
Adds option to make timing configurable
Browse files Browse the repository at this point in the history
  • Loading branch information
robgonnella committed Dec 26, 2023
1 parent 1bf7788 commit 65136da
Show file tree
Hide file tree
Showing 10 changed files with 231 additions and 10 deletions.
38 changes: 35 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ sudo go-lanscan --no-progress --json

# run only arp scanning (skip syn scanning)
sudo go-lanscan --arp-only

# set timing - this is how fast packets are sent to hosts
# default is 100µs between packets
# the faster you send packets (shorter the timing), the less accurate the results will be
sudo go-lanscan --timing 1ms # set to 1 millisecond
sudo go-lanscan --timing 500µs # set to 500 microseconds
sudo go-lanscan --timing 500us # alternate symbol for microseconds
```

## Package Usage
Expand All @@ -84,6 +91,31 @@ First you must install the following dependencies

You can provide the following options to all scanners

- Provide specific timing duration

This option is used to set a specific time to wait between sending packets
to hosts. The default is 100µs. The shorter the timing, the faster packets
will be sent, and the less accurate your results will be

```go
timing := time.Microsecond * 200

fullScanner := scanner.NewFullScanner(
netInfo,
targets,
ports,
listenPort,
scanner.WithTiming(timing),
)

// or
fullScanner.SetTiming(timing)

// or
option := scanner.WithTiming(timing)
option(fullScanner)
```

- Provide channel for notifications when packet requests are sent to target

```go
Expand All @@ -100,11 +132,11 @@ You can provide the following options to all scanners
)

// or
option := scanner.WithRequestNotifications(requests)
option(requests)
synScanner.SetRequestNotifications(requests)

// or
synScanner.SetRequestNotifications(requests)
option := scanner.WithRequestNotifications(requests)
option(synScanner)
```

- Provide your own idle timeout. If no packets are received from our targets
Expand Down
110 changes: 110 additions & 0 deletions internal/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,98 @@
package cli

import (
"fmt"
"os"
"strings"
"time"

"github.com/jedib0t/go-pretty/table"
"github.com/spf13/cobra"

"github.com/robgonnella/go-lanscan/internal/core"
"github.com/robgonnella/go-lanscan/internal/logger"
"github.com/robgonnella/go-lanscan/internal/util"
"github.com/robgonnella/go-lanscan/pkg/network"
"github.com/robgonnella/go-lanscan/pkg/oui"
"github.com/robgonnella/go-lanscan/pkg/scanner"
)

func printConfiguration(
coreScanner scanner.Scanner,
targets []string,
cidr string,
ports []string,
ifaceName string,
listenPort uint16,
timing string,
vendorInfo,
printJson,
arpOnly,
progress bool,
outFile string,
) {
var configTable = table.NewWriter()

configTable.SetOutputMirror(os.Stdout)

configTable.AppendRow(table.Row{
"scannerType",
fmt.Sprintf("%T", coreScanner),
})

configTable.AppendRow(table.Row{
"targets",
targets,
})

configTable.AppendRow(table.Row{
"cidr",
cidr,
})

configTable.AppendRow(table.Row{
"ports",
ports,
})

configTable.AppendRow(table.Row{
"interface",
ifaceName,
})

configTable.AppendRow(table.Row{
"listenPort",
listenPort,
})

configTable.AppendRow(table.Row{
"timing",
timing,
})

configTable.AppendRow(table.Row{
"vendorInfo",
vendorInfo,
})

configTable.AppendRow(table.Row{
"json",
printJson,
})

configTable.AppendRow(table.Row{
"arpOnly",
arpOnly,
})

configTable.AppendRow(table.Row{
"progress",
progress,
})

go configTable.Render()
}

func Root(
runner core.Runner,
userNet network.Network,
Expand All @@ -23,6 +103,7 @@ func Root(
var printJson bool
var noProgress bool
var ports string
var timing string
var idleTimeoutSeconds int
var listenPort uint16
var ifaceName string
Expand All @@ -36,6 +117,8 @@ func Root(
Short: "Scan your LAN!",
Long: `CLI to scan your Local Area Network`,
RunE: func(cmd *cobra.Command, args []string) error {
log := logger.New()

portList := strings.Split(ports, ",")

if ifaceName != userNet.Interface().Name {
Expand Down Expand Up @@ -74,6 +157,15 @@ func Root(
coreScanner.IncludeVendorInfo(vendorRepo)
}

timingDuration, err := time.ParseDuration(timing)

if err != nil {
log.Error().Err(err).Msg("invalid timing value")
return err
}

coreScanner.SetTiming(timingDuration)

portLen := util.PortTotal(portList)

targetLen := util.TotalTargets(targets)
Expand All @@ -92,10 +184,28 @@ func Root(
outFile,
)

if !noProgress {
printConfiguration(
coreScanner,
targets,
userNet.Cidr(),
portList,
userNet.Interface().Name,
listenPort,
timing,
vendorInfo,
printJson,
arpOnly,
!noProgress,
outFile,
)
}

return runner.Run()
},
}

cmd.Flags().StringVar(&timing, "timing", "100µs", "set time between packet sends - the faster you send the less accurate the result will be")
cmd.Flags().BoolVar(&printJson, "json", false, "output json instead of table text")
cmd.Flags().BoolVar(&arpOnly, "arp-only", false, "only perform arp scanning (skip syn scanning)")
cmd.Flags().BoolVar(&noProgress, "no-progress", false, "disable all output except for final results")
Expand Down
39 changes: 37 additions & 2 deletions internal/cli/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func TestRootCommand(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

t.Run("initializes and runs full scanner", func(st *testing.T) {
t.Run("returns error if invalid timing duration is supplied", func(st *testing.T) {
mockNetwork := mock_network.NewMockNetwork(ctrl)

mockRunner := mock_core.NewMockRunner(ctrl)
Expand All @@ -40,6 +40,41 @@ func TestRootCommand(t *testing.T) {

mockNetwork.EXPECT().IPNet().AnyTimes().Return(mockIPNet)

cmd, err := cli.Root(mockRunner, mockNetwork, mockVendor)

assert.NoError(st, err)

cmd.SetArgs([]string{"--timing", "nope"})

err = cmd.Execute()

assert.Error(st, err)
})

t.Run("initializes and runs full scanner and includes vendor info", func(st *testing.T) {
mockNetwork := mock_network.NewMockNetwork(ctrl)

mockRunner := mock_core.NewMockRunner(ctrl)

mockVendor := mock_oui.NewMockVendorRepo(ctrl)

mockMAC, _ := net.ParseMAC("00:00:00:00:00:00")

mockCidr := "172.17.1.1/32"

_, mockIPNet, _ := net.ParseCIDR(mockCidr)

mockNetwork.EXPECT().Interface().AnyTimes().Return(&net.Interface{
Name: "test-interface",
HardwareAddr: mockMAC,
})

mockNetwork.EXPECT().Cidr().AnyTimes().Return(mockCidr)

mockNetwork.EXPECT().IPNet().AnyTimes().Return(mockIPNet)

mockVendor.EXPECT().UpdateVendors().Times(1)

mockRunner.EXPECT().Initialize(
gomock.Any(),
1,
Expand All @@ -56,7 +91,7 @@ func TestRootCommand(t *testing.T) {

assert.NoError(st, err)

cmd.SetArgs([]string{})
cmd.SetArgs([]string{"--vendor"})

err = cmd.Execute()

Expand Down
12 changes: 12 additions & 0 deletions mock/scanner/scanner.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 8 additions & 2 deletions pkg/scanner/arpscan.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type ArpScanner struct {
requestNotifier chan *Request
scanning bool
lastPacketSentAt time.Time
timing time.Duration
idleTimeout time.Duration
vendorRepo oui.VendorRepo
scanningMux *sync.RWMutex
Expand All @@ -51,7 +52,8 @@ func NewArpScanner(
cap: &defaultPacketCapture{},
networkInfo: networkInfo,
resultChan: make(chan *ScanResult),
idleTimeout: time.Second * 5,
timing: defaultTiming,
idleTimeout: defaultIdleTimeout,
scanning: false,
lastPacketSentAt: time.Time{},
scanningMux: &sync.RWMutex{},
Expand Down Expand Up @@ -108,7 +110,7 @@ func (s *ArpScanner) Scan() error {

go s.readPackets()

limiter := time.NewTicker(defaultAccuracy)
limiter := time.NewTicker(s.timing)
defer limiter.Stop()

if len(s.targets) == 0 {
Expand Down Expand Up @@ -142,6 +144,10 @@ func (s *ArpScanner) Stop() {
}
}

func (s *ArpScanner) SetTiming(d time.Duration) {
s.timing = d
}

func (s *ArpScanner) SetRequestNotifications(c chan *Request) {
s.requestNotifier = c
}
Expand Down
5 changes: 5 additions & 0 deletions pkg/scanner/fullscan.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,11 @@ func (s *FullScanner) Stop() {
s.debug.Info().Msg("all scanners stopped")
}

func (s *FullScanner) SetTiming(d time.Duration) {
s.arpScanner.SetTiming(d)
s.synScanner.SetTiming(d)
}

func (s *FullScanner) SetRequestNotifications(c chan *Request) {
s.arpScanner.SetRequestNotifications(c)
s.synScanner.SetRequestNotifications(c)
Expand Down
11 changes: 10 additions & 1 deletion pkg/scanner/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ import (
// How long to wait before sending next packet
// the faster you send packets the more packets
// will be missed when reading
const defaultAccuracy = time.Microsecond * 100
const defaultTiming = time.Microsecond * 100

// If no packets are received for this period of time, exit with timeout
const defaultIdleTimeout = time.Second * 5

type ScannerOption = func(s Scanner)

Expand All @@ -38,3 +41,9 @@ func WithPacketCapture(cap PacketCapture) ScannerOption {
s.SetPacketCapture(cap)
}
}

func WithTiming(duration time.Duration) ScannerOption {
return func(s Scanner) {
s.SetTiming(duration)
}
}
3 changes: 3 additions & 0 deletions pkg/scanner/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ func TestOptions(t *testing.T) {
scanner.NewArpScanner(
[]string{},
netInfo,
scanner.WithTiming(time.Millisecond),
scanner.WithIdleTimeout(time.Second*5),
scanner.WithPacketCapture(testPacketCapture),
scanner.WithRequestNotifications(requestNotifier),
Expand All @@ -41,6 +42,7 @@ func TestOptions(t *testing.T) {
[]string{},
[]string{},
54321,
scanner.WithTiming(time.Millisecond),
scanner.WithIdleTimeout(time.Second*5),
scanner.WithPacketCapture(testPacketCapture),
scanner.WithRequestNotifications(requestNotifier),
Expand All @@ -52,6 +54,7 @@ func TestOptions(t *testing.T) {
netInfo,
[]string{},
54321,
scanner.WithTiming(time.Millisecond),
scanner.WithIdleTimeout(time.Second*5),
scanner.WithPacketCapture(testPacketCapture),
scanner.WithRequestNotifications(requestNotifier),
Expand Down
Loading

0 comments on commit 65136da

Please sign in to comment.