Skip to content

Commit fcf23fd

Browse files
robgonnellaactions-user
authored andcommitted
Adds option to make timing configurable
1 parent 1bf7788 commit fcf23fd

File tree

10 files changed

+130
-9
lines changed

10 files changed

+130
-9
lines changed

README.md

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# go-lanscan
2-
![Coverage](https://img.shields.io/badge/Coverage-91.8%25-brightgreen)
2+
![Coverage](https://img.shields.io/badge/Coverage-91.4%25-brightgreen)
33

44
A network cli and golang package that allows you to perform arp and syn
55
scanning on a local area network.
@@ -62,6 +62,13 @@ sudo go-lanscan --no-progress --json
6262

6363
# run only arp scanning (skip syn scanning)
6464
sudo go-lanscan --arp-only
65+
66+
# set timing - this is how fast packets are sent to hosts
67+
# default is 100µs between packets
68+
# the faster you send packets (shorter the timing), the less accurate the results will be
69+
sudo go-lanscan --timing 1ms # set to 1 millisecond
70+
sudo go-lanscan --timing 500µs # set to 500 microseconds
71+
sudo go-lanscan --timing 500us # alternate symbol for microseconds
6572
```
6673

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

8592
You can provide the following options to all scanners
8693

94+
- Provide specific timing duration
95+
96+
This option is used to set a specific time to wait between sending packets
97+
to hosts. The default is 100µs. The shorter the timing, the faster packets
98+
will be sent, and the less accurate your results will be
99+
100+
```go
101+
timing := time.Microsecond * 200
102+
103+
fullScanner := scanner.NewFullScanner(
104+
netInfo,
105+
targets,
106+
ports,
107+
listenPort,
108+
scanner.WithTiming(timing),
109+
)
110+
111+
// or
112+
fullScanner.SetTiming(timing)
113+
114+
// or
115+
option := scanner.WithTiming(timing)
116+
option(fullScanner)
117+
```
118+
87119
- Provide channel for notifications when packet requests are sent to target
88120

89121
```go
@@ -100,11 +132,11 @@ You can provide the following options to all scanners
100132
)
101133

102134
// or
103-
option := scanner.WithRequestNotifications(requests)
104-
option(requests)
135+
synScanner.SetRequestNotifications(requests)
105136

106137
// or
107-
synScanner.SetRequestNotifications(requests)
138+
option := scanner.WithRequestNotifications(requests)
139+
option(synScanner)
108140
```
109141

110142
- Provide your own idle timeout. If no packets are received from our targets

internal/cli/root.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/spf13/cobra"
1010

1111
"github.com/robgonnella/go-lanscan/internal/core"
12+
"github.com/robgonnella/go-lanscan/internal/logger"
1213
"github.com/robgonnella/go-lanscan/internal/util"
1314
"github.com/robgonnella/go-lanscan/pkg/network"
1415
"github.com/robgonnella/go-lanscan/pkg/oui"
@@ -23,6 +24,7 @@ func Root(
2324
var printJson bool
2425
var noProgress bool
2526
var ports string
27+
var timing string
2628
var idleTimeoutSeconds int
2729
var listenPort uint16
2830
var ifaceName string
@@ -36,6 +38,8 @@ func Root(
3638
Short: "Scan your LAN!",
3739
Long: `CLI to scan your Local Area Network`,
3840
RunE: func(cmd *cobra.Command, args []string) error {
41+
log := logger.New()
42+
3943
portList := strings.Split(ports, ",")
4044

4145
if ifaceName != userNet.Interface().Name {
@@ -74,6 +78,15 @@ func Root(
7478
coreScanner.IncludeVendorInfo(vendorRepo)
7579
}
7680

81+
timingDuration, err := time.ParseDuration(timing)
82+
83+
if err != nil {
84+
log.Error().Err(err).Msg("invalid timing value")
85+
return err
86+
}
87+
88+
coreScanner.SetTiming(timingDuration)
89+
7790
portLen := util.PortTotal(portList)
7891

7992
targetLen := util.TotalTargets(targets)
@@ -96,6 +109,7 @@ func Root(
96109
},
97110
}
98111

112+
cmd.Flags().StringVar(&timing, "timing", "100µs", "set time between packet sends - the faster you send the less accurate the result will be")
99113
cmd.Flags().BoolVar(&printJson, "json", false, "output json instead of table text")
100114
cmd.Flags().BoolVar(&arpOnly, "arp-only", false, "only perform arp scanning (skip syn scanning)")
101115
cmd.Flags().BoolVar(&noProgress, "no-progress", false, "disable all output except for final results")

internal/cli/root_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,39 @@ func TestRootCommand(t *testing.T) {
1818
ctrl := gomock.NewController(t)
1919
defer ctrl.Finish()
2020

21+
t.Run("returns error if invalid timing duration is supplied", func(st *testing.T) {
22+
mockNetwork := mock_network.NewMockNetwork(ctrl)
23+
24+
mockRunner := mock_core.NewMockRunner(ctrl)
25+
26+
mockVendor := mock_oui.NewMockVendorRepo(ctrl)
27+
28+
mockMAC, _ := net.ParseMAC("00:00:00:00:00:00")
29+
30+
mockCidr := "172.17.1.1/32"
31+
32+
_, mockIPNet, _ := net.ParseCIDR(mockCidr)
33+
34+
mockNetwork.EXPECT().Interface().AnyTimes().Return(&net.Interface{
35+
Name: "test-interface",
36+
HardwareAddr: mockMAC,
37+
})
38+
39+
mockNetwork.EXPECT().Cidr().AnyTimes().Return(mockCidr)
40+
41+
mockNetwork.EXPECT().IPNet().AnyTimes().Return(mockIPNet)
42+
43+
cmd, err := cli.Root(mockRunner, mockNetwork, mockVendor)
44+
45+
assert.NoError(st, err)
46+
47+
cmd.SetArgs([]string{"--timing", "nope"})
48+
49+
err = cmd.Execute()
50+
51+
assert.Error(st, err)
52+
})
53+
2154
t.Run("initializes and runs full scanner", func(st *testing.T) {
2255
mockNetwork := mock_network.NewMockNetwork(ctrl)
2356

mock/scanner/scanner.go

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/scanner/arpscan.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ type ArpScanner struct {
3030
requestNotifier chan *Request
3131
scanning bool
3232
lastPacketSentAt time.Time
33+
timing time.Duration
3334
idleTimeout time.Duration
3435
vendorRepo oui.VendorRepo
3536
scanningMux *sync.RWMutex
@@ -51,7 +52,8 @@ func NewArpScanner(
5152
cap: &defaultPacketCapture{},
5253
networkInfo: networkInfo,
5354
resultChan: make(chan *ScanResult),
54-
idleTimeout: time.Second * 5,
55+
timing: defaultTiming,
56+
idleTimeout: defaultIdleTimeout,
5557
scanning: false,
5658
lastPacketSentAt: time.Time{},
5759
scanningMux: &sync.RWMutex{},
@@ -108,7 +110,7 @@ func (s *ArpScanner) Scan() error {
108110

109111
go s.readPackets()
110112

111-
limiter := time.NewTicker(defaultAccuracy)
113+
limiter := time.NewTicker(s.timing)
112114
defer limiter.Stop()
113115

114116
if len(s.targets) == 0 {
@@ -142,6 +144,10 @@ func (s *ArpScanner) Stop() {
142144
}
143145
}
144146

147+
func (s *ArpScanner) SetTiming(d time.Duration) {
148+
s.timing = d
149+
}
150+
145151
func (s *ArpScanner) SetRequestNotifications(c chan *Request) {
146152
s.requestNotifier = c
147153
}

pkg/scanner/fullscan.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,11 @@ func (s *FullScanner) Stop() {
148148
s.debug.Info().Msg("all scanners stopped")
149149
}
150150

151+
func (s *FullScanner) SetTiming(d time.Duration) {
152+
s.arpScanner.SetTiming(d)
153+
s.synScanner.SetTiming(d)
154+
}
155+
151156
func (s *FullScanner) SetRequestNotifications(c chan *Request) {
152157
s.arpScanner.SetRequestNotifications(c)
153158
s.synScanner.SetRequestNotifications(c)

pkg/scanner/options.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ import (
1111
// How long to wait before sending next packet
1212
// the faster you send packets the more packets
1313
// will be missed when reading
14-
const defaultAccuracy = time.Microsecond * 100
14+
const defaultTiming = time.Microsecond * 100
15+
16+
// If no packets are received for this period of time, exit with timeout
17+
const defaultIdleTimeout = time.Second * 5
1518

1619
type ScannerOption = func(s Scanner)
1720

@@ -38,3 +41,9 @@ func WithPacketCapture(cap PacketCapture) ScannerOption {
3841
s.SetPacketCapture(cap)
3942
}
4043
}
44+
45+
func WithTiming(duration time.Duration) ScannerOption {
46+
return func(s Scanner) {
47+
s.SetTiming(duration)
48+
}
49+
}

pkg/scanner/options_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ func TestOptions(t *testing.T) {
3030
scanner.NewArpScanner(
3131
[]string{},
3232
netInfo,
33+
scanner.WithTiming(time.Millisecond),
3334
scanner.WithIdleTimeout(time.Second*5),
3435
scanner.WithPacketCapture(testPacketCapture),
3536
scanner.WithRequestNotifications(requestNotifier),
@@ -41,6 +42,7 @@ func TestOptions(t *testing.T) {
4142
[]string{},
4243
[]string{},
4344
54321,
45+
scanner.WithTiming(time.Millisecond),
4446
scanner.WithIdleTimeout(time.Second*5),
4547
scanner.WithPacketCapture(testPacketCapture),
4648
scanner.WithRequestNotifications(requestNotifier),
@@ -52,6 +54,7 @@ func TestOptions(t *testing.T) {
5254
netInfo,
5355
[]string{},
5456
54321,
57+
scanner.WithTiming(time.Millisecond),
5558
scanner.WithIdleTimeout(time.Second*5),
5659
scanner.WithPacketCapture(testPacketCapture),
5760
scanner.WithRequestNotifications(requestNotifier),

pkg/scanner/synscan.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ type SynScanner struct {
3737
requestNotifier chan *Request
3838
scanning bool
3939
lastPacketSentAt time.Time
40+
timing time.Duration
4041
idleTimeout time.Duration
4142
scanningMux *sync.RWMutex
4243
packetSentAtMux *sync.RWMutex
@@ -62,7 +63,8 @@ func NewSynScanner(
6263
ports: ports,
6364
listenPort: listenPort,
6465
resultChan: make(chan *ScanResult),
65-
idleTimeout: time.Second * 5,
66+
timing: defaultTiming,
67+
idleTimeout: defaultIdleTimeout,
6668
scanning: false,
6769
lastPacketSentAt: time.Time{},
6870
scanningMux: &sync.RWMutex{},
@@ -135,7 +137,7 @@ func (s *SynScanner) Scan() error {
135137

136138
go s.readPackets()
137139

138-
limiter := time.NewTicker(defaultAccuracy)
140+
limiter := time.NewTicker(s.timing)
139141
defer limiter.Stop()
140142

141143
for _, target := range s.targets {
@@ -170,6 +172,10 @@ func (s *SynScanner) Stop() {
170172
}
171173
}
172174

175+
func (s *SynScanner) SetTiming(d time.Duration) {
176+
s.timing = d
177+
}
178+
173179
func (s *SynScanner) SetRequestNotifications(c chan *Request) {
174180
s.requestNotifier = c
175181
}

pkg/scanner/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ type Scanner interface {
3030
Scan() error
3131
Stop()
3232
Results() chan *ScanResult
33+
SetTiming(d time.Duration)
3334
SetRequestNotifications(c chan *Request)
3435
SetIdleTimeout(d time.Duration)
3536
IncludeVendorInfo(repo oui.VendorRepo)

0 commit comments

Comments
 (0)