Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion onvif/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from onvif.definition import SERVICES
from onvif.exceptions import ONVIFAuthError, ONVIFError, ONVIFTimeoutError
from requests import Response
from urllib.parse import urlparse, urlunparse

from .const import KEEPALIVE_EXPIRY
from .managers import NotificationManager, PullPointManager
Expand Down Expand Up @@ -457,6 +458,47 @@ async def get_capabilities(self) -> dict[str, Any]:
await self.update_xaddrs()
return self._capabilities

def rewrite_xaddr(self, original_xaddr):
"""Replace host:port in XAddr with the connection host:port"""
if not original_xaddr:
return None

parsed = urlparse(original_xaddr)

# Check if the reported address differs from connection address
# If they match, no rewriting needed
if parsed.hostname == self.host and (
not parsed.port or parsed.port == self.port
):
return original_xaddr

# Build new netloc with our connection host/port
if self.port and self.port != 80:
new_netloc = f"{self.host}:{self.port}"
else:
new_netloc = self.host

# Reconstruct URL with new host:port but same path
rewritten = urlunparse(
(
parsed.scheme, # Keep original scheme (http/https)
new_netloc, # Use connection host:port
parsed.path, # Keep original path
parsed.params, # Keep params
parsed.query, # Keep query
parsed.fragment, # Keep fragment
)
)

logger.debug(
"%s: NAT detected - rewriting XAddr from %s to %s",
self.host,
original_xaddr,
rewritten,
)

return rewritten

async def update_xaddrs(self):
"""Update xaddrs for services."""
self.dt_diff = None
Expand Down Expand Up @@ -489,7 +531,11 @@ async def update_xaddrs(self):
try:
if name.lower() in SERVICES and capability is not None:
namespace = SERVICES[name.lower()]["ns"]
self.xaddrs[namespace] = normalize_url(capability["XAddr"])
original_xaddr = normalize_url(capability["XAddr"])
# Rewrite the xaddr for NAT before storing
rewritten_xaddr = self.rewrite_xaddr(original_xaddr)
self.xaddrs[namespace] = rewritten_xaddr
capability["XAddr"] = rewritten_xaddr
except Exception:
logger.exception("Unexpected service type")
try:
Expand Down