forked from Ullaakut/cameradar
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdiscover.go
164 lines (140 loc) · 4.42 KB
/
discover.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package cmrdr
import (
"bufio"
"encoding/xml"
"fmt"
"io/ioutil"
"os/exec"
"strings"
"github.com/pkg/errors"
v "gopkg.in/go-playground/validator.v9"
)
// These constants detail the different level of nmap speed presets
// that determine the timeout values and wether or not nmap makes use of parallelism
const (
// PARANOIAC NO PARALLELISM | 5min timeout | 100ms to 10s round-trip time timeout | 5mn scan delay
PARANOIAC = 0
// SNEAKY NO PARALLELISM | 15sec timeout | 100ms to 10s round-trip time timeout | 15s scan delay
SNEAKY = 1
// POLITE NO PARALLELISM | 1sec timeout | 100ms to 10s round-trip time timeout | 400ms scan delay
POLITE = 2
// NORMAL PARALLELISM | 1sec timeout | 100ms to 10s round-trip time timeout | 0s scan delay
NORMAL = 3
// AGGRESSIVE PARALLELISM | 500ms timeout | 100ms to 1250ms round-trip time timeout | 0s scan delay
AGGRESSIVE = 4
// INSANE PARALLELISM | 250ms timeout | 50ms to 300ms round-trip time timeout | 0s scan delay
INSANE = 5
)
// Allows unit tests to override the exec function to avoid launching a real command
// during the tests. The NmapRun method will soon be refactored with an adaptor in order
// to make it possible to mock all external calls.
var execCommand = exec.Command
// NmapRun runs nmap on the specified targets's specified ports, using the given nmap speed.
func NmapRun(targets, ports, resultFilePath string, nmapSpeed int, enableLogs bool) error {
if nmapSpeed < PARANOIAC || nmapSpeed > INSANE {
return fmt.Errorf("invalid nmap speed value '%d'. Should be between '%d' and '%d'", nmapSpeed, PARANOIAC, INSANE)
}
cmdArgs := fmt.Sprintf(
"-T%d -A -p %s -oX %s %s",
nmapSpeed,
ports,
resultFilePath,
targets)
if enableLogs {
fmt.Printf("command: nmap %s\n", cmdArgs)
}
args := strings.Split(cmdArgs, " ")
// Prepare nmap command
cmd := execCommand("nmap", args...)
// Pipe stdout to be able to write the logs in realtime
stdout, err := cmd.StdoutPipe()
if err != nil {
return errors.Wrap(err, "couldn't get stdout pipe")
}
// Execute the nmap command
if err := cmd.Start(); err != nil {
return errors.Wrap(err, "coudln't run nmap command")
}
// Scan the pipe until an end of file or an error occurs
in := bufio.NewScanner(stdout)
for in.Scan() {
if enableLogs {
fmt.Println(in.Text())
}
}
if err := in.Err(); err != nil {
if enableLogs {
fmt.Printf("error: %s\n", err)
}
}
return nil
}
// NmapParseResults returns a slice of streams from an NMap XML result file.
// To generate one yourself, use the -X option when running NMap.
func NmapParseResults(nmapResultFilePath string) ([]Stream, error) {
var streams []Stream
// Open & Read XML file
content, err := ioutil.ReadFile(nmapResultFilePath)
if err != nil {
return streams, errors.Wrap(err, "could not read nmap result file at "+nmapResultFilePath+":")
}
// Unmarshal content of XML file into data structure
result := &nmapResult{}
err = xml.Unmarshal(content, &result)
if err != nil {
return streams, err
}
// Iterate on hosts to try to find hosts with ports that
// - serve RTSP
// - are open
validate := v.New()
for _, host := range result.Hosts {
if host.Ports.Ports == nil {
continue
}
for _, port := range host.Ports.Ports {
err = validate.Struct(port)
if err != nil {
continue
}
for _, addr := range host.Addresses {
err = validate.Struct(addr)
if err != nil {
continue
}
streams = append(streams, Stream{
Device: port.Service.Product,
Address: addr.Addr,
Port: port.PortID,
})
}
}
}
return streams, nil
}
// Discover scans the target networks and tries to find RTSP streams within them.
//
// targets can be:
//
// - a subnet (e.g.: 172.16.100.0/24)
// - an IP (e.g.: 172.16.100.10)
// - a hostname (e.g.: localhost)
// - a range of IPs (e.g.: 172.16.100.10-20)
//
// ports can be:
//
// - one or multiple ports and port ranges separated by commas (e.g.: 554,8554-8560,18554-28554)
func Discover(targets, ports, nmapResultPath string, speed int, log bool) ([]Stream, error) {
var streams []Stream
// Run nmap command to discover open ports on the specified targets & ports
err := NmapRun(targets, ports, nmapResultPath, speed, log)
if err != nil {
return streams, err
}
// Get found streams from nmap results
streams, err = NmapParseResults(nmapResultPath)
if err != nil {
return streams, err
}
return streams, nil
}