Oberon/A2/TFTP.Mod

MODULE TFTP; (** AUTHOR "be"; PURPOSE "TFTP client"; *) IMPORT IP, UDP, Files, Random, KernelLog; CONST &#9;Ok &#61; UDP.Ok; &#9;(* General Settings *) &#9;TFTPPort &#61; 69; &#9;MaxSocketRetries &#61; 64; &#9;MaxRetries &#61; 5; &#9;MaxWait &#61; 3; &#9;BlockSize &#61; 512; &#9;DataTimeout &#61; 3000; (* ms *) &#9;AckTimeout &#61; 3000; (* ms *) &#9;(* Packet Types *) &#9;RRQ &#61; 1; &#9;WRQ &#61; 2; &#9;DATA &#61; 3; &#9;ACK &#61; 4; &#9;ERROR &#61; 5; &#9;TFTPId &#61; "TFTP Client&#58; "; TYPE &#9;ErrorMsg &#61; ARRAY 32 OF CHAR; &#9;TFTPClient* &#61; OBJECT &#9;&#9;(* Log functions *) &#9;&#9;PROCEDURE LogEnter(level&#58; SIGNED32); &#9;&#9;BEGIN IF (TraceLevel &#62;&#61; level) THEN KernelLog.Enter END &#9;&#9;END LogEnter; &#9;&#9;PROCEDURE LogExit(level&#58; SIGNED32); &#9;&#9;BEGIN IF (TraceLevel &#62;&#61; level) THEN KernelLog.Exit END &#9;&#9;END LogExit; &#9;&#9;PROCEDURE Log(level&#58; SIGNED32; s&#58; ARRAY OF CHAR); &#9;&#9;BEGIN IF (TraceLevel &#62;&#61; level) THEN KernelLog.String(s) END &#9;&#9;END Log; &#9;&#9;PROCEDURE LogInt(level, i&#58; SIZE); &#9;&#9;BEGIN IF (TraceLevel &#62;&#61; level) THEN KernelLog.Int(i, 0) END &#9;&#9;END LogInt; &#9;&#9;(* Get2 - reads a (big endian) 16bit value from &#39;buf&#39; at position &#39;ofs&#39;..&#39;ofs&#39;+1 *) &#9;&#9;PROCEDURE Get2(VAR buf&#58; ARRAY OF CHAR; ofs&#58; SIGNED32)&#58; SIGNED32; &#9;&#9;BEGIN RETURN ORD(buf&#91;ofs&#93;)*100H + ORD(buf&#91;ofs+1&#93;) &#9;&#9;END Get2; &#9;&#9;(* Put2 - writes a (big endian) 16bit value to &#39;buf&#39; at position &#39;ofs&#39;..&#39;ofs&#39;+1 *) &#9;&#9;PROCEDURE Put2(VAR buf&#58; ARRAY OF CHAR; ofs, value&#58; SIGNED32); &#9;&#9;BEGIN buf&#91;ofs&#93; &#58;&#61; CHR(value DIV 100H MOD 100H); buf&#91;ofs+1&#93; &#58;&#61; CHR(value MOD 100H) &#9;&#9;END Put2; &#9;&#9;(* PacketType - returns the type of a packet *) &#9;&#9;PROCEDURE PacketType(VAR buf&#58; ARRAY OF CHAR)&#58; SIGNED32; &#9;&#9;BEGIN RETURN Get2(buf, 0) &#9;&#9;END PacketType; &#9;&#9;(* ExtractString - extracts a 0X terminated 8bit string from a buffer *) &#9;&#9;PROCEDURE ExtractString(VAR buf&#58; ARRAY OF CHAR; VAR ofs&#58; SIGNED32; VAR s&#58; ARRAY OF CHAR); &#9;&#9;VAR pos&#58; SIGNED32; &#9;&#9;BEGIN &#9;&#9;&#9;WHILE (ofs &#60; LEN(buf)) &#38; (buf&#91;ofs&#93; # 0X) DO &#9;&#9;&#9;&#9;IF (pos &#60; LEN(s)-1) THEN s&#91;pos&#93; &#58;&#61; buf&#91;ofs&#93;; INC(pos) END; &#9;&#9;&#9;&#9;INC(ofs) &#9;&#9;&#9;END; &#9;&#9;&#9;s&#91;pos&#93; &#58;&#61; 0X; INC(ofs) &#9;&#9;END ExtractString; &#9;&#9;(* PutString - puts a 0X terminated 8bit string to a buffer *) &#9;&#9;PROCEDURE PutString(VAR buf&#58; ARRAY OF CHAR; VAR ofs&#58; SIGNED32; s&#58; ARRAY OF CHAR); &#9;&#9;VAR pos&#58; SIGNED32; &#9;&#9;BEGIN &#9;&#9;&#9;WHILE (pos &#60; LEN(s)) &#38; (s&#91;pos&#93; # 0X) DO &#9;&#9;&#9;&#9;IF (ofs &#60; LEN(buf)-1) THEN buf&#91;ofs&#93; &#58;&#61; s&#91;pos&#93;; INC(ofs) END; &#9;&#9;&#9;&#9;INC(pos) &#9;&#9;&#9;END; &#9;&#9;&#9;buf&#91;ofs&#93; &#58;&#61; 0X; INC(ofs) &#9;&#9;END PutString; &#9;&#9;(* ReceiveAck - receives a server answer *) &#9;&#9;PROCEDURE ReceiveAck(socket&#58; UDP.Socket; VAR fip&#58; IP.Adr; VAR fport&#58; SIGNED32; blockNr&#58; SIGNED32; VAR ack&#58; ARRAY OF CHAR)&#58; BOOLEAN; &#9;&#9;VAR ip&#58; IP.Adr; port, ofs, wait&#58; SIGNED32; len&#58; SIZE; res&#58; INTEGER; acked&#58; BOOLEAN; msg&#58; ARRAY 256 OF CHAR; &#9;&#9;BEGIN &#9;&#9;&#9;wait &#58;&#61; 0; &#9;&#9;&#9;REPEAT &#9;&#9;&#9;&#9;INC(wait); &#9;&#9;&#9;&#9;LogEnter(3); Log(3, TFTPId); Log(3, "waiting for ack... "); &#9;&#9;&#9;&#9;IF (wait &#62; 1) THEN Log(3, "(retry "); LogInt(3, wait); Log(3, ")") END; &#9;&#9;&#9;&#9;LogExit(3); &#9;&#9;&#9;&#9;acked &#58;&#61; FALSE; &#9;&#9;&#9;&#9;socket.Receive(ack, 0, LEN(ack), AckTimeout, ip, port, len, res); &#9;&#9;&#9;&#9;LogEnter(3); Log(3, TFTPId); &#9;&#9;&#9;&#9;IF (res &#61; UDP.Timeout) THEN Log(3, "timeout") &#9;&#9;&#9;&#9;ELSIF (res &#61; UDP.BufferOverflow) THEN Log(3, "buffer overflow ("); LogInt(3, -len); Log(3, " bytes)") &#9;&#9;&#9;&#9;ELSIF (res &#61; Ok) THEN &#9;&#9;&#9;&#9;&#9;acked &#58;&#61; (PacketType(ack) &#61; ACK) &#38; (Get2(ack, 2) &#61; blockNr) &#38; (IP.AdrsEqual(ip, fip)) &#38; ((fport &#61; port) OR (fport &#61; -1)); &#9;&#9;&#9;&#9;&#9;IF acked THEN Log(3, "got ack") ELSE Log(3, "ack failed") END; &#9;&#9;&#9;&#9;&#9;IF (PacketType(ack) &#61; ERROR) THEN &#9;&#9;&#9;&#9;&#9;&#9;wait &#58;&#61; MaxWait + 1; &#9;&#9;&#9;&#9;&#9;&#9;ofs &#58;&#61; 4; ExtractString(ack, ofs, msg); &#9;&#9;&#9;&#9;&#9;&#9;Log(3, "; error "); LogInt(3, Get2(ack, 2)); Log(3, "&#58; "); Log(3, msg) &#9;&#9;&#9;&#9;&#9;END &#9;&#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;&#9;Log(3, "unknown error "); LogInt(3, SIGNED32(res)) &#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;LogExit(3) &#9;&#9;&#9;UNTIL acked OR (res # Ok) OR (wait &#62; MaxWait); &#9;&#9;&#9;IF acked &#38; (fport &#61; -1) THEN fport &#58;&#61; port END; &#9;&#9;&#9;RETURN acked &#9;&#9;END ReceiveAck; &#9;&#9;(* SendAck - sends an ack packet *) &#9;&#9;PROCEDURE SendAck(socket&#58; UDP.Socket; fip&#58; IP.Adr; fport&#58; SIGNED32; blockNr&#58; SIGNED32; VAR res&#58; INTEGER); &#9;&#9;VAR ackHdr&#58; ARRAY 4 OF CHAR; retries&#58; SIGNED32; &#9;&#9;BEGIN &#9;&#9;&#9;Put2(ackHdr, 0, ACK); Put2(ackHdr, 2, blockNr); &#9;&#9;&#9;REPEAT &#9;&#9;&#9;&#9;INC(retries); &#9;&#9;&#9;&#9;socket.Send(fip, fport, ackHdr, 0, LEN(ackHdr), res); &#9;&#9;&#9;UNTIL (res &#61; Ok) OR (retries &#62; MaxRetries) &#9;&#9;END SendAck; &#9;&#9;(* SendError - sends an error packet *) &#9;&#9;PROCEDURE SendError(socket&#58; UDP.Socket; fip&#58; IP.Adr; fport&#58; SIGNED32; errNo&#58; SIGNED16; s&#58; ErrorMsg; VAR res&#58; INTEGER); &#9;&#9;VAR errHdr&#58; ARRAY BlockSize+4 OF CHAR; p, retries&#58; SIGNED32; &#9;&#9;BEGIN &#9;&#9;&#9;Put2(errHdr, 0, ERROR); Put2(errHdr, 2, errNo); &#9;&#9;&#9;IF ((errNo &#61; 0) &#38; (s &#61; "")) OR ((errNo &#62; 0) &#38; (errNo &#60; 8)) THEN s &#58;&#61; errorMsg&#91;errNo&#93; END; &#9;&#9;&#9;WHILE (p &#60; BlockSize-1) &#38; (s&#91;p&#93; # 0X) DO errHdr&#91;4+p&#93; &#58;&#61; s&#91;p&#93;; INC(p) END; &#9;&#9;&#9;errHdr&#91;4+p&#93; &#58;&#61; 0X; &#9;&#9;&#9;REPEAT &#9;&#9;&#9;&#9;INC(retries); &#9;&#9;&#9;&#9;socket.Send(fip, fport, errHdr, 0, p+4, res) &#9;&#9;&#9;UNTIL (res &#61; Ok) OR (retries &#62; MaxRetries) &#9;&#9;END SendError; &#9;&#9;(* GetSocket - finds &#38; initializes a free UDP socket *) &#9;&#9;PROCEDURE GetSocket*(VAR socket&#58; UDP.Socket)&#58; BOOLEAN; &#9;&#9;VAR retries, lport&#58; SIGNED32; res&#58; INTEGER; &#9;&#9;BEGIN &#9;&#9;&#9;REPEAT &#9;&#9;&#9;&#9;INC(retries); lport &#58;&#61; 1024 + generator.Integer MOD 64512; &#9;&#9;&#9;&#9;NEW(socket, lport, res) &#9;&#9;&#9;UNTIL (res # UDP.PortInUse) OR (retries &#62; MaxSocketRetries); &#9;&#9;&#9;IF (res &#61; UDP.PortInUse) THEN socket &#58;&#61; NIL END; &#9;&#9;&#9;RETURN (socket # NIL) &#9;&#9;END GetSocket; &#9;&#9;(* Send - send a file to TFTP server at fip&#58;fport *) &#9;&#9;PROCEDURE Send*(localFN, remoteFN&#58; ARRAY OF CHAR; fip&#58; IP.Adr; fport&#58; SIGNED32)&#58; BOOLEAN; &#9;&#9;VAR buf, ack&#58; ARRAY 4+BlockSize OF CHAR; socket&#58; UDP.Socket; msg&#58; ARRAY 256 OF CHAR; &#9;&#9;&#9;file&#58; Files.File; r&#58; Files.Rider; ofs, retries, blockNr, errNo&#58; SIGNED32; res&#58; INTEGER; acked&#58; BOOLEAN; &#9;&#9;BEGIN &#9;&#9;&#9;acked &#58;&#61; FALSE; &#9;&#9;&#9;file &#58;&#61; Files.Old(localFN); &#9;&#9;&#9;IF (file # NIL) THEN &#9;&#9;&#9;&#9;IF GetSocket(socket) THEN &#9;&#9;&#9;&#9;&#9;LogEnter(1); Log(1, TFTPId); Log(1, "sending &#39;"); Log(1, localFN); Log(1, "&#39;"); LogExit(1); &#9;&#9;&#9;&#9;&#9;file.Set(r, 0); &#9;&#9;&#9;&#9;&#9;(* issue a WRQ *) &#9;&#9;&#9;&#9;&#9;Put2(buf, 0, WRQ); &#9;&#9;&#9;&#9;&#9;ofs &#58;&#61; 2; &#9;&#9;&#9;&#9;&#9;PutString(buf, ofs, remoteFN); &#9;&#9;&#9;&#9;&#9;PutString(buf, ofs, "netascii"); &#9;&#9;&#9;&#9;&#9;socket.Send(fip, fport, buf, 0, ofs, res); &#9;&#9;&#9;&#9;&#9;(* wait for ACK/ERROR *) &#9;&#9;&#9;&#9;&#9;fport &#58;&#61; -1; (* allow change of fport *) &#9;&#9;&#9;&#9;&#9;IF ReceiveAck(socket, fip, fport, 0, ack) THEN &#9;&#9;&#9;&#9;&#9;&#9;(* send file *) &#9;&#9;&#9;&#9;&#9;&#9;blockNr &#58;&#61; 0; acked &#58;&#61; TRUE; &#9;&#9;&#9;&#9;&#9;&#9;WHILE &#126;r.eof &#38; acked DO &#9;&#9;&#9;&#9;&#9;&#9;&#9;INC(blockNr); &#9;&#9;&#9;&#9;&#9;&#9;&#9;Put2(buf, 0, DATA); &#9;&#9;&#9;&#9;&#9;&#9;&#9;Put2(buf, 2, blockNr); &#9;&#9;&#9;&#9;&#9;&#9;&#9;file.ReadBytes(r, buf, 4, BlockSize); &#9;&#9;&#9;&#9;&#9;&#9;&#9;REPEAT &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;INC(retries); &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;LogEnter(3); Log(3, TFTPId); Log(3, "sending block "); LogInt(3, blockNr); &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;Log(3, " ("); LogInt(3, BlockSize-r.res); Log(3, " bytes) "); &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;IF (retries &#62; 1) THEN Log(3, "(retry "); LogInt(3, retries); Log(3, ")") END; &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;LogExit(3); &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;socket.Send(fip, fport, buf, 0, 4 + BlockSize - r.res, res); &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;acked &#58;&#61; ReceiveAck(socket, fip, fport, blockNr, ack) &#9;&#9;&#9;&#9;&#9;&#9;&#9;UNTIL acked OR (retries &#62; MaxRetries); &#9;&#9;&#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;&#9;&#9;LogEnter(1); Log(1, TFTPId); &#9;&#9;&#9;&#9;&#9;&#9;IF r.eof &#38; acked THEN Log(1, "file successfully sent") &#9;&#9;&#9;&#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;&#9;&#9;&#9;Log(1, "sending failed"); &#9;&#9;&#9;&#9;&#9;&#9;&#9;IF (PacketType(ack) &#61; ERROR) THEN &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;Log(1, "&#58;"); &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;errNo &#58;&#61; Get2(ack, 2); &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;IF (errNo &#62; 0) &#38; (errNo &#60; 8) THEN Log(1, errorMsg&#91;errNo&#93;) &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;ELSIF (errNo &#61; 0) THEN &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;ofs &#58;&#61; 4; &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;ExtractString(ack, ofs, msg); &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;Log(1, msg) &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;END &#9;&#9;&#9;&#9;&#9;&#9;&#9;END &#9;&#9;&#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;&#9;&#9;LogExit(1) &#9;&#9;&#9;&#9;&#9;ELSE	(* no ACK or block number # 0 *) &#9;&#9;&#9;&#9;&#9;&#9;LogEnter(1); Log(1, TFTPId); Log(1, TFTPId); Log(1, "sending failed"); &#9;&#9;&#9;&#9;&#9;&#9;IF (PacketType(ack) &#61; ERROR) THEN &#9;&#9;&#9;&#9;&#9;&#9;&#9;Log(1, "&#58; "); &#9;&#9;&#9;&#9;&#9;&#9;&#9;errNo &#58;&#61; Get2(ack, 2); &#9;&#9;&#9;&#9;&#9;&#9;&#9;IF (errNo &#62; 0) &#38; (errNo &#60; 8) THEN Log(1, errorMsg&#91;errNo&#93;) &#9;&#9;&#9;&#9;&#9;&#9;&#9;ELSIF (errNo &#61; 0) THEN &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;ofs &#58;&#61; 4; &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;ExtractString(ack, ofs, msg); &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;Log(1, msg) &#9;&#9;&#9;&#9;&#9;&#9;&#9;END &#9;&#9;&#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;&#9;&#9;LogExit(1) &#9;&#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;&#9;socket.Close &#9;&#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;&#9;LogEnter(1); Log(1, TFTPId); Log(1, "can&#39;t get a free socket"); LogExit(1) &#9;&#9;&#9;&#9;END &#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;LogEnter(1); Log(1, TFTPId); Log(1, "file not found"); LogExit(1) &#9;&#9;&#9;END; &#9;&#9;&#9;RETURN acked &#9;&#9;END Send; &#9;&#9;(* Receive - receive a file from TFTP server at source&#58;port *) &#9;&#9;PROCEDURE Receive*(remoteFN, localFN&#58; ARRAY OF CHAR; fip&#58; IP.Adr; fport&#58; SIGNED32)&#58; BOOLEAN; &#9;&#9;VAR socket&#58; UDP.Socket; ofs, blockNr, port, retries, waitPacket&#58; SIGNED32; len&#58; SIZE; res&#58; INTEGER; ok, Abort&#58; BOOLEAN; ip&#58; IP.Adr; &#9;&#9;&#9;buf&#58; ARRAY 4+BlockSize OF CHAR; file&#58; Files.File; r&#58; Files.Rider; &#9;&#9;BEGIN &#9;&#9;&#9;ok &#58;&#61; FALSE; &#9;&#9;&#9;IF GetSocket(socket) THEN &#9;&#9;&#9;&#9;LogEnter(1); Log(1, TFTPId); Log(1, "receiving &#39;"); Log(1, remoteFN); Log(1, "&#39;"); LogExit(1); &#9;&#9;&#9;&#9;(* issue a RRQ *) &#9;&#9;&#9;&#9;Put2(buf, 0, RRQ); &#9;&#9;&#9;&#9;ofs &#58;&#61; 2; &#9;&#9;&#9;&#9;PutString(buf, ofs, remoteFN); &#9;&#9;&#9;&#9;PutString(buf, ofs, "netascii"); &#9;&#9;&#9;&#9;socket.Send(fip, fport, buf, 0, ofs, res); &#9;&#9;&#9;&#9;fport &#58;&#61; -1; (* allow change of fport *) &#9;&#9;&#9;&#9;file &#58;&#61; Files.New(localFN); &#9;&#9;&#9;&#9;ASSERT(file # NIL); &#9;&#9;&#9;&#9;file.Set(r, 0); &#9;&#9;&#9;&#9;Files.Register(file); &#9;&#9;&#9;&#9;blockNr &#58;&#61; 0; &#9;&#9;&#9;&#9;REPEAT &#9;&#9;&#9;&#9;&#9;INC(blockNr); &#9;&#9;&#9;&#9;&#9;LogEnter(3); Log(3, TFTPId); Log(3, "receiving block "); LogInt(3, blockNr); &#9;&#9;&#9;&#9;&#9;IF (retries &#62; 1) THEN Log(3, " (retry "); LogInt(3, retries); Log(3, ")") END; &#9;&#9;&#9;&#9;&#9;LogExit(3); &#9;&#9;&#9;&#9;&#9;REPEAT &#9;&#9;&#9;&#9;&#9;&#9;socket.Receive(buf, 0, LEN(buf), DataTimeout, ip, port, len, res); &#9;&#9;&#9;&#9;&#9;&#9;DEC(len, 4); &#9;&#9;&#9;&#9;&#9;&#9;IF (res &#61; Ok) THEN &#9;&#9;&#9;&#9;&#9;&#9;&#9;res &#58;&#61; -1; &#9;&#9;&#9;&#9;&#9;&#9;&#9;IF (IP.AdrsEqual(ip, fip)) &#38; ((fport &#61; port) OR (fport &#61; -1)) THEN &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;IF (fport &#61; -1) THEN fport &#58;&#61; port END; &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;IF (PacketType(buf) &#61; DATA) THEN &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;IF (Get2(buf, 2) &#61; blockNr) THEN &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;file.WriteBytes(r, buf, 4, len); &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;file.Update; &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;IF (r.res &#61; 0) THEN &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;LogEnter(3); Log(3, TFTPId); LogInt(3, len); Log(3, " bytes written"); LogExit(3); &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;SendAck(socket, fip, fport, blockNr, res); &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;Abort &#58;&#61; res # Ok &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;LogEnter(3); Log(3, TFTPId); Log(3, errorMsg&#91;3&#93;); LogExit(3); &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;SendError(socket, fip, fport, 3, "", res); &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;Abort &#58;&#61; TRUE &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;END &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;ELSE (* bad block number *) &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;INC(waitPacket); len &#58;&#61; BlockSize; &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;LogEnter(3); Log(3, TFTPId); Log(3, "Bad block number (expected "); LogInt(3, blockNr); &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;Log(3, ", got "); LogInt(3, Get2(buf, 2)); Log(3, ")"); LogExit(3) &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;END &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;ELSE (* wrong packet type *) &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;LogEnter(3); Log(3, TFTPId); Log(3, errorMsg&#91;4&#93;); LogExit(3); &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;SendError(socket, fip, fport, 4, "", res); &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;Abort &#58;&#61; TRUE &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;END &#9;&#9;&#9;&#9;&#9;&#9;&#9;ELSE (* ip/port changed *) &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;LogEnter(3); Log(3, TFTPId); Log(3, errorMsg&#91;5&#93;); LogExit(3); &#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;SendError(socket, fip, fport, 5,"", res) &#9;&#9;&#9;&#9;&#9;&#9;&#9;END &#9;&#9;&#9;&#9;&#9;&#9;ELSIF (res &#61; UDP.Timeout) THEN &#9;&#9;&#9;&#9;&#9;&#9;&#9;INC(waitPacket); len &#58;&#61; BlockSize; &#9;&#9;&#9;&#9;&#9;&#9;&#9;LogEnter(3); Log(3, TFTPId); Log(3, "Timeout ("); LogInt(3, waitPacket); Log(3, ")"); LogExit(3) &#9;&#9;&#9;&#9;&#9;&#9;ELSE (* unknown error (UDP/IP error) *) &#9;&#9;&#9;&#9;&#9;&#9;&#9;LogEnter(3); Log(3, TFTPId); Log(3, errorMsg&#91;0&#93;); LogExit(3); &#9;&#9;&#9;&#9;&#9;&#9;&#9;SendError(socket, fip, fport, 0, "", res); &#9;&#9;&#9;&#9;&#9;&#9;&#9;Abort &#58;&#61; TRUE &#9;&#9;&#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;&#9;UNTIL (res &#61; Ok) OR Abort OR (waitPacket &#62; MaxWait); &#9;&#9;&#9;&#9;UNTIL Abort OR (waitPacket &#62; MaxWait) OR (len &#60; BlockSize); &#9;&#9;&#9;&#9;LogEnter(1); Log(1, TFTPId); &#9;&#9;&#9;&#9;IF &#126;Abort &#38; (waitPacket &#60;&#61; MaxWait) &#38; (len &#60; BlockSize) THEN &#9;&#9;&#9;&#9;&#9;Log(1, "file successfully received"); &#9;&#9;&#9;&#9;&#9;file.Update; &#9;&#9;&#9;&#9;&#9;ok &#58;&#61; TRUE; &#9;&#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;&#9;Log(1, "error receiveing file") &#9;&#9;&#9;&#9;END; &#9;&#9;&#9;&#9;LogExit(1); &#9;&#9;&#9;&#9;socket.Close &#9;&#9;&#9;ELSE &#9;&#9;&#9;&#9;LogEnter(1); Log(1, TFTPId); Log(1, "can&#39;t get a free socket"); LogExit(1) &#9;&#9;&#9;END; &#9;&#9;&#9;RETURN ok &#9;&#9;END Receive; &#9;END TFTPClient; VAR TraceLevel&#58; SIGNED32; &#9;errorMsg&#58; ARRAY 8 OF ErrorMsg; &#9;generator&#58; Random.Generator; PROCEDURE Send*(localFN, remoteFN&#58; ARRAY OF CHAR; ip&#58; IP.Adr)&#58; BOOLEAN; VAR client&#58; TFTPClient; BEGIN &#9;NEW(client); &#9;RETURN client.Send(localFN, remoteFN, ip, TFTPPort) END Send; PROCEDURE Receive*(removeFN, localFN&#58; ARRAY OF CHAR; ip&#58; IP.Adr)&#58; BOOLEAN; VAR client&#58; TFTPClient; BEGIN &#9;NEW(client); &#9;RETURN client.Receive(removeFN, localFN, ip, TFTPPort) END Receive; PROCEDURE TraceLevel0*; BEGIN TraceLevel &#58;&#61; 0 END TraceLevel0; PROCEDURE TraceLevel1*; BEGIN TraceLevel &#58;&#61; 1 END TraceLevel1; PROCEDURE TraceLevel2*; BEGIN TraceLevel &#58;&#61; 2 END TraceLevel2; PROCEDURE TraceLevel3*; BEGIN TraceLevel &#58;&#61; 3 END TraceLevel3; BEGIN &#9;errorMsg&#91;0&#93; &#58;&#61; "Undefined error."; &#9;errorMsg&#91;1&#93; &#58;&#61; "File not found."; &#9;errorMsg&#91;2&#93; &#58;&#61; "Access violation."; &#9;errorMsg&#91;3&#93; &#58;&#61; "Disk full."; &#9;errorMsg&#91;4&#93; &#58;&#61; "Illegal TFTP operation."; &#9;errorMsg&#91;5&#93; &#58;&#61; "Unknown transfer ID."; &#9;errorMsg&#91;6&#93; &#58;&#61; "File already exists."; &#9;errorMsg&#91;7&#93; &#58;&#61; "No such user."; &#9;TraceLevel &#58;&#61; 1; &#9;NEW(generator) END TFTP. TFTP.TraceLevel3 TestTFTP.Mod