Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

ptftpd

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ptftpd - npm Package Compare versions

Comparing version
1.0
to
1.1
+6
-6
DESCRIPTION.rst

@@ -11,8 +11,8 @@ pTFTPd - A pure-Python TFTP tool suite

- ``bootpd`` : a BOOTP server (RFC951 and RFC1497 compliant)
- ``dhcpd`` : a simple, stripped-down DHCP server.
- ``ptftpd`` : the TFTP server (RFC1350, 2347, 2348 and 2349 compliant)
- ``pxed`` : a one-call PXE server using dhcpd and ptftpd.
- ``ptftp`` : a simple TFTP client (RFC1350, 2347, 2348 and 2349
compliant and capable)
- ``bootpd``: a BOOTP server (RFC951 and RFC1497 compliant)
- ``dhcpd``: a simple, stripped-down DHCP server.
- ``ptftpd``: the TFTP server (RFC1350, 2347, 2348 and 2349 compliant)
- ``pxed``: a one-call PXE server using dhcpd and ptftpd.
- ``ptftp``: a simple TFTP client (RFC1350, 2347, 2348 and 2349 compliant and
capable)

@@ -19,0 +19,0 @@ They all support the ``--help`` option to present the usage summary to

Metadata-Version: 2.0
Name: ptftpd
Version: 1.0
Version: 1.1
Summary: pTFTPd, a pure-Python TFTP tool suite that works

@@ -24,8 +24,8 @@ Home-page: https://github.com/mpetazzoni/ptftpd

- ``bootpd`` : a BOOTP server (RFC951 and RFC1497 compliant)
- ``dhcpd`` : a simple, stripped-down DHCP server.
- ``ptftpd`` : the TFTP server (RFC1350, 2347, 2348 and 2349 compliant)
- ``pxed`` : a one-call PXE server using dhcpd and ptftpd.
- ``ptftp`` : a simple TFTP client (RFC1350, 2347, 2348 and 2349
compliant and capable)
- ``bootpd``: a BOOTP server (RFC951 and RFC1497 compliant)
- ``dhcpd``: a simple, stripped-down DHCP server.
- ``ptftpd``: the TFTP server (RFC1350, 2347, 2348 and 2349 compliant)
- ``pxed``: a one-call PXE server using dhcpd and ptftpd.
- ``ptftp``: a simple TFTP client (RFC1350, 2347, 2348 and 2349 compliant and
capable)

@@ -32,0 +32,0 @@ They all support the ``--help`` option to present the usage summary to

@@ -1,1 +0,1 @@

