diff --git a/programs/network/ftpc/ftpc.asm b/programs/network/ftpc/ftpc.asm index 55d017f847..7903ab8be8 100644 --- a/programs/network/ftpc/ftpc.asm +++ b/programs/network/ftpc/ftpc.asm @@ -27,8 +27,8 @@ OPERATION_NONE = 0 OPERATION_LIST = 1 OPERATION_RETR = 2 OPERATION_STOR = 3 -OPERATION_RDIR = 4 - +OPERATION_RDIR = 4 + use32 ; standard header db 'MENUET01' ; signature @@ -38,7 +38,7 @@ use32 dd mem+0x1000 ; required memory dd mem+0x1000 ; stack pointer dd buf_cmd ; parameters - dd 0 ; path + dd path ; path include '../../macros.inc' purge mov,add,sub @@ -51,10 +51,10 @@ include 'servercommands.inc' start: ; initialize heap for using dynamic blocks - mcall 68,11 - test eax,eax - je exit2 - + mcall 68,11 + test eax,eax + je exit2 + ; disable all events except network event mcall 40, EV_STACK ; load libraries @@ -64,7 +64,42 @@ start: ; initialize console invoke con_start, 1 invoke con_init, 80, 25, 80, 250, str_title +; find path to main settings file (ftpc.ini) + mov edi, path ; Calculate the length of zero-terminated string + xor al, al + mov ecx, 1024 + repne scasb + dec edi + mov esi, str_ini ; append it with '.ini', 0 + movsd + movsb +; get settings from ini + invoke ini.get_str, path, str_active, str_ip, str_active_ip, 16, 0 + mov esi, str_active_ip + .ip_loop: + lodsb + test al, al + jz .ip_ok + cmp al, ' ' + je .ip_ok + cmp al, '.' + jne .ip_loop + mov byte[esi-1], ',' + jmp .ip_loop + .ip_ok: + mov byte[esi-1], 0 + + invoke ini.get_int, path, str_active, str_port_start, 64000 + mov [acti_port_start], ax + + invoke ini.get_int, path, str_active, str_port_stop, 65000 + mov [acti_port_stop], ax + + invoke ini.get_str, path, str_general, str_dir, buf_buffer1, BUFFERSIZE, 0 + mcall 30, 1, buf_buffer1 ; set working directory + ; Check for parameters, if there are some, resolve the address right away +; TODO: parse ftp://user:password@server.com:port/folder/subfolder type urls. cmp byte [buf_cmd], 0 jne resolve @@ -88,13 +123,41 @@ main: invoke con_write_asciiz, str_newline resolve: + mov [sockaddr1.port], 21 shl 8 + ; delete terminating '\n' mov esi, buf_cmd @@: lodsb + cmp al, ':' + je .do_port cmp al, 0x20 ja @r mov byte [esi-1], 0 + jmp .done + + .do_port: + xor eax, eax + xor ebx, ebx + mov byte [esi-1], 0 + .portloop: + lodsb + cmp al, 0x20 + jbe .port_done + sub al, '0' + jb error_hostname + cmp al, 9 + ja error_hostname + lea ebx, [ebx*4 + ebx] + shl ebx, 1 + add ebx, eax + jmp .portloop + + .port_done: + xchg bl, bh + mov [sockaddr1.port], bx + + .done: ; Say to the user that we're resolving invoke con_write_asciiz, str_resolve invoke con_write_asciiz, buf_cmd @@ -118,10 +181,10 @@ resolve: mcall socket, AF_INET4, SOCK_STREAM, 0 cmp eax, -1 je error_socket - mov [socketnum], eax + mov [controlsocket], eax ; connect to the server invoke con_write_asciiz, str_connect - mcall connect, [socketnum], sockaddr1, 18 + mcall connect, [controlsocket], sockaddr1, 18 cmp eax, -1 je error_connect mov [status], STATUS_CONNECTING @@ -151,7 +214,7 @@ wait_for_servercommand: mcall 26, 9 cmp eax, [timeout] jge error_timeout - mcall recv, [socketnum], buf_buffer1, BUFFERSIZE, MSG_DONTWAIT + mcall recv, [controlsocket], buf_buffer1, BUFFERSIZE, MSG_DONTWAIT test eax, eax jnz .got_data cmp ebx, EWOULDBLOCK @@ -202,9 +265,9 @@ wait_for_usercommand: ; Are there any files in the transfer queue? - cmp [queued], 0 + cmp [queued], 0 ja transfer_queued ; Yes, transfer those first. - + ; change color to green for user input invoke con_set_flags, 0x0a @@ -240,8 +303,8 @@ wait_for_usercommand: je cmd_bye cmp dword[buf_cmd], "rdir" - je cmd_rdir - + je cmd_rdir + cmp byte[buf_cmd+4], " " jne @f @@ -289,7 +352,7 @@ wait_for_usercommand: invoke con_write_asciiz, str_pass mov dword[buf_cmd], "PASS" mov byte[buf_cmd+4], " " - invoke con_set_flags, 0x00 ; black text on black background for password + invoke con_set_flags, 0x00 ; black text on black background for password .send: ; read string @@ -304,29 +367,139 @@ wait_for_usercommand: lea esi, [edi-buf_cmd] mov word[edi-2], 0x0a0d ; and send it to the server - mcall send, [socketnum], buf_cmd, , 0 + mcall send, [controlsocket], buf_cmd, , 0 invoke con_write_asciiz, str_newline - invoke con_set_flags, 0x07 ; reset color + invoke con_set_flags, 0x07 ; reset color jmp wait_for_servercommand -open_dataconnection: ; only passive for now.. - cmp [status], STATUS_LOGGED_IN - jne .fail +; files for rdir operation are queued +transfer_queued: - mcall send, [socketnum], str_PASV, str_PASV.length, 0 + mov esi, [ptr_queue] ; always pointing to current part of ptr_fname_start + mov edi, buf_cmd+5 ; always point to filename for retr command + .build_filename: + lodsb + stosb + cmp al, 10 + je .get_file ; filename ends with character 10 + test al, al + jnz .build_filename + + ; Error occured, we reached the end of the buffer before [queued] reached 0 + mov [queued], 0 + mcall 68, 13, [ptr_fname] ; free buffer + test eax, eax + jz error_heap + jmp wait_for_usercommand + + .get_file: + mov byte[edi], 0 ; end filename with 0 byte + mov [ptr_queue], esi + dec [queued] + jnz cmd_retr + + mcall 68, 13, [ptr_fname] ; free buffer + test eax, eax + jz error_heap + jmp cmd_retr + + + +open_dataconnection: + + test [mode], 1 + jnz .active + + mcall send, [controlsocket], str_PASV, str_PASV.length, 0 ret - .fail: - invoke con_get_flags + .active: + mcall socket, AF_INET4, SOCK_STREAM, 0 + cmp eax, -1 + je error_socket + mov [datasocket], eax + + mov ax, [acti_port_start] + xchg al, ah + mov [sockaddr2.port], ax + + mcall bind, [datasocket], sockaddr2, 18 + cmp eax, -1 + je error_socket + + mcall listen, [datasocket], 1 + cmp eax, -1 + je error_socket + + mov dword[buf_buffer1], 'PORT' + mov byte[buf_buffer1+4], ' ' + mov edi, buf_buffer1+5 + mov esi, str_active_ip + .loop: + lodsb + test al, al + jz .ip_ok + stosb + jmp .loop + .ip_ok: + mov al, ',' + stosb + movzx eax, byte[sockaddr2.port+0] + call dword_ascii + mov al, ',' + stosb + movzx eax, byte[sockaddr2.port+1] + call dword_ascii + mov ax, 0x0a0d + stosw + lea esi, [edi - buf_buffer1] + mcall send, [controlsocket], buf_buffer1, , 0 + + mcall accept, [datasocket], sockaddr2, 18 ; time to accept the awaiting connection.. + cmp eax, -1 + je error_socket push eax - invoke con_set_flags, 0x0c ; print errors in red - invoke con_write_asciiz, str_err_socket - invoke con_set_flags ; reset color + mcall close, [datasocket] + pop [datasocket] + + mcall recv, [controlsocket], buf_buffer1, BUFFERSIZE, 0 + ret +; eax = input +; edi = ptr where to write +dword_ascii: + + push edx ebx ecx + mov ebx, 10 + xor ecx, ecx + + @@: + xor edx, edx + div ebx + add edx, '0' + push dx + inc ecx + test eax, eax + jnz @r + + @@: + pop ax + stosb + dec ecx + jnz @r + + pop ecx ebx edx + ret + +error_hostname: + invoke con_set_flags, 0x0c ; print errors in red + invoke con_write_asciiz, str_err_host + jmp wait_for_keypress + error_connect: invoke con_set_flags, 0x0c ; print errors in red invoke con_write_asciiz, str_err_connect @@ -345,24 +518,25 @@ error_socket: error_resolve: invoke con_set_flags, 0x0c ; print errors in red invoke con_write_asciiz, str_err_resolve + jmp wait_for_keypress error_heap: - invoke con_set_flags, 0x0c ; print errors in red + invoke con_set_flags, 0x0c ; print errors in red invoke con_write_asciiz, str_err_heap - + wait_for_keypress: invoke con_set_flags, 0x07 ; reset color to grey invoke con_write_asciiz, str_push invoke con_getch2 - mcall close, [socketnum] + mcall close, [controlsocket] jmp main done: invoke con_exit, 1 exit: - mcall close, [socketnum] -exit2: + mcall close, [controlsocket] +exit2: mcall -1 @@ -378,9 +552,10 @@ str_resolve db 'Resolving ',0 str_newline db 10,0 str_err_resolve db 10,'Name resolution failed.',10,0 str_err_socket db 10,'Socket error.',10,0 -str_err_heap db 10,'Cannot allocate memory from heap.',10,0 +str_err_heap db 10,'Cannot allocate memory from heap.',10,0 str_err_timeout db 10,'Timeout - no response from server.',10,0 str_err_connect db 10,'Cannot connect to the server.',10,0 +str_err_host db 10,'Invalid hostname.',10,0 str8 db ' (',0 str9 db ')',10,0 str_push db 'Push any key to continue.',0 @@ -408,10 +583,19 @@ str_help db "available commands:",10 db "retr - retreive file from the server",10 db "rmd - remove directory from the server",10 db "stor - store file on the server",10 - db "rdir - retreive all files from current server dir",10 + db "rdir - retreive all files from current server dir",10 db 10,0 -queued dd 0 +str_ini db '.ini', 0 +str_active db 'active', 0 +str_port_start db 'port_start', 0 +str_port_stop db 'port_stop', 0 +str_ip db 'ip', 0 +str_dir db 'dir', 0 +str_general db 'general', 0 + +queued dd 0 +mode db 0 ; passive = 0, active = 1 ; FTP strings @@ -420,21 +604,21 @@ str_PASV db 'PASV',13,10 sockaddr1: dw AF_INET4 -.port dw 0x1500 ; 21 -.ip dd 0 +.port dw ? +.ip dd ? rb 10 sockaddr2: dw AF_INET4 -.port dw 0 -.ip dd 0 +.port dw ? +.ip dd ? rb 10 ; import align 4 @IMPORT: -library network, 'network.obj', console, 'console.obj' +library network, 'network.obj', console, 'console.obj', libini, 'libini.obj' import network, \ getaddrinfo, 'getaddrinfo', \ @@ -454,35 +638,46 @@ import console, \ con_get_flags, 'con_get_flags', \ con_set_flags, 'con_set_flags' +import libini, \ + ini.get_str, 'ini_get_str',\ + ini.get_int, 'ini_get_int' + i_end: ; uninitialised data status db ? -active_passive db ? -socketnum dd ? +controlsocket dd ? datasocket dd ? offset dd ? size dd ? operation dd ? - -size_fname dd ? -ptr_queue dd ? timeout dd ? -ptr_fname_start dd ? + +ptr_fname dd ? +size_fname dd ? +ptr_queue dd ? + +acti_port_start dw ? +acti_port_stop dw ? +acti_port dw ? + +str_active_ip rb 16 filestruct: -.subfn dd ? -.offset dd ? - dd ? -.size dd ? -.ptr dd ? -.name rb 1024 + .subfn dd ? + .offset dd ? + dd ? + .size dd ? + .ptr dd ? + .name rb 1024 buf_buffer1 rb BUFFERSIZE+1 buf_buffer2 rb BUFFERSIZE+1 -buf_cmd rb 1024 ; buffer for holding command string +buf_cmd rb 1024 ; buffer for holding command string + +path rb 1024 mem: diff --git a/programs/network/ftpc/ftpc.ini b/programs/network/ftpc/ftpc.ini new file mode 100644 index 0000000000..c793f98861 --- /dev/null +++ b/programs/network/ftpc/ftpc.ini @@ -0,0 +1,12 @@ +[general] +; 0 = passive / 1 = active +mode=0 +dir=/tmp0/1/ + +[active] +; Local starting port for active connections +port_start=2000 +; end port +port_stop=5000 +; IP (If you're behind a NAT, use external IP) +ip=10.0.0.1 \ No newline at end of file diff --git a/programs/network/ftpc/servercommands.inc b/programs/network/ftpc/servercommands.inc index 3fd4afa8ec..650f8bd413 100644 --- a/programs/network/ftpc/servercommands.inc +++ b/programs/network/ftpc/servercommands.inc @@ -91,8 +91,8 @@ pasv_ok: invoke con_write_asciiz, str_open mcall connect, [datasocket], sockaddr2, 18 -; cmp eax, -1 -; je error_socket + cmp eax, -1 + je error_socket jmp wait_for_servercommand .fail: @@ -116,9 +116,9 @@ data_loop: cmp [operation], OPERATION_RETR je .retr - cmp [operation], OPERATION_RDIR - je .rdir - + cmp [operation], OPERATION_RDIR + je .rdir + ; not retreiving, just print to console invoke con_write_asciiz, buf_buffer2 jmp data_loop @@ -155,109 +155,40 @@ data_loop: mov [operation], OPERATION_NONE jmp wait_for_servercommand - .rdir: - cmp [size_fname], 0 - jne .realloc + ; alloc/realloc memory block to store filenames + mov ecx, eax ; eax is size of buffer received + inc ecx + add ecx, [size_fname] ; added old size to form new required size + mcall 68, 20, , [ptr_fname] ; realloc + test eax, eax + je error_heap + mov [ptr_fname], eax ; eax contains the new block now + mov [ptr_queue], eax - .malloc: ; create a new dynamic block - mov ecx, eax - inc ecx - - mcall 68,12 ; eax now points to new buffer - - test eax,eax - je error_heap - - mov [ptr_fname_start], eax - - jmp .rdir_init - - .realloc: ; expand block created with .malloc - - mov ecx, eax ; eax is size of buffer received - inc ecx - - add ecx, [size_fname] ; added old size to form new required size - - mcall 68,20,,[ptr_fname_start] - - test eax, eax - je error_heap - - mov [ptr_fname_start], eax ; eax contains the new block now - add eax, [size_fname] - - .rdir_init: ; copies filenames into our buffer - - mov esi, buf_buffer2 - mov edi, eax - + ; copy filenames into fname buffer + mov esi, buf_buffer2 + mov edi, eax + add edi, [size_fname] .copy_buf: - lodsb - cmp al,13 ; ignore any \r character - je .copy_buf - stosb + lodsb + cmp al, 13 ; ignore any carriage return character + je .copy_buf + stosb + cmp al, 10 ; linefeed marks end of filename + je @f + inc [queued] + @@: + test al, al ; 0 marks end of buffer + jne .copy_buf - cmp al, 10 - jne .not_end - inc [queued] + ; All received filenames have been copied, calculate new size of fname buffer + dec edi ; dont count the trailing 0 byte + sub edi, [ptr_fname] + mov [size_fname], edi + jmp data_loop - .not_end: - test al,al - jne .copy_buf - - dec edi - dec edi - mov eax, [ptr_fname_start] - mov [ptr_queue], eax - - sub edi, eax ; edi contains the current size now - mov [size_fname], edi - - jmp data_loop - - -; files for rdir operation are queued -transfer_queued: - - mov esi, [ptr_queue] ; always pointing to current part of ptr_fname_start - mov edi, buf_cmd+5 ; always point to filename for retr command - - .build_filename: - lodsb - stosb - - cmp al,10 - je .get_file ; filename ends with character 10 - test al,al - jz .null_found ; this should be end of buffer - jmp .build_filename - - .null_found: - - mov [queued],0 - jmp .free - - .get_file: - dec [queued] - jnz .after_free - - .free: - mcall 68,13,[ptr_fname_start] ; freeing the buffer - test eax,eax - jz error_heap - jmp wait_for_usercommand - - .after_free: - xor al,al ; appending 0 after retr command - stosb - mov eax, esi - mov [ptr_queue], eax - - jmp cmd_retr - close_datacon: cmp [operation], OPERATION_NONE je wait_for_usercommand @@ -266,7 +197,6 @@ close_datacon: jmp wait_for_usercommand - ascii_dec: xor ebx, ebx diff --git a/programs/network/ftpc/usercommands.inc b/programs/network/ftpc/usercommands.inc index 55b250c195..c5b77722a4 100644 --- a/programs/network/ftpc/usercommands.inc +++ b/programs/network/ftpc/usercommands.inc @@ -11,10 +11,10 @@ cmd_bye: ; Send BYE message to the server mov dword[buf_cmd], "BYE" + 13 shl 24 mov byte[buf_cmd+4], 10 - mcall send, [socketnum], buf_cmd, 5, 0 + mcall send, [controlsocket], buf_cmd, 5, 0 ; Close the control connection - mcall close, [socketnum] + mcall close, [controlsocket] jmp main @@ -22,7 +22,7 @@ cmd_pwd: mov dword[buf_cmd], "PWD" + 13 shl 24 mov byte[buf_cmd+4], 10 - mcall send, [socketnum], buf_cmd, 5, 0 + mcall send, [controlsocket], buf_cmd, 5, 0 jmp wait_for_servercommand @@ -38,7 +38,7 @@ cmd_cwd: lea esi, [edi - buf_cmd] mov word [edi - 2], 0x0a0d - mcall send, [socketnum], buf_cmd, , 0 + mcall send, [controlsocket], buf_cmd, , 0 jmp wait_for_servercommand @@ -55,7 +55,7 @@ cmd_dele: lea esi, [edi - buf_cmd] mov word [edi - 2], 0x0a0d - mcall send, [socketnum], buf_cmd, , 0 + mcall send, [controlsocket], buf_cmd, , 0 jmp wait_for_servercommand @@ -67,7 +67,7 @@ cmd_list: mov dword[buf_cmd], "LIST" mov word[buf_cmd+4], 0x0a0d - mcall send, [socketnum], buf_cmd, 6, 0 + mcall send, [controlsocket], buf_cmd, 6, 0 jmp wait_for_servercommand @@ -109,7 +109,7 @@ cmd_retr: repne scasb lea esi, [edi - buf_cmd] mov dword[edi - 2], 0x0a0d - mcall send, [socketnum], buf_cmd, , 0 + mcall send, [controlsocket], buf_cmd, , 0 invoke con_write_asciiz, buf_cmd ; print command jmp wait_for_servercommand @@ -120,15 +120,16 @@ cmd_rdir: ; Request filename list from the server - call open_dataconnection - - mov [size_fname], 0 + call open_dataconnection + + mov [ptr_fname], 0 + mov [size_fname], 0 mov dword[buf_cmd], "NLST" mov word[buf_cmd+4], 0x0a0d - mcall send, [socketnum], buf_cmd, 6, 0 + mcall send, [controlsocket], buf_cmd, 6, 0 - jmp wait_for_servercommand - + jmp wait_for_servercommand + cmd_stor: call open_dataconnection @@ -154,7 +155,7 @@ cmd_stor: repne scasb lea esi, [edi - buf_cmd] mov word [edi - 2], 0x0a0d - mcall send, [socketnum], buf_cmd, , 0 + mcall send, [controlsocket], buf_cmd, , 0 jmp wait_for_servercommand @@ -162,6 +163,8 @@ cmd_stor: cmd_lcwd: mov esi, buf_cmd+5 + cmp byte[esi], 10 + je .print mov ecx, 256-5 .loop: lodsb @@ -173,6 +176,7 @@ cmd_lcwd: .done: mov byte[esi-1], 0 mcall 30, 1, buf_cmd+5 ; set working directory + .print: mcall 30, 2, buf_cmd, 256 ; and read it again invoke con_write_asciiz, str_lcwd @@ -186,7 +190,7 @@ cmd_cdup: mov dword[buf_cmd], "CDUP" mov word[buf_cmd+4], 0x0d0a - mcall send, [socketnum], buf_cmd, 6, 0 + mcall send, [controlsocket], buf_cmd, 6, 0 jmp wait_for_servercommand @@ -202,7 +206,7 @@ cmd_rmd: lea esi, [edi - buf_cmd] mov word [edi - 2], 0x0a0d - mcall send, [socketnum], buf_cmd, , 0 + mcall send, [controlsocket], buf_cmd, , 0 jmp wait_for_servercommand @@ -218,7 +222,7 @@ cmd_mkd: lea esi, [edi - buf_cmd] mov word [edi - 2], 0x0a0d - mcall send, [socketnum], buf_cmd, , 0 + mcall send, [controlsocket], buf_cmd, , 0 jmp wait_for_servercommand