BOOTP and TFTP servers in Python?
Rosimildo da Silva
rdasilva at connecttel.com
Sun Jan 14 09:52:15 EST 2001
Grant Edwards wrote:
>
> I need a self-contained BOOTP and TFTP server application for Linux
> (portibility to other Unices would be a plus). I'd like to write it in
> Python if possible.
>
> Is it possible to write a BOOTP server in Python using the standard socket
> libs? Since TFTP is a normal UDP based protocol, I know a TFTP server is
> possible: does one exist already?
Somebody posted this a couple of weeks back....:
----------------------------------------------------------------------------------
[[ This message was both posted and mailed: see
the "To," "Cc," and "Newsgroups" headers for details. ]]
hola.
Gregory Trubetskoy <grisha at ispol.com> wrote:
> is there by a chance a tftplib.py or something that implements tftp
in
> Python out there?
I have this thing lying around. You will probably want to modify it,
but it's a complete TFTP server implementation.
I have started a tftplib.py, but I never finished it...
-----------------------------
#!/usr/bin/env python
#
# Standard imports
#
import string
import struct, socket, select
import thread
#
# Default variables
#
TFTP_PORT = 69
#
# TFTP Errors
#
class TFTPError(Exception):
pass
#
# A class for a TFTP Connection
#
class TFTPConnection:
RRQ = 1
WRQ = 2
DATA = 3
ACK = 4
ERR = 5
HDRSIZE = 4 # number of bytes for OPCODE and BLOCK in header
def __init__(self, host="", port=0,
blocksize=512, timeout=2.0, retry=5 ):
self.host = host
self.port = port
self.blocksize = blocksize
self.timeout = timeout
self.retry = retry
self.client_addr = None
self.sock = None
self.active = 0
self.blockNumber = 0
self.lastpkt = ""
self.mode = ""
self.filename = ""
self.file = None
self.bind(host, port)
# end __init__
def bind(self, host="", port=0):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.sock = sock
if host or port:
sock.bind(host, port)
# end start
def send(self, pkt=""):
self.sock.sendto(pkt, self.client_addr)
self.lastpkt = pkt
# end send
def recv(self):
sock = self.sock
F = sock.fileno()
client_addr = self.client_addr
timeout = self.timeout
retry = self.retry
while retry:
r,w,e = select.select( [F], [], [F], timeout)
if not r:
# We timed out -- retransmit
retry = retry - 1
self.retransmit()
else:
# Read data packet
pktsize = self.blocksize + self.HDRSIZE
data, addr = sock.recvfrom(pktsize)
if addr == client_addr:
break
else:
raise TFTPError(4, "Transfer timed out")
# end while
return self.parse(data)
# end recv
def parse(self, data, unpack=struct.unpack):
buf = buffer(data)
pkt = {}
opcode = pkt["opcode"] = unpack("!h", buf[:2])[0]
if ( opcode == self.RRQ ) or ( opcode == self.WRQ ):
filename, mode, junk = string.split(data[2:], "\000")
pkt["filename"] = filename
pkt["mode"] = mode
elif opcode == self.DATA:
block = pkt["block"] = unpack("!h", buf[2:4])[0]
data = pkt["data"] = buf[4:]
elif opcode == self.ACK:
block = pkt["block"] = unpack("!h", buf[2:4])[0]
elif opcode == self.ERR:
errnum = pkt["errnum"] = unpack("!h", buf[2:4])[0]
errtxt = pkt["errtxt"] = buf[4:-1]
else:
raise TFTPError(4, "Unknown packet type")
return pkt
# end recv
def retransmit(self):
self.sock.sendto(self.lastpkt, self.client_addr)
return
# end retransmit
def connect(self, addr, data):
self.client_addr = addr
RRQ = self.RRQ
WRQ = self.WRQ
DATA = self.DATA
ACK = self.ACK
ERR = self.ERR
try:
pkt = self.parse(data)
opcode = pkt["opcode"]
if opcode not in (RRQ, WRQ):
raise TFTPError(4, "Bad request")
# Start lock-step transfer
self.active = 1
if opcode == RRQ:
self.handleRRQ(pkt)
else:
self.handleWRQ(pkt)
# Loop until done
while self.active:
pkt = self.recv()
opcode = pkt["opcode"]
if opcode == DATA:
self.recvData(pkt)
elif opcode == ACK:
self.recvAck(pkt)
elif opcode == ERR:
self.recvErr(pkt)
else:
raise TFTPError(5, "Invalid opcode")
except TFTPError, detail:
self.sendError( detail[0], detail[1] )
return
# end connection
def recvAck(self, pkt):
if pkt["block"] == self.blockNumber:
# We received the correct ACK
self.handleACK(pkt)
return
# end recvAck
def recvData(self, pkt):
if pkt["block"] == self.blockNumber:
# We received the correct DATA packet
self.active = ( self.blocksize == len(pkt["data"]) )
self.handleDATA(pkt)
return
# end recvError
def recvErr(self, pkt):
self.handleERR(pkt)
self.retransmit()
# end recvErr
def sendData(self, data, pack=struct.pack):
blocksize = self.blocksize
block = self.blockNumber = self.blockNumber + 1
lendata = len(data)
format = "!hh%ds" % lendata
pkt = pack(format, self.DATA, block, data)
self.send(pkt)
self.active = (len(data) == blocksize)
# end sendData
def sendAck(self, pack=struct.pack):
block = self.blockNumber
self.blockNumber = self.blockNumber + 1
format = "!hh"
pkt = pack(format, self.ACK, block)
self.send(pkt)
# end sendAck
def sendError(self, errnum, errtext, pack=struct.pack):
errtext = errtext + "\000"
format = "!hh%ds" % len(errtext)
outdata = pack(format, self.ERR, errnum, errtext)
self.sock.sendto(outdata, self.client_addr)
return
# end sendError
#
#
# Override these handle* methods as needed
#
#
def handleRRQ(self, pkt):
filename = pkt["filename"]
mode = pkt["mode"]
self.file = self.readRequest(filename, mode)
self.sendData( self.file.read(self.blocksize) )
return
# end readFile
def handleWRQ(self, pkt):
filename = pkt["filename"]
mode = pkt["mode"]
self.file = self.writeRequest(filename, mode)
self.sendAck()
return
# end writeFile
def handleACK(self, pkt):
if self.active:
self.sendData( self.file.read(self.blocksize) )
return
# end handle ACK
def handleDATA(self, pkt):
self.sendAck()
data = pkt["data"]
self.file.write( data )
# end handleDATA
def handleERR(self, pkt):
print pkt["errtxt"]
return
# end handleERR
#
# You should definitely override these
#
def readRequest(self, filename, mode):
from StringIO import StringIO
return StringIO("")
# end readRequest
def writeRequest(self, filename, mode):
from StringIO import StringIO
return StringIO()
# end writeRequest
# end class TFTPConnection
#
# Simple TFTP Server
# Each connection is handled in its own thread.
#
class TFTPServer:
"""TFTP Server
Implements a threaded TFTP Server. Each request is handled
in its own thread
"""
def __init__(self, host="", port=TFTP_PORT,
conn=TFTPConnection, srcports=[] ):
self.host = host
self.port = port
self.conn = conn
self.srcports = srcports
self.sock = None
self.bind(host, port)
# end __init__
def bind(self, host, port):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.sock = sock
sock.bind(host, port)
# end start
def forever(self):
while 1:
data, addr = self.sock.recvfrom(516)
self.handle(addr, data)
# end serve_forever
def handle(self, addr, data):
if self.srcports:
nextport = self.srcports.pop(0)
self.srcports.append( nextport )
T = self.conn( self.host, nextport )
else:
T = self.conn( self.host )
thread.start_new_thread( T.connect, (addr, data) )
return
# end handle
# end class TFTPServer
if __name__ == "__main__":
import sys
from StringIO import StringIO
#
# Subclass to create our own TFTP Connection object
#
class MyTFTP( TFTPConnection ):
def readRequest(self, filename, mode):
randomstring = "Here is a sample string"
return StringIO( randomstring )
def writeRequest(self, filename, mode):
return StringIO()
# end class
port = 3000 + TFTP_PORT
if sys.argv[1:]:
port = string.atoi(sys.argv[1])
try:
serv = TFTPServer( "", port, conn=MyTFTP )
serv.forever()
except KeyboardInterrupt, SystemExit:
pass
------------------------------------------------------------------------------
--
Rosimildo da Silva rdasilva at connectel.com
ConnectTel, Inc. Austin, TX -- USA
Phone : 512-338-1111 Fax : 512-918-0449
Company Page: http://www.connecttel.com
Home Page: http://members.nbci.com/rosimildo/
More information about the Python-list
mailing list