8531c006d4
* 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>
114 lines
2.8 KiB
Go
114 lines
2.8 KiB
Go
package masscan
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/netip"
|
|
"strings"
|
|
|
|
"github.com/Ullaakut/cameradar/v6"
|
|
"github.com/Ullaakut/cameradar/v6/pkg/ports"
|
|
masscanlib "github.com/Ullaakut/masscan"
|
|
)
|
|
|
|
// Reporter reports scan progress and debug information.
|
|
type Reporter interface {
|
|
Debug(step cameradar.Step, message string)
|
|
Progress(step cameradar.Step, message string)
|
|
}
|
|
|
|
// Runner is something that can run a masscan scan.
|
|
type Runner interface {
|
|
Run(ctx context.Context) (*masscanlib.Run, error)
|
|
}
|
|
|
|
// Scanner scans targets and ports for RTSP streams.
|
|
type Scanner struct {
|
|
runner Runner
|
|
reporter Reporter
|
|
}
|
|
|
|
// New returns a Scanner configured with the provided targets and ports.
|
|
func New(targets, ports []string, reporter Reporter) (*Scanner, error) {
|
|
runner, err := masscanlib.NewScanner(
|
|
masscanlib.WithTargets(targets...),
|
|
masscanlib.WithPorts(ports...),
|
|
masscanlib.WithOpenOnly(),
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("creating masscan scanner: %w", err)
|
|
}
|
|
|
|
return &Scanner{
|
|
runner: runner,
|
|
reporter: reporter,
|
|
}, nil
|
|
}
|
|
|
|
// Scan discovers RTSP streams on the configured targets and ports.
|
|
func (s *Scanner) Scan(ctx context.Context) ([]cameradar.Stream, error) {
|
|
return runScan(ctx, s.runner, s.reporter)
|
|
}
|
|
|
|
func runScan(ctx context.Context, runner Runner, reporter Reporter) ([]cameradar.Stream, error) {
|
|
results, err := runner.Run(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("scanning network: %w", err)
|
|
}
|
|
|
|
for _, warning := range results.Warnings() {
|
|
reporter.Debug(cameradar.StepScan, "masscan warning: "+warning)
|
|
}
|
|
|
|
var streams []cameradar.Stream
|
|
for _, host := range results.Hosts {
|
|
address := strings.TrimSpace(host.Address)
|
|
if address == "" {
|
|
reporter.Progress(cameradar.StepScan, "Skipping host with empty address")
|
|
continue
|
|
}
|
|
|
|
addr, err := netip.ParseAddr(address)
|
|
if err != nil {
|
|
reporter.Progress(cameradar.StepScan, fmt.Sprintf("Skipping invalid address %q: %v", host.Address, err))
|
|
continue
|
|
}
|
|
|
|
for _, port := range host.Ports {
|
|
if port.Status != "open" {
|
|
continue
|
|
}
|
|
|
|
if port.Number <= 0 || port.Number > 65535 {
|
|
reporter.Progress(cameradar.StepScan, fmt.Sprintf("Skipping invalid port %d on %s", port.Number, host.Address))
|
|
continue
|
|
}
|
|
|
|
scheme := ports.InferTunnelScheme(uint16(port.Number), "")
|
|
|
|
streams = append(streams, cameradar.Stream{
|
|
Address: addr,
|
|
Port: uint16(port.Number),
|
|
Scheme: scheme,
|
|
})
|
|
}
|
|
}
|
|
|
|
reporter.Progress(cameradar.StepScan, fmt.Sprintf("Found %d RTSP streams", len(streams)))
|
|
updateSummary(reporter, streams)
|
|
|
|
return streams, nil
|
|
}
|
|
|
|
type summaryUpdater interface {
|
|
UpdateSummary(streams []cameradar.Stream)
|
|
}
|
|
|
|
func updateSummary(reporter Reporter, streams []cameradar.Stream) {
|
|
updater, ok := reporter.(summaryUpdater)
|
|
if !ok {
|
|
return
|
|
}
|
|
updater.UpdateSummary(streams)
|
|
}
|