2006-12-17 06:08:05 +00:00
|
|
|
import socket, os, re, time, random
|
2009-08-16 02:36:58 +00:00
|
|
|
import select
|
2006-12-17 06:08:05 +00:00
|
|
|
from TftpShared import *
|
|
|
|
from TftpPacketTypes import *
|
|
|
|
from TftpPacketFactory import *
|
|
|
|
|
|
|
|
class TftpServer(TftpSession):
|
|
|
|
"""This class implements a tftp server object."""
|
|
|
|
|
2009-07-21 13:40:24 +00:00
|
|
|
def __init__(self, tftproot='/tftpboot', dyn_file_func=None):
|
|
|
|
"""Class constructor. It takes two optional arguments. tftproot is
|
2006-12-17 06:08:05 +00:00
|
|
|
the path to the tftproot directory to serve files from and/or write
|
2009-07-21 13:40:24 +00:00
|
|
|
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."""
|
2006-12-17 06:08:05 +00:00
|
|
|
self.listenip = None
|
|
|
|
self.listenport = None
|
|
|
|
self.sock = None
|
2009-08-16 02:36:58 +00:00
|
|
|
# FIXME: What about multiple roots?
|
2009-04-07 21:22:37 +00:00
|
|
|
self.root = os.path.abspath(tftproot)
|
2009-08-16 02:36:58 +00:00
|
|
|
self.dyn_file_func = dyn_file_func
|
2006-12-17 06:08:05 +00:00
|
|
|
# A dict of handlers, where each session is keyed by a string like
|
|
|
|
# ip:tid for the remote end.
|
|
|
|
self.handlers = {}
|
|
|
|
|
|
|
|
if os.path.exists(self.root):
|
2009-08-16 02:36:58 +00:00
|
|
|
log.debug("tftproot %s does exist" % self.root)
|
2006-12-17 06:08:05 +00:00
|
|
|
if not os.path.isdir(self.root):
|
|
|
|
raise TftpException, "The tftproot must be a directory."
|
|
|
|
else:
|
2009-08-16 02:36:58 +00:00
|
|
|
log.debug("tftproot %s is a directory" % self.root)
|
2006-12-17 06:08:05 +00:00
|
|
|
if os.access(self.root, os.R_OK):
|
2009-08-16 02:36:58 +00:00
|
|
|
log.debug("tftproot %s is readable" % self.root)
|
2006-12-17 06:08:05 +00:00
|
|
|
else:
|
|
|
|
raise TftpException, "The tftproot must be readable"
|
|
|
|
if os.access(self.root, os.W_OK):
|
2009-08-16 02:36:58 +00:00
|
|
|
log.debug("tftproot %s is writable" % self.root)
|
2006-12-17 06:08:05 +00:00
|
|
|
else:
|
2009-08-16 02:36:58 +00:00
|
|
|
log.warning("The tftproot %s is not writable" % self.root)
|
2006-12-17 06:08:05 +00:00
|
|
|
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()
|
2008-07-30 18:09:58 +00:00
|
|
|
|
2007-02-09 18:48:48 +00:00
|
|
|
# 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'
|
2009-08-16 02:36:58 +00:00
|
|
|
log.info("Server requested on ip %s, port %s"
|
2007-02-09 18:48:48 +00:00
|
|
|
% (listenip, listenport))
|
2006-12-17 06:08:05 +00:00
|
|
|
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
|
|
|
|
|
2009-08-16 02:36:58 +00:00
|
|
|
log.info("Starting receive loop...")
|
2006-12-17 06:08:05 +00:00
|
|
|
while True:
|
|
|
|
# Build the inputlist array of sockets to select() on.
|
|
|
|
inputlist = []
|
|
|
|
inputlist.append(self.sock)
|
2009-08-16 02:36:58 +00:00
|
|
|
for key in self.sessions:
|
|
|
|
inputlist.append(self.sessions[key].sock)
|
2008-07-30 18:09:58 +00:00
|
|
|
|
2006-12-17 06:08:05 +00:00
|
|
|
# Block until some socket has input on it.
|
2009-08-16 02:36:58 +00:00
|
|
|
log.debug("Performing select on this inputlist: %s" % inputlist)
|
2006-12-17 06:08:05 +00:00
|
|
|
readyinput, readyoutput, readyspecial = select.select(inputlist,
|
|
|
|
[],
|
|
|
|
[],
|
|
|
|
SOCK_TIMEOUT)
|
2008-07-30 18:09:58 +00:00
|
|
|
|
2006-12-17 06:08:05 +00:00
|
|
|
deletion_list = []
|
2008-07-30 18:09:58 +00:00
|
|
|
|
2009-08-16 02:36:58 +00:00
|
|
|
# Handle the available data, if any. Maybe we timed-out.
|
2006-12-17 06:08:05 +00:00
|
|
|
for readysock in readyinput:
|
2009-08-16 02:36:58 +00:00
|
|
|
# Is the traffic on the main server socket? ie. new session?
|
2006-12-17 06:08:05 +00:00
|
|
|
if readysock == self.sock:
|
2009-08-16 02:36:58 +00:00
|
|
|
log.debug("Data ready on our main socket")
|
2006-12-17 06:08:05 +00:00
|
|
|
buffer, (raddress, rport) = self.sock.recvfrom(MAX_BLKSIZE)
|
|
|
|
|
2009-08-16 02:36:58 +00:00
|
|
|
log.debug("Read %d bytes" % len(buffer))
|
2006-12-17 06:08:05 +00:00
|
|
|
|
2009-08-16 02:36:58 +00:00
|
|
|
recvpkt = tftp_factory.parse(buffer)
|
|
|
|
# FIXME: Is this the best way to do a session key? What
|
|
|
|
# about symmetric udp?
|
|
|
|
key = "%s:%s" % (raddress, rport)
|
2008-07-30 18:09:58 +00:00
|
|
|
|
2009-08-16 02:36:58 +00:00
|
|
|
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)
|
|
|
|
self.sessions[key].start(buffer)
|
2006-12-17 06:08:05 +00:00
|
|
|
else:
|
2009-08-16 02:36:58 +00:00
|
|
|
log.warn("received traffic on main socket for "
|
|
|
|
"existing session??")
|
2008-07-30 18:09:58 +00:00
|
|
|
|
2006-12-17 06:08:05 +00:00
|
|
|
else:
|
2009-08-16 02:36:58 +00:00
|
|
|
# Must find the owner of this traffic.
|
|
|
|
for key in self.session:
|
|
|
|
if readysock == self.session[key].sock:
|
2006-12-17 06:08:05 +00:00
|
|
|
try:
|
2009-08-16 02:36:58 +00:00
|
|
|
self.session[key].cycle()
|
|
|
|
if self.session[key].state == None:
|
|
|
|
log.info("Successful transfer.")
|
|
|
|
deletion_list.append(key)
|
2006-12-17 06:08:05 +00:00
|
|
|
break
|
|
|
|
except TftpException, err:
|
|
|
|
deletion_list.append(key)
|
2009-08-16 02:36:58 +00:00
|
|
|
log.error("Fatal exception thrown from "
|
|
|
|
"handler: %s" % str(err))
|
2006-12-17 06:08:05 +00:00
|
|
|
|
|
|
|
else:
|
2009-08-16 02:36:58 +00:00
|
|
|
log.error("Can't find the owner for this packet. "
|
|
|
|
"Discarding.")
|
2006-12-17 06:08:05 +00:00
|
|
|
|
2009-08-16 02:36:58 +00:00
|
|
|
log.debug("Looping on all handlers to check for timeouts")
|
2006-12-17 06:08:05 +00:00
|
|
|
now = time.time()
|
2009-08-16 02:36:58 +00:00
|
|
|
for key in self.sessions:
|
2006-12-17 06:08:05 +00:00
|
|
|
try:
|
2009-08-16 02:36:58 +00:00
|
|
|
self.sessions[key].checkTimeout(now)
|
2006-12-17 06:08:05 +00:00
|
|
|
except TftpException, err:
|
2009-08-16 02:36:58 +00:00
|
|
|
log.error("Fatal exception thrown from handler: %s"
|
2006-12-17 06:08:05 +00:00
|
|
|
% str(err))
|
|
|
|
deletion_list.append(key)
|
|
|
|
|
2009-08-16 02:36:58 +00:00
|
|
|
log.debug("Iterating deletion list.")
|
2006-12-17 06:08:05 +00:00
|
|
|
for key in deletion_list:
|
2009-08-16 02:36:58 +00:00
|
|
|
if self.sessions.has_key(key):
|
|
|
|
log.debug("Deleting handler %s" % key)
|
|
|
|
del self.sessions[key]
|
2006-12-17 06:08:05 +00:00
|
|
|
deletion_list = []
|