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

Adds reverse dns lookup option #25

Merged
merged 1 commit into from
Mar 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# go-lanscan
![Coverage](https://img.shields.io/badge/Coverage-92.2%25-brightgreen)
![Coverage](https://img.shields.io/badge/Coverage-91.8%25-brightgreen)

A network cli and golang package that allows you to perform arp and syn
scanning on a local area network.
Expand Down Expand Up @@ -70,6 +70,9 @@ sudo go-lanscan --targets 192.22.22.1,192.168.1.1-192.168.1.50,192.56.42.1/24
# include vendor look-ups on mac addresses (scan will be a little slower)
sudo go-lanscan --vendor

# include reverse dns lookup for hostnames
sudo go-lanscan --hostnames

# update static database used for vendor lookups
# static file is located at ~/.config/go-lanscan/oui.txt
sudo go-lanscan update-vendors
Expand Down Expand Up @@ -215,5 +218,24 @@ queries against this file. The file is stored at `~/.config/go-lanscan/oui.txt`
option(arpScanner)
```

- Perform reverse dns lookup to find hostnames for found devices

```go
arpScanner := scanner.NewArpScanner(
targets,
netInfo,
arpResults,
arpDone,
scanner.WithHostnames(true)
)

// or
arpScanner.IncludeHostnames(true)

// or
option := scanner.WithHostnames(true)
option(arpScanner)
```

[golang]: https://go.dev/doc/install
[libpcap]: https://github.com/the-tcpdump-group/libpcap
1 change: 1 addition & 0 deletions examples/arp/arpscan.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func main() {
userNet,
scanner.WithIdleTimeout(time.Second*time.Duration(idleTimeout)),
scanner.WithVendorInfo(vendorRepo),
scanner.WithHostnames(true),
)

go func() {
Expand Down
13 changes: 13 additions & 0 deletions internal/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func printConfiguration(
listenPort uint16,
timing string,
vendorInfo,
hostNames,
printJSON,
arpOnly,
progress bool,
Expand Down Expand Up @@ -76,6 +77,11 @@ func printConfiguration(
vendorInfo,
})

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

configTable.AppendRow(table.Row{
"json",
printJSON,
Expand Down Expand Up @@ -114,6 +120,7 @@ func Root(
var ifaceName string
var targets []string
var vendorInfo bool
var hostNames bool
var arpOnly bool
var outFile string

Expand Down Expand Up @@ -160,6 +167,10 @@ func Root(
coreScanner.IncludeVendorInfo(vendorRepo)
}

if hostNames {
coreScanner.IncludeHostNames(true)
}

timingDuration, err := time.ParseDuration(timing)

if err != nil {
Expand Down Expand Up @@ -197,6 +208,7 @@ func Root(
listenPort,
timing,
vendorInfo,
hostNames,
printJSON,
arpOnly,
!progressDisabled,
Expand All @@ -219,6 +231,7 @@ func Root(
cmd.Flags().StringSliceVarP(&targets, "targets", "t", []string{userNet.Cidr()}, "set targets for scanning")
cmd.Flags().StringVar(&outFile, "out-file", "", "outputs final report to file")
cmd.Flags().BoolVar(&vendorInfo, "vendor", false, "include vendor info (takes a little longer)")
cmd.Flags().BoolVar(&hostNames, "hostnames", false, "perform reverse dns lookup for hostnames")

cmd.AddCommand(newVersion())
cmd.AddCommand(newUpdateVendors(vendorRepo))
Expand Down
11 changes: 8 additions & 3 deletions internal/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
type DeviceResult struct {
IP net.IP `json:"ip"`
MAC net.HardwareAddr `json:"mac"`
Hostname string `json:"hostname"`
Vendor string `json:"vendor"`
Status scanner.Status `json:"status"`
OpenPorts []scanner.Port `json:"openPorts"`
Expand All @@ -34,12 +35,14 @@ func (r *DeviceResult) Serializable() interface{} {
return struct {
IP string `json:"ip"`
MAC string `json:"mac"`
Hostname string `json:"hostname"`
Vendor string `json:"vendor"`
Status string `json:"status"`
OpenPorts []scanner.Port `json:"openPorts"`
}{
IP: r.IP.String(),
MAC: r.MAC.String(),
Hostname: r.Hostname,
Vendor: r.Vendor,
Status: string(r.Status),
OpenPorts: r.OpenPorts,
Expand Down Expand Up @@ -217,6 +220,7 @@ func (c *Core) processArpResult(result *scanner.ArpScanResult) {
IP: result.IP,
MAC: result.MAC,
Status: scanner.StatusOnline,
Hostname: result.Hostname,
Vendor: result.Vendor,
OpenPorts: []scanner.Port{},
})
Expand Down Expand Up @@ -277,10 +281,10 @@ func (c *Core) printArpResults() {

var arpTable = table.NewWriter()
arpTable.SetOutputMirror(os.Stdout)
arpTable.AppendHeader(table.Row{"IP", "MAC", "VENDOR"})
arpTable.AppendHeader(table.Row{"IP", "MAC", "HOSTNAME", "VENDOR"})

for _, t := range c.results.Devices {
arpTable.AppendRow(table.Row{t.IP.String(), t.MAC.String(), t.Vendor})
arpTable.AppendRow(table.Row{t.IP.String(), t.MAC.String(), t.Hostname, t.Vendor})
}

output := arpTable.Render()
Expand Down Expand Up @@ -319,7 +323,7 @@ func (c *Core) printSynResults() {

var synTable = table.NewWriter()
synTable.SetOutputMirror(os.Stdout)
synTable.AppendHeader(table.Row{"IP", "MAC", "VENDOR", "STATUS", "OPEN PORTS"})
synTable.AppendHeader(table.Row{"IP", "MAC", "HOSTNAME", "VENDOR", "STATUS", "OPEN PORTS"})

for _, r := range c.results.Devices {
openPorts := []string{}
Expand All @@ -331,6 +335,7 @@ func (c *Core) printSynResults() {
synTable.AppendRow(table.Row{
r.IP.String(),
r.MAC.String(),
r.Hostname,
r.Vendor,
r.Status,
openPorts,
Expand Down
9 changes: 5 additions & 4 deletions internal/core/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ func TestDeviceResult(t *testing.T) {
t.Run("is serializable", func(st *testing.T) {
mac, _ := net.ParseMAC("00:00:00:00:00:00")
result := &core.DeviceResult{
IP: net.ParseIP("127.0.0.1"),
MAC: mac,
Vendor: "unknown",
Status: scanner.StatusOnline,
IP: net.ParseIP("127.0.0.1"),
MAC: mac,
Hostname: "unknown",
Vendor: "unknown",
Status: scanner.StatusOnline,
OpenPorts: []scanner.Port{
{
ID: 22,
Expand Down
1 change: 1 addition & 0 deletions internal/mock/core/core.go

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

1 change: 1 addition & 0 deletions internal/mock/scripts/bump-version/version/version.go

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

1 change: 1 addition & 0 deletions mock/network/network.go

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

1 change: 1 addition & 0 deletions mock/oui/oui.go

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

13 changes: 13 additions & 0 deletions mock/scanner/scanner.go

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

28 changes: 21 additions & 7 deletions pkg/scanner/arpscan.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type ArpScanner struct {
timing time.Duration
idleTimeout time.Duration
vendorRepo oui.VendorRepo
hostNamesEnables bool
scanningMux *sync.RWMutex
packetSentAtMux *sync.RWMutex
debug logger.DebugLogger
Expand All @@ -44,6 +45,7 @@ func NewArpScanner(
options ...Option,
) *ArpScanner {
scanner := &ArpScanner{
cancel: make(chan struct{}),
targets: targets,
cap: &defaultPacketCapture{},
networkInfo: networkInfo,
Expand Down Expand Up @@ -106,7 +108,6 @@ func (s *ArpScanner) Scan() error {

s.scanningMux.Unlock()
s.handle = handle
s.cancel = make(chan struct{})

go s.readPackets()

Expand Down Expand Up @@ -138,9 +139,9 @@ func (s *ArpScanner) Scan() error {

// Stop stops the scanner
func (s *ArpScanner) Stop() {
if s.cancel != nil {
close(s.cancel)
}
go func() {
s.cancel <- struct{}{}
}()

if s.handle != nil {
s.handle.Close()
Expand All @@ -164,6 +165,11 @@ func (s *ArpScanner) SetIdleTimeout(duration time.Duration) {
s.idleTimeout = duration
}

// IncludeHostNames sets whether reverse dns look up is performed to find hostname
func (s *ArpScanner) IncludeHostNames(v bool) {
s.hostNamesEnables = v
}

// IncludeVendorInfo sets whether or not to include vendor info in the scan
func (s *ArpScanner) IncludeVendorInfo(repo oui.VendorRepo) {
s.vendorRepo = repo
Expand Down Expand Up @@ -316,9 +322,10 @@ func (s *ArpScanner) writePacketData(ip net.IP) error {

func (s *ArpScanner) processResult(ip net.IP, mac net.HardwareAddr) {
arpResult := &ArpScanResult{
IP: ip,
MAC: mac,
Vendor: "unknown",
IP: ip,
MAC: mac,
Hostname: "unknown",
Vendor: "unknown",
}

if s.vendorRepo != nil {
Expand All @@ -329,6 +336,13 @@ func (s *ArpScanner) processResult(ip net.IP, mac net.HardwareAddr) {
}
}

if s.hostNamesEnables {
addr, err := net.LookupAddr(ip.String())
if err == nil && len(addr) > 0 {
arpResult.Hostname = addr[0]
}
}

go func() {
s.resultChan <- &ScanResult{
Type: ARPResult,
Expand Down
15 changes: 10 additions & 5 deletions pkg/scanner/fullscan.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ func NewFullScanner(
)

scanner := &FullScanner{
cancel: make(chan struct{}),
netInfo: netInfo,
targets: targets,
listenPort: listenPort,
Expand Down Expand Up @@ -95,8 +96,6 @@ func (s *FullScanner) Scan() error {
s.scanning = true
s.scanningMux.Unlock()

s.cancel = make(chan struct{})

defer s.reset()

go func() {
Expand Down Expand Up @@ -138,9 +137,9 @@ func (s *FullScanner) Scan() error {

// Stop stops the scanner
func (s *FullScanner) Stop() {
if s.cancel != nil {
close(s.cancel)
}
go func() {
s.cancel <- struct{}{}
}()

if s.arpScanner != nil {
s.arpScanner.Stop()
Expand Down Expand Up @@ -171,6 +170,12 @@ func (s *FullScanner) SetIdleTimeout(d time.Duration) {
s.synScanner.SetIdleTimeout(d)
}

// IncludeHostNames sets whether reverse dns look up is performed to find hostname
func (s *FullScanner) IncludeHostNames(v bool) {
s.arpScanner.IncludeHostNames(v)
s.synScanner.IncludeHostNames(v)
}

// IncludeVendorInfo sets whether or not to include vendor info when scanning
func (s *FullScanner) IncludeVendorInfo(repo oui.VendorRepo) {
s.arpScanner.IncludeVendorInfo(repo)
Expand Down
7 changes: 7 additions & 0 deletions pkg/scanner/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ func WithVendorInfo(repo oui.VendorRepo) Option {
}
}

// WithHostnames sets whether or not to perform reverse dns lookup
func WithHostnames(v bool) Option {
return func(s Scanner) {
s.IncludeHostNames(v)
}
}

// WithPacketCapture sets the packet capture implementation for the scanner
func WithPacketCapture(cap PacketCapture) Option {
return func(s Scanner) {
Expand Down
3 changes: 3 additions & 0 deletions pkg/scanner/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ func TestOptions(t *testing.T) {
scanner.WithPacketCapture(testPacketCapture),
scanner.WithRequestNotifications(requestNotifier),
scanner.WithVendorInfo(vendorRepo),
scanner.WithHostnames(true),
)

scanner.NewFullScanner(
Expand All @@ -47,6 +48,7 @@ func TestOptions(t *testing.T) {
scanner.WithPacketCapture(testPacketCapture),
scanner.WithRequestNotifications(requestNotifier),
scanner.WithVendorInfo(vendorRepo),
scanner.WithHostnames(true),
)

scanner.NewSynScanner(
Expand All @@ -59,6 +61,7 @@ func TestOptions(t *testing.T) {
scanner.WithPacketCapture(testPacketCapture),
scanner.WithRequestNotifications(requestNotifier),
scanner.WithVendorInfo(vendorRepo),
scanner.WithHostnames(true),
)
})
}
Loading