Skip to content

Commit 65136da

Browse files
committed
Adds option to make timing configurable
1 parent 1bf7788 commit 65136da

File tree

10 files changed

+231
-10
lines changed

10 files changed

+231
-10
lines changed

README.md

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -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: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,98 @@
33
package cli
44

55
import (
6+
"fmt"
7+
"os"
68
"strings"
79
"time"
810

11+
"github.com/jedib0t/go-pretty/table"
912
"github.com/spf13/cobra"
1013

1114
"github.com/robgonnella/go-lanscan/internal/core"
15+
"github.com/robgonnella/go-lanscan/internal/logger"
1216
"github.com/robgonnella/go-lanscan/internal/util"
1317
"github.com/robgonnella/go-lanscan/pkg/network"
1418
"github.com/robgonnella/go-lanscan/pkg/oui"
1519
"github.com/robgonnella/go-lanscan/pkg/scanner"
1620
)
1721

22+
func printConfiguration(
23+
coreScanner scanner.Scanner,
24+
targets []string,
25+
cidr string,
26+
ports []string,
27+
ifaceName string,
28+
listenPort uint16,
29+
timing string,
30+
vendorInfo,
31+
printJson,
32+
arpOnly,
33+
progress bool,
34+
outFile string,
35+
) {
36+
var configTable = table.NewWriter()
37+
38+
configTable.SetOutputMirror(os.Stdout)
39+
40+
configTable.AppendRow(table.Row{
41+
"scannerType",
42+
fmt.Sprintf("%T", coreScanner),
43+
})
44+
45+
configTable.AppendRow(table.Row{
46+
"targets",
47+
targets,
48+
})
49+
50+
configTable.AppendRow(table.Row{
51+
"cidr",
52+
cidr,
53+
})
54+
55+
configTable.AppendRow(table.Row{
56+
"ports",
57+
ports,
58+
})
59+
60+
configTable.AppendRow(table.Row{
61+
"interface",
62+
ifaceName,
63+
})
64+
65+
configTable.AppendRow(table.Row{
66+
"listenPort",
67+
listenPort,
68+
})
69+
70+
configTable.AppendRow(table.Row{
71+
"timing",
72+
timing,
73+
})
74+
75+
configTable.AppendRow(table.Row{
76+
"vendorInfo",
77+
vendorInfo,
78+
})
79+
80+
configTable.AppendRow(table.Row{
81+
"json",
82+
printJson,
83+
})
84+
85+
configTable.AppendRow(table.Row{
86+
"arpOnly",
87+
arpOnly,
88+
})
89+
90+
configTable.AppendRow(table.Row{
91+
"progress",
92+
progress,
93+
})
94+
95+
go configTable.Render()
96+
}
97+
1898
func Root(
1999
runner core.Runner,
20100
userNet network.Network,
@@ -23,6 +103,7 @@ func Root(
23103
var printJson bool
24104
var noProgress bool
25105
var ports string
106+
var timing string
26107
var idleTimeoutSeconds int
27108
var listenPort uint16
28109
var ifaceName string
@@ -36,6 +117,8 @@ func Root(
36117
Short: "Scan your LAN!",
37118
Long: `CLI to scan your Local Area Network`,
38119
RunE: func(cmd *cobra.Command, args []string) error {
120+
log := logger.New()
121+
39122
portList := strings.Split(ports, ",")
40123

41124
if ifaceName != userNet.Interface().Name {
@@ -74,6 +157,15 @@ func Root(
74157
coreScanner.IncludeVendorInfo(vendorRepo)
75158
}
76159

160+
timingDuration, err := time.ParseDuration(timing)
161+
162+
if err != nil {
163+
log.Error().Err(err).Msg("invalid timing value")
164+
return err
165+
}
166+
167+
coreScanner.SetTiming(timingDuration)
168+
77169
portLen := util.PortTotal(portList)
78170

79171
targetLen := util.TotalTargets(targets)
@@ -92,10 +184,28 @@ func Root(
92184
outFile,
93185
)
94186

187+
if !noProgress {
188+
printConfiguration(
189+
coreScanner,
190+
targets,
191+
userNet.Cidr(),
192+
portList,
193+
userNet.Interface().Name,
194+
listenPort,
195+
timing,
196+
vendorInfo,
197+
printJson,
198+
arpOnly,
199+
!noProgress,
200+
outFile,
201+
)
202+
}
203+
95204
return runner.Run()
96205
},
97206
}
98207

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

internal/cli/root_test.go

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

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

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

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

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+
54+
t.Run("initializes and runs full scanner and includes vendor info", func(st *testing.T) {
55+
mockNetwork := mock_network.NewMockNetwork(ctrl)
56+
57+
mockRunner := mock_core.NewMockRunner(ctrl)
58+
59+
mockVendor := mock_oui.NewMockVendorRepo(ctrl)
60+
61+
mockMAC, _ := net.ParseMAC("00:00:00:00:00:00")
62+
63+
mockCidr := "172.17.1.1/32"
64+
65+
_, mockIPNet, _ := net.ParseCIDR(mockCidr)
66+
67+
mockNetwork.EXPECT().Interface().AnyTimes().Return(&net.Interface{
68+
Name: "test-interface",
69+
HardwareAddr: mockMAC,
70+
})
71+
72+
mockNetwork.EXPECT().Cidr().AnyTimes().Return(mockCidr)
73+
74+
mockNetwork.EXPECT().IPNet().AnyTimes().Return(mockIPNet)
75+
76+
mockVendor.EXPECT().UpdateVendors().Times(1)
77+
4378
mockRunner.EXPECT().Initialize(
4479
gomock.Any(),
4580
1,
@@ -56,7 +91,7 @@ func TestRootCommand(t *testing.T) {
5691

5792
assert.NoError(st, err)
5893

59-
cmd.SetArgs([]string{})
94+
cmd.SetArgs([]string{"--vendor"})
6095

6196
err = cmd.Execute()
6297

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),

0 commit comments

Comments
 (0)