Skip to content

Commit 8df76cf

Browse files
committed
Merge pull request #50
150602d adding IPv6 supprt to addr messages with example code (EthanHeilman)
2 parents 22d0256 + 150602d commit 8df76cf

3 files changed

Lines changed: 142 additions & 6 deletions

File tree

bitcoin/net.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@
2424

2525
PROTO_VERSION = 60002
2626
CADDR_TIME_VERSION = 31402
27+
IPV4_COMPAT = b"\x00" * 10 + b"\xff" * 2
28+
2729

2830
class CAddress(Serializable):
2931
def __init__(self, protover=PROTO_VERSION):
3032
self.protover = protover
3133
self.nTime = 0
3234
self.nServices = 1
33-
self.pchReserved = b"\x00" * 10 + b"\xff" * 2
35+
self.pchReserved = IPV4_COMPAT
3436
self.ip = "0.0.0.0"
3537
self.port = 0
3638

@@ -40,19 +42,35 @@ def stream_deserialize(cls, f, without_time=False):
4042
if c.protover >= CADDR_TIME_VERSION and not without_time:
4143
c.nTime = struct.unpack(b"<I", ser_read(f, 4))[0]
4244
c.nServices = struct.unpack(b"<Q", ser_read(f, 8))[0]
43-
c.pchReserved = ser_read(f, 12)
44-
c.ip = socket.inet_ntoa(ser_read(f, 4))
45+
46+
packedIP = ser_read(f, 16)
47+
48+
if bytes(packedIP[0:12]) == IPV4_COMPAT: # IPv4
49+
c.ip = socket.inet_ntop(socket.AF_INET, packedIP[12:16])
50+
else: #IPv6
51+
c.ip = socket.inet_ntop(socket.AF_INET6, packedIP)
52+
4553
c.port = struct.unpack(b">H", ser_read(f, 2))[0]
4654
return c
4755

56+
4857
def stream_serialize(self, f, without_time=False):
4958
if self.protover >= CADDR_TIME_VERSION and not without_time:
5059
f.write(struct.pack(b"<I", self.nTime))
5160
f.write(struct.pack(b"<Q", self.nServices))
52-
f.write(self.pchReserved)
53-
f.write(socket.inet_aton(self.ip))
61+
62+
protocol = socket.AF_INET
63+
if ":" in self.ip: # determine if address is IPv6
64+
f.write(socket.inet_pton(socket.AF_INET6, self.ip))
65+
else:
66+
f.write(self.pchReserved)
67+
f.write(socket.inet_pton(socket.AF_INET, self.ip))
68+
5469
f.write(struct.pack(b">H", self.port))
5570

71+
72+
73+
5674
def __repr__(self):
5775
return "CAddress(nTime=%d nServices=%i ip=%s port=%i)" % (self.nTime, self.nServices, self.ip, self.port)
5876

bitcoin/tests/test_net.py

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,63 @@
1414
from bitcoin.net import CAddress
1515

1616
class Test_CAddress(unittest.TestCase):
17-
def test_serialization(self):
17+
def test_serializationSimple(self):
1818
c = CAddress()
1919
cSerialized = c.serialize()
2020
cDeserialized = CAddress.deserialize(cSerialized)
2121
cSerializedTwice = cDeserialized.serialize()
2222
self.assertEqual(cSerialized, cSerializedTwice)
23+
24+
def test_serializationIPv4(self):
25+
c = CAddress()
26+
c.ip = "1.1.1.1"
27+
c.port = 8333
28+
c.nTime = 1420576401
29+
30+
cSerialized = c.serialize()
31+
cDeserialized = CAddress.deserialize(cSerialized)
32+
33+
self.assertEqual(c, cDeserialized)
34+
35+
cSerializedTwice = cDeserialized.serialize()
36+
self.assertEqual(cSerialized, cSerializedTwice)
37+
38+
39+
def test_serializationIPv6(self):
40+
c = CAddress()
41+
c.ip = "1234:ABCD:1234:ABCD:1234:00:ABCD:1234"
42+
c.port = 8333
43+
c.nTime = 1420576401
44+
45+
cSerialized = c.serialize()
46+
cDeserialized = CAddress.deserialize(cSerialized)
47+
48+
self.assertEqual(c, cDeserialized)
49+
50+
cSerializedTwice = cDeserialized.serialize()
51+
self.assertEqual(cSerialized, cSerializedTwice)
52+
53+
54+
def test_serializationDiff(self):
55+
# Sanity check that the serialization code preserves differences
56+
c1 = CAddress()
57+
c1.ip = "1.1.1.1"
58+
c1.port = 8333
59+
c1.nTime = 1420576401
60+
61+
c2 = CAddress()
62+
c2.ip = "1.1.1.2"
63+
c2.port = 8333
64+
c2.nTime = 1420576401
65+
66+
self.assertNotEqual(c1, c2)
67+
68+
c1Serialized = c1.serialize()
69+
c2Serialized = c2.serialize()
70+
71+
self.assertNotEqual(c1Serialized, c2Serialized)
72+
73+
c1Deserialized = CAddress.deserialize(c1Serialized)
74+
c2Deserialized = CAddress.deserialize(c2Serialized)
75+
76+
self.assertNotEqual(c1Deserialized, c2Deserialized)

examples/send-addrs-msg.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import socket, time, bitcoin
2+
from bitcoin.messages import msg_version, msg_verack, msg_addr
3+
from bitcoin.net import CAddress
4+
5+
6+
PORT = 18333
7+
8+
bitcoin.SelectParams('testnet')
9+
10+
def version_pkt(client_ip, server_ip):
11+
msg = msg_version()
12+
msg.nVersion = 70002
13+
msg.addrTo.ip = server_ip
14+
msg.addrTo.port = PORT
15+
msg.addrFrom.ip = client_ip
16+
msg.addrFrom.port = PORT
17+
18+
return msg
19+
20+
def addr_pkt( str_addrs ):
21+
msg = msg_addr()
22+
addrs = []
23+
for i in str_addrs:
24+
addr = CAddress()
25+
addr.port = 18333
26+
addr.nTime = int(time.time())
27+
addr.ip = i
28+
29+
addrs.append( addr )
30+
msg.addrs = addrs
31+
return msg
32+
33+
s = socket.socket()
34+
35+
server_ip = "192.168.0.149"
36+
client_ip = "192.168.0.13"
37+
38+
s.connect( (server_ip,PORT) )
39+
40+
# Send Version packet
41+
s.send( version_pkt(client_ip, server_ip).to_bytes() )
42+
43+
# Get Version reply
44+
print s.recv(1924)
45+
46+
# Send Verack
47+
s.send( msg_verack().to_bytes() )
48+
# Get Verack
49+
print s.recv(1024)
50+
51+
# Send Addrs
52+
s.send( addr_pkt(["252.11.1.2", "EEEE:7777:8888:AAAA::1"]).to_bytes() )
53+
54+
time.sleep(1)
55+
s.close()
56+
57+
# debug log on the server should look like:
58+
# accepted connection 192.168.0.13:39979
59+
# send version message: version 70002, blocks=317947, us=****, them=0.0.0.0:0, peer=192.168.0.13:39979
60+
# receive version message: /pythonbitcoin0.0.1/: version 70002, blocks=-1, us=192.168.0.149:18333, them=192.168.0.13:18333, peer=192.168.0.13:39979
61+
# Added 2 addresses from 192.168.0.13: 3 tried, 1706 new
62+
# disconnecting node 192.168.0.13:39979
63+
64+

0 commit comments

Comments
 (0)