feat: support http tunneled rtsp (#419)

* enhancement: supporting http tunneled rtsp

* refactor: simplify HTTP tunnel support per review feedback

- Extract streamCandidate() for nmap port classification
- Add isCommonHTTPPort() for masscan and nmap fallback
- Move URL building to Stream.String() and Stream.URL()
- Pass Stream directly to attack methods instead of individual args
- Add TLS config for HTTPS tunnel support
- Make auth detection non-fatal for tunneled streams
- Rename HTTPTunnel to UseHTTPTunnel

* - Testing the auth workflow for the tunneled streams is not blocking the rest of the pipeline since I changed the return values to Auth unknown and nil
- added extra ports in the test according to suggestions

* fixing some lint errors

* removing the unused buildrtspurl

* delayed the urlstream call for clarity

removed error messages

refactored the test that used the deprecated buildTRSPurl to use stream.URL and stream.String() methods

* extracting iscommonHTTP port to pkg/ports (package ports)

switching on u.scheme to create proper schemes for http and https

* refactor: replace HTTP tunnel bool with scheme-based detection; enable TLS only for HTTPS-tunneled streams

* chore: simnplify InferTunnelScheme and newRTSPClient

* fix: remove rendundant check in streamCandidate

* fix: typo in parseScheme

* tests: coverage for new schemes

* fix: use RTSP and not RTSPS for HTTPS URLs

* fix: tunneled RTSP scheme handling and auth detection fallback

* ui: render empty credentials as none in summary and TUI

* chore: ignore duplicate-string warning for none literal

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix: rtsps probe headers

---------

Co-authored-by: Brendan Le Glaunec <brendan@glaulabs.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Lawrence Arryl Lopez
2026-03-18 01:41:20 -04:00
committed by GitHub
parent 14dcb74e89
commit 8531c006d4
15 changed files with 626 additions and 136 deletions
+19 -1
View File
@@ -7,6 +7,7 @@ import (
"strings"
"github.com/Ullaakut/cameradar/v6"
"github.com/Ullaakut/cameradar/v6/pkg/ports"
nmaplib "github.com/Ullaakut/nmap/v4"
)
@@ -67,7 +68,8 @@ func runScan(ctx context.Context, nmap Runner, reporter Reporter) ([]cameradar.S
continue
}
if !strings.Contains(port.Service.Name, "rtsp") {
isCandidate := streamCandidate(port.Service.Name, port.ID)
if !isCandidate {
continue
}
@@ -78,10 +80,12 @@ func runScan(ctx context.Context, nmap Runner, reporter Reporter) ([]cameradar.S
continue
}
scheme := ports.InferTunnelScheme(port.ID, port.Service.Name)
streams = append(streams, cameradar.Stream{
Device: port.Service.Product,
Address: addr,
Port: port.ID,
Scheme: scheme,
})
}
}
@@ -104,3 +108,17 @@ func updateSummary(reporter Reporter, streams []cameradar.Stream) {
}
updater.UpdateSummary(streams)
}
// Extracting the classifying logic to an external function to avoid nesting if loops.
func streamCandidate(serviceName string, port uint16) bool {
serviceName = strings.ToLower(strings.TrimSpace(serviceName))
if strings.Contains(serviceName, "rtsp") {
return true
}
if ports.InferTunnelScheme(port, serviceName) != "" {
return true
}
return false
}
+49 -1
View File
@@ -44,8 +44,56 @@ func TestScanner_Scan(t *testing.T) {
Address: netip.MustParseAddr("127.0.0.1"),
Port: 8554,
},
{
Device: "ACME",
Address: netip.MustParseAddr("127.0.0.1"),
Port: 80,
Scheme: "http",
},
},
wantProgress: "Found 1 RTSP streams",
wantProgress: "Found 2 RTSP streams",
},
{
name: "keeps rtsp and http candidates while filtering closed ports",
result: buildRun(nmaplib.Host{
Addresses: []nmaplib.Address{
{Addr: "127.0.0.1"},
{Addr: "not-an-ip"},
},
Ports: []nmaplib.Port{
openPort(8554, "rtsp", "ACME"),
closedPort(554, "rtsp", "ACME"),
openPort(80, "http", "ACME"),
openPort(9443, "https", "ACME"),
openPort(8443, "", "ACME"),
},
}),
wantStreams: []cameradar.Stream{
{
Device: "ACME",
Address: netip.MustParseAddr("127.0.0.1"),
Port: 8554,
},
{
Device: "ACME",
Address: netip.MustParseAddr("127.0.0.1"),
Port: 80,
Scheme: "http",
},
{
Device: "ACME",
Address: netip.MustParseAddr("127.0.0.1"),
Port: 9443,
Scheme: "https",
},
{
Device: "ACME",
Address: netip.MustParseAddr("127.0.0.1"),
Port: 8443,
Scheme: "https",
},
},
wantProgress: "Found 4 RTSP streams",
},
{
name: "collects multiple hosts",