/usr/share/pyshared/tftpy/TftpServer.py is in python-tftpy 0.6.0-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 | """This module implements the TFTP Server functionality. Instantiate an
instance of the server, and then run the listen() method to listen for client
requests. Logging is performed via a standard logging object set in
TftpShared."""
import socket, os, time
import select
from TftpShared import *
from TftpPacketTypes import *
from TftpPacketFactory import TftpPacketFactory
from TftpContexts import TftpContextServer
class TftpServer(TftpSession):
"""This class implements a tftp server object. Run the listen() method to
listen for client requests. It takes two optional arguments. tftproot is
the path to the tftproot directory to serve files from and/or write them
to. dyn_file_func is a callable that must return a file-like object to
read from during downloads. This permits the serving of dynamic
content."""
def __init__(self, tftproot='/tftpboot', dyn_file_func=None):
self.listenip = None
self.listenport = None
self.sock = None
# FIXME: What about multiple roots?
self.root = os.path.abspath(tftproot)
self.dyn_file_func = dyn_file_func
# A dict of sessions, where each session is keyed by a string like
# ip:tid for the remote end.
self.sessions = {}
if os.path.exists(self.root):
log.debug("tftproot %s does exist" % self.root)
if not os.path.isdir(self.root):
raise TftpException, "The tftproot must be a directory."
else:
log.debug("tftproot %s is a directory" % self.root)
if os.access(self.root, os.R_OK):
log.debug("tftproot %s is readable" % self.root)
else:
raise TftpException, "The tftproot must be readable"
if os.access(self.root, os.W_OK):
log.debug("tftproot %s is writable" % self.root)
else:
log.warning("The tftproot %s is not writable" % self.root)
else:
raise TftpException, "The tftproot does not exist."
def listen(self,
listenip="",
listenport=DEF_TFTP_PORT,
timeout=SOCK_TIMEOUT):
"""Start a server listening on the supplied interface and port. This
defaults to INADDR_ANY (all interfaces) and UDP port 69. You can also
supply a different socket timeout value, if desired."""
tftp_factory = TftpPacketFactory()
# Don't use new 2.5 ternary operator yet
# listenip = listenip if listenip else '0.0.0.0'
if not listenip: listenip = '0.0.0.0'
log.info("Server requested on ip %s, port %s"
% (listenip, listenport))
try:
# FIXME - sockets should be non-blocking
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.sock.bind((listenip, listenport))
except socket.error, err:
# Reraise it for now.
raise
log.info("Starting receive loop...")
while True:
# Build the inputlist array of sockets to select() on.
inputlist = []
inputlist.append(self.sock)
for key in self.sessions:
inputlist.append(self.sessions[key].sock)
# Block until some socket has input on it.
log.debug("Performing select on this inputlist: %s" % inputlist)
readyinput, readyoutput, readyspecial = select.select(inputlist,
[],
[],
SOCK_TIMEOUT)
deletion_list = []
# Handle the available data, if any. Maybe we timed-out.
for readysock in readyinput:
# Is the traffic on the main server socket? ie. new session?
if readysock == self.sock:
log.debug("Data ready on our main socket")
buffer, (raddress, rport) = self.sock.recvfrom(MAX_BLKSIZE)
log.debug("Read %d bytes" % len(buffer))
# Forge a session key based on the client's IP and port,
# which should safely work through NAT.
key = "%s:%s" % (raddress, rport)
if not self.sessions.has_key(key):
log.debug("Creating new server context for "
"session key = %s" % key)
self.sessions[key] = TftpContextServer(raddress,
rport,
timeout,
self.root,
self.dyn_file_func)
try:
self.sessions[key].start(buffer)
except TftpException, err:
deletion_list.append(key)
log.error("Fatal exception thrown from "
"session %s: %s" % (key, str(err)))
else:
log.warn("received traffic on main socket for "
"existing session??")
log.info("Currently handling these sessions:")
for session_key, session in self.sessions.items():
log.info(" %s" % session)
else:
# Must find the owner of this traffic.
for key in self.sessions:
if readysock == self.sessions[key].sock:
log.info("Matched input to session key %s"
% key)
try:
self.sessions[key].cycle()
if self.sessions[key].state == None:
log.info("Successful transfer.")
deletion_list.append(key)
except TftpException, err:
deletion_list.append(key)
log.error("Fatal exception thrown from "
"session %s: %s"
% (key, str(err)))
# Break out of for loop since we found the correct
# session.
break
else:
log.error("Can't find the owner for this packet. "
"Discarding.")
log.debug("Looping on all sessions to check for timeouts")
now = time.time()
for key in self.sessions:
try:
self.sessions[key].checkTimeout(now)
except TftpTimeout, err:
log.error(str(err))
self.sessions[key].retry_count += 1
if self.sessions[key].retry_count >= TIMEOUT_RETRIES:
log.debug("hit max retries on %s, giving up"
% self.sessions[key])
deletion_list.append(key)
else:
log.debug("resending on session %s"
% self.sessions[key])
self.sessions[key].state.resendLast()
log.debug("Iterating deletion list.")
for key in deletion_list:
log.info('')
log.info("Session %s complete" % key)
if self.sessions.has_key(key):
log.debug("Gathering up metrics from session before deleting")
self.sessions[key].end()
metrics = self.sessions[key].metrics
if metrics.duration == 0:
log.info("Duration too short, rate undetermined")
else:
log.info("Transferred %d bytes in %.2f seconds"
% (metrics.bytes, metrics.duration))
log.info("Average rate: %.2f kbps" % metrics.kbps)
log.info("%.2f bytes in resent data" % metrics.resent_bytes)
log.info("%d duplicate packets" % metrics.dupcount)
log.debug("Deleting session %s" % key)
del self.sessions[key]
log.debug("Session list is now %s" % self.sessions)
else:
log.warn("Strange, session %s is not on the deletion list"
% key)
|