Skip to content

Commit b3e3c39

Browse files
committed
add ipv6 support from msoulier#98
1 parent bef1ea2 commit b3e3c39

File tree

3 files changed

+30
-11
lines changed

3 files changed

+30
-11
lines changed

partftpy/TftpClient.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99

1010
import logging
11+
import socket
1112
import types
1213

1314
from .TftpContexts import TftpContextClientDownload, TftpContextClientUpload
@@ -22,14 +23,15 @@ class TftpClient(TftpSession):
2223
download can be initiated via the download() method, or an upload via the
2324
upload() method."""
2425

25-
def __init__(self, host, port=69, options={}, localip=""):
26+
def __init__(self, host, port=69, options={}, localip="", af_family=socket.AF_INET):
2627
TftpSession.__init__(self)
2728
self.context = None
2829
self.host = host
2930
self.iport = port
3031
self.filename = None
3132
self.options = options
3233
self.localip = localip
34+
self.af_family = af_family
3335
if "blksize" in self.options:
3436
size = self.options["blksize"]
3537
tftpassert(int == type(size), "blksize must be an int")
@@ -71,6 +73,7 @@ def download(
7173
timeout,
7274
retries=retries,
7375
localip=self.localip,
76+
af_family=self.af_family,
7477
ports=ports
7578
)
7679
self.context.start()

partftpy/TftpContexts.py

+17-6
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,15 @@ def add_dup(self, pkt):
8484
class TftpContext(object):
8585
"""The base class of the contexts."""
8686

87-
def __init__(self, host, port, timeout, retries=DEF_TIMEOUT_RETRIES, localip="", ports=None):
87+
def __init__(self, host, port, timeout, retries=DEF_TIMEOUT_RETRIES, localip="", af_family=socket.AF_INET, ports=None):
8888
"""Constructor for the base context, setting shared instance
8989
variables."""
9090
self.file_to_transfer = None
9191
self.fileobj = None
9292
self.options = None
9393
self.packethook = None
94-
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
94+
self.af_family = af_family
95+
self.sock = socket.socket(af_family, socket.SOCK_DGRAM)
9596
for n in ports or [0]:
9697
try:
9798
if localip != "":
@@ -110,6 +111,7 @@ def __init__(self, host, port, timeout, retries=DEF_TIMEOUT_RETRIES, localip="",
110111
self.next_block = 0
111112
self.factory = TftpPacketFactory()
112113
# Note, setting the host will also set self.address, as it's a property.
114+
self.address = ""
113115
self.host = host
114116
self.port = port
115117
# The port associated with the TID
@@ -170,7 +172,12 @@ def sethost(self, host):
170172
of the host that is set.
171173
"""
172174
self.__host = host
173-
self.address = socket.gethostbyname(host)
175+
if self.af_family == socket.AF_INET:
176+
self.address = socket.gethostbyname(host)
177+
elif self.af_family == socket.AF_INET6:
178+
self.address = socket.getaddrinfo(host, 0)[0][4][0]
179+
else:
180+
raise ValueError("af_family is not supported")
174181

175182
host = property(gethost, sethost)
176183

@@ -191,7 +198,9 @@ def cycle(self):
191198
something, and dispatch appropriate action to that response.
192199
"""
193200
try:
194-
(buffer, (raddress, rport)) = self.sock.recvfrom(MAX_BLKSIZE)
201+
buffer, rai = self.sock.recvfrom(MAX_BLKSIZE)
202+
raddress = rai[0]
203+
rport = rai[1]
195204
except socket.timeout:
196205
log.warning("Timeout waiting for traffic, retrying...")
197206
raise TftpTimeout("Timed-out waiting for traffic")
@@ -247,9 +256,10 @@ def __init__(
247256
dyn_file_func=None,
248257
upload_open=None,
249258
retries=DEF_TIMEOUT_RETRIES,
259+
af_family=socket.AF_INET,
250260
ports=None,
251261
):
252-
TftpContext.__init__(self, host, port, timeout, retries, ports=ports)
262+
TftpContext.__init__(self, host, port, timeout, retries, af_family=af_family, ports=ports)
253263
# At this point we have no idea if this is a download or an upload. We
254264
# need to let the start state determine that.
255265
self.state = TftpStateServerStart(self)
@@ -305,9 +315,10 @@ def __init__(
305315
timeout,
306316
retries=DEF_TIMEOUT_RETRIES,
307317
localip="",
318+
af_family=socket.AF_INET,
308319
ports=None,
309320
):
310-
TftpContext.__init__(self, host, port, timeout, retries, localip, ports)
321+
TftpContext.__init__(self, host, port, timeout, retries, localip, af_family, ports)
311322
self.file_to_transfer = filename
312323
self.options = options
313324
self.packethook = packethook

partftpy/TftpServer.py

+9-4
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ def listen(
8585
listenport=DEF_TFTP_PORT,
8686
timeout=SOCK_TIMEOUT,
8787
retries=DEF_TIMEOUT_RETRIES,
88+
af_family=socket.AF_INET,
8889
ports=None,
8990
):
9091
"""Start a server listening on the supplied interface and port. This
@@ -93,12 +94,13 @@ def listen(
9394
tftp_factory = TftpPacketFactory()
9495

9596
listenip = listenip or "0.0.0.0"
96-
log.info("listening @ %s:%s", listenip, listenport)
97+
ip_str = listenip if af_family == socket.AF_INET else "[%s]" % (listenip,)
98+
log.info("listening @ %s:%s", ip_str, listenport)
9799
try:
98100
# FIXME - sockets should be non-blocking
99-
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
101+
self.sock = socket.socket(af_family, socket.SOCK_DGRAM)
100102
self.sock.bind((listenip, listenport))
101-
_, self.listenport = self.sock.getsockname()
103+
self.listenport = self.sock.getsockname()[1]
102104
except OSError as err:
103105
# Reraise it for now.
104106
raise err
@@ -153,7 +155,9 @@ def listen(
153155
# Is the traffic on the main server socket? ie. new session?
154156
if readysock == self.sock:
155157
log.debug("Data ready on our main socket")
156-
buffer, (raddress, rport) = self.sock.recvfrom(MAX_BLKSIZE)
158+
buffer, rai = self.sock.recvfrom(MAX_BLKSIZE)
159+
raddress = rai[0]
160+
rport = rai[1]
157161

158162
log.debug("Read %d bytes", len(buffer))
159163

@@ -177,6 +181,7 @@ def listen(
177181
self.dyn_file_func,
178182
self.upload_open,
179183
retries=retries,
184+
af_family=af_family,
180185
ports=ports,
181186
)
182187
try:

0 commit comments

Comments
 (0)