Skip to content

Commit 67db97e

Browse files
committed
Move out address codec implementations into submodules
1 parent 4a99835 commit 67db97e

File tree

10 files changed

+215
-118
lines changed

10 files changed

+215
-118
lines changed

multiaddr/codec.py

Lines changed: 29 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
1-
import base58
2-
import base64
3-
import os
1+
from __future__ import absolute_import
42

5-
import idna
6-
from netaddr import IPAddress
7-
import six
8-
import struct
93
import varint
104

115
from .protocols import P_DNS
@@ -25,23 +19,6 @@
2519
from .protocols import read_varint_code
2620

2721

28-
if hasattr(os, "fsencode") and hasattr(os, "fsdecode"):
29-
fsencode = os.fsencode
30-
fsdecode = os.fsdecode
31-
else: # PY2
32-
import sys
33-
34-
def fsencode(path):
35-
if not isinstance(path, six.binary_type):
36-
path = path.encode(sys.getfilesystemencoding())
37-
return path
38-
39-
def fsdecode(path):
40-
if not isinstance(path, six.text_type):
41-
path = path.decode(sys.getfilesystemencoding())
42-
return path
43-
44-
4522
def string_to_bytes(string):
4623
if not string:
4724
return b''
@@ -90,117 +67,53 @@ def bytes_to_string(buf):
9067

9168
def address_string_to_bytes(proto, addr_string):
9269
if proto.code == P_IP4: # ipv4
93-
try:
94-
return IPAddress(addr_string, version=4).packed
95-
except Exception:
96-
raise ValueError("failed to parse ip4 addr: %s" % addr_string)
70+
from .codecs import ip4
71+
return ip4.to_bytes(proto, addr_string)
9772
elif proto.code == P_IP6: # ipv6
98-
try:
99-
return IPAddress(addr_string, version=6).packed
100-
except Exception:
101-
raise ValueError("failed to parse ip6 addr: %s" % addr_string)
73+
from .codecs import ip6
74+
return ip6.to_bytes(proto, addr_string)
10275
# tcp udp dccp sctp
10376
elif proto.code in [P_TCP, P_UDP, P_DCCP, P_SCTP]:
104-
try:
105-
return struct.pack('>H', int(addr_string, 10))
106-
except ValueError as ex:
107-
raise ValueError("failed to parse %s addr: %s"
108-
% (proto.name, str(ex)))
109-
except struct.error:
110-
raise ValueError("failed to parse %s addr: %s" %
111-
(proto.name, "greater than 65536"))
77+
from .codecs import uint16be
78+
return uint16be.to_bytes(proto, addr_string)
11279
elif proto.code == P_ONION:
113-
addr = addr_string.split(":")
114-
if len(addr) != 2:
115-
raise ValueError(
116-
"failed to parse %s addr: %s does not contain a port number."
117-
% (proto.name, addr_string))
118-
119-
# onion address without the ".onion" substring
120-
if len(addr[0]) != 16:
121-
raise ValueError(
122-
"failed to parse %s addr: %s not a Tor onion address."
123-
% (proto.name, addr_string))
124-
try:
125-
onion_host_bytes = base64.b32decode(addr[0].upper())
126-
except Exception as ex:
127-
raise ValueError(
128-
"failed to decode base32 %s addr: %s %s"
129-
% (proto.name, addr_string, str(ex)))
130-
131-
# onion port number
132-
try:
133-
port = int(addr[1])
134-
except Exception as ex:
135-
raise ValueError("failed to parse %s addr: %s"
136-
% (proto.name, str(ex)))
137-
if port >= 65536:
138-
raise ValueError("failed to parse %s addr: %s"
139-
% (proto.name, "port greater than 65536"))
140-
if port < 1:
141-
raise ValueError("failed to parse %s addr: %s"
142-
% (proto.name, "port less than 1"))
143-
144-
return b''.join([onion_host_bytes, struct.pack('>H', port)])
80+
from .codecs import onion
81+
return onion.to_bytes(proto, addr_string)
14582
elif proto.code == P_P2P: # ipfs
146-
# the address is a varint prefixed multihash string representation
147-
try:
148-
if six.PY2 and isinstance(addr_string, unicode):
149-
addr_string = addr_string.encode("ascii")
150-
mm = base58.b58decode(addr_string)
151-
except Exception as ex:
152-
raise ValueError("failed to parse p2p addr: %s %s"
153-
% (addr_string, str(ex)))
154-
size = varint.encode(len(mm))
155-
if len(mm) < 5:
156-
# TODO - port go-multihash so we can do this correctly
157-
raise ValueError("invalid P2P multihash: %s" % mm)
158-
return b''.join([size, mm])
83+
from .codecs import multihash
84+
return multihash.to_bytes(proto, addr_string)
15985
elif proto.code == P_UNIX:
160-
addr_string_bytes = fsencode(addr_string)
161-
size = varint.encode(len(addr_string_bytes))
162-
return b''.join([size, addr_string_bytes])
86+
from .codecs import fspath
87+
return fspath.to_bytes(proto, addr_string)
16388
elif proto.code in (P_DNS, P_DNS4, P_DNS6):
164-
addr_string_bytes = idna.encode(addr_string, uts46=True)
165-
size = varint.encode(len(addr_string_bytes))
166-
return b''.join([size, addr_string_bytes])
89+
from .codecs import idna
90+
return idna.to_bytes(proto, addr_string)
16791
else:
16892
raise ValueError("failed to parse %s addr: unknown" % proto.name)
16993

17094

171-
packed_net_bytes_to_int = None
172-
173-
17495
def address_bytes_to_string(proto, buf):
175-
global packed_net_bytes_to_int
176-
if packed_net_bytes_to_int is None:
177-
from .util import packed_net_bytes_to_int
178-
17996
if proto.code == P_IP4:
180-
return six.text_type(IPAddress(packed_net_bytes_to_int(buf), 4))
97+
from .codecs import ip4
98+
return ip4.to_string(proto, buf)
18199
elif proto.code == P_IP6:
182-
return six.text_type(IPAddress(packed_net_bytes_to_int(buf), 6))
100+
from .codecs import ip6
101+
return ip6.to_string(proto, buf)
183102
elif proto.code in [P_TCP, P_UDP, P_DCCP, P_SCTP]:
184-
if len(buf) != 2:
185-
raise ValueError("Not a uint16")
186-
return six.text_type(struct.unpack('>H', buf)[0])
103+
from .codecs import uint16be
104+
return uint16be.to_string(proto, buf)
187105
elif proto.code == P_ONION:
188-
addr_bytes, port_bytes = (buf[:-2], buf[-2:])
189-
addr = base64.b32encode(addr_bytes).decode('ascii').lower()
190-
port = six.text_type(struct.unpack('>H', port_bytes)[0])
191-
return u':'.join([addr, port])
106+
from .codecs import onion
107+
return onion.to_string(proto, buf)
192108
elif proto.code == P_P2P:
193-
size, num_bytes_read = read_varint_code(buf)
194-
buf = buf[num_bytes_read:]
195-
if len(buf) != size:
196-
raise ValueError("inconsistent lengths")
197-
return base58.b58encode(buf).decode('ascii')
109+
from .codecs import multihash
110+
return multihash.to_string(proto, buf)
198111
elif proto.code == P_UNIX:
199-
size, num_bytes_read = read_varint_code(buf)
200-
return fsdecode(buf[num_bytes_read:])
112+
from .codecs import fspath
113+
return fspath.to_string(proto, buf)
201114
elif proto.code in (P_DNS, P_DNS4, P_DNS6):
202-
size, num_bytes_read = read_varint_code(buf)
203-
return idna.decode(buf[num_bytes_read:])
115+
from .codecs import idna
116+
return idna.to_string(proto, buf)
204117
raise ValueError("unknown protocol")
205118

206119

multiaddr/codecs/__init__.py

Whitespace-only changes.

multiaddr/codecs/fspath.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from __future__ import absolute_import
2+
import os
3+
4+
import six
5+
import varint
6+
7+
from ..protocols import read_varint_code
8+
9+
10+
if hasattr(os, "fsencode") and hasattr(os, "fsdecode"):
11+
fsencode = os.fsencode
12+
fsdecode = os.fsdecode
13+
else: # PY2
14+
import sys
15+
16+
def fsencode(path):
17+
if not isinstance(path, six.binary_type):
18+
path = path.encode(sys.getfilesystemencoding())
19+
return path
20+
21+
def fsdecode(path):
22+
if not isinstance(path, six.text_type):
23+
path = path.decode(sys.getfilesystemencoding())
24+
return path
25+
26+
27+
def to_bytes(proto, string):
28+
bytes = fsencode(string)
29+
size = varint.encode(len(bytes))
30+
return b''.join([size, bytes])
31+
32+
33+
def to_string(proto, buf):
34+
size, num_bytes_read = read_varint_code(buf)
35+
return fsdecode(buf[num_bytes_read:])

multiaddr/codecs/idna.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from __future__ import absolute_import
2+
3+
import idna
4+
import varint
5+
6+
from ..protocols import read_varint_code
7+
8+
9+
def to_bytes(proto, string):
10+
bytes = idna.encode(string, uts46=True)
11+
size = varint.encode(len(bytes))
12+
return b''.join([size, bytes])
13+
14+
15+
def to_string(proto, buf):
16+
size, num_bytes_read = read_varint_code(buf)
17+
return idna.decode(buf[num_bytes_read:])

