diff --git a/attack.go b/attack.go index aba41d2..9a00a5b 100644 --- a/attack.go +++ b/attack.go @@ -14,120 +14,110 @@ func doNotWrite([]uint8, interface{}) bool { return true } -func routeAttack(camera Stream, route string, timeout time.Duration, enableLogs bool) bool { - easy := curl.EasyInit() - defer easy.Cleanup() +func routeAttack(c Curler, camera Stream, route string, timeout time.Duration, enableLogs bool) bool { + attackURL := fmt.Sprintf( + "rtsp://%s:%s@%s:%d/%s", + camera.Username, + camera.Password, + camera.Address, + camera.Port, + route, + ) - if easy != nil { - attackURL := fmt.Sprintf( - "rtsp://%s:%s@%s:%d/%s", - camera.Username, - camera.Password, - camera.Address, - camera.Port, - route, - ) + if enableLogs { + // Debug logs when logs are enabled + c.Setopt(curl.OPT_VERBOSE, 1) + } else { + // Do not write sdp in stdout + c.Setopt(curl.OPT_WRITEFUNCTION, doNotWrite) + } - if enableLogs { - // Debug logs when logs are enabled - easy.Setopt(curl.OPT_VERBOSE, 1) - } else { - // Do not write sdp in stdout - easy.Setopt(curl.OPT_WRITEFUNCTION, doNotWrite) - } + // Do not send a body in the describe request + c.Setopt(curl.OPT_NOBODY, 1) + // Send a request to the URL of the camera we want to attack + c.Setopt(curl.OPT_URL, attackURL) + // Set the RTSP STREAM URI as the camera URL + c.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL) + // 2 is CURL_RTSPREQ_DESCRIBE + c.Setopt(curl.OPT_RTSP_REQUEST, 2) + // Set custom timeout + c.Setopt(curl.OPT_TIMEOUT_MS, int(timeout/time.Millisecond)) - // Do not send a body in the describe request - easy.Setopt(curl.OPT_NOBODY, 1) - // Send a request to the URL of the camera we want to attack - easy.Setopt(curl.OPT_URL, attackURL) - // Set the RTSP STREAM URI as the camera URL - easy.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL) - // 2 is CURL_RTSPREQ_DESCRIBE - easy.Setopt(curl.OPT_RTSP_REQUEST, 2) - // Set custom timeout - easy.Setopt(curl.OPT_TIMEOUT_MS, int(timeout/time.Millisecond)) + // Perform the request + err := c.Perform() + if err != nil { + fmt.Printf("\nERROR: curl timeout on camera '%s' reached after %s.\nconsider increasing the timeout (-T, --timeout parameter) to at least 5000ms if scanning an unstable network.\n", camera.Address, timeout.String()) + return false + } - // Perform the request - err := easy.Perform() - if err != nil { - fmt.Printf("\nERROR: curl timeout on camera '%s' reached after %s.\nconsider increasing the timeout (-T, --timeout parameter) to at least 5000ms if scanning an unstable network.\n", camera.Address, timeout.String()) - return false - } + // Get return code for the request + rc, err := c.Getinfo(curl.INFO_RESPONSE_CODE) + if err != nil { + return false + } - // Get return code for the request - rc, err := easy.Getinfo(curl.INFO_RESPONSE_CODE) - if err != nil { - return false - } - - // If it's a 401 or 403, it means that the credentials are wrong but the route might be okay - // If it's a 200, the camera is accessed successfully - if rc == 200 || rc == 401 || rc == 403 { - return true - } + // If it's a 401 or 403, it means that the credentials are wrong but the route might be okay + // If it's a 200, the camera is accessed successfully + if rc == 200 || rc == 401 || rc == 403 { + return true } return false } -func credAttack(camera Stream, username string, password string, timeout time.Duration, enableLogs bool) bool { - easy := curl.EasyInit() - defer easy.Cleanup() +func credAttack(c Curler, camera Stream, username string, password string, timeout time.Duration, enableLogs bool) bool { + attackURL := fmt.Sprintf( + "rtsp://%s:%s@%s:%d/%s", + username, + password, + camera.Address, + camera.Port, + camera.Route, + ) - if easy != nil { - attackURL := fmt.Sprintf( - "rtsp://%s:%s@%s:%d/%s", - username, - password, - camera.Address, - camera.Port, - camera.Route, - ) + if enableLogs { + // Debug logs when logs are enabled + c.Setopt(curl.OPT_VERBOSE, 1) + } else { + // Do not write sdp in stdout + c.Setopt(curl.OPT_WRITEFUNCTION, doNotWrite) + } - if enableLogs { - // Debug logs when logs are enabled - easy.Setopt(curl.OPT_VERBOSE, 1) - } else { - // Do not write sdp in stdout - easy.Setopt(curl.OPT_WRITEFUNCTION, doNotWrite) - } + // Do not send a body in the describe request + c.Setopt(curl.OPT_NOBODY, 1) + // Send a request to the URL of the camera we want to attack + c.Setopt(curl.OPT_URL, attackURL) + // Set the RTSP STREAM URI as the camera URL + c.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL) + // 2 is CURL_RTSPREQ_DESCRIBE + c.Setopt(curl.OPT_RTSP_REQUEST, 2) + // Set custom timeout + c.Setopt(curl.OPT_TIMEOUT_MS, int(timeout/time.Millisecond)) - // Do not send a body in the describe request - easy.Setopt(curl.OPT_NOBODY, 1) - // Send a request to the URL of the camera we want to attack - easy.Setopt(curl.OPT_URL, attackURL) - // Set the RTSP STREAM URI as the camera URL - easy.Setopt(curl.OPT_RTSP_STREAM_URI, attackURL) - // 2 is CURL_RTSPREQ_DESCRIBE - easy.Setopt(curl.OPT_RTSP_REQUEST, 2) - // Set custom timeout - easy.Setopt(curl.OPT_TIMEOUT_MS, int(timeout/time.Millisecond)) + // Perform the request + err := c.Perform() + if err != nil { + fmt.Printf("\nERROR: curl timeout on camera '%s' reached after %s.\nconsider increasing the timeout (-T, --timeout parameter) to at least 5000ms if scanning an unstable network.\n", camera.Address, timeout.String()) + return false + } - // Perform the request - err := easy.Perform() - if err != nil { - fmt.Printf("\nERROR: curl timeout on camera '%s' reached after %s.\nconsider increasing the timeout (-T, --timeout parameter) to at least 5000ms if scanning an unstable network.\n", camera.Address, timeout.String()) - return false - } + // Get return code for the request + rc, err := c.Getinfo(curl.INFO_RESPONSE_CODE) + if err != nil { + return false + } - // Get return code for the request - rc, err := easy.Getinfo(curl.INFO_RESPONSE_CODE) - if err != nil { - return false - } - - // If it's a 404, it means that the route is incorrect but the credentials might be okay - // If it's a 200, the camera is accessed successfully - if rc == 200 || rc == 404 { - return true - } + // If it's a 404, it means that the route is incorrect but the credentials might be okay + // If it's a 200, the camera is accessed successfully + if rc == 200 || rc == 404 { + return true } return false } -func attackCameraCredentials(target Stream, credentials Credentials, resultsChan chan<- Stream, timeout time.Duration, log bool) { +func attackCameraCredentials(c Curler, target Stream, credentials Credentials, resultsChan chan<- Stream, timeout time.Duration, log bool) { for _, username := range credentials.Usernames { for _, password := range credentials.Passwords { - ok := credAttack(target, username, password, timeout, log) + ok := credAttack(c, target, username, password, timeout, log) if ok { target.CredentialsFound = true target.Username = username @@ -141,9 +131,9 @@ func attackCameraCredentials(target Stream, credentials Credentials, resultsChan resultsChan <- target } -func attackCameraRoute(target Stream, routes Routes, resultsChan chan<- Stream, timeout time.Duration, log bool) { +func attackCameraRoute(c Curler, target Stream, routes Routes, resultsChan chan<- Stream, timeout time.Duration, log bool) { for _, route := range routes { - ok := routeAttack(target, route, timeout, log) + ok := routeAttack(c, target, route, timeout, log) if ok { target.RouteFound = true target.Route = route @@ -157,12 +147,7 @@ func attackCameraRoute(target Stream, routes Routes, resultsChan chan<- Stream, // AttackCredentials attempts to guess the provided targets' credentials using the given // dictionary or the default dictionary if none was provided by the user. -func AttackCredentials(targets []Stream, credentials Credentials, timeout time.Duration, log bool) ([]Stream, error) { - err := curl.GlobalInit(curl.GLOBAL_ALL) - if err != nil { - return targets, errors.Wrap(err, "could not initialize curl") - } - +func AttackCredentials(c Curler, targets []Stream, credentials Credentials, timeout time.Duration, log bool) ([]Stream, error) { attacks := make(chan Stream) defer close(attacks) @@ -170,10 +155,10 @@ func AttackCredentials(targets []Stream, credentials Credentials, timeout time.D for _, target := range targets { err := validate.Struct(target) if err != nil { - return targets, errors.Wrap(err, "invalid streams") + return targets, errors.Wrap(err, "invalid targets") } - go attackCameraCredentials(target, credentials, attacks, timeout, log) + go attackCameraCredentials(c, target, credentials, attacks, timeout, log) } attackResults := []Stream{} @@ -197,12 +182,7 @@ func AttackCredentials(targets []Stream, credentials Credentials, timeout time.D // AttackRoute attempts to guess the provided targets' streaming routes using the given // dictionary or the default dictionary if none was provided by the user. -func AttackRoute(targets []Stream, routes Routes, timeout time.Duration, log bool) ([]Stream, error) { - err := curl.GlobalInit(curl.GLOBAL_ALL) - if err != nil { - return targets, errors.Wrap(err, "could not initialize curl") - } - +func AttackRoute(c Curler, targets []Stream, routes Routes, timeout time.Duration, log bool) ([]Stream, error) { attacks := make(chan Stream) defer close(attacks) @@ -210,10 +190,10 @@ func AttackRoute(targets []Stream, routes Routes, timeout time.Duration, log boo for _, target := range targets { err := validate.Struct(target) if err != nil { - return targets, errors.Wrap(err, "invalid streams") + return targets, errors.Wrap(err, "invalid targets") } - go attackCameraRoute(target, routes, attacks, timeout, log) + go attackCameraRoute(c, target, routes, attacks, timeout, log) } attackResults := []Stream{} diff --git a/attack_test.go b/attack_test.go index 60ef04a..9b4e0f6 100644 --- a/attack_test.go +++ b/attack_test.go @@ -1,16 +1,35 @@ package cmrdr import ( + "errors" "fmt" "os" "testing" "time" + curl "github.com/andelf/go-curl" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) -// Again, since these tests use the curl library, I don't want to spend ages trying to mock -// the lib right now. +type CurlerMock struct { + mock.Mock +} + +func (m *CurlerMock) Setopt(opt int, param interface{}) error { + args := m.Called(opt, param) + return args.Error(0) +} + +func (m *CurlerMock) Perform() error { + args := m.Called() + return args.Error(0) +} + +func (m *CurlerMock) Getinfo(info curl.CurlInfo) (interface{}, error) { + args := m.Called(info) + return args.Int(0), args.Error(1) +} func TestAttackCredentials(t *testing.T) { validStream1 := Stream{ @@ -25,58 +44,135 @@ func TestAttackCredentials(t *testing.T) { Port: 1337, } + invalidStream := Stream{ + Device: "InvalidDevice", + } + fakeTargets := []Stream{validStream1, validStream2} + invalidTargets := []Stream{invalidStream} fakeCredentials := Credentials{ Usernames: []string{"admin", "root"}, Passwords: []string{"12345", "root"}, } - vectors := []struct { + testCases := []struct { targets []Stream credentials Credentials timeout time.Duration log bool + status int + + performErr error + getInfoErr error + invalidTargets bool + expectedStreams []Stream expectedErrMsg string }{ - // Valid baseline + // Credentials found + { + targets: fakeTargets, + credentials: fakeCredentials, + timeout: 1 * time.Millisecond, + + status: 404, + + expectedStreams: fakeTargets, + }, + // Camera accessed + { + targets: fakeTargets, + credentials: fakeCredentials, + timeout: 1 * time.Millisecond, + + status: 200, + + expectedStreams: fakeTargets, + }, + // Invalid targets + { + targets: invalidTargets, + credentials: fakeCredentials, + timeout: 1 * time.Millisecond, + + invalidTargets: true, + + expectedErrMsg: "invalid targets", + expectedStreams: invalidTargets, + }, + // curl perform fails + { + targets: fakeTargets, + credentials: fakeCredentials, + timeout: 1 * time.Millisecond, + + performErr: errors.New("dummy error"), + + expectedStreams: fakeTargets, + expectedErrMsg: "no credentials found", + }, + // curl getinfo fails + { + targets: fakeTargets, + credentials: fakeCredentials, + timeout: 1 * time.Millisecond, + + getInfoErr: errors.New("dummy error"), + + expectedStreams: fakeTargets, + expectedErrMsg: "no credentials found", + }, + // Credentials not found { targets: fakeTargets, credentials: fakeCredentials, timeout: 1 * time.Millisecond, log: true, + status: 403, + expectedStreams: fakeTargets, expectedErrMsg: "no credentials found", }, - // Valid baseline without logs + // Logging disabled { targets: fakeTargets, credentials: fakeCredentials, timeout: 1 * time.Millisecond, log: false, + status: 403, + expectedStreams: fakeTargets, expectedErrMsg: "no credentials found", }, - // TODO: Refacto and make tests with all possible error cases } - for i, vector := range vectors { - results, err := AttackCredentials(vector.targets, vector.credentials, vector.timeout, vector.log) + for i, test := range testCases { + curlerMock := &CurlerMock{} - if len(vector.expectedErrMsg) > 0 { + if !test.invalidTargets { + curlerMock.On("Setopt", mock.Anything, mock.Anything).Return(nil) + curlerMock.On("Perform").Return(test.performErr) + if test.performErr == nil { + curlerMock.On("Getinfo", mock.Anything).Return(test.status, test.getInfoErr) + } + } + + results, err := AttackCredentials(curlerMock, test.targets, test.credentials, test.timeout, test.log) + + if len(test.expectedErrMsg) > 0 { if err == nil { - fmt.Printf("unexpected success in AttackCredentials test, iteration %d. expected error: %s\n", i, vector.expectedErrMsg) + fmt.Printf("unexpected success in AttackCredentials test, iteration %d. expected error: %s\n", i, test.expectedErrMsg) os.Exit(1) } - assert.Contains(t, err.Error(), vector.expectedErrMsg, "wrong error message") + assert.Contains(t, err.Error(), test.expectedErrMsg, "wrong error message") } else { if err != nil { fmt.Printf("unexpected error in AttackCredentials test, iteration %d: %v\n", i, err) os.Exit(1) } - for _, stream := range vector.expectedStreams { + for _, stream := range test.expectedStreams { foundStream := false for _, result := range results { if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port { @@ -86,8 +182,8 @@ func TestAttackCredentials(t *testing.T) { assert.Equal(t, true, foundStream, "wrong streams parsed") } } - assert.Equal(t, len(vector.expectedStreams), len(results), "wrong streams parsed") - + assert.Equal(t, len(test.expectedStreams), len(results), "wrong streams parsed") + curlerMock.AssertExpectations(t) } } @@ -104,19 +200,92 @@ func TestAttackRoute(t *testing.T) { Port: 1337, } + invalidStream := Stream{ + Device: "InvalidDevice", + } + fakeTargets := []Stream{validStream1, validStream2} fakeRoutes := Routes{"live.sdp", "media.amp"} + invalidTargets := []Stream{invalidStream} - vectors := []struct { + testCases := []struct { targets []Stream routes Routes timeout time.Duration log bool + status int + + performErr error + getInfoErr error + invalidTargets bool + expectedStreams []Stream expectedErrMsg string }{ - // Valid baseline + // Route found + { + targets: fakeTargets, + routes: fakeRoutes, + timeout: 1 * time.Millisecond, + + status: 403, + + expectedStreams: fakeTargets, + }, + // Route found + { + targets: fakeTargets, + routes: fakeRoutes, + timeout: 1 * time.Millisecond, + + status: 401, + + expectedStreams: fakeTargets, + }, + // Camera accessed + { + targets: fakeTargets, + routes: fakeRoutes, + timeout: 1 * time.Millisecond, + + status: 200, + + expectedStreams: fakeTargets, + }, + // Invalid targets + { + targets: invalidTargets, + routes: fakeRoutes, + timeout: 1 * time.Millisecond, + invalidTargets: true, + + expectedErrMsg: "invalid targets", + expectedStreams: invalidTargets, + }, + // curl perform fails + { + targets: fakeTargets, + routes: fakeRoutes, + timeout: 1 * time.Millisecond, + + performErr: errors.New("dummy error"), + + expectedStreams: fakeTargets, + expectedErrMsg: "no routes found", + }, + // curl getinfo fails + { + targets: fakeTargets, + routes: fakeRoutes, + timeout: 1 * time.Millisecond, + + getInfoErr: errors.New("dummy error"), + + expectedStreams: fakeTargets, + expectedErrMsg: "no routes found", + }, + // Routes not found { targets: fakeTargets, routes: fakeRoutes, @@ -126,7 +295,7 @@ func TestAttackRoute(t *testing.T) { expectedStreams: fakeTargets, expectedErrMsg: "no routes found", }, - // Valid baseline without logs + // Logs disabled { targets: fakeTargets, routes: fakeRoutes, @@ -136,23 +305,32 @@ func TestAttackRoute(t *testing.T) { expectedStreams: fakeTargets, expectedErrMsg: "no routes found", }, - // TODO: Refacto and make tests with all possible error cases } - for i, vector := range vectors { - results, err := AttackRoute(vector.targets, vector.routes, vector.timeout, vector.log) + for i, test := range testCases { + curlerMock := &CurlerMock{} - if len(vector.expectedErrMsg) > 0 { + if !test.invalidTargets { + curlerMock.On("Setopt", mock.Anything, mock.Anything).Return(nil) + curlerMock.On("Perform").Return(test.performErr) + if test.performErr == nil { + curlerMock.On("Getinfo", mock.Anything).Return(test.status, test.getInfoErr) + } + } + + results, err := AttackRoute(curlerMock, test.targets, test.routes, test.timeout, test.log) + + if len(test.expectedErrMsg) > 0 { if err == nil { - fmt.Printf("unexpected success in AttackRoute test, iteration %d. expected error: %s\n", i, vector.expectedErrMsg) + fmt.Printf("unexpected success in AttackRoute test, iteration %d. expected error: %s\n", i, test.expectedErrMsg) os.Exit(1) } - assert.Contains(t, err.Error(), vector.expectedErrMsg, "wrong error message") + assert.Contains(t, err.Error(), test.expectedErrMsg, "wrong error message") } else { if err != nil { fmt.Printf("unexpected error in AttackRoute test, iteration %d: %v\n", i, err) os.Exit(1) } - for _, stream := range vector.expectedStreams { + for _, stream := range test.expectedStreams { foundStream := false for _, result := range results { if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port { @@ -162,6 +340,11 @@ func TestAttackRoute(t *testing.T) { assert.Equal(t, true, foundStream, "wrong streams parsed") } } - assert.Equal(t, len(vector.expectedStreams), len(results), "wrong streams parsed") + assert.Equal(t, len(test.expectedStreams), len(results), "wrong streams parsed") + curlerMock.AssertExpectations(t) } } + +func TestDotWrite(t *testing.T) { + assert.Equal(t, true, doNotWrite(nil, nil)) +} diff --git a/cameradar/cameradar.go b/cameradar/cameradar.go index 0f3f7da..400d1f4 100644 --- a/cameradar/cameradar.go +++ b/cameradar/cameradar.go @@ -8,12 +8,13 @@ import ( "time" "github.com/EtixLabs/cameradar" + + curl "github.com/andelf/go-curl" + "github.com/fatih/color" "github.com/gernest/wow" "github.com/gernest/wow/spin" "github.com/spf13/pflag" "github.com/spf13/viper" - - "github.com/fatih/color" ) type options struct { @@ -92,6 +93,13 @@ func main() { w := startSpinner(options.EnableLogs) + err = curl.GlobalInit(curl.GLOBAL_ALL) + c := curl.EasyInit() + if err != nil || c == nil { + printErr(errors.New("libcurl initialization failed")) + } + defer curl.GlobalCleanup() + updateSpinner(w, "Loading dictionaries...", options.EnableLogs) gopath := os.Getenv("GOPATH") options.Credentials = strings.Replace(options.Credentials, "", gopath, 1) @@ -116,15 +124,14 @@ func main() { } // Most cameras will be accessed successfully with these two attacks - updateSpinner(w, "Found "+fmt.Sprint(len(streams))+" streams. Attacking their routes...", options.EnableLogs) - streams, err = cmrdr.AttackRoute(streams, routes, time.Duration(options.Timeout)*time.Millisecond, options.EnableLogs) + streams, err = cmrdr.AttackRoute(c, streams, routes, time.Duration(options.Timeout)*time.Millisecond, options.EnableLogs) if err != nil && len(streams) > 0 { printErr(err) } updateSpinner(w, "Found "+fmt.Sprint(len(streams))+" streams. Attacking their credentials...", options.EnableLogs) - streams, err = cmrdr.AttackCredentials(streams, credentials, time.Duration(options.Timeout)*time.Millisecond, options.EnableLogs) + streams, err = cmrdr.AttackCredentials(c, streams, credentials, time.Duration(options.Timeout)*time.Millisecond, options.EnableLogs) if err != nil && len(streams) > 0 { printErr(err) } @@ -135,7 +142,7 @@ func main() { if stream.RouteFound == false || stream.CredentialsFound == false { updateSpinner(w, "Found "+fmt.Sprint(len(streams))+" streams. Final attack...", options.EnableLogs) - streams, err = cmrdr.AttackRoute(streams, routes, time.Duration(options.Timeout)*time.Millisecond, options.EnableLogs) + streams, err = cmrdr.AttackRoute(c, streams, routes, time.Duration(options.Timeout)*time.Millisecond, options.EnableLogs) if err != nil && len(streams) > 0 { printErr(err) } diff --git a/cameradur b/cameradur deleted file mode 100755 index f85a3c3..0000000 Binary files a/cameradur and /dev/null differ diff --git a/curl.go b/curl.go new file mode 100644 index 0000000..9b837b9 --- /dev/null +++ b/curl.go @@ -0,0 +1,13 @@ +package cmrdr + +import ( + curl "github.com/andelf/go-curl" +) + +// Curler is an interface that implements the CURL interface of the go-curl library +// Used for mocking +type Curler interface { + Setopt(opt int, param interface{}) error + Perform() error + Getinfo(info curl.CurlInfo) (interface{}, error) +} diff --git a/discover_test.go b/discover_test.go index a76e07e..d0b4573 100644 --- a/discover_test.go +++ b/discover_test.go @@ -37,7 +37,7 @@ func TestNmapRun(t *testing.T) { execCommand = fakeExecCommand defer func() { execCommand = exec.Command }() - vectors := []struct { + testCases := []struct { targets string ports string resultFilePath string @@ -65,14 +65,14 @@ func TestNmapRun(t *testing.T) { expectedErrMsg: "invalid nmap speed value", }, } - for _, vector := range vectors { - err := NmapRun(vector.targets, vector.ports, vector.resultFilePath, vector.nmapSpeed, vector.enableLogs) - if len(vector.expectedErrMsg) > 0 { + for _, test := range testCases { + err := NmapRun(test.targets, test.ports, test.resultFilePath, test.nmapSpeed, test.enableLogs) + if len(test.expectedErrMsg) > 0 { if err == nil { - fmt.Printf("unexpected success. expected error: %s\n", vector.expectedErrMsg) + fmt.Printf("unexpected success. expected error: %s\n", test.expectedErrMsg) os.Exit(1) } - assert.Contains(t, err.Error(), vector.expectedErrMsg, "wrong error message") + assert.Contains(t, err.Error(), test.expectedErrMsg, "wrong error message") } else { if err != nil { fmt.Printf("unexpected error: %v\n", err) @@ -107,7 +107,7 @@ func TestNmapParseResults(t *testing.T) { Port: 1337, } - vectors := []struct { + testCases := []struct { fileExists bool streamsXML *nmapResult @@ -165,7 +165,7 @@ func TestNmapParseResults(t *testing.T) { fileExists: true, }, // File exists - // Two invalid streams, no error + // Two invalid targets, no error { fileExists: true, expectedStreams: []Stream{invalidStreamNoPort, invalidStreamNoAddress}, @@ -261,11 +261,11 @@ func TestNmapParseResults(t *testing.T) { expectedErrMsg: "expected element type but have ", }, } - for i, vector := range vectors { + for i, test := range testCases { filePath := "/tmp/cameradar_test_parse_results_" + fmt.Sprint(i) + ".xml" // create file - if vector.fileExists { + if test.fileExists { _, err := os.Create(filePath) if err != nil { fmt.Printf("could not create xml file for NmapParseResults: %v. iteration: %d. file path: %s\n", err, i, filePath) @@ -273,10 +273,10 @@ func TestNmapParseResults(t *testing.T) { } // marshal and write - if vector.streamsXML != nil { - streams, err := xml.Marshal(vector.streamsXML) + if test.streamsXML != nil { + streams, err := xml.Marshal(test.streamsXML) if err != nil { - fmt.Printf("invalid streams for NmapParseResults: %v. iteration: %d. streams: %v\n", err, i, vector.streamsXML) + fmt.Printf("invalid targets for NmapParseResults: %v. iteration: %d. streams: %v\n", err, i, test.streamsXML) os.Exit(1) } @@ -295,18 +295,18 @@ func TestNmapParseResults(t *testing.T) { } results, err := NmapParseResults(filePath) - if len(vector.expectedErrMsg) > 0 { + if len(test.expectedErrMsg) > 0 { if err == nil { - fmt.Printf("unexpected success. expected error: %s\n", vector.expectedErrMsg) + fmt.Printf("unexpected success. expected error: %s\n", test.expectedErrMsg) os.Exit(1) } - assert.Contains(t, err.Error(), vector.expectedErrMsg, "wrong error message") + assert.Contains(t, err.Error(), test.expectedErrMsg, "wrong error message") } else { if err != nil { fmt.Printf("unexpected error: %v\n", err) os.Exit(1) } - for _, stream := range vector.expectedStreams { + for _, stream := range test.expectedStreams { foundStream := false for _, result := range results { if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port { @@ -316,7 +316,7 @@ func TestNmapParseResults(t *testing.T) { assert.Equal(t, true, foundStream, "wrong streams parsed") } } - assert.Equal(t, len(vector.expectedStreams), len(results), "wrong streams parsed") + assert.Equal(t, len(test.expectedStreams), len(results), "wrong streams parsed") } } @@ -348,7 +348,7 @@ func TestDiscover(t *testing.T) { Port: 1337, } - vectors := []struct { + testCases := []struct { targets string ports string resultFilePath string @@ -526,7 +526,7 @@ func TestDiscover(t *testing.T) { enableLogs: false, }, // File exists - // Two invalid streams, no error + // Two invalid targets, no error { fileExists: true, expectedStreams: []Stream{invalidStreamNoPort, invalidStreamNoAddress}, @@ -642,11 +642,11 @@ func TestDiscover(t *testing.T) { enableLogs: false, }, } - for i, vector := range vectors { + for i, test := range testCases { filePath := "/tmp/cameradar_test_discover_" + fmt.Sprint(i) + ".xml" // create file - if vector.fileExists { + if test.fileExists { _, err := os.Create(filePath) if err != nil { fmt.Printf("could not create xml file for Discover: %v. iteration: %d. file path: %s\n", err, i, filePath) @@ -654,10 +654,10 @@ func TestDiscover(t *testing.T) { } // marshal and write - if vector.streamsXML != nil { - streams, err := xml.Marshal(vector.streamsXML) + if test.streamsXML != nil { + streams, err := xml.Marshal(test.streamsXML) if err != nil { - fmt.Printf("invalid streams for Discover: %v. iteration: %d. streams: %v\n", err, i, vector.streamsXML) + fmt.Printf("invalid targets for Discover: %v. iteration: %d. streams: %v\n", err, i, test.streamsXML) os.Exit(1) } @@ -675,20 +675,20 @@ func TestDiscover(t *testing.T) { } } - results, err := Discover(vector.targets, vector.ports, filePath, vector.nmapSpeed, vector.enableLogs) + results, err := Discover(test.targets, test.ports, filePath, test.nmapSpeed, test.enableLogs) - if len(vector.expectedErrMsg) > 0 { + if len(test.expectedErrMsg) > 0 { if err == nil { - fmt.Printf("unexpected success in Discover test, iteration %d. expected error: %s\n", i, vector.expectedErrMsg) + fmt.Printf("unexpected success in Discover test, iteration %d. expected error: %s\n", i, test.expectedErrMsg) os.Exit(1) } - assert.Contains(t, err.Error(), vector.expectedErrMsg, "wrong error message") + assert.Contains(t, err.Error(), test.expectedErrMsg, "wrong error message") } else { if err != nil { fmt.Printf("unexpected error in Discover test, iteration %d: %v\n", i, err) os.Exit(1) } - for _, stream := range vector.expectedStreams { + for _, stream := range test.expectedStreams { foundStream := false for _, result := range results { if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port { @@ -698,6 +698,6 @@ func TestDiscover(t *testing.T) { assert.Equal(t, true, foundStream, "wrong streams parsed") } } - assert.Equal(t, len(vector.expectedStreams), len(results), "wrong streams parsed") + assert.Equal(t, len(test.expectedStreams), len(results), "wrong streams parsed") } } diff --git a/helpers_test.go b/helpers_test.go index a070d9d..b3c6d91 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -31,7 +31,7 @@ func TestReplace(t *testing.T) { Port: 1337, } - vectors := []struct { + testCases := []struct { streams []Stream newStream Stream @@ -45,10 +45,10 @@ func TestReplace(t *testing.T) { expectedStreams: []Stream{validStream1, validStream2, invalidStreamNoPortModified}, }, } - for _, vector := range vectors { - streams := replace(vector.streams, vector.newStream) + for _, test := range testCases { + streams := replace(test.streams, test.newStream) - for _, stream := range vector.streams { + for _, stream := range test.streams { foundStream := false for _, result := range streams { if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port { @@ -69,7 +69,7 @@ func TestGetCameraRTSPURL(t *testing.T) { Port: 1337, } - vectors := []struct { + testCases := []struct { stream Stream expectedRTSPURL string @@ -81,9 +81,9 @@ func TestGetCameraRTSPURL(t *testing.T) { expectedRTSPURL: "rtsp://ullaakut:ba69897483886f0d2b0afb6345b76c0c@1.2.3.4:1337/cameradar.sdp", }, } - for _, vector := range vectors { - output := GetCameraRTSPURL(vector.stream) - assert.Equal(t, vector.expectedRTSPURL, output, "wrong RTSP URL generated") + for _, test := range testCases { + output := GetCameraRTSPURL(test.stream) + assert.Equal(t, test.expectedRTSPURL, output, "wrong RTSP URL generated") } } @@ -92,7 +92,7 @@ func TestGetCameraAdminPanelURL(t *testing.T) { Address: "1.2.3.4", } - vectors := []struct { + testCases := []struct { stream Stream expectedRTSPURL string @@ -104,8 +104,8 @@ func TestGetCameraAdminPanelURL(t *testing.T) { expectedRTSPURL: "http://1.2.3.4/", }, } - for _, vector := range vectors { - output := GetCameraAdminPanelURL(vector.stream) - assert.Equal(t, vector.expectedRTSPURL, output, "wrong Admin Panel URL generated") + for _, test := range testCases { + output := GetCameraAdminPanelURL(test.stream) + assert.Equal(t, test.expectedRTSPURL, output, "wrong Admin Panel URL generated") } } diff --git a/loaders_test.go b/loaders_test.go index 02806e0..4d5443c 100644 --- a/loaders_test.go +++ b/loaders_test.go @@ -16,7 +16,7 @@ func TestLoadCredentials(t *testing.T) { Passwords: []string{"12345", "root"}, } - vectors := []struct { + testCases := []struct { input []byte fileExists bool @@ -47,17 +47,17 @@ func TestLoadCredentials(t *testing.T) { input: []byte("{\"invalid\":\"json\"}"), }, } - for i, vector := range vectors { + for i, test := range testCases { filePath := "/tmp/cameradar_test_load_credentials_" + fmt.Sprint(i) + ".xml" // create file - if vector.fileExists { + if test.fileExists { _, err := os.Create(filePath) if err != nil { fmt.Printf("could not create xml file for LoadCredentials: %v. iteration: %d. file path: %s\n", err, i, filePath) os.Exit(1) } - err = ioutil.WriteFile(filePath, vector.input, 0644) + err = ioutil.WriteFile(filePath, test.input, 0644) if err != nil { fmt.Printf("could not write xml file for LoadCredentials: %v. iteration: %d. file path: %s\n", err, i, filePath) os.Exit(1) @@ -65,18 +65,18 @@ func TestLoadCredentials(t *testing.T) { } result, err := LoadCredentials(filePath) - if len(vector.expectedErrMsg) > 0 { + if len(test.expectedErrMsg) > 0 { if err == nil { - fmt.Printf("unexpected success in LoadCredentials test, iteration %d. expected error: %s\n", i, vector.expectedErrMsg) + fmt.Printf("unexpected success in LoadCredentials test, iteration %d. expected error: %s\n", i, test.expectedErrMsg) os.Exit(1) } - assert.Contains(t, err.Error(), vector.expectedErrMsg, "wrong error message") + assert.Contains(t, err.Error(), test.expectedErrMsg, "wrong error message") } else { if err != nil { fmt.Printf("unexpected error in LoadCredentials test, iteration %d: %v\n", i, err) os.Exit(1) } - for _, expectedUsername := range vector.expectedOutput.Usernames { + for _, expectedUsername := range test.expectedOutput.Usernames { foundUsername := false for _, username := range result.Usernames { if username == expectedUsername { @@ -85,7 +85,7 @@ func TestLoadCredentials(t *testing.T) { } assert.Equal(t, true, foundUsername, "wrong usernames parsed") } - for _, expectedPassword := range vector.expectedOutput.Passwords { + for _, expectedPassword := range test.expectedOutput.Passwords { foundPassword := false for _, password := range result.Passwords { if password == expectedPassword { @@ -102,7 +102,7 @@ func TestLoadRoutes(t *testing.T) { routesJSONString := []byte("admin\nroot") validRoutes := Routes{"admin", "root"} - vectors := []struct { + testCases := []struct { input []byte fileExists bool @@ -127,17 +127,17 @@ func TestLoadRoutes(t *testing.T) { input: []byte(""), }, } - for i, vector := range vectors { + for i, test := range testCases { filePath := "/tmp/cameradar_test_load_routes_" + fmt.Sprint(i) + ".xml" // create file - if vector.fileExists { + if test.fileExists { _, err := os.Create(filePath) if err != nil { fmt.Printf("could not create xml file for LoadRoutes: %v. iteration: %d. file path: %s\n", err, i, filePath) os.Exit(1) } - err = ioutil.WriteFile(filePath, vector.input, 0644) + err = ioutil.WriteFile(filePath, test.input, 0644) if err != nil { fmt.Printf("could not write xml file for LoadRoutes: %v. iteration: %d. file path: %s\n", err, i, filePath) os.Exit(1) @@ -145,18 +145,18 @@ func TestLoadRoutes(t *testing.T) { } result, err := LoadRoutes(filePath) - if len(vector.expectedErrMsg) > 0 { + if len(test.expectedErrMsg) > 0 { if err == nil { - fmt.Printf("unexpected success in LoadRoutes test, iteration %d. expected error: %s\n", i, vector.expectedErrMsg) + fmt.Printf("unexpected success in LoadRoutes test, iteration %d. expected error: %s\n", i, test.expectedErrMsg) os.Exit(1) } - assert.Contains(t, err.Error(), vector.expectedErrMsg, "wrong error message") + assert.Contains(t, err.Error(), test.expectedErrMsg, "wrong error message") } else { if err != nil { fmt.Printf("unexpected error in LoadRoutes test, iteration %d: %v\n", i, err) os.Exit(1) } - for _, expectedRoute := range vector.expectedOutput { + for _, expectedRoute := range test.expectedOutput { foundRoute := false for _, route := range result { if route == expectedRoute { @@ -168,3 +168,95 @@ func TestLoadRoutes(t *testing.T) { } } } + +func TestParseCredentialsFromString(t *testing.T) { + defaultCredentials := Credentials{ + Usernames: []string{ + "", + "admin", + "Admin", + "Administrator", + "root", + "supervisor", + "ubnt", + "service", + "Dinion", + "administrator", + "admin1", + }, + Passwords: []string{ + "", + "admin", + "9999", + "123456", + "pass", + "camera", + "1234", + "12345", + "fliradmin", + "system", + "jvc", + "meinsm", + "root", + "4321", + "111111", + "1111111", + "password", + "ikwd", + "supervisor", + "ubnt", + "wbox123", + "service", + }, + } + + testCases := []struct { + str string + expectedResult Credentials + }{ + { + str: "{\"usernames\":[\"\",\"admin\",\"Admin\",\"Administrator\",\"root\",\"supervisor\",\"ubnt\",\"service\",\"Dinion\",\"administrator\",\"admin1\"],\"passwords\":[\"\",\"admin\",\"9999\",\"123456\",\"pass\",\"camera\",\"1234\",\"12345\",\"fliradmin\",\"system\",\"jvc\",\"meinsm\",\"root\",\"4321\",\"111111\",\"1111111\",\"password\",\"ikwd\",\"supervisor\",\"ubnt\",\"wbox123\",\"service\"]}", + expectedResult: defaultCredentials, + }, + { + str: "{}", + expectedResult: Credentials{}, + }, + { + str: "{\"invalid_field\":42}", + expectedResult: Credentials{}, + }, + { + str: "not json", + expectedResult: Credentials{}, + }, + } + for _, test := range testCases { + parsedCredentials, _ := ParseCredentialsFromString(test.str) + assert.Equal(t, test.expectedResult, parsedCredentials, "unexpected result, parse error") + } +} + +func TestParseRoutesFromString(t *testing.T) { + testCases := []struct { + str string + expectedResult Routes + }{ + { + str: "a\nb\nc", + expectedResult: []string{"a", "b", "c"}, + }, + { + str: "a", + expectedResult: []string{"a"}, + }, + { + str: "", + expectedResult: []string{""}, + }, + } + for _, test := range testCases { + parsedRoutes := ParseRoutesFromString(test.str) + assert.Equal(t, test.expectedResult, parsedRoutes, "unexpected result, parse error") + } +}