{"classifiers": ["Operating System :: OS Independent", "Programming Language :: Python"], "extensions": {"python.commands": {"wrap_console": {"bootpd": "ptftplib.bootpserver:main", "dhcpd": "ptftplib.dhcpserver:main", "ptftp": "ptftplib.tftpclient:main", "ptftpd": "ptftplib.tftpserver:main", "pxed": "ptftplib.pxeserver:main"}}, "python.details": {"contacts": [{"email": "maxime.petazzoni@bulix.org", "name": "Maxime Petazzoni", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/mpetazzoni/ptftpd"}}, "python.exports": {"console_scripts": {"bootpd": "ptftplib.bootpserver:main", "dhcpd": "ptftplib.dhcpserver:main", "ptftp": "ptftplib.tftpclient:main", "ptftpd": "ptftplib.tftpserver:main", "pxed": "ptftplib.pxeserver:main"}}}, "extras": [], "generator": "bdist_wheel (0.29.0)", "license": "GNU General Public License v2", "metadata_version": "2.0", "name": "ptftpd", "run_requires": [{"requires": ["netifaces"]}], "summary": "pTFTPd, a pure-Python TFTP tool suite that works", "version": "1.0"}
{"classifiers": ["Operating System :: OS Independent", "Programming Language :: Python"], "extensions": {"python.commands": {"wrap_console": {"bootpd": "ptftplib.bootpserver:main", "dhcpd": "ptftplib.dhcpserver:main", "ptftp": "ptftplib.tftpclient:main", "ptftpd": "ptftplib.tftpserver:main", "pxed": "ptftplib.pxeserver:main"}}, "python.details": {"contacts": [{"email": "maxime.petazzoni@bulix.org", "name": "Maxime Petazzoni", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/mpetazzoni/ptftpd"}}, "python.exports": {"console_scripts": {"bootpd": "ptftplib.bootpserver:main", "dhcpd": "ptftplib.dhcpserver:main", "ptftp": "ptftplib.tftpclient:main", "ptftpd": "ptftplib.tftpserver:main", "pxed": "ptftplib.pxeserver:main"}}}, "extras": [], "generator": "bdist_wheel (0.29.0)", "license": "GNU General Public License v2", "metadata_version": "2.0", "name": "ptftpd", "run_requires": [{"requires": ["netifaces"]}], "summary": "pTFTPd, a pure-Python TFTP tool suite that works", "version": "1.1"}

@@ -1,1 +0,1 @@

{"is_release": false, "git_version": "5409dd1"}
{"is_release": false, "git_version": "5e46541"}

@@ -38,3 +38,2 @@ #!/usr/bin/env python

import sys
import time

@@ -86,3 +85,4 @@ l = logging.getLogger('bootpd')

def get_ip_config_for_interface(iface):
def get_ip_config_for_iface(iface):
"""Retrieve and return the IP address/netmask and MAC address of the

@@ -92,3 +92,3 @@ given interface."""

if 'linux' not in sys.platform:
raise NotImplementedError("get_ip_address_for_interface is not "
raise NotImplementedError("get_ip_address_for_iface is not "
"implemented on your OS.")

@@ -109,5 +109,6 @@

mac = fcntl.ioctl(s.fileno(), SIOCGIFHWADDR, ifname)
return ip_from_response(ip), ip_from_response(mask),\
mac_from_response(mac)
return ip_from_response(ip), ip_from_response(mask), \
mac_from_response(mac)
def compute_checksum(message):

@@ -133,2 +134,3 @@ """Calculates the 16-bit one's complement of the one's complement sum

def _pack_ip(ip_addr):

@@ -138,2 +140,3 @@ """Pack a dotted quad IP string into a 4 byte string."""

def _unpack_ip(ip_addr):

@@ -143,2 +146,3 @@ """Unpack a 4 byte IP address into a dotted quad string."""

def _pack_mac(mac_addr):

@@ -149,8 +153,11 @@ """Pack a MAC address (00:00:00:00:00:00) into a 6 byte string."""

class NotBootpPacketError(Exception):
"""Packet being decoded is not a BOOTP packet."""
class UninterestingBootpPacket(Exception):
"""Packet is BOOTP, but we just don't care about it."""
class BootpPacket(object):

@@ -163,4 +170,4 @@ def __init__(self, pkt):

# Strip off the ethernet frame and check the IP packet
# type. It should be UDP (0x11)
# Strip off the ethernet frame and check the IP packet type. It should
# be UDP (0x11)
pkt = pkt[14:]

@@ -170,5 +177,5 @@ if ord(pkt[9]) != IP_UDP_PROTO:

# Strip off the IP header and check the source/destination
# ports in the UDP datagram. The packet should be from port 68
# to port 67 to be BOOTP. We don't care about checksum here
# Strip off the IP header and check the source/destination ports in the
# UDP datagram. The packet should be from port 68 to port 67 to be
# BOOTP. We don't care about checksum here
header_len = (ord(pkt[0]) & 0xF) * 4

@@ -180,5 +187,5 @@ pkt = pkt[header_len:]

# Looks like a BOOTP request. Strip off the UDP headers,
# parse out the interesting data from the base BOOTP packet
# and check that the magic cookie is right.
# Looks like a BOOTP request. Strip off the UDP headers, parse out the
# interesting data from the base BOOTP packet and check that the magic
# cookie is right.
pkt = pkt[8:]

@@ -202,6 +209,7 @@ bootp_fmt = '!4xL20x6s10x64s128xL'

class BOOTPServer(object):
def __init__(self, interface, bootfile, router=None, tftp_server=None):
self.interface = interface
self.ip, self.netmask, self.mac = get_ip_config_for_interface(interface)
self.ip, self.netmask, self.mac = get_ip_config_for_iface(interface)
self.hostname = socket.gethostname()

@@ -224,3 +232,3 @@ self.bootfile = bootfile

except (NotBootpPacketError, UninterestingBootpPacket):
continue;
continue

@@ -256,4 +264,4 @@ def handle_bootp_request(self, pkt):

'128s' # PXE boot file
'L' # Magic cookie
, 0x2, 0x1, 0x6, request_pkt.xid, 0x8000,
'L', # Magic cookie
0x2, 0x1, 0x6, request_pkt.xid, 0x8000,
_pack_ip(client_ip), _pack_ip(self.tftp_server),

@@ -295,3 +303,3 @@ request_pkt.client_mac, self.hostname,

ip_header2 = struct.pack('4s4s', _pack_ip(self.ip),
_pack_ip('255.255.255.255'))
_pack_ip('255.255.255.255'))
checksum = compute_checksum(ip_header1 + ip_header2)

@@ -319,8 +327,7 @@

# Exclude using the server's address, the network's
# address, the broadcast address, and any IP already in
# use.
# Exclude using the server's address, the network's address, the
# broadcast address, and any IP already in use.
if (client_ip == server_ip or
(client_ip & netmask) == 0 or
(client_ip | netmask) == 0xFFFFFFFF):
(client_ip & netmask) == 0 or
(client_ip | netmask) == 0xFFFFFFFF):
continue

@@ -334,2 +341,3 @@

def main():

@@ -336,0 +344,0 @@ import optparse

@@ -38,3 +38,3 @@ #!/usr/bin/env python

import notify
from . import notify

@@ -87,6 +87,7 @@ l = notify.getLogger('dhcpd')

def get_ip_config_for_interface(iface):
def get_ip_config_for_iface(iface):
"""Retrieve and return the IP address/netmask of the given interface."""
if 'linux' not in sys.platform:
raise NotImplementedError("get_ip_address_for_interface is not "
raise NotImplementedError("get_ip_address_for_iface is not "
"implemented on your OS.")

@@ -109,2 +110,3 @@

def _dhcp_options(options):

@@ -118,3 +120,3 @@ """Generate a sequence of DHCP options from a raw byte stream."""

if code == 0:
i+=1
i += 1
continue

@@ -130,2 +132,3 @@ if code == 255:

def _pack_ip(ip_addr):

@@ -135,2 +138,3 @@ """Pack a dotted quad IP string into a 4 byte string."""

def _unpack_ip(ip_addr):

@@ -140,2 +144,3 @@ """Unpack a 4 byte IP address into a dotted quad string."""

def _pack_mac(mac_addr):

@@ -146,4 +151,6 @@ """Pack a MAC address (00:00:00:00:00:00) into a 6 byte string."""

def _unpack_uuid(uuid):
"""Unpack a PXE UUID to its long form (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)."""
"""Unpack a PXE UUID to its long form
(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)."""
fields = ['%02x' % x for x in struct.unpack('!16B', uuid)]

@@ -156,8 +163,11 @@ return '%s-%s-%s-%s-%s' % (''.join(fields[:4]),

class NotDhcpPacketError(Exception):
"""Packet being decoded is not a DHCP packet."""
class UninterestingDhcpPacket(Exception):
"""Packet is DHCP, but not of interest to us."""
class DhcpPacket(object):

@@ -170,4 +180,4 @@ def __init__(self, pkt):

# Strip off the ethernet frame and check the IP packet
# type. It should be UDP (0x11)
# Strip off the ethernet frame and check the IP packet type. It should
# be UDP (0x11)
pkt = pkt[14:]

@@ -177,5 +187,5 @@ if ord(pkt[9]) != IP_UDP_PROTO:

# Strip off the IP header and check the source/destination
# ports in the UDP datagram. The packet should be from port 68
# to port 67 to tentatively be DHCP.
# Strip off the IP header and check the source/destination ports in the
# UDP datagram. The packet should be from port 68 to port 67 to
# tentatively be DHCP.
header_len = (ord(pkt[0]) & 0xF) * 4

@@ -187,5 +197,4 @@ pkt = pkt[header_len:]

# Looks like a DHCP request. Parse out the interesting data
# from the base DHCP packet and check that the magic cookie is
# right.
# Looks like a DHCP request. Parse out the interesting data from the
# base DHCP packet and check that the magic cookie is right.
dhcp_fmt = '!12xL20x6s202xL'

@@ -229,6 +238,7 @@ dhcp_size = struct.calcsize(dhcp_fmt)

class DHCPServer(object):
def __init__(self, interface, bootfile, router=None, tftp_server=None):
self.interface = interface
self.ip, self.netmask, self.mac = get_ip_config_for_interface(interface)
self.ip, self.netmask, self.mac = get_ip_config_for_iface(interface)
self.bootfile = bootfile

@@ -250,3 +260,3 @@ self.router = router or self.ip

except (NotDhcpPacketError, UninterestingDhcpPacket):
continue;
continue

@@ -268,3 +278,3 @@ def handle_dhcp_request(self, pkt):

if (pkt.requested_ip and
pkt.requested_ip not in self.ips_allocated):
pkt.requested_ip not in self.ips_allocated):
self.ips_allocated[pkt.requested_ip] = timeout

@@ -281,3 +291,4 @@ ip = pkt.requested_ip

current = time.time()
old = [ip for ip,to in self.ips_allocated.iteritems() if to <= current]
old = [ip for ip, to in self.ips_allocated.iteritems()
if to <= current]
for ip in old:

@@ -301,4 +312,4 @@ l.info('Lease on %s expired' % ip)

'128s' # PXE boot file
'L' # Magic cookie
, 0x2, 0x1, 0x6, request_pkt.xid,
'L', # Magic cookie
0x2, 0x1, 0x6, request_pkt.xid,
_pack_ip(client_ip), _pack_ip(self.tftp_server),

@@ -332,8 +343,10 @@ request_pkt.client_mac, self.bootfile,

# Now the IP datagram...
ip_header1 = struct.pack('!BxH4xBB', 0x45, 20+len(reply), 0xFF, IP_UDP_PROTO)
ip_header1 = struct.pack('!BxH4xBB', 0x45, 20+len(reply), 0xFF,
IP_UDP_PROTO)
ip_header2 = struct.pack('4s4s', _pack_ip(self.ip),
_pack_ip('255.255.255.255'))
_pack_ip('255.255.255.255'))
# Header checksum computation
checksum = 0
for v in struct.unpack('!5H', ip_header1) + struct.unpack('!4H', ip_header2):
for v in struct.unpack('!5H', ip_header1) + \
struct.unpack('!4H', ip_header2):
checksum += v

@@ -367,4 +380,4 @@ if checksum > 2**16:

if (client_ip == server_ip or
(client_ip & netmask) == 0 or
(client_ip | netmask) == 0xFFFFFFFF):
(client_ip & netmask) == 0 or
(client_ip | netmask) == 0xFFFFFFFF):
continue

@@ -378,2 +391,3 @@

def main():

@@ -404,4 +418,4 @@ import optparse

notify.StreamEngine.install(l, stream=sys.stdout,
loglevel=options.loglevel,
format='%(levelname)s(%(name)s): %(message)s')
loglevel=options.loglevel,
format='%(levelname)s(%(name)s): %(message)s')

@@ -424,2 +438,1 @@ try:

pass

@@ -31,3 +31,2 @@ # Author: Maxime Petazzoni

import logging
import os
import sys

@@ -46,2 +45,3 @@

class NullEngine(logging.Handler):

@@ -82,3 +82,3 @@ """A no-op notification engine. Simply results in no logging messages being

def install(logger, stream=sys.stderr, loglevel=logging.WARNING,
format='%(message)s'):
format='%(message)s'):
handler = StreamEngine(stream, loglevel, format)

@@ -97,2 +97,3 @@ logger.addHandler(handler)

class DetailledStreamEngine(StreamEngine):

@@ -117,3 +118,3 @@ """The DetailledStreamEngine is a extension of the StreamEngine define

def install(logger, stream=sys.stderr, loglevel=logging.INFO,
format='%(message)s (%(host)s:%(port)d#%(file)s %(state)s)'):
format='%(message)s (%(host)s:%(port)d#%(file)s %(state)s)'):
handler = DetailledStreamEngine(stream, loglevel, format)

@@ -123,2 +124,3 @@ handler.addFilter(DetailFilter())

class CallbackEngine(logging.Handler):

@@ -161,2 +163,3 @@ """The CallbackEngine is another notification engine, using a callback

def getLogger(name):

@@ -172,2 +175,1 @@ """Return a named logger usable with the notification engines defined in

return l

@@ -22,3 +22,4 @@ # Author: Maxime Petazzoni

import notify
from . import notify
l = notify.getLogger('tftp-proto')

@@ -28,7 +29,7 @@ notify.NullEngine.install(l)

# Uncomment these lines to enable full protocol dump.
# import sys
# import logging
#
# notify.StreamEngine.install(l, sys.stderr, logging.DEBUG)
import sys
import logging
notify.StreamEngine.install(l, sys.stderr, logging.DEBUG)
# The following values are defined in the following RFC documents:

@@ -116,2 +117,3 @@ # - RFC1350 - The TFTP Protocol (revision 2)

class TFTPHelper:

@@ -362,3 +364,4 @@ """

l.debug(" < %s: #%d (%d bytes)" % (TFTP_OPS[OP_DATA], num, len(data)))
l.debug(" < %s: #%d (%d bytes)" %
(TFTP_OPS[OP_DATA], num, len(data)))
return num, data

@@ -437,3 +440,3 @@ except struct.error:

if opts.has_key(TFTP_OPTION_BLKSIZE):
if TFTP_OPTION_BLKSIZE in opts:
blksize = int(opts[TFTP_OPTION_BLKSIZE])

@@ -447,3 +450,3 @@ if blksize >= TFTP_BLKSIZE_MIN and blksize <= TFTP_BLKSIZE_MAX:

if opts.has_key(TFTP_OPTION_TIMEOUT):
if TFTP_OPTION_TIMEOUT in opts:
timeout = int(opts[TFTP_OPTION_TIMEOUT])

@@ -455,3 +458,3 @@ if timeout >= TFTP_TIMEOUT_MIN and timeout <= TFTP_TIMEOUT_MAX:

if opts.has_key(TFTP_OPTION_TSIZE):
if TFTP_OPTION_TSIZE in opts:
used[TFTP_OPTION_TSIZE] = int(opts[TFTP_OPTION_TSIZE])

@@ -458,0 +461,0 @@

@@ -42,2 +42,3 @@ #!/usr/bin/env python

class DHCPThread(threading.Thread):

@@ -52,2 +53,3 @@ def __init__(self, iface, bootfile, router):

def main():

@@ -80,10 +82,10 @@ import optparse

notify.StreamEngine.install(l, stream=sys.stdout,
loglevel=options.loglevel,
format='%(levelname)s(%(name)s): %(message)s')
loglevel=options.loglevel,
format='%(levelname)s(%(name)s): %(message)s')
notify.StreamEngine.install(dhcpserver.l, stream=sys.stdout,
loglevel=options.loglevel,
format='%(levelname)s(%(name)s): %(message)s')
loglevel=options.loglevel,
format='%(levelname)s(%(name)s): %(message)s')
notify.StreamEngine.install(tftpserver.l, stream=sys.stdout,
loglevel=options.loglevel,
format='%(levelname)s(%(name)s): %(message)s')
loglevel=options.loglevel,
format='%(levelname)s(%(name)s): %(message)s')

@@ -112,2 +114,1 @@ try:

pass

@@ -22,6 +22,4 @@ # Author: Maxime Petazzoni

import os
import re
import stat
import proto
from . import proto

@@ -32,3 +30,3 @@ STATE_SEND = 1

STATE_RECV = 8
STATE_RECV_OACK = 16
STATE_RECV_ACK = 16
STATE_ERROR = 32

@@ -38,2 +36,3 @@

class TFTPState:

@@ -63,7 +62,8 @@ """

(self.peer, self.op, self.path, self.filename, self.mode) = \
(peer, op, path, filename, mode)
(peer, op, path, filename, mode)
self.filepath = os.path.abspath(os.path.join(self.path, self.filename))
self.tid = None # Transfer ID
self.file = None # File object to read from or write to
self.file = None # File object to read from or
# write to
self.filesize = 0 # File size in bytes

@@ -75,4 +75,5 @@ self.state = None # Current transaction state

# Option defaults
self.opts = { proto.TFTP_OPTION_BLKSIZE:
proto.TFTP_DEFAULT_PACKET_SIZE }
self.opts = {
proto.TFTP_OPTION_BLKSIZE: proto.TFTP_DEFAULT_PACKET_SIZE
}

@@ -116,3 +117,5 @@ self.last_seen = datetime.today()

def __str__(self):
s = "TFTPState/%s for %s<%s>\n" % (proto.TFTP_OPS[self.op], self.peer, self.tid)
s = "TFTPState/%s for %s<%s>\n" % (proto.TFTP_OPS[self.op],
self.peer,
self.tid)
s += " filepath: %s\n" % self.filepath

@@ -158,3 +161,3 @@ s += " mode : %s\n" % self.mode

if opts.has_key(proto.TFTP_OPTION_TSIZE) and opts[proto.TFTP_OPTION_TSIZE] == 0:
if opts.get(proto.TFTP_OPTION_TSIZE) == 0:
opts[proto.TFTP_OPTION_TSIZE] = self.filesize

@@ -177,10 +180,10 @@

if self.state == STATE_SEND_OACK:
if self.state == STATE_SEND:
return self.__next_send()
elif self.state == STATE_SEND_OACK:
return self.__next_send_oack()
elif self.state == STATE_RECV_OACK:
return self.__next_recv_oack()
elif self.state == STATE_SEND:
return self.__next_send()
elif self.state == STATE_RECV:
return self.__next_recv()
elif self.state == STATE_RECV_ACK:
return self.__next_recv_ack()
elif self.state == STATE_ERROR:

@@ -191,15 +194,2 @@ return self.__next_error()

def __next_send_oack(self):
if self.op == proto.OP_RRQ:
self.state = STATE_SEND
else:
self.state = STATE_RECV
return proto.TFTPHelper.createOACK(self.opts)
def __next_recv_oack(self):
self.state = STATE_RECV
return proto.TFTPHelper.createACK(0)
def __next_send(self):

@@ -233,2 +223,10 @@ blksize = self.opts[proto.TFTP_OPTION_BLKSIZE]

def __next_send_oack(self):
self.state = STATE_SEND if self.op == proto.OP_RRQ else STATE_RECV
return proto.TFTPHelper.createOACK(self.opts)
def __next_recv_ack(self):
self.state = STATE_RECV
return proto.TFTPHelper.createACK(0)
def __next_recv(self):

@@ -251,3 +249,4 @@ # Convert CRLF to LF if needed

else:
print 'Undefined error occured: %s!' % errno.errorcode[e.errno]
print('Undefined error occured: {}!'
.format(errno.errorcode[e.errno]))
return proto.TFTPHelper.createERROR(proto.ERROR_UNDEF)

@@ -254,0 +253,0 @@

@@ -35,4 +35,2 @@ #!/usr/bin/env python

from datetime import datetime
from datetime import timedelta
import errno
import os

@@ -44,7 +42,6 @@ import shutil

import tempfile
import time
import notify
import proto
import state
from . import notify
from . import proto
from . import state

@@ -68,2 +65,3 @@ l = notify.getLogger('tftp')

class TFTPClient:

@@ -82,3 +80,4 @@ """

Args:
peer (tuple): a (host, port) tuple describing the server to connect to.
peer (tuple): a (host, port) tuple describing the server to connect
to.
opts (dict): a dictionnary of TFTP option values to use,

@@ -101,3 +100,3 @@ or None to disable them (defaults to None).

self.opts = _PTFTP_RFC1350_OPTS
print 'Running in RFC1350 compliance mode.'
print('Running in RFC1350 compliance mode.')
else:

@@ -108,3 +107,3 @@ if not self.opts:

# This one is mandatory
if not self.opts.has_key(proto.TFTP_OPTION_BLKSIZE):
if proto.TFTP_OPTION_BLKSIZE not in self.opts:
self.opts[proto.TFTP_OPTION_BLKSIZE] = \

@@ -127,3 +126,3 @@ _PTFTP_DEFAULT_OPTS[proto.TFTP_OPTION_BLKSIZE]

self.sock.settimeout(state.STATE_TIMEOUT_SECS)
print "Connected to %s:%d." % (self.peer[0], self.peer[1])
print('Connected to {}:{}.'.format(self.peer[0], self.peer[1]))

@@ -175,3 +174,3 @@ def finish(self):

else:
print 'Unrecognized command. Try help.'
print('Unrecognized command. Try help.')

@@ -200,16 +199,2 @@ self.finish()

def __receive_packet(self):
"""
Receive a single UDP packet from the server.
Args:
None.
Returns:
A tuple (opcode, packet data) if the packet was received succesfully
and valid, False otherwise.
"""
return opcode, request
def handle(self):

@@ -237,3 +222,2 @@ """

# Process incoming packet until the state is cleared by the

@@ -243,5 +227,5 @@ # end of a succesfull transmission or an error

try:
(request, (_, rport)) = self.sock.recvfrom(recvsize)
(request, (raddress, rport)) = self.sock.recvfrom(recvsize)
if not len(request):
(request, (_, rport)) = self.sock.recvfrom(recvsize)
(request, (raddress, rport)) = self.sock.recvfrom(recvsize)

@@ -258,7 +242,10 @@ # Still nothing?

self.PTFTP_STATE.tid = rport
print 'Communicating with {}:{}.'.format(self.peer[0], self.PTFTP_STATE.tid)
print('Communicating with {}:{}.'
.format(self.peer[0], self.PTFTP_STATE.tid))
if self.PTFTP_STATE.tid != rport:
l.debug('Ignoring packet from {}:{}, we are connected to {}:{}.'
.format(raddress, rport, raddress, self.peer[0], self.PTFTP_STATE.tid))
l.debug(
'Ignoring packet from {}:{}, we are connected to {}:{}.'
.format(raddress, rport, raddress, self.peer[0],
self.PTFTP_STATE.tid))
continue

@@ -274,4 +261,5 @@

response = proto.TFTPHelper.createERROR(proto.ERROR_ILLEGAL_OP)
elif not proto.TFTP_OPS.has_key(opcode):
self.error = (True, "Unknown or unsupported operation %d!" % opcode)
elif opcode not in proto.TFTP_OPS:
self.error = (True,
"Unknown or unsupported operation %d!" % opcode)
response = proto.TFTPHelper.createERROR(proto.ERROR_ILLEGAL_OP)

@@ -284,4 +272,4 @@ return

self.error = (True, 'Operation not supported.')
response = proto.TFTPHelper.createERROR(proto.ERROR_UNDEF,
'Operation not supported.')
response = proto.TFTPHelper.createERROR(
proto.ERROR_UNDEF, 'Operation not supported.')

@@ -293,7 +281,8 @@ if not response:

if response:
self.sock.sendto(response, (self.peer[0], self.PTFTP_STATE.tid))
self.sock.sendto(response,
(self.peer[0], self.PTFTP_STATE.tid))
def serveOACK(self, op, request):
"""
Serves OACK packets.
Serves OACK packets.

@@ -330,3 +319,3 @@ Args:

if self.PTFTP_STATE.state == state.STATE_RECV:
self.PTFTP_STATE.state = state.STATE_RECV_OACK
self.PTFTP_STATE.state = state.STATE_RECV_ACK

@@ -337,3 +326,3 @@ return self.PTFTP_STATE.next()

"""
Serves ACK packets.
Serves ACK packets.

@@ -359,3 +348,4 @@ Args:

if self.PTFTP_STATE.packetnum != num:
self.error = (True, 'Got ACK with incoherent data packet number.')
self.error = (True,
'Got ACK with incoherent data packet number.')
self.PTFTP_STATE.state = state.STATE_ERROR

@@ -365,3 +355,3 @@ self.PTFTP_STATE.error = proto.ERROR_ILLEGAL_OP

if not self.rfc1350 and num >= proto.TFTP_PACKETNUM_MAX-1:
print 'Packet number wraparound.'
print('Packet number wraparound.')

@@ -374,3 +364,3 @@ return self.PTFTP_STATE.next()

print 'ERROR: Unexpected ACK!'
print('ERROR: Unexpected ACK!')
self.error = (True, None)

@@ -412,9 +402,9 @@ return proto.TFTPHelper.createERROR(proto.ERROR_ILLEGAL_OP)

if (not self.PTFTP_STATE.done and not self.rfc1350 and
num >= proto.TFTP_PACKETNUM_MAX-1):
print 'Packet number wraparound.'
if not self.PTFTP_STATE.done and not self.rfc1350 and \
num >= proto.TFTP_PACKETNUM_MAX - 1:
print('Packet number wraparound.')
return self.PTFTP_STATE.next()
print 'ERROR: Unexpected DATA!'
print('ERROR: Unexpected DATA!')
self.error = (True, None)

@@ -459,3 +449,3 @@ return proto.TFTPHelper.createERROR(proto.ERROR_ILLEGAL_OP)

if len(args) < 1 or len(args) > 2:
print 'Usage: get [-f] <filename>'
print('Usage: get [-f] <filename>')
return False

@@ -477,4 +467,4 @@

open(filename)
print "Error: local file %s already exists!" % filename
print 'Use get -f to overwrite the local file.'
print('Error: local file {} already exists!'.format(filename))
print('Use get -f to overwrite the local file.')
return False

@@ -485,3 +475,4 @@ except IOError:

self.PTFTP_STATE = state.TFTPState(self.peer, proto.OP_RRQ,
'', filepath, self.transfer_mode, not self.rfc1350)
'', filepath, self.transfer_mode,
not self.rfc1350)

@@ -498,5 +489,5 @@ # Then, before sending anything to the server, open the file

except IOError, e:
print 'Error:', os.strerror(e.errno)
print "Can't write to temporary file %s!" % \
self.PTFTP_STATE.file.name
print('Error: {}'.format(os.strerror(e.errno)))
print('Can\'t write to temporary file {}!'
.format(self.PTFTP_STATE.file.name))
return False

@@ -512,3 +503,2 @@

# Everything's OK, let's go

@@ -527,3 +517,3 @@ print "Retrieving '%s' from the remote host..." % filename

if error and errmsg:
print 'Error:', errmsg
print('Error: {}'.format(errmsg))
# Remove the temporary file on error. The destionation file,

@@ -539,9 +529,11 @@ # if it already existed, is left untouched.

except IOError, e:
print 'Error:', os.strerror(e.errno)
print "Can't copy temporary file to local file %s!" % filename
print('Error: {}'.format(os.strerror(e.errno)))
print('Can\'t copy temporary file to local file {}!'
.format(filename))
return False
print ("Transfer complete, %d bytes (%.2f kB/s)" %
(self.PTFTP_STATE.filesize,
self.__get_speed(self.PTFTP_STATE.filesize, transfer_time)))
print('Transfer complete, {} bytes ({:.2f} kB/s)'
.format(self.PTFTP_STATE.filesize,
self.__get_speed(self.PTFTP_STATE.filesize,
transfer_time)))
self.PTFTP_STATE.file.close()

@@ -562,10 +554,10 @@ os.remove(self.PTFTP_STATE.file.name)

if len(args) != 1:
print 'Usage: put <filename>'
print('Usage: put <filename>')
return
filepath = args[0]
filename = os.path.split(filepath)
self.PTFTP_STATE = state.TFTPState(self.peer, proto.OP_WRQ,
'', filepath, self.transfer_mode, not self.rfc1350)
'', filepath, self.transfer_mode,
not self.rfc1350)

@@ -578,4 +570,4 @@ try:

except IOError, e:
print 'Error:', os.strerror(e.errno)
print "Can't read from local file %s!" % filepath
print('Error: {}'.format(os.strerror(e.errno)))
print('Can\'t read from local file {}!'.format(filepath))
return False

@@ -591,3 +583,2 @@

# Everything's OK, let's go

@@ -606,8 +597,9 @@ print "Pushing '%s' to the remote host..." % filepath

if error and errmsg:
print 'Error:', errmsg
print('Error: {}'.format(errmsg))
return False
print ("Transfer complete, %d bytes (%.2f kB/s)" %
(self.PTFTP_STATE.filesize,
self.__get_speed(self.PTFTP_STATE.filesize, transfer_time)))
print('Transfer complete, {} bytes ({:.2f} kB/s)'
.format(self.PTFTP_STATE.filesize,
self.__get_speed(self.PTFTP_STATE.filesize,
transfer_time)))
return True

@@ -617,8 +609,9 @@

if len(args) > 1:
print 'Usage: mode [newmode]'
print('Usage: mode [newmode]')
return
if not len(args):
print "Current transfer mode: %s." % self.transfer_mode
print 'Available transfer modes:', ', '.join(proto.TFTP_MODES)
print('Current transfer mode: {}.'.format(self.transfer_mode))
print('Available transfer modes: {}'
.format(', '.join(proto.TFTP_MODES)))
return

@@ -628,13 +621,15 @@

self.transfer_mode = args[0].lower()
print "Mode set to %s." % self.transfer_mode
print('Mode set to {}.'.format(self.transfer_mode))
else:
print 'Unknown transfer mode, use one of:', ', '.join(proto.TFTP_MODES)
print('Unknown transfer mode, use one of: {}'
.format(', '.join(proto.TFTP_MODES)))
def blksize(self, args):
if len(args) > 1:
print 'Usage: blksize [newsize]'
print('Usage: blksize [newsize]')
return
if not len(args):
print "Current block size: %d byte(s)." % self.opts[proto.TFTP_OPTION_BLKSIZE]
print('Current block size: {} byte(s).'
.format(self.opts[proto.TFTP_OPTION_BLKSIZE]))
return

@@ -644,9 +639,12 @@

self.opts[proto.TFTP_OPTION_BLKSIZE] = int(args[0])
print "Block size set to %d byte(s)." % self.opts[proto.TFTP_OPTION_BLKSIZE]
print('Block size set to {} byte(s).'
.format(self.opts[proto.TFTP_OPTION_BLKSIZE]))
except ValueError:
print 'Block size must be a number!'
print('Block size must be a number!')
def __get_speed(self, filesize, time):
return filesize / 1024.0 / (time.seconds + time.microseconds / 1000000.0)
return (filesize / 1024.0 /
(time.seconds + time.microseconds / 1000000.0))
def usage():

@@ -699,3 +697,3 @@ print "usage: %s [options]" % sys.argv[0]

except ValueError:
print 'Port must be a number!'
print('Port must be a number!')
return 2

@@ -706,3 +704,3 @@ if opt in ('-b', '--blksize'):

except ValueError:
print 'Block size must be a number!'
print('Block size must be a number!')
return 2

@@ -713,3 +711,4 @@ if opt in ('-m', '--mode'):

else:
print 'Transfer mode must be one of:', ', '.join(proto.TFTP_MODES)
print('Transfer mode must be one of: {}'
.format(', '.join(proto.TFTP_MODES)))
return 2

@@ -721,3 +720,3 @@ if opt in ('-r', '--rfc1350'):

client.serve_forever()
print 'Goodbye.'
print('Goodbye.')
return 0

@@ -730,2 +729,1 @@

pass

@@ -41,3 +41,2 @@ #!/usr/bin/env python

import stat
import struct
import sys

@@ -47,5 +46,5 @@ import threading

import notify
import proto
import state
from . import notify
from . import proto
from . import state

@@ -58,3 +57,4 @@ l = notify.getLogger('tftpd')

def get_ip_config_for_interface(iface):
def get_ip_config_for_iface(iface):
"""Retrieve and return the IP address/netmask and MAC address of the

@@ -73,5 +73,7 @@ given interface."""

class TFTPServerConfigurationError(Exception):
"""The configuration of the pTFTPd is incorrect."""
class TFTPServerHandler(SocketServer.DatagramRequestHandler):

@@ -98,4 +100,4 @@ """

if not proto.TFTP_OPS.has_key(opcode):
l.error("Unknown operation %d" % opn)
if opcode not in proto.TFTP_OPS:
l.error("Unknown operation %d" % opcode)
response = proto.TFTPHelper.createERROR(proto.ERROR_ILLEGAL_OP)

@@ -109,5 +111,6 @@ self.wfile.write(response)

except AttributeError:
l.error("Unsupported operation %s" % op)
response = proto.TFTPHelper.createERROR(proto.ERROR_UNDEF,
'Operation not supported by server.')
l.error("Unsupported operation %s" % opcode)
response = proto.TFTPHelper.createERROR(
proto.ERROR_UNDEF,
'Operation not supported by server.')

@@ -142,4 +145,4 @@ response = handler(opcode, request[2:])

peer_state = state.TFTPState(self.client_address, op,
self.server.root, filename, mode,
not self.server.strict_rfc1350)
self.server.root, filename, mode,
not self.server.strict_rfc1350)

@@ -151,3 +154,3 @@ if not peer_state.filepath.startswith(self.server.root):

l.warning('Out-of-jail path requested: %s!' % filename,
extra=peer_state.extra(notify.TRANSFER_FAILED))
extra=peer_state.extra(notify.TRANSFER_FAILED))
return self.finish_state(peer_state)

@@ -162,4 +165,4 @@

l.info('Serving file %s to host %s...' %
(filename, self.client_address[0]),
extra=peer_state.extra(notify.TRANSFER_STARTED))
(filename, self.client_address[0]),
extra=peer_state.extra(notify.TRANSFER_STARTED))

@@ -218,4 +221,4 @@ # Only set options if not running in RFC1350 compliance mode

peer_state = state.TFTPState(self.client_address, op,
self.server.root, filename, mode,
not self.server.strict_rfc1350)
self.server.root, filename, mode,
not self.server.strict_rfc1350)

@@ -248,5 +251,5 @@ if not peer_state.filepath.startswith(self.server.root):

peer_state.packetnum = 0
peer_state.state = state.STATE_RECV
peer_state.state = state.STATE_RECV_ACK
l.info('Upload of %s began.' % filename,
extra=peer_state.extra(notify.TRANSFER_STARTED))
extra=peer_state.extra(notify.TRANSFER_STARTED))
except IOError:

@@ -326,4 +329,4 @@ peer_state.state = state.STATE_ERROR

if (not self.server.strict_rfc1350 and
num == proto.TFTP_PACKETNUM_MAX-1):
if not self.server.strict_rfc1350 and \
num == proto.TFTP_PACKETNUM_MAX - 1:
l.debug('Packet number wraparound.')

@@ -347,3 +350,3 @@

l.info('Transfer of file %s completed.' % peer_state.filename,
extra=peer_state.extra(notify.TRANSFER_COMPLETED))
extra=peer_state.extra(notify.TRANSFER_COMPLETED))
del self.server.clients[self.client_address]

@@ -385,3 +388,3 @@ return None

l.warning('Illegal TFTP option received.',
extra=peer_state.extra(TRANSFER_FAILED))
extra=peer_state.extra(notify.TRANSFER_FAILED))
return proto.TFTPHelper.createERROR(proto.ERROR_ILLEGAL_OP)

@@ -439,15 +442,15 @@

if self.client_address not in self.server.clients:
return None
# An error packet immediately terminates a connection
if self.server.clients.has_key(self.client_address):
peer_state = self.server.clients[self.client_address]
peer_state = self.server.clients[self.client_address]
l.warning('Error packet received!',
extra=peer_state.extra(notify.TRANSFER_FAILED))
if peer_state.op == proto.OP_WRQ:
peer_state.purge()
del self.server.clients[self.client_address]
l.warning('Error packet received!',
extra=peer_state.extra(notify.TRANSFER_FAILED))
if peer_state.op == proto.OP_WRQ:
peer_state.purge()
del self.server.clients[self.client_address]
return None
class TFTPServerGarbageCollector(threading.Thread):

@@ -484,2 +487,3 @@ """

class TFTPServer(object):

@@ -496,4 +500,5 @@ def __init__(self, iface, root, port=_PTFTPD_DEFAULT_PORT,

self.ip, self.netmask, self.mac = get_ip_config_for_interface(self.iface)
self.server = SocketServer.UDPServer((self.ip, port), TFTPServerHandler)
self.ip, self.netmask, self.mac = get_ip_config_for_iface(self.iface)
self.server = SocketServer.UDPServer((self.ip, port),
TFTPServerHandler)
self.server.root = self.root

@@ -542,8 +547,8 @@ self.server.strict_rfc1350 = self.strict_rfc1350

notify.StreamEngine.install(l, stream=sys.stdout,
loglevel=options.loglevel,
format='%(levelname)s(%(name)s): %(message)s')
loglevel=options.loglevel,
format='%(levelname)s(%(name)s): %(message)s')
try:
server = TFTPServer(iface, root, options.port, options.strict_rfc1350)
server.serve_forever();
server.serve_forever()
except TFTPServerConfigurationError, e:

@@ -566,2 +571,1 @@ sys.stderr.write('TFTP server configuration error: %s!' %

pass
name = 'ptftpd'
version = '1.0'
version = '1.1'
+18
-18

@@ -1,19 +0,19 @@

ptftpd-1.0.dist-info/DESCRIPTION.rst,sha256=5KV85F1tKL1TBOAlZjX_G-xjAvmN2zOu5VUngHbuHRg,2930
ptftpd-1.0.dist-info/METADATA,sha256=mQ_7PbN1dYsUZQq58OUWhBoRWTYaYCpCSOYp_PGk4jU,3323
ptftpd-1.0.dist-info/RECORD,,
ptftpd-1.0.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110
ptftpd-1.0.dist-info/entry_points.txt,sha256=GHbaAos5ilos__c34JO80WSFVMwOj35D7fJURauuNC8,185
ptftpd-1.0.dist-info/metadata.json,sha256=ysyNGgrCVPyD8Pz8vChFtdqqxNYsJgebLDDRZQrIU1c,1066
ptftpd-1.0.dist-info/pbr.json,sha256=Syrmcw56rgOdsaBF9MO6f9IAx57Bz6lAiLqy9aXLbzU,47
ptftpd-1.0.dist-info/top_level.txt,sha256=bT3CSdhcwUBOuDY05owwMyoebq1zulemqLL7roFudIQ,9
ptftpd-1.0.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
ptftpd-1.1.dist-info/DESCRIPTION.rst,sha256=YwkcOW3iL0quNYbeZgI4-yIjBuGXfDagaEuo0oP9BhA,2913
ptftpd-1.1.dist-info/METADATA,sha256=QbCzIpucIAcolWnYegS59qCH4jQgjaPMU8WGVuHoK9g,3306
ptftpd-1.1.dist-info/RECORD,,
ptftpd-1.1.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110
ptftpd-1.1.dist-info/entry_points.txt,sha256=GHbaAos5ilos__c34JO80WSFVMwOj35D7fJURauuNC8,185
ptftpd-1.1.dist-info/metadata.json,sha256=6CIN0FD5vC0nOrz193b2UxA3icrdC8MPjJQAcXeTPTE,1066
ptftpd-1.1.dist-info/pbr.json,sha256=x7N9vTeIqxGvEPPcRwXUSQCUi4j7JjRerG7IOw3f_3A,47
ptftpd-1.1.dist-info/top_level.txt,sha256=bT3CSdhcwUBOuDY05owwMyoebq1zulemqLL7roFudIQ,9
ptftpd-1.1.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
ptftplib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
ptftplib/bootpserver.py,sha256=EQ_yCIK3eOlbA_19hpaAyYuSngZ7otxyTC6ZJ3J2SCU,12944
ptftplib/dhcpserver.py,sha256=byd_HLphsphhz7U4IrO_XwUHmaJ3UWufn2gBtd2PI08,15397
ptftplib/notify.py,sha256=lXZt-QILN4F_dPPR4_6kN_5MDsphUN8WrofrLcgLh54,5815
ptftplib/proto.py,sha256=nSfZCT1YuGn4NnCg8nhG7dNt7IQc40B2lyawph0etvk,13928
ptftplib/pxeserver.py,sha256=77piAL5v9LoyPnHqiU1uml_1-c-bSDYkuA7D9DF1mwU,3811
ptftplib/state.py,sha256=LRZ3q7G56sDabW6Wf_z4_qiV_AuEts5FTgmfyJD-UVM,8107
ptftplib/tftpclient.py,sha256=0pMgm-4zPhsR_-kG0dY641_4wZ-m6Jd4_MnknjX9XfU,22615
ptftplib/tftpserver.py,sha256=s6FHf1KtsOSkh_9ioEatnVBLRgtKXgjlCjVTR8EMERY,20268
ptftplib/version.py,sha256=AOBoThJHfzIt_0O4nQrZ083ekgdJIZr8fj1UbHyUDng,32
ptftplib/bootpserver.py,sha256=SbJI30_TWjTjF4nb4T1oyY_pZJlXoVqhJKgcQJVzG54,12917
ptftplib/dhcpserver.py,sha256=ZVsjDUT7RwrZLhURo1kKIPLl0jW8DA7pLKR9q7_DSBg,15523
ptftplib/notify.py,sha256=KnFnnxWob3jZknjtdxQR0udm69UE1QucMf62IxE-Xwc,5816
ptftplib/proto.py,sha256=U0cHhzBAi5nLYrG1hRG5KQUFLZYHYTJ3VqyzG_JVbEI,13932
ptftplib/pxeserver.py,sha256=cUScHY3yQJM_5oAyM7rlWFgkF1olHT1IuLCKHNDMTAU,3956
ptftplib/state.py,sha256=7rtG685HiAS0_WqlBuZUj3MOtBuNkfQGEucxNNxF5jc,8156
ptftplib/tftpclient.py,sha256=fl1aSzpy4Xy7ci-V_Zix2pB6piWBgwMgWSbuLSl6tmk,22941
ptftplib/tftpserver.py,sha256=invFFNPWC61hMo_bBu7WCrg5bqUuojJ9l4064rXvhrY,20465
ptftplib/version.py,sha256=Hq_NV-o1ZWyYRo2PwLuIcpAY3U8F_yNPUSNVCQI96QA,32