;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; FTPS ; FTP Server ; ; Compile with FASM for Menuet ; ; note: telnet == 23, ftp cmd == 21, data on 20 use32 org 0x0 db 'MENUET01' ; 8 byte id dd 1 ; header version dd START ; program start dd I_END ; program image size dd 0x170000 ; required amount of memory dd 0x7FFF0 ; esp = 0x7FFF0 dd 0, 0 ; no params, no path include 'macros.inc' ; Various states of client connection USER_NONE equ 0 ; Awaiting a connection USER_CONNECTED equ 1 ; User just connected, prompt given USER_USERNAME equ 2 ; User given username USER_LOGGED_IN equ 3 ; User given password START: ; start of execution ; Clear the screen memory mov eax, ' ' mov edi,text mov ecx,80*30 /4 cld rep stosd call draw_window ; init the receive buffer pointer mov [buffptr], buff ; Init FTP server state machine mov [state], USER_NONE ; Open the listening socket call connect still: ; check connection status mov eax,53 mov ebx,6 ; Get socket status mov ecx,[CmdSocket] int 0x40 mov ebx, [CmdSocketStatus] mov [CmdSocketStatus], eax cmp eax, ebx je waitev ; If the socket closed by remote host, open it again. cmp eax, 7 je con ; If socket closed by Reset, open it again cmp eax, 11 je con ; If a user has just connected, start by outputting welcome msg cmp eax, 4 jne noc mov esi, loginStr0 mov edx, loginStr0_end - loginStr0 call outputStr mov [state], USER_CONNECTED jmp noc con: ; Need to call disconnect, since a remote close does not fully ; close the socket call disconnect call connect jmp noc noc: ; Display the changed connected status call draw_window waitev: mov eax,23 ; wait here for event mov ebx,1 ; Delay for up to 1s int 0x40 cmp eax,1 ; redraw request ? je red cmp eax,2 ; key in buffer ? je key cmp eax,3 ; button in buffer ? je button ; any data from the socket? mov eax, 53 mov ebx, 2 ; Get # of bytes in input queue mov ecx, [CmdSocket] int 0x40 test eax, eax jz still read_input: mov eax, 53 mov ebx, 3 ; Get a byte from socket in bl mov ecx, [CmdSocket] int 0x40 call ftpRxCmdData ; process incoming ftp command ; Keep processing data until there is no more to process mov eax, 53 mov ebx, 2 ; Get # of bytes in input queue mov ecx, [CmdSocket] int 0x40 cmp eax, 0 jne read_input ; Now redraw the text text field. ; Probably not required, since ftp requires no ; console i/o. ; Leave in for now, for debugging. ; (fall through to "red:") ; call draw_text ; jmp still red: ; REDRAW WINDOW call draw_window jmp still key: ; KEY mov eax,2 ; get but ignore int 0x40 jmp still button: mov eax,17 int 0x40 cmp ah,1 jne still ; Exit button pressed, so close socket and quit mov eax,53 mov ebx,8 mov ecx,[CmdSocket] int 0x40 ; ... terminate program mov eax,-1 int 0x40 jmp still ; ********************************************* ; ******* WINDOW DEFINITIONS AND DRAW ******** ; ********************************************* draw_window: pusha mov eax,12 mov ebx,1 int 0x40 xor eax,eax ; DRAW WINDOW mov ebx,100*65536+491 + 8 +15 mov ecx,100*65536+270 + 20 ; 20 for status bar mov edx,0x13000000 mov edi,labelt int 0x40 ; draw status bar mov eax, 13 mov ebx, 4*65536+484 + 8 +15 mov ecx, 270*65536 + 3 mov edx, 0x00557799 int 0x40 mov esi,contlen-contt ; display connected status mov edx, contt cmp [CmdSocketStatus], 4 ; 4 is connected je pcon mov esi,discontlen-discontt mov edx, discontt pcon: mov eax,4 ; status text mov ebx,380*65536+276 mov ecx,0x00ffffff int 0x40 ; Draw the text on the screen, clearing it first ; This can go when we loose debuggin info. xor eax,eax mov edi,text+80*30 mov ecx,80*30 /4 cld rep stosd call draw_text mov eax,12 mov ebx,2 int 0x40 popa ret ;*************************************************************************** ; Function ; draw_text ; ; Description ; Updates the text on the screen. This is part of the debugging code ; ; Inputs ; Character to add in bl ; ;*************************************************************************** draw_text: pusha mov esi,text mov eax,0 mov ebx,0 newletter: mov cl,[esi] cmp cl,[esi+30*80] jz noletter yesletter: mov [esi+30*80],cl ; erase character pusha mov edx, 0 ; bg colour mov ecx, ebx add ecx, 26 shl ecx, 16 mov cx, 9 mov ebx, eax add ebx, 6 shl ebx, 16 mov bx, 6 mov eax, 13 int 0x40 popa ; draw character pusha mov ecx, 0x00ffffff push bx mov ebx,eax add ebx,6 shl ebx,16 pop bx add bx,26 mov eax,4 mov edx,esi mov esi,1 int 0x40 popa noletter: add esi,1 add eax,6 cmp eax,80*6 jb newletter mov eax,0 add ebx,10 cmp ebx,24*10 jb newletter popa ret ;*************************************************************************** ; Function ; ftpRxCmdData ; ; Description ; Prcoesses incoming command data, calling a handler for each command. ; Commands are built up in buff before being processed. ; ; Inputs ; Character to add in bl ; ;*************************************************************************** ftpRxCmdData: ; Quit if we are not connected ;( This case shouldn't be necessary, but be safe ) mov al, [state] cmp al, USER_NONE je frcd_exit ; Store the incoming character mov esi, [buffptr] mov [esi], bl inc esi mov [buffptr], esi ; For debugging, show the data coming in pusha call printChar popa ; Do we have an end of line? (LF) ; if not, just exit cmp bl, 0x0a jne frcd_exit ; OK we have a complete command. ; Process, and send response ; There are a number of states involved in ftp, ; to do with logging in. mov al, [state] cmp al, USER_CONNECTED jne fs001 ; This should be the username ; TODO validate username ; OK, username accepted - ask for password mov esi, loginStr1 mov edx, loginStr1_end - loginStr1 call outputStr mov [state], USER_USERNAME ; init the receive buffer pointer mov [buffptr], buff jmp frcd_exit fs001: cmp al, USER_USERNAME jne fs002 ; This should be the password ; TODO validate password ; OK, password accepted - show they are logged in mov esi, loginStr2 mov edx, loginStr2_end - loginStr2 call outputStr mov [state], USER_LOGGED_IN ; init the receive buffer pointer mov [buffptr], buff jmp frcd_exit fs002: cmp al, USER_LOGGED_IN jne fs003 ; This should be a cmd call findCmd mov eax, [cmdPtr] cmp eax, 0 je fs002b call [cmdPtr] fs002a: ; init the receive buffer pointer mov [buffptr], buff jmp frcd_exit fs002b: ; an unsupported command was entered. ; Tell user that the command is not supported mov esi, unsupStr mov edx, unsupStr_end - unsupStr call outputStr jmp fs002a fs003: frcd_exit: ret ;*************************************************************************** ; Function ; outputStr ; ; Description ; Sends a string over the 'Command' socket ; ; Inputs ; String in esi ; Length in edx ; ;*************************************************************************** outputStr: push esi push edx mov eax,53 mov ebx,7 mov ecx,[CmdSocket] int 0x40 pop edx pop esi cmp eax, 0 je os_exit ; The TCP/IP transmit queue is full; Wait a bit, then retry pusha mov eax,5 mov ebx,1 ; Delay for up 100ms int 0x40 popa jmp outputStr os_exit: ret ;*************************************************************************** ; Function ; outputDataStr ; ; Description ; Sends a string over the 'Data' socket ; ; Inputs ; String in esi ; Length in edx ; ;*************************************************************************** outputDataStr: push esi push edx mov eax,53 mov ebx,7 mov ecx,[DataSocket] int 0x40 pop edx pop esi cmp eax, 0 je ods_exit ; The TCP/IP transmit queue is full; Wait a bit, then retry pusha mov eax,5 mov ebx,20 ; Delay for upto 200ms int 0x40 popa jmp outputDataStr ods_exit: ret ;*************************************************************************** ; Function ; printChar ; ; Description ; Writes a character to the screen; Used to display the data coming ; in from the user. Really only useful for debugging. ; ; Inputs ; Character in bl ; ;*************************************************************************** printChar: cmp bl,13 ; BEGINNING OF LINE jne nobol mov ecx,[pos] add ecx,1 boll1: sub ecx,1 mov eax,ecx xor edx,edx mov ebx,80 div ebx cmp edx,0 jne boll1 mov [pos],ecx jmp newdata nobol: cmp bl,10 ; LINE DOWN jne nolf addx1: add [pos],dword 1 mov eax,[pos] xor edx,edx mov ecx,80 div ecx cmp edx,0 jnz addx1 mov eax,[pos] jmp cm1 nolf: cmp bl,8 ; BACKSPACE jne nobasp mov eax,[pos] dec eax mov [pos],eax mov [eax+text],byte 32 mov [eax+text+60*80],byte 0 jmp newdata nobasp: cmp bl,15 ; CHARACTER jbe newdata putcha: mov eax,[pos] mov [eax+text],bl mov eax,[pos] add eax,1 cm1: mov ebx,[scroll+4] imul ebx,80 cmp eax,ebx jb noeaxz mov esi,text+80 mov edi,text mov ecx,ebx cld rep movsb mov eax,ebx sub eax,80 noeaxz: mov [pos],eax newdata: ret ;*************************************************************************** ; Function ; disconnect ; ; Description ; Closes the command socket ; ; Inputs ; None ; ;*************************************************************************** disconnect: mov eax, 53 ; Stack Interface mov ebx,8 ; Close TCP socket mov ecx,[CmdSocket] int 0x40 ret ;*************************************************************************** ; Function ; disconnectData ; ; Description ; Closes the data socket ; ; Inputs ; None ; ;*************************************************************************** disconnectData: ; This delay would be better done by allowing the socket code ; to wait for all data to pass through the stack before closing pusha mov eax,5 mov ebx,200 ; Delay for 2s int 0x40 popa mov eax, 53 ; Stack Interface mov ebx,8 ; Close TCP socket mov ecx,[DataSocket] int 0x40 ret ;*************************************************************************** ; Function ; connect ; ; Description ; Opens the command socket ; ; Inputs ; None ; ;*************************************************************************** connect: pusha mov eax, 53 ; Stack Interface mov ebx, 5 ; Open TCP socket mov esi, 0 ; No remote IP address mov edx, 0 ; No remote port mov ecx, 21 ; ftp command port id mov edi, 0 ; passive open int 0x40 mov [CmdSocket], eax popa ret ;*************************************************************************** ; Function ; connectData ; ; Description ; Opens the data socket ; ; Inputs ; None ; ;*************************************************************************** connectData: pusha mov eax, 53 ; Stack Interface mov ebx, 5 ; Open TCP socket mov esi, [DataIP] ; remote IP address mov edx, [DataPort] ; remote port mov ecx, 20 ; ftp data port id mov edi, 1 ; active open int 0x40 mov [DataSocket], eax popa ret ;*************************************************************************** ; Function ; findCmd ; ; Description ; Scans the command string for a valid command. The command string ; is in the global variable buff. ; ; Returns result in cmdPtr. This will be zero if none found ; ; Inputs ; None ; ;*************************************************************************** findCmd: ; Setup to return 'none' in cmdPtr, if we find no cmd mov eax, 0 mov [cmdPtr], eax cld mov esi, buff mov edi, CMDList fc000: cmp [edi], byte 0 ; Are we at the end of the CMDList? je fc_exit fc000a: cmpsb je fc_nextbyte ; Command is different - move to the next entry in cmd table mov esi, buff fc001: ; skip to the next command in the list cmp [edi], byte 0 je fc002 inc edi jmp fc001 fc002: add edi, 5 jmp fc000 fc_nextbyte: ; Have we reached the end of the CMD text? cmp [edi], byte 0 je fc_got ; Yes - so we have a match jmp fc000a ; No - loop back fc_got: ; Copy the function pointer for the selected command inc edi mov eax, [edi] mov [cmdPtr], eax fc_exit: ret ;*************************************************************************** ; Function ; decStr2Byte ; ; Description ; Converts the decimal string pointed to by esi to a byte ; ; Inputs ; string ptr in esi ; ; Outputs ; esi points to next character not in string ; eax holds result ( 0..255) ; ;*************************************************************************** decStr2Byte: xor eax, eax xor ebx, ebx mov ecx, 3 dsb001: mov bl, [esi] cmp bl, '0' jb dsb_exit cmp bl, '9' ja dsb_exit imul eax, 10 add eax, ebx sub eax, '0' inc esi loop dsb001 dsb_exit: ret ;*************************************************************************** ; Function ; parsePortStr ; ; Description ; Converts the parameters of the PORT command, and stores them in the ; appropriate variables. ; ; Inputs ; None ( string in global variable buff ) ; ; Outputs ; None ; ;*************************************************************************** parsePortStr: ; skip past the PORT text to get the the parameters. The command format ; is ; PORT i,i,i,i,p,p,0x0d,0x0a ; where i and p are decimal byte values, high byte first. xor eax, eax mov [DataIP], eax mov [DataPort], eax mov esi, buff + 4 ; Start at first space character pps001: cmp [esi], byte ' ' ; Look for first non space character jne pps002 inc esi jmp pps001 pps002: call decStr2Byte add [DataIP], eax ror dword [DataIP], 8 inc esi call decStr2Byte add [DataIP], eax ror dword [DataIP], 8 inc esi call decStr2Byte add [DataIP], eax ror dword [DataIP], 8 inc esi call decStr2Byte add [DataIP], eax ror dword [DataIP], 8 inc esi call decStr2Byte add [DataPort], eax shl [DataPort], 8 inc esi call decStr2Byte add [DataPort], eax ret ;*************************************************************************** ; Function ; sendDir ; ; Description ; Transmits the directory listing over the data connection. ; The data connection is already open. ; ; Inputs ; None ; ; Outputs ; None ; ;*************************************************************************** sendDir: mov ebx, dirinfoblock and dword [ebx+4], 0 ; start from zero block sd001: ; Read the next DirBlocksPerCall (=16) blocks mov eax, 70 int 0x40 ; Did we read anything? test eax, eax jz @f cmp eax, 6 jnz sd_exit @@: test ebx, ebx jz sd_exit ; Parse these blocks. There could be up to 16 files specified mov esi, text + 0x1300 + 0x20 sd002: dec ebx js sd004 push ebx ; OK, lets parse the entry. Ignore volume entries test byte [esi], 8 jnz sd003 ; Valid file or directory. Start to compose the string we will send mov edi, dirStr ; If we have been called as a result of an NLST command, we only display ; the filename cmp [buff], byte 'N' jz sd006 mov [edi], byte '-' test byte [esi], 10h jz @f mov [edi], byte 'd' @@: ; Ok, now copy across the directory listing text that will be constant ; ( I dont bother looking at the read only or archive bits ) mov ebx, tmplStr @@: inc edi mov al, [ebx] test al, al jz @f mov [edi], al inc ebx jmp @b @@: ; point to the last character of the string; ; We will write the file size here, backwards push edi ; Remember where the string ends dec edi ; eax holds the number mov eax, [esi+32] mov ebx, 10 @@: xor edx, edx div ebx add dl, '0' mov [edi], dl dec edi test eax, eax jnz @b pop edi ; now create the time & date fields ;timeStr: db ' Jan 1 2000 ',0 mov al, ' ' stosb movzx eax, byte [esi+28+1] mov eax, dword [months + (eax-1)*4] stosd mov al, byte [esi+28] aam test ah, ah jz sd005a xchg al, ah add ax, '00' jmp sd005b sd005a: add al, '0' mov ah, ' ' sd005b: stosw mov al, ' ' mov ecx, 6 rep stosb push edi movzx eax, word [esi+28+2] @@: xor edx, edx div ebx add dl, '0' mov [edi], dl dec edi test eax, eax jnz @b pop edi inc edi mov al, ' ' stosb sd006: ; ** End of copying ; now copy the filename across lea ebx, [esi+40] @@: mov al, [ebx] inc ebx test al, al jz @f stosb jmp @b @@: terminate: ; Now terminate the line by putting CRLF sequence in mov al, 0x0D stosb mov al, 0x0A stosb ; Send the completed line to the user over data socket push esi mov esi, dirStr mov edx, edi sub edx, esi call outputDataStr pop esi sd003: ; Move to next entry in the block pop ebx add esi, 304 jmp sd002 sd004: mov ebx, dirinfoblock add dword [ebx+4], DirBlocksPerCall jmp sd001 sd_exit: ret ;*************************************************************************** ; Function ; setupFilePath ; ; Description ; Copies the file name from the input request string into the ; file descriptor ; ; Inputs ; None ; ; Outputs ; None ; ;*************************************************************************** setupFilePath: mov esi, buff + 4 ; Point to (1 before) first character of file ; Skip any trailing spaces or / character sfp001: inc esi cmp [esi], byte ' ' je sfp001 cmp [esi], byte '/' je sfp001 ; esi points to start of filename. ; Copy across the directory path '/' ; into the fileinfoblock mov edi, filename mov dword [edi], '/RD/' mov word [edi+4], '1/' add edi, 6 ; Copy across the filename sfp002: cld movsb cmp [esi], byte 0x0d jne sfp002 mov [edi], byte 0 ret ;*************************************************************************** ; Function ; sendFile ; ; Description ; Transmits the requested file over the open data socket ; The file to send is named in the buff string ; ; Inputs ; None ; ; Outputs ; None ; ;*************************************************************************** sendFile: call setupFilePath ; init fileblock descriptor, for file read mov ebx, fileinfoblock and dword [ebx], 0 ; read cmd and dword [ebx+4], 0 ; first block sf002a: ; now read the file.. mov eax,70 int 0x40 test eax, eax jz @f cmp eax, 6 jnz sf_exit @@: push eax mov esi, text + 0x1300 mov edx, ebx call outputDataStr pop eax test eax, eax jnz sf_exit ; wait a bit mov eax, 5 mov ebx, 10 int 0x40 mov ebx, fileinfoblock add dword [ebx+4], edx jmp sf002a sf_exit: ret ;*************************************************************************** ; Function ; getFile ; ; Description ; Receives the specified file over the open data socket ; The file to receive is named in the buff string ; ; Inputs ; None ; ; Outputs ; None ; ;*************************************************************************** getFile: call setupFilePath ; init fileblock descriptor, for file write xor eax, eax mov [fsize], eax ; Start filelength at 0 mov [fileinfoblock+4], eax ; set to 0 inc eax inc eax mov [fileinfoblock], eax ; write cmd ; Read data from the socket until the socket closes ; loop ; loop ; read byte from socket ; write byte to file buffer ; until no more bytes in socket ; sleep 100ms ; until socket no longer connected ; write file to ram gf000: mov eax, 53 mov ebx, 2 ; Get # of bytes in input queue mov ecx, [DataSocket] int 0x40 test eax, eax je gf_sleep mov eax, 53 mov ebx, 3 ; Get a byte from socket in bl mov ecx, [DataSocket] int 0x40 ; returned data in bl mov esi, text + 0x1300 add esi, dword [fsize] mov [esi], bl inc dword [fsize] ; dummy, write to screen ;call printChar jmp gf000 gf_sleep: ; Check to see if socket closed... mov eax,53 mov ebx,6 ; Get socket status mov ecx,[DataSocket] int 0x40 cmp eax, 7 jne gf001 ; still open, so just sleep a bit ; Finished, so write the file mov eax, [fsize] mov [fileinfoblock+12], eax mov eax,70 mov ebx,fileinfoblock int 0x40 ret ; Finished gf001: ; wait a bit mov eax,5 mov ebx,10 ; Delay for up 100ms int 0x40 jmp gf000 ; try for more data ;*************************************************************************** ; COMMAND HANDLERS FOLLOW ; ; These handlers implement the functionality of each supported FTP Command ; ;*************************************************************************** cmdPWD: ; OK, show the directory name text mov esi, ramdir mov edx, ramdir_end - ramdir call outputStr ; TODO give real directory ret cmdCWD: ; Only / is valid for the ramdisk cmp [buff+5], byte 0x0d jne ccwd_000 ; OK, show the directory name text mov esi, chdir mov edx, chdir_end - chdir jmp ccwd_001 ccwd_000: ; Tell user there is no such directory mov esi, noFileStr mov edx, noFileStr_end - noFileStr ccwd_001: call outputStr ret cmdQUIT: ; The remote end will do the close; We just ; say goodbye. mov esi, byeStr mov edx, byeStr_end - byeStr call outputStr ret cmdABOR: ; Close port call disconnectData mov esi, abortStr mov edx, abortStr_end - abortStr call outputStr ret cmdPORT: ; TODO ; Copy the IP and port values to DataIP and DataPort call parsePortStr ; Indicate the command was accepted mov esi, cmdOKStr mov edx, cmdOKStr_end - cmdOKStr call outputStr ret cmdnoop: ; Indicate the command was accepted mov esi, cmdOKStr mov edx, cmdOKStr_end - cmdOKStr call outputStr ret cmdTYPE: ; TODO ; Note the type field selected - reject if needed. ; Indicate the command was accepted mov esi, cmdOKStr mov edx, cmdOKStr_end - cmdOKStr call outputStr ret cmdsyst: ; Indicate the system type mov esi, systStr mov edx, systStr_end - systStr call outputStr ret cmdDELE: call setupFilePath mov ebx, fileinfoblock mov dword [ebx], 8 and dword [ebx+4], 0 push dword [ebx+12] push dword [ebx+16] and dword [ebx+12], 0 and dword [ebx+16], 0 mov eax, 70 int 0x40 pop dword [ebx+16] pop dword [ebx+12] test eax, eax jne cmdDele_err mov esi, delokStr mov edx, delokStr_end - delokStr call outputStr jmp cmdDele_exit cmdDele_err: mov esi, noFileStr mov edx, noFileStr_end - noFileStr call outputStr cmdDele_exit: ret cmdNLST: cmdLIST: ; Indicate the command was accepted mov esi, startStr mov edx, startStr_end - startStr call outputStr call connectData ; Wait for socket to establish cl001: ; wait a bit mov eax,5 mov ebx,10 ; Delay for up 100ms int 0x40 ; check connection status mov eax,53 mov ebx,6 ; Get socket status mov ecx,[DataSocket] int 0x40 cmp eax, 4 jne cl001 ; send directory listing call sendDir ; Close port call disconnectData mov esi, endStr mov edx, endStr_end - endStr call outputStr ret cmdRETR: ; Indicate the command was accepted mov esi, startStr mov edx, startStr_end - startStr call outputStr call connectData ; Wait for socket to establish cr001: ; wait a bit mov eax,5 mov ebx,10 ; Delay for up 100ms int 0x40 ; check connection status mov eax,53 mov ebx,6 ; Get socket status mov ecx,[DataSocket] int 0x40 cmp eax, 4 jne cr001 ; send data to remote user call sendFile ; Close port call disconnectData mov esi, endStr mov edx, endStr_end - endStr call outputStr ret cmdSTOR: ; Indicate the command was accepted mov esi, storStr mov edx, storStr_end - storStr call outputStr call connectData ; Wait for socket to establish cs001: ; wait a bit mov eax,5 mov ebx,10 ; Delay for up 100ms int 0x40 ; check connection status mov eax,53 mov ebx,6 ; Get socket status mov ecx,[DataSocket] int 0x40 cmp eax, 4 je @f cmp eax, 7 jne cs001 @@: ; get data file from remote user call getFile mov esi, endStr mov edx, endStr_end - endStr call outputStr ; Close port call disconnectData ret ; DATA AREA ; This is the list of supported commands, and the function to call ; The list end with a NULL. CMDList: db 'pwd',0 dd cmdPWD db 'PWD',0 dd cmdPWD db 'XPWD',0 dd cmdPWD db 'xpwd',0 dd cmdPWD db 'QUIT',0 dd cmdQUIT db 'quit',0 dd cmdQUIT db 'PORT',0 dd cmdPORT db 'port',0 dd cmdPORT db 'LIST',0 dd cmdLIST db 'list',0 dd cmdLIST db 'NLST',0 dd cmdNLST db 'nlst',0 dd cmdNLST db 'TYPE',0 dd cmdTYPE db 'type',0 dd cmdTYPE db 'syst',0 dd cmdsyst db 'noop',0 dd cmdnoop db 'CWD',0 dd cmdCWD db 'cwd',0 dd cmdCWD db 'RETR',0 dd cmdRETR db 'retr',0 dd cmdRETR db 'DELE',0 dd cmdDELE db 'dele',0 dd cmdDELE db 'stor',0 dd cmdSTOR db 'STOR',0 dd cmdSTOR db 'ABOR',0 dd cmdABOR db 'abor',0 dd cmdABOR db 0xff,0xf4,0xff,0xf2,'ABOR',0 dd cmdABOR db 0 cmdPtr dd 0 CmdSocket dd 0x0 CmdSocketStatus dd 0x0 DataSocket dd 0x0 DataSocketStatus dd 0x0 DataPort dd 0x00 DataIP dd 0x00 pos dd 80 * 1 scroll dd 1 dd 24 labelt db 'FTP Server v0.1',0 contt db 'Connected' contlen: discontt db 'Disconnected' discontlen: cmdOKStr: db '200 Command OK',0x0d,0x0a cmdOKStr_end: loginStr0: db '220- Menuet FTP Server v0.1',0x0d,0x0a db '220 Username and Password required',0x0d,0x0a loginStr0_end: loginStr1: db '331 Password now required',0x0d,0x0a loginStr1_end: loginStr2: db '230 You are now logged in.',0x0d,0x0a loginStr2_end: byeStr: db '221 Bye bye!',0x0d,0x0a byeStr_end: systStr: db '215 UNIX system type',0x0d,0x0a systStr_end: ramdir: db '257 "/"',0x0d,0x0a ramdir_end: chdir: db '200 directory changed to /',0x0d,0x0a chdir_end: unsupStr: db '500 Unsupported command',0x0d,0x0a unsupStr_end: noFileStr: db '550 No such file',0x0d,0x0a noFileStr_end: delokStr: db '250 DELE command successful',0x0d,0x0a delokStr_end: startStr: db '150 Here it comes...',0x0d,0x0a startStr_end: storStr: db '150 Connecting for STOR',0x0d,0x0a storStr_end: endStr: db '226 Transfer OK, Closing connection',0x0d,0x0a endStr_end: abortStr: db '225 Abort successful',0x0d,0x0a abortStr_end: ; This is the buffer used for building up a directory listing line dirStr: times 128 db 0 ; This is template string used in building up a directory listing line tmplStr: db 'rw-rw-rw- 1 0 0 ',0 months: dd 'Jan ','Feb ','Mar ','Apr ','May ','Jun ' dd 'Jul ','Aug ','Sep ','Oct ','Nov ','Dec ' fileinfoblock: dd 0x00 dd 0x00 dd 0x00 dd 0x200 ; bytes to read dd text + 0x1300 ; data area filename: times 256 db 0 ; The following lines define data for reading a directory block DirBlocksPerCall = 16 dirinfoblock: dd 1 dd 0x00 dd 0x00 dd DirBlocksPerCall dd text + 0x1300 ; data area ; The 'filename' for a directory listing dirpath db '/RD/1',0 fsize: dd 0 state db 0 buffptr dd 0 buff: times 256 db 0 ; Could put this after iend ; Ram use at the end of the application: ; text : 2400 bytes for screen memory ; text + 0x1300 : file data area text: I_END: