EnetTftp.Mod 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549
  1. MODULE EnetTftp;
  2. (**
  3. AUTHOR Timothée Martiel, 2015
  4. PURPOSE Ethernet network stack, TFTP protocol.
  5. *)
  6. IMPORT
  7. SYSTEM, T := Trace,
  8. EnetBase, EnetInterfaces, EnetStreams, EnetTiming, EnetTrace, EnetUdp;
  9. CONST
  10. (* Error Status *)
  11. FileNotFound * = 1;
  12. AccessViolation * = 2;
  13. DiskFull * = 3;
  14. IllegalOperation * = 4;
  15. UnknownTransferId * = 5;
  16. FileAlreadyExists * = 6;
  17. NoSuchUser * = 7;
  18. TimeoutExpired * = 1000;
  19. TransferBusy * = 1001; (** Transfer has not yet completed and cannot be used for another file *)
  20. TooMuchData * = 1002; (** User tried to send more data as there are in the file *)
  21. (* TFTP transfer states *)
  22. Idle * = 0; (** Transfer is idle and waiting for order *)
  23. WaitForData * = 1; (** Read transfer is receiving data *)
  24. WaitForAck * = 2; (** Write transfer is waiting for remote acknowledge *)
  25. Error * = 3; (** Transfer was terminated with an error *)
  26. WaitForUser * = 4; (** Write transfer is waiting for user to provide data *)
  27. (* Transfer modes *)
  28. ModeNetAscii * = 'netascii'; (** Text file transfer mode *)
  29. ModeOctet * = 'octet'; (** Binary file transfer mode *)
  30. ModeMail * = 'mail'; (** Mail file transfer mode *)
  31. (* Error handling *)
  32. Timeout * = 5000; (** Acknowledge and waiting timeout, in ms *)
  33. MaxRetries * = 3; (** Maximal number of retries for one transmission *)
  34. (* Opcodes *)
  35. OpRequestRead * = 1; (** Transfer is from remote to local. Warning: used as header opcode, do not change value *)
  36. OpRequestWrite * = 2; (** Transfer is from local to remote. Warnong: used as header opcode, do not change value *)
  37. OpData = 3;
  38. OpAck = 4;
  39. OpError = 5;
  40. (** Data transfer block size *)
  41. DataTxLen = 512;
  42. (** TFTP Header Length *)
  43. HeaderLength = 4;
  44. (** Server listening port *)
  45. TftpPort = 69;
  46. LocalPortMin = 60000;
  47. LocalPortMax = 65000;
  48. (** Produce Tracing *)
  49. Trace = FALSE;
  50. TYPE
  51. (**
  52. File transfer abstraction.
  53. Represents a unique file transfer. The datastructure can be reused multiple times.
  54. *)
  55. Transfer * = POINTER TO TransferDesc;
  56. TransferDesc * = RECORD
  57. data: ARRAY DataTxLen + HeaderLength OF CHAR; (** Internal packet buffer. Holds last sent packet *)
  58. timeoutTask: EnetBase.TaskHandler; (** Task for timeout handling *)
  59. socket: EnetUdp.Socket; (** Underlying UDP socket *)
  60. remoteAdr: EnetBase.IpAddr; (** Remote host address *)
  61. dataLength, (** Length of last sent packet *)
  62. localPort, (** Local UDP port on which the socket is listening *)
  63. remotePort: EnetBase.Int; (** Remote host UDP port *)
  64. handler: DataReceiveHandler; (** TFTP packet receiver *)
  65. handlerParam *: ANY; (** Custom parameter for the packet receiver, set by the client *)
  66. block, (** Current block ID *)
  67. remLength, (** Remaining write transfer length (0 for read transfers *)
  68. retries, (** Number of times current transfer has been retried *)
  69. op -, (** OpRequestRead for read transfer or OpRequestWrite for write transfer *)
  70. res -, (** Transfer result code *)
  71. state -: EnetBase.Int; (** Transfer state *)
  72. next: Transfer;
  73. END;
  74. (**
  75. TFTP Receiver.
  76. Is called by a receive transfer on each received data blocks.
  77. 'transfer' is the transfer, 'buf', 'ofs' and 'len' represent the received data. 'res' is the result code and 'end' signals the last block of a transfer (error or transfer completed).
  78. 'packet' points to the receive packet if no error occurred. If 'res' is not 0 then 'packet' is NIL.
  79. *)
  80. DataReceiveHandler * = PROCEDURE {DELEGATE} (transfer: Transfer; VAR buf: ARRAY OF CHAR; ofs, len, res: EnetBase.Int; packet: EnetBase.Packet; end: BOOLEAN);
  81. Listener * = PROCEDURE {DELEGATE} (transfer: Transfer; CONST file, mode: ARRAY OF CHAR; packet: EnetBase.Packet; VAR res: EnetBase.Int): BOOLEAN;
  82. (** Object wrapper for boolean for the simple blocking task handler *)
  83. Boolean = POINTER TO RECORD value: BOOLEAN END;
  84. VAR
  85. (** List of all transfers. Used for finding transfers by sockets *)
  86. transfers: Transfer;
  87. blockingCompletion: EnetBase.TaskHandler;
  88. (** Last used local port *)
  89. lastPort: EnetBase.Int;
  90. timeout: EnetTiming.Time;
  91. PROCEDURE SetListener * (listener: Listener; port: EnetBase.Int);
  92. BEGIN
  93. END SetListener;
  94. (**
  95. Start sending a file to host 'destination' with name 'name', mode 'mode'.
  96. The file is 'length' bytes long and its content is sent using 'SendData'.
  97. *)
  98. PROCEDURE WriteFile * (VAR transfer: Transfer; CONST name, mode: ARRAY OF CHAR; length: EnetBase.Int; CONST destination: EnetBase.IpAddr; VAR res: EnetBase.Int): BOOLEAN;
  99. BEGIN
  100. (* Transfer must not be used *)
  101. IF transfer = NIL THEN
  102. NEW(transfer);
  103. transfer.next := transfers;
  104. transfers := transfer
  105. END;
  106. IF (transfer.state # Idle) & (transfer.state # Error) THEN res := TransferBusy; RETURN FALSE END;
  107. (* Setup UDP layer *)
  108. transfer.localPort := GetLocalPort();
  109. IF (transfer.socket = NIL) & ~EnetUdp.NewSocket(transfer.socket, transfer.localPort, res) THEN RETURN FALSE END;
  110. transfer.remoteAdr := destination;
  111. transfer.remotePort := TftpPort;
  112. blockingCompletion := GetBlockingCompletion();
  113. IF ~EnetUdp.SetRecvHandler(transfer.socket, HandlePacket, res) THEN RETURN FALSE END;
  114. IF ~EnetUdp.SetDestination(transfer.socket, transfer.remoteAdr, transfer.remotePort, blockingCompletion, res) THEN RETURN FALSE END;
  115. IF res = EnetBase.OpInProgress THEN
  116. WHILE ~blockingCompletion.param(Boolean).value & EnetInterfaces.UpdateAll(res) DO END
  117. END;
  118. IF res # 0 THEN RETURN FALSE END;
  119. transfer.remLength := length;
  120. transfer.block := 0;
  121. transfer.op := OpRequestWrite;
  122. (* Send WRQ *)
  123. WriteRequest(OpRequestWrite, name, mode, transfer.data, transfer.dataLength);
  124. transfer.state := WaitForAck;
  125. IF ~EnetUdp.Send(transfer.socket, transfer.data, 0, transfer.dataLength, {}, NIL, res) THEN RETURN FALSE END;
  126. RETURN TRUE
  127. END WriteFile;
  128. (**
  129. Start receiving file 'name' with mode 'mode' from host 'source'.
  130. The receiver 'handler' is called on all received data buffers.
  131. *)
  132. PROCEDURE ReadFile * (VAR transfer: Transfer; CONST name, mode: ARRAY OF CHAR; CONST source: EnetBase.IpAddr; handler: DataReceiveHandler; handlerParam: ANY; VAR res: EnetBase.Int): BOOLEAN;
  133. BEGIN
  134. res := 0;
  135. (* Transfer must not be used *)
  136. IF transfer = NIL THEN
  137. NEW(transfer);
  138. NEW(transfer.timeoutTask);
  139. transfer.next := transfers;
  140. transfers := transfer
  141. END;
  142. transfer.handler := handler;
  143. transfer.handlerParam := handlerParam;
  144. IF (transfer.state # Idle) & (transfer.state # Error) THEN res := TransferBusy; RETURN FALSE END;
  145. (* Setup UDP layer *)
  146. transfer.remoteAdr := source;
  147. transfer.remotePort := TftpPort;
  148. IF (transfer.socket = NIL) THEN
  149. transfer.localPort := GetLocalPort();
  150. IF ~EnetUdp.NewSocket(transfer.socket, transfer.localPort, res) THEN RETURN FALSE END;
  151. IF ~EnetUdp.SetRecvHandler(transfer.socket, HandlePacket, res) THEN RETURN FALSE END
  152. END;
  153. blockingCompletion := GetBlockingCompletion();
  154. IF ~EnetUdp.SetDestination(transfer.socket, transfer.remoteAdr, transfer.remotePort, blockingCompletion, res) THEN RETURN FALSE END;
  155. IF res = EnetBase.OpInProgress THEN
  156. WHILE ~blockingCompletion.param(Boolean).value & EnetInterfaces.UpdateAll(res) DO END
  157. END;
  158. IF res # 0 THEN RETURN FALSE END;
  159. IF Trace THEN EnetTrace.StringLn("EnetTftp: Found read source, initiating transfer") END;
  160. transfer.timeoutTask.param := transfer;
  161. transfer.timeoutTask.handle := TimeoutHandler;
  162. transfer.remLength := 0;
  163. transfer.block := 0;
  164. transfer.op := OpRequestWrite;
  165. (* Send RRQ *)
  166. WriteRequest(OpRequestRead, name, mode, transfer.data, transfer.dataLength);
  167. transfer.block := 1;
  168. transfer.state := WaitForData;
  169. transfer.retries := 0;
  170. IF ~SendWithTimeout(transfer, res) THEN RETURN FALSE END;
  171. RETURN TRUE
  172. END ReadFile;
  173. (**
  174. Send file data for the transfer.
  175. Can be called multiple times per transfer.
  176. Transfer is automatically terminated when its remaining length has reached 0
  177. *)
  178. PROCEDURE SendData * (transfer: Transfer; CONST buf: ARRAY OF CHAR; ofs, len: EnetBase.Int; completionHandler: EnetBase.TaskHandler; VAR res: EnetBase.Int): BOOLEAN;
  179. VAR
  180. blockLen, i: EnetBase.Int;
  181. BEGIN
  182. IF len > transfer.remLength THEN NotifyError(transfer, TooMuchData, FALSE, FALSE); RETURN FALSE END;
  183. FOR i := 0 TO len DIV DataTxLen DO
  184. WHILE (transfer.state = WaitForAck) & EnetInterfaces.UpdateAll(res) DO END;
  185. IF (res # 0) THEN
  186. NotifyError(transfer, res, FALSE, FALSE);
  187. RETURN FALSE
  188. ELSIF transfer.state # WaitForUser THEN
  189. ASSERT(transfer.state = Error);
  190. res := transfer.res;
  191. RETURN FALSE
  192. END;
  193. blockLen := MIN(DataTxLen, len);
  194. DEC(transfer.remLength, blockLen);
  195. transfer.data[0] := CHR(OpData DIV 100H MOD 100H);
  196. transfer.data[1] := CHR(OpData MOD 100H);
  197. transfer.data[2] := CHR(transfer.block DIV 100H MOD 100H);
  198. transfer.data[3] := CHR(transfer.block MOD 100H);
  199. IF blockLen # 0 THEN
  200. SYSTEM.MOVE(ADDRESSOF(buf[ofs]), ADDRESSOF(transfer.data[4]), blockLen)
  201. END;
  202. transfer.dataLength := blockLen + HeaderLength;
  203. transfer.retries := 0;
  204. IF ~SendWithTimeout(transfer, res) THEN NotifyError(transfer, res, FALSE, FALSE); RETURN FALSE END;
  205. transfer.state := WaitForAck;
  206. INC(ofs, blockLen);
  207. DEC(len, blockLen)
  208. END;
  209. res := transfer.res;
  210. RETURN transfer.state # Error
  211. END SendData;
  212. (**
  213. Get error string from TFTP error code.
  214. *)
  215. PROCEDURE GetErrorString * (code: EnetBase.Int; VAR str: ARRAY OF CHAR);
  216. BEGIN
  217. CASE code OF
  218. FileNotFound: str := "File not found"
  219. |AccessViolation: str := "Access violation"
  220. |DiskFull: str := "DiskFull"
  221. |IllegalOperation: str := "IllegalOperation"
  222. |UnknownTransferId: str := "Unknown transfer id"
  223. |FileAlreadyExists: str := "File already exists"
  224. |NoSuchUser: str := "No such user"
  225. ELSE
  226. str := ""
  227. END
  228. END GetErrorString;
  229. (**
  230. Initialize a reader on a file transfer.
  231. 'reader' is the reader. It must be allocated.
  232. 'transfer' is the TFTP transfer.
  233. 'name' is the file name.
  234. 'mode' is the TFTP transfer mode (octet, netascii or mail).
  235. 'source' is the TFTP server IP address.
  236. 'bufferSize' is the stream internal buffer size.
  237. *)
  238. PROCEDURE InitReader * (reader: EnetStreams.Reader; VAR transfer: Transfer; CONST name, mode: ARRAY OF CHAR; CONST source: EnetBase.IpAddr; bufferSize: EnetBase.Int): BOOLEAN;
  239. VAR
  240. ok: BOOLEAN;
  241. BEGIN
  242. ASSERT(reader # NIL);
  243. EnetStreams.InitReader(reader^, bufferSize, transfer);
  244. ok := ReadFile(transfer, name, mode, source, StreamReceiveHandler, reader, reader.res);
  245. IF ok THEN reader.res := 0 END;
  246. RETURN ok
  247. END InitReader;
  248. (**
  249. Initialize a writer on a file transfer.
  250. 'writer' is the stream. It must be allocated.
  251. 'transfer' is the TFTP transfer descriptor.
  252. 'name' is the file name.
  253. 'mode' is the TFTP transfer mode.
  254. 'dest' is the TFTP server address.
  255. 'bufferSize' is the internal stream buffer size.
  256. 'length' is the total length of the transmitted file.
  257. *)
  258. PROCEDURE InitWriter * (writer: EnetStreams.Writer; VAR transfer: Transfer; CONST name, mode: ARRAY OF CHAR; CONST dest: EnetBase.IpAddr; bufferSize, length: EnetBase.Int): BOOLEAN;
  259. BEGIN
  260. ASSERT(writer # NIL);
  261. EnetStreams.InitWriter(writer^, bufferSize, transfer, {}, SendFromWriter);
  262. RETURN WriteFile(transfer, name, mode, length, dest, writer.res)
  263. END InitWriter;
  264. (** Handle reception of a TFTP packet *)
  265. PROCEDURE HandlePacket (socket: EnetUdp.Socket; CONST remoteAdr: EnetBase.IpAddr; remotePort: EnetBase.Int; VAR data: ARRAY OF CHAR; dataOfs, dataLen: EnetBase.Int; packet: EnetBase.Packet);
  266. VAR
  267. transfer: Transfer;
  268. res, opcode, block, sendLength: EnetBase.Int;
  269. BEGIN
  270. (* Get transfer *)
  271. transfer := GetTransferBySocket(socket);
  272. ASSERT(transfer # NIL);
  273. IF Trace THEN EnetTrace.StringLn("EnetTftp: Received TFTP packet") END;
  274. (* Check remote address and port consistency *)
  275. IF remoteAdr # transfer.remoteAdr THEN
  276. IF Trace THEN EnetTrace.StringLn("EnetTftp: Packet not from remote host") END;
  277. RETURN
  278. END;
  279. IF (transfer.remotePort # TftpPort) & (remotePort # transfer.remotePort) THEN
  280. (* Not the first data packet of a receive: not allowed to change port *)
  281. IF Trace THEN EnetTrace.StringLn("EnetTftp: Packet not from remote port") END;
  282. RETURN
  283. END;
  284. (* ACK of request specifies new transaction port *)
  285. IF remotePort # transfer.remotePort THEN
  286. IF Trace THEN EnetTrace.StringLn("EnetTftp: Updating remote port: " & transfer.remotePort & " -> " & remotePort) END;
  287. transfer.remotePort := remotePort;
  288. blockingCompletion := GetBlockingCompletion();
  289. IF ~EnetUdp.SetDestination(transfer.socket, transfer.remoteAdr, transfer.remotePort, blockingCompletion, res) THEN
  290. NotifyError(transfer, res, TRUE, FALSE);
  291. RETURN
  292. END;
  293. IF res = EnetBase.OpInProgress THEN
  294. WHILE ~blockingCompletion.param(Boolean).value & EnetInterfaces.UpdateAll(res) DO END
  295. END
  296. END;
  297. (* Transfer must be waiting for data or for ack *)
  298. IF (transfer.state # WaitForData) & (transfer.state # WaitForAck) THEN
  299. IF Trace THEN EnetTrace.StringLn("EnetTftp: Tx is not waiting for packet (" & transfer.state & ")") END;
  300. RETURN
  301. END;
  302. EnetBase.RemoveTask(transfer.socket.intf, transfer.timeoutTask);
  303. GetHeader(data, dataOfs, opcode, block);
  304. IF opcode = OpError THEN
  305. IF Trace THEN EnetTrace.StringLn("EnetTftp: Received error message: " & block) END;
  306. NotifyError(transfer, block, transfer.state = WaitForData, FALSE);
  307. RETURN
  308. ELSIF transfer.state = WaitForData THEN
  309. IF (opcode # OpData) THEN
  310. IF Trace THEN EnetTrace.StringLn("EnetTftp: Opcode is not 'data': " & opcode) END;
  311. NotifyError(transfer, IllegalOperation, TRUE, TRUE);
  312. RETURN
  313. END;
  314. IF (block # transfer.block) THEN
  315. IF Trace THEN EnetTrace.StringLn("EnetTftp: Block # is not as expected (" & block & " instead of " & transfer.block & ")") END;
  316. NotifyError(transfer, IllegalOperation, TRUE, TRUE);
  317. RETURN
  318. END;
  319. WriteAck(transfer.block, transfer.data, sendLength);
  320. IF dataLen - HeaderLength < DataTxLen THEN
  321. (* Last packet: send an ack without waiting for next packet *)
  322. IF ~EnetUdp.Send(transfer.socket, transfer.data, 0, sendLength, {}, blockingCompletion, res) THEN
  323. NotifyError(transfer, res, TRUE, FALSE);
  324. RETURN
  325. END;
  326. transfer.state := Idle;
  327. ELSE
  328. (* Other packets to receive: use timeout *)
  329. transfer.dataLength := sendLength;
  330. transfer.retries := 0;
  331. IF ~SendWithTimeout(transfer, res) THEN
  332. NotifyError(transfer, res, TRUE, FALSE);
  333. RETURN
  334. END;
  335. INC(transfer.block)
  336. END;
  337. (* Call handler *)
  338. INC(packet.payloadOffs, HeaderLength);
  339. transfer.handler(transfer, data, dataOfs + HeaderLength, dataLen - HeaderLength, 0, packet, dataLen - HeaderLength < DataTxLen)
  340. ELSE
  341. (* Waiting for ACK *)
  342. IF opcode # OpAck THEN
  343. IF Trace THEN EnetTrace.StringLn("EnetTftp: Opcode is not 'ack' (" & opcode & ")") END;
  344. NotifyError(transfer, IllegalOperation, FALSE, TRUE);
  345. RETURN
  346. END;
  347. IF (block # transfer.block) THEN
  348. IF Trace THEN EnetTrace.StringLn("EnetTftp: Block # is not as expected (" & block & " instead of " & transfer.block & ")") END;
  349. NotifyError(transfer, IllegalOperation, FALSE, TRUE);
  350. RETURN
  351. END;
  352. IF transfer.remLength > 0 THEN
  353. IF Trace THEN EnetTrace.StringLn("EnetTftp: Still " & transfer.remLength & " B to transfer") END;
  354. transfer.state := WaitForUser
  355. ELSE
  356. IF Trace THEN EnetTrace.StringLn("EnetTftp: Transfer finished") END;
  357. transfer.state := Idle
  358. END;
  359. INC(transfer.block)
  360. END
  361. END HandlePacket;
  362. (** Handle Timeout *)
  363. PROCEDURE TimeoutHandler (handler: EnetBase.TaskHandler);
  364. VAR
  365. transfer: Transfer;
  366. res: EnetBase.Int;
  367. BEGIN
  368. transfer := handler.param(Transfer);
  369. IF ((transfer.state = WaitForData) OR (transfer.state = WaitForAck)) THEN
  370. IF Trace THEN EnetTrace.StringLn("EnetTftp: Timeout") END;
  371. IF transfer.retries = MaxRetries THEN
  372. IF Trace THEN EnetTrace.StringLn("EnetTftp: Max number of retries exceeded, transfer error") END;
  373. NotifyError(transfer, TimeoutExpired, TRUE, FALSE);
  374. RETURN
  375. END;
  376. INC(transfer.retries);
  377. IF ~SendWithTimeout(transfer, res) THEN
  378. NotifyError(transfer, TimeoutExpired, TRUE, FALSE);
  379. RETURN
  380. END
  381. END
  382. END TimeoutHandler;
  383. (** Sends a packet for a transfer with a timeout for reception *)
  384. PROCEDURE SendWithTimeout (transfer: Transfer; VAR res: EnetBase.Int): BOOLEAN;
  385. BEGIN
  386. IF Trace THEN EnetTrace.StringLn("EnetTftp: Sending packet (" & transfer.dataLength & " B)") END;
  387. EnetBase.ScheduleTask(transfer.socket.intf, transfer.timeoutTask, FALSE, timeout);
  388. RETURN EnetUdp.Send(transfer.socket, transfer.data, 0, transfer.dataLength, {}, NIL, res)
  389. END SendWithTimeout;
  390. (** Sets the transfer error state to 'res'. *)
  391. PROCEDURE NotifyError (transfer: Transfer; res: EnetBase.Int; doHandle, sendErrorMsg: BOOLEAN);
  392. VAR
  393. ignoreRes: EnetBase.Int;
  394. ignore: BOOLEAN;
  395. BEGIN
  396. transfer.res := res;
  397. transfer.state := Error;
  398. IF doHandle THEN transfer.handler(transfer, transfer.data, 0, 0, res, NIL, TRUE) END;
  399. IF sendErrorMsg THEN
  400. transfer.data[0] := CHR(OpError DIV 100H MOD 100H);
  401. transfer.data[1] := CHR(OpError MOD 100H);
  402. transfer.data[2] := CHR(res DIV 100H MOD 100H);
  403. transfer.data[3] := CHR(res MOD 100H);
  404. transfer.data[4] := 0X;
  405. ignore := EnetUdp.Send(transfer.socket, transfer.data, 0, 5, {}, NIL, ignoreRes)
  406. END
  407. END NotifyError;
  408. PROCEDURE GetHeader(CONST data: ARRAY OF CHAR; ofs: EnetBase.Int; VAR opcode, block: EnetBase.Int);
  409. BEGIN
  410. opcode := ORD(data[ofs]) * 100H + ORD(data[ofs + 1]); INC(ofs, 2);
  411. block := ORD(data[ofs]) * 100H + ORD(data[ofs + 1])
  412. END GetHeader;
  413. PROCEDURE WriteRequest (opcode: EnetBase.Int; CONST filename, mode: ARRAY OF CHAR; VAR packet: ARRAY OF CHAR; VAR length: EnetBase.Int);
  414. BEGIN
  415. ASSERT((opcode = OpRequestRead) OR (opcode = OpRequestWrite));
  416. ASSERT(filename # '');
  417. ASSERT((mode = ModeNetAscii) OR (mode = ModeOctet) OR (mode = ModeMail));
  418. length := 0;
  419. packet[length] := CHR(opcode DIV 100H MOD 100H); INC(length);
  420. packet[length] := CHR(opcode MOD 100H); INC(length);
  421. CopyString(filename, packet, length);
  422. packet[length] := 0X; INC(length);
  423. CopyString(mode, packet, length);
  424. packet[length] := 0X; INC(length)
  425. END WriteRequest;
  426. PROCEDURE WriteAck (block: EnetBase.Int; VAR packet: ARRAY OF CHAR; VAR length: EnetBase.Int);
  427. BEGIN
  428. length := 0;
  429. packet[length] := CHR(OpAck DIV 100H MOD 100H);
  430. packet[length + 1] := CHR(OpAck MOD 100H); INC(length, 2);
  431. packet[length] := CHR(block DIV 100H MOD 100H);
  432. packet[length + 1] := CHR(block MOD 100H); INC(length, 2)
  433. END WriteAck;
  434. PROCEDURE GetTransferBySocket (socket: EnetUdp.Socket): Transfer;
  435. VAR
  436. cur: Transfer;
  437. BEGIN
  438. cur := transfers;
  439. WHILE (cur # NIL) & (cur.socket # socket) DO cur := cur.next END;
  440. RETURN cur
  441. END GetTransferBySocket;
  442. PROCEDURE CopyString (CONST src: ARRAY OF CHAR; VAR dest: ARRAY OF CHAR; VAR destOfs: EnetBase.Int);
  443. VAR
  444. len: EnetBase.Int;
  445. BEGIN
  446. len := 0;
  447. WHILE src[len] # 0X DO INC(len) END;
  448. IF len >= LEN(dest) THEN len := LEN(dest) END;
  449. SYSTEM.MOVE(ADDRESSOF(src[0]), ADDRESSOF(dest[destOfs]), len);
  450. INC(destOfs, len)
  451. END CopyString;
  452. PROCEDURE GetBlockingCompletion (): EnetBase.TaskHandler;
  453. VAR
  454. b: Boolean;
  455. BEGIN
  456. IF blockingCompletion = NIL THEN
  457. NEW(blockingCompletion);
  458. NEW(b);
  459. blockingCompletion.handle := BlockingCompletion;
  460. blockingCompletion.param := b;
  461. END;
  462. blockingCompletion.param(Boolean).value := FALSE;
  463. RETURN blockingCompletion
  464. END GetBlockingCompletion;
  465. PROCEDURE GetLocalPort (): LONGINT;
  466. VAR
  467. port: LONGINT;
  468. BEGIN
  469. port := lastPort;
  470. INC(lastPort);
  471. IF lastPort > LocalPortMax THEN lastPort := LocalPortMin END;
  472. RETURN port
  473. END GetLocalPort;
  474. PROCEDURE BlockingCompletion (t: EnetBase.TaskHandler);
  475. BEGIN
  476. t.param(Boolean).value := TRUE
  477. END BlockingCompletion;
  478. PROCEDURE StreamReceiveHandler (transfer: Transfer; VAR buffer: ARRAY OF CHAR; ofs, len, res: EnetBase.Int; packet: EnetBase.Packet; end: BOOLEAN);
  479. BEGIN
  480. IF end THEN
  481. transfer.handlerParam(EnetStreams.Reader).enetEndOfStream := TRUE
  482. END;
  483. IF res = 0 THEN
  484. packet.ownedByUser := TRUE;
  485. ASSERT(EnetBase.PacketFifoPut(transfer.handlerParam(EnetStreams.Reader).enetPackets, packet))
  486. END
  487. END StreamReceiveHandler;
  488. PROCEDURE SendFromWriter (access: ANY; CONST buf: ARRAY OF CHAR; ofs, len: LONGINT; flags: SET; VAR res: LONGINT);
  489. VAR
  490. ignore: BOOLEAN;
  491. BEGIN
  492. ignore := SendData(access(Transfer), buf, ofs, len, NIL, res)
  493. END SendFromWriter;
  494. PROCEDURE Init;
  495. BEGIN
  496. lastPort := LocalPortMin;
  497. timeout := EnetTiming.fromMilli(Timeout)
  498. END Init;
  499. BEGIN
  500. Init
  501. END EnetTftp.