Skip to content
Open
Show file tree
Hide file tree
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
32 changes: 26 additions & 6 deletions multiaddr/multiaddr.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,12 +240,32 @@ def decapsulate(self, addr: Union["Multiaddr", str]) -> "Multiaddr":
For example:
/ip4/1.2.3.4/tcp/80 decapsulate /ip4/1.2.3.4 = /tcp/80
"""
addr_str = str(addr)
s = str(self)
i = s.rindex(addr_str)
if i < 0:
raise ValueError(f"Address {s} does not contain subaddress: {addr_str}")
return Multiaddr(s[:i])
other = Multiaddr(addr) if not isinstance(addr, Multiaddr) else addr
other_components = list(bytes_iter(other.to_bytes()))
self_components = list(bytes_iter(self._bytes))

last_match_end = -1
for i in range(len(self_components)):
match = True
for j, (_, proto, _, value) in enumerate(other_components):
if i + j >= len(self_components):
match = False
break
_, s_proto, _, s_value = self_components[i + j]
if s_proto != proto or s_value != value:
match = False
break

if match:
last_match_end = self_components[i][0] # byte offset

if last_match_end < 0:
raise ValueError(f"Address {self} does not contain subaddress: {addr}")

if last_match_end == 0:
return Multiaddr("")

return Multiaddr(self._bytes[:last_match_end])

def decapsulate_code(self, code: int) -> "Multiaddr":
"""
Expand Down
1 change: 1 addition & 0 deletions newsfragments/109.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed an issue where decapsulate produced incorrect results for repeated protocols and substring collisions.
15 changes: 15 additions & 0 deletions tests/test_multiaddr.py
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,21 @@ def test_decapsulate():
u = Multiaddr("/udp/1234")
assert a.decapsulate(u) == Multiaddr("/ip4/127.0.0.1")

# Issue #109 Case 1 — Repeated protocol
ma1 = Multiaddr("/ip4/1.2.3.4/tcp/80/ip4/5.6.7.8/tcp/443")
assert ma1.decapsulate("/ip4/5.6.7.8") == Multiaddr("/ip4/1.2.3.4/tcp/80")

# Issue #109 Case 2 — Substring collision
ma2 = Multiaddr("/dns4/example.com/tcp/80")
import pytest

with pytest.raises(ValueError, match="does not contain subaddress"):
ma2.decapsulate("/tcp/8")

# Issue #109 Case 3 — Value contains protocol name
ma3 = Multiaddr("/dns4/tcp.example.com/tcp/80")
assert ma3.decapsulate("/tcp/80") == Multiaddr("/dns4/tcp.example.com")


def test__repr():
a = Multiaddr("/ip4/127.0.0.1/udp/1234")
Expand Down
Loading