Skip to content

UpgradeScheme incorrectly overrides sub-service URLs returned by GetCapabilities/GetServices #26

@kumaakh

Description

@kumaakh

Summary

UpgradeScheme in NvtSession.fs unconditionally promotes sub-service URLs (media, events, PTZ, etc.) from HTTP to HTTPS whenever the device service connection is HTTPS — even when those URLs were explicitly returned by GetCapabilities / GetServices on a different port. This breaks any camera that intentionally serves its device endpoint over HTTPS but its media endpoint over plain HTTP.

Root Cause

The device service is the authoritative source for sub-service URLs. When GetCapabilities returns http://camera:80/onvif/Media, that is the correct address for the media service — the camera is telling us it is HTTP-only on port 80. UpgradeScheme must not override this.

Current code in NvtSession.fs:

if deviceUri.Scheme = https && url.Scheme = http then
    b.Scheme <- https
    if b.Port = 80 then b.Port <- 443
    b.Uri

This unconditionally upgrades any HTTP sub-service URL to HTTPS when the device URI is HTTPS. If the camera returns http://camera:80/onvif/Media as the media xAddr, it becomes https://camera:443/onvif/Media. The camera only serves /onvif/Media on HTTP:80 — the HTTPS endpoint at :443 returns a TCP RST or CommunicationException.

Reproduction Cases

Case 1 — Milesight camera (192.168.1.190):

  • Device service: https://192.168.1.190:443/onvif/device_service
  • GetCapabilities returns: XAddr = http://192.168.1.190:80/onvif/Media
  • UpgradeScheme maps to: https://192.168.1.190:443/onvif/Media
  • Result: GetProfiles → connection refused (port 443 does not serve /onvif/Media)

Integration test failure:

System.Net.Sockets.SocketException: No connection could be made because the target machine actively refused it 192.168.1.190:443

Case 2 — Field camera 10.102.10.7 (confirmed in production logs):

[UpgradeScheme] deviceUri=https://10.102.10.7/onvif/device_service
               input=http://10.102.10.7/onvif/Media → output=https://10.102.10.7/onvif/Media  ← WRONG
[withMedia2HttpFallback] non-retryable exception (CommunicationException): propagating
[withMedia1HttpFallback] non-retryable exception (CommunicationException): propagating

Both Media1 and Media2 fail. HTTP fallback also fails because port 80 refuses the upgraded HTTPS connection.

Fix

UpgradeScheme should only upgrade the scheme of a sub-service URL if the sub-service URL's port matches the device service port — meaning the camera misconfigured the scheme but is pointing at the same endpoint. If the ports differ, the camera is intentionally advertising a different endpoint and we must use the URL verbatim.

// Only upgrade if sub-service port matches device service port
// (camera advertised wrong scheme but same endpoint)
if deviceUri.Scheme = https && url.Scheme = http 
   && (url.Port = deviceUri.Port || (url.IsDefaultPort && deviceUri.Port = 443)) then
    b.Scheme <- https
    b.Uri
else
    url  // trust the URL as returned by GetCapabilities/GetServices

Also fix HttpsIntegrationTests.cs:65 which hardcodes :443 in ServicePointManager.FindServicePoint — should use _httpsPort.

Impact

Cameras with split-scheme deployments (HTTPS device service, HTTP media service) fail completely — CommunicationException on both Media1 and Media2, no streaming possible.

Affects: Milesight cameras (Sprint 6), field cameras 10.102.10.7 and variants.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions