1
- import base58
2
- import base64
3
- import os
1
+ from __future__ import absolute_import
2
+ import importlib
4
3
5
- import idna
6
- from netaddr import IPAddress
7
- import six
8
- import struct
9
4
import varint
10
5
11
- from .protocols import P_DNS
12
- from .protocols import P_DNS4
13
- from .protocols import P_DNS6
14
- from .protocols import P_DCCP
15
- from .protocols import P_IP4
16
- from .protocols import P_IP6
17
- from .protocols import P_P2P
18
- from .protocols import P_ONION
19
6
from .protocols import protocol_with_code
20
7
from .protocols import protocol_with_name
21
- from .protocols import P_SCTP
22
- from .protocols import P_TCP
23
- from .protocols import P_UDP
24
- from .protocols import P_UNIX
25
8
from .protocols import read_varint_code
26
9
27
10
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
11
+ CODEC_CACHE = {}
12
+ def find_codec_by_name (name ):
13
+ if not name :
14
+ raise ValueError ("unknown protocol codec" )
15
+ codec = CODEC_CACHE .get (name )
16
+ if not codec :
17
+ codec = CODEC_CACHE [name ] = importlib .import_module (".codecs.{0}" .format (name ), __package__ )
18
+ return codec
43
19
44
20
45
21
def string_to_bytes (string ):
@@ -65,7 +41,10 @@ def string_to_bytes(string):
65
41
"protocol requires address, none given: %s" % proto .name )
66
42
if proto .path :
67
43
sp = ["/" + "/" .join (sp )]
68
- bs .append (address_string_to_bytes (proto , sp .pop (0 )))
44
+ if not proto .codec :
45
+ raise ValueError ("failed to parse %s addr: unknown" % proto .name )
46
+ codec = find_codec_by_name (proto .codec )
47
+ bs .append (codec .to_bytes (proto , sp .pop (0 )))
69
48
return b'' .join (bs )
70
49
71
50
@@ -79,7 +58,8 @@ def bytes_to_string(buf):
79
58
maddr_component += proto .name
80
59
size = size_for_addr (proto , buf )
81
60
if size > 0 :
82
- addr = address_bytes_to_string (proto , buf [:size ])
61
+ codec = find_codec_by_name (proto .codec )
62
+ addr = codec .to_string (proto , buf [:size ])
83
63
if not (proto .path and addr [0 ] == '/' ):
84
64
maddr_component += '/'
85
65
maddr_component += addr
@@ -88,122 +68,6 @@ def bytes_to_string(buf):
88
68
return '/' .join (st )
89
69
90
70
91
- def address_string_to_bytes (proto , addr_string ):
92
- 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 )
97
- 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 )
102
- # tcp udp dccp sctp
103
- 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" ))
112
- 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 )])
145
- 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 ])
159
- 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 ])
163
- 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 ])
167
- else :
168
- raise ValueError ("failed to parse %s addr: unknown" % proto .name )
169
-
170
-
171
- packed_net_bytes_to_int = None
172
-
173
-
174
- 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
-
179
- if proto .code == P_IP4 :
180
- return six .text_type (IPAddress (packed_net_bytes_to_int (buf ), 4 ))
181
- elif proto .code == P_IP6 :
182
- return six .text_type (IPAddress (packed_net_bytes_to_int (buf ), 6 ))
183
- 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 ])
187
- 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 ])
192
- 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' )
198
- elif proto .code == P_UNIX :
199
- size , num_bytes_read = read_varint_code (buf )
200
- return fsdecode (buf [num_bytes_read :])
201
- 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 :])
204
- raise ValueError ("unknown protocol" )
205
-
206
-
207
71
def size_for_addr (proto , buf ):
208
72
if proto .size >= 0 :
209
73
return proto .size // 8
0 commit comments