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