multiaddr/codecs/ip4.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from __future__ import absolute_import
2+
3+
import netaddr
4+
import six
5+
6+
from ..util import packed_net_bytes_to_int
7+
8+
9+
def to_bytes(proto, string):
10+
try:
11+
return netaddr.IPAddress(string, version=4).packed
12+
except Exception:
13+
raise ValueError("failed to parse ip4 addr: %s" % string)
14+
15+
16+
def to_string(proto, buf):
17+
ip_addr = netaddr.IPAddress(packed_net_bytes_to_int(buf), version=4)
18+
return six.text_type(ip_addr)

multiaddr/codecs/ip6.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from __future__ import absolute_import
2+
3+
import netaddr
4+
import six
5+
6+
from ..util import packed_net_bytes_to_int
7+
8+
9+
def to_bytes(proto, string):
10+
try:
11+
return netaddr.IPAddress(string, version=6).packed
12+
except Exception:
13+
raise ValueError("failed to parse ip4 addr: %s" % string)
14+
15+
16+
def to_string(proto, buf):
17+
ip_addr = netaddr.IPAddress(packed_net_bytes_to_int(buf), version=6)
18+
return six.text_type(ip_addr)

multiaddr/codecs/multihash.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
from __future__ import absolute_import
2+
3+
import base58
4+
import six
5+
import varint
6+
7+
from ..protocols import read_varint_code
8+
9+
10+
def to_bytes(proto, string):
11+
# the address is a varint prefixed multihash string representation
12+
try:
13+
if six.PY2 and isinstance(string, unicode):
14+
string = string.encode("ascii")
15+
mm = base58.b58decode(string)
16+
except Exception as ex:
17+
raise ValueError("failed to parse p2p addr: %s %s" % (string, str(ex)))
18+
size = varint.encode(len(mm))
19+
if len(mm) < 5:
20+
# TODO - port go-multihash so we can do this correctly
21+
raise ValueError("invalid P2P multihash: %s" % mm)
22+
return b''.join([size, mm])
23+
24+
25+
def to_string(proto, buf):
26+
size, num_bytes_read = read_varint_code(buf)
27+
buf = buf[num_bytes_read:]
28+
if len(buf) != size:
29+
raise ValueError("inconsistent lengths")
30+
return base58.b58encode(buf).decode('ascii')

multiaddr/codecs/onion.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
from __future__ import absolute_import
2+
import base64
3+
import struct
4+
5+
import six
6+
7+
8+
def to_bytes(proto, string):
9+
addr = string.split(":")
10+
if len(addr) != 2:
11+
raise ValueError(
12+
"failed to parse %s addr: %s does not contain a port number."
13+
% (proto.name, string))
14+
15+
# onion address without the ".onion" substring
16+
if len(addr[0]) != 16:
17+
raise ValueError(
18+
"failed to parse %s addr: %s not a Tor onion address."
19+
% (proto.name, string))
20+
try:
21+
onion_host_bytes = base64.b32decode(addr[0].upper())
22+
except Exception as ex:
23+
raise ValueError(
24+
"failed to decode base32 %s addr: %s %s"
25+
% (proto.name, string, str(ex)))
26+
27+
# onion port number
28+
try:
29+
port = int(addr[1])
30+
except Exception as ex:
31+
raise ValueError("failed to parse %s addr: %s"
32+
% (proto.name, str(ex)))
33+
if port >= 65536:
34+
raise ValueError("failed to parse %s addr: %s"
35+
% (proto.name, "port greater than 65536"))
36+
if port < 1:
37+
raise ValueError("failed to parse %s addr: %s"
38+
% (proto.name, "port less than 1"))
39+
40+
return b''.join((onion_host_bytes, struct.pack('>H', port)))
41+
42+
43+
def to_string(proto, buf):
44+
addr_bytes, port_bytes = (buf[:-2], buf[-2:])
45+
addr = base64.b32encode(addr_bytes).decode('ascii').lower()
46+
port = six.text_type(struct.unpack('>H', port_bytes)[0])
47+
return u':'.join([addr, port])

multiaddr/codecs/uint16be.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from __future__ import absolute_import
2+
import struct
3+
4+
import six
5+
6+
7+
def to_bytes(proto, string):
8+
try:
9+
return struct.pack('>H', int(string, 10))
10+
except ValueError as ex:
11+
raise ValueError("failed to parse %s addr: %s"
12+
% (proto.name, str(ex)))
13+
except struct.error:
14+
raise ValueError("failed to parse %s addr: %s" %
15+
(proto.name, "greater than 65536"))
16+
17+
18+
def to_string(proto, buf):
19+
if len(buf) != 2:
20+
raise ValueError("Not a uint16")
21+
return six.text_type(struct.unpack('>H', buf)[0])

tox.ini

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
envlist = py27, py34, py35, py36, py37, pypy3
33

44
[testenv]
5-
setenv =
6-
PYTHONPATH = {toxinidir}:{toxinidir}/multiaddr
75
commands = py.test --cov=./
86

97
; If you want to make tox run the tests with the same versions, create a

0 commit comments

Comments
 (0)