diff --git a/programs/network/ftpd/commands.inc b/programs/network/ftpd/commands.inc index 6ea220af3..e1cc5560b 100644 --- a/programs/network/ftpd/commands.inc +++ b/programs/network/ftpd/commands.inc @@ -1,1288 +1,1288 @@ - - -struct thread_data - rb 1024 - stack rb 0 - - home_dir rb 1024 ; home directory in wich the user is locked, asciiz - work_dir rb 1024 ; working directory, must at all times begin and end with a '/', asciiz - fpath rb 1024*3 ; file path, combination of home_dir, work_dir and filename - ; Will also be used to temporarily store username - - type db ? ; ASCII/EBDIC/IMAGE/.. - mode db ? ; active/passive - socketnum dd ? ; Commands socket - state dd ? ; disconnected/logging in/logged in/.. - passivesocknum dd ? ; when in passive mode, this is the listening socket - datasocketnum dd ? ; socket used for data transfers - permissions dd ? ; read/write/execute/.... - buffer_ptr dd ? - pid dd ? ; Process id of the current thread - - datasock sockaddr_in - - buffer rb BUFFERSIZE -ends - -;------------------------------------------------ -; parse_cmd -; -; Internal function wich uses the 'commands' -; table to call an appropriate cmd_xx function. -; -; input: esi = ptr to ascii commands -; ecx = number of bytes input -; ebp = pointer to thread_data structure -; -; output: none -; -;------------------------------------------------ -align 4 -parse_cmd: ; esi must point to command - - cmp byte [esi], 0x20 ; skip all leading characters - ja .ok - inc esi - dec ecx - cmp ecx, 3 - jb .error - jmp parse_cmd - .ok: - cmp byte [esi+3], 0x20 - ja @f - mov byte [esi+3], 0 - @@: - - mov eax, [esi] - and eax, not 0x20202020 ; convert to upper case - mov edi, commands ; list of commands to scan - .scanloop: - cmp eax, [edi] - je .got_it - - add edi, 5*4 - cmp byte [edi], 0 - jne .scanloop - - .error: - cmp [ebp + thread_data.state], STATE_ACTIVE - jb login_first - sendFTP "500 Unsupported command" - ret - - .got_it: - mov eax, [ebp + thread_data.state] - jmp dword [edi + 4 + eax] - - -align 4 -iglobal -commands: ; all commands must be in uppercase - - dd 'ABOR', login_first, login_first, login_first, cmdABOR -; dd 'ACCT', login_first, login_first, login_first, cmd_ACCT -; dd 'APPE', login_first, login_first, login_first, cmd_APPE - dd 'CDUP', login_first, login_first, login_first, cmdCDUP - dd 'CWD', login_first, login_first, login_first, cmdCWD - dd 'DELE', login_first, login_first, login_first, cmdDELE -; dd 'HELP', login_first, login_first, login_first, cmd_HELP - dd 'LIST', login_first, login_first, login_first, cmdLIST -; dd 'MDTM', login_first, login_first, login_first, cmd_MDTM -; dd 'MKD', login_first, login_first, login_first, cmd_MKD -; dd 'MODE', login_first, login_first, login_first, cmd_MODE -; dd 'NLST', login_first, login_first, login_first, cmdNLST - dd 'NOOP', login_first, login_first, login_first, cmdNOOP - dd 'PASS', cmdPASS.0, cmdPASS , cmdPASS.2, cmdPASS.3 - dd 'PASV', login_first, login_first, login_first, cmdPASV - dd 'PORT', login_first, login_first, login_first, cmdPORT - dd 'PWD', login_first, login_first, login_first, cmdPWD - dd 'QUIT', cmdQUIT, cmdQUIT, cmdQUIT, cmdQUIT -; dd 'REIN', login_first, login_first, login_first, cmd_REIN -; dd 'REST', login_first, login_first, login_first, cmd_REST - dd 'RETR', login_first, login_first, login_first, cmdRETR -; dd 'RMD', login_first, login_first, login_first, cmd_RMD -; dd 'RNFR', login_first, login_first, login_first, cmd_RNFR -; dd 'RNTO', login_first, login_first, login_first, cmd_RNTO -; dd 'SITE', login_first, login_first, login_first, cmd_SITE -; dd 'SIZE', login_first, login_first, login_first, cmd_SIZE -; dd 'STAT', login_first, login_first, login_first, cmd_STAT - dd 'STOR', login_first, login_first, login_first, cmdSTOR -; dd 'STOU', login_first, login_first, login_first, cmd_STOU -; dd 'STRU', login_first, login_first, login_first, cmd_STRU - dd 'SYST', login_first, login_first, login_first, cmdSYST - dd 'TYPE', login_first, login_first, login_first, cmdTYPE - dd 'USER', cmdUSER, cmdUSER, cmdUSER, cmdUSER.2 - db 0 ; end marker -endg - -align 4 -login_first: - sendFTP "530 Please login with USER and PASS" - ret - -align 4 -permission_denied: - sendFTP "550 Permission denied" - ret - -align 4 -socketerror: - invoke con_set_flags, 0x0c - invoke con_write_asciiz, str_sockerr - invoke con_set_flags, 0x07 - - sendFTP "425 Can't open data connection" - ret - -align 4 -abort_transfer: - and [ebp + thread_data.permissions], not ABORT - mov [ebp + thread_data.mode], MODE_NOTREADY - invoke file.close, ebx - mcall close, [ebp + thread_data.datasocketnum] - - sendFTP "530 Transfer aborted" - ret - -align 4 -ip_to_dword: ; esi = ptr to str, cl = separator ('.', ',') - - call ascii_to_byte - mov bl, al - cmp byte [esi], cl - jne .err - inc esi - - call ascii_to_byte - mov bh, al - cmp byte [esi], cl - jne .err - inc esi - - shl ebx, 16 - - call ascii_to_byte - mov bl, al - cmp byte [esi], cl - jne .err - inc esi - - call ascii_to_byte - mov bh, al - - ror ebx, 16 - ret - - .err: - xor ebx, ebx - ret - -align 4 ; esi = ptr to str, output in eax -ascii_to_byte: - - xor eax, eax - push ebx - - .loop: - movzx ebx, byte[esi] - sub bl, '0' - jb .done - cmp bl, 9 - ja .done - lea eax, [eax*4 + eax] ; - shl eax, 1 ; eax = eax * 10 - add eax, ebx - inc esi - - jmp .loop - - .done: - pop ebx - ret - -align 4 -dword_to_ascii: ; edi = ptr where to write, eax is number - - push edx ebx ecx - mov ebx, 10 - xor ecx, ecx - - @@: - xor edx, edx - div ebx - add edx, '0' - pushw dx - inc ecx - test eax, eax - jnz @r - - @@: - popw ax - stosb - dec ecx - jnz @r - - pop ecx ebx edx - ret - -align 4 -create_path: ; combine home_dir and work_dir strings into fpath - - lea edi, [ebp + thread_data.fpath] - lea esi, [ebp + thread_data.home_dir] - mov ecx, 1024 - .loop1: - lodsb - cmp al, 0x20 - jb .next - stosb - loop .loop1 - .next: - - cmp byte[edi-1], '/' - jne @f - dec edi - @@: - - lea esi, [ebp + thread_data.work_dir] - mov ecx, 1024 - .loop2: - lodsb - cmp al, 0x20 - jb .done - stosb - loop .loop2 - - .done: - xor al, al - stosb - ret - - -align 4 -nextpasvport: - - inc [pasv_port] - - mov ax, [pasv_port] - cmp ax, [pasv_start] - jb .restart - cmp ax, [pasv_end] - ja .restart - - ret - - .restart: - pushw [pasv_start] - popw [pasv_port] - - ret - - -align 4 -open_datasock: - - cmp [ebp + thread_data.mode], MODE_PASSIVE_OK - je .already_open - -; If we are in active mode, it's time to open the data socket.. - cmp [ebp + thread_data.mode], MODE_ACTIVE - jne .not_active - mov ecx, [ebp + thread_data.datasocketnum] - lea edx, [ebp + thread_data.datasock] - mov esi, sizeof.thread_data.datasock - mcall connect - cmp eax, -1 - je .socketerror - invoke con_write_asciiz, str_datasock2 - ret - - .already_open: - invoke con_write_asciiz, str_alopen - ret - - .socketerror: - add esp, 4 - jmp socketerror - -; If we are still in passive_wait, it's time we accept an incomming call.. - .not_active: - cmp [ebp + thread_data.mode], MODE_PASSIVE_WAIT - jne .socketerror - - .try_now: - mov ecx, [ebp + thread_data.passivesocknum] - lea edx, [ebp + thread_data.datasock] - mov esi, sizeof.thread_data.datasock - mcall accept - cmp eax, -1 - jne .pasv_ok - mov [ebp + thread_data.mode], MODE_PASSIVE_FAILED ; assume that we will fail - mcall 23, 200 - mcall accept - cmp eax, -1 - je .socketerror - .pasv_ok: - mov [ebp + thread_data.datasocketnum], eax - mov [ebp + thread_data.mode], MODE_PASSIVE_OK - mcall close ; [ebp + thread_data.passivesocknum] - mov [ebp + thread_data.passivesocknum], -1 - invoke con_write_asciiz, str_datasock - - ret - - -;------------------------------------------------ -; "ABOR" -; -; This command aborts the current filetransfer. -; -;------------------------------------------------ -align 4 -cmdABOR: - - or [ebp + thread_data.permissions], ABORT - sendFTP "250 Command succesul" - ret - -;------------------------------------------------ -; "CDUP" -; -; Change the directory to move up one level. -; -;------------------------------------------------ -align 4 -cmdCDUP: - - test [ebp + thread_data.permissions], PERMISSION_CD - jz permission_denied - - cmp byte [ebp + thread_data.work_dir+1], 0 ; are we in "/" ? - je .done ; if so, we cant go up.. - -; find the end of asciiz string work_dir - mov ecx, 1024 - xor al, al - lea edi, [ebp + thread_data.work_dir] - repne scasb -; return 2 characters (right before last /) - sub edi, 3 -; and now search backwards, for a '/' - mov al,'/' - neg ecx - add ecx, 1024 - std - repne scasb - cld -; terminate the string here - mov byte[edi+2], 0 - - .done: -; Print the new working dir on the console - lea eax, [ebp + thread_data.work_dir] - invoke con_write_asciiz, eax - invoke con_write_asciiz, str_newline - - sendFTP "250 Command succesul" - ret - -;------------------------------------------------ -; "CWD" -; -; Change Working Directory. -; -;------------------------------------------------ -align 4 -cmdCWD: - - test [ebp + thread_data.permissions], PERMISSION_CD - jz permission_denied - -; do we have enough parameters? - sub ecx, 4 - jbe .err - -; get ready to copy the path - add esi, 4 - mov ecx, 1024 - lea edi, [ebp + thread_data.work_dir] - -; if received dir starts with '/', we will simply copy it -; If not, we will append the current path with the received path. - cmp byte [esi], '/' - je .copyloop - -; Find the end of work_dir string. - xor al, al - .find_zero: - repne scasb - dec edi - -; and now append work_dir with received string - mov ecx, 1024 - -; scan for end byte, or '.' - .copyloop: - lodsb - cmp al, 0x20 - jb .done -;;; cmp al, '.' ; '..' means we must go up one dir TODO -;;; je .up - stosb - loop .copyloop - -; now, now make sure it ends with '/', 0 - .done: - cmp byte [edi-1], '/' - je @f - mov byte [edi], '/' - inc edi - @@: - mov byte [edi], 0 - -; TODO: Check directory on disk - - -; Print the new working dir on the console - lea eax, [ebp + thread_data.work_dir] - invoke con_write_asciiz, eax - invoke con_write_asciiz, str_newline - - sendFTP "250 Command succesful" - ret - - .err: - sendFTP "550 Directory does not exist" - ret - -;------------------------------------------------ -; "DELE" -; -; Delete a file from the server. -; -;------------------------------------------------ -align 4 -cmdDELE: - - test [ebp + thread_data.permissions], PERMISSION_DELETE - jz permission_denied - - ; Create path - cmp ecx, 1024 + 5 - jae .err - - sub ecx, 5 - jb .err - - call create_path - dec edi - lea esi, [ebp + thread_data.buffer + 5] - mov ecx, 1024 - cmp byte [esi], '/' - jne .loop - inc esi - .loop: - lodsb - cmp al, 0x20 - jl .done - stosb - loop .loop - .done: - xor al, al - stosb - - lea ebx, [ebp + thread_data.fpath] - invoke con_write_asciiz, ebx - invoke con_write_asciiz, str_newline - ; called fs function - push ebx - dec esp - mov byte[esp], 0 - push dword 0 - push dword 0 - push dword 0 - push dword 0 - push dword 8 - mov ebx, esp - mcall 70 - add esp, 6*4 + 1 - - test eax, eax - jnz .err - - sendFTP "250 Command succesful" - ret -.err: - sendFTP "550 No such file" - ret - -;------------------------------------------------ -; "LIST" -; -; List the files in the current working directory. -; -;------------------------------------------------ -align 4 -cmdLIST: - - test [ebp + thread_data.permissions], PERMISSION_EXEC - jz permission_denied - - call open_datasock - -; Create fpath from home_dir and work_dir - call create_path - - lea ebx, [ebp + thread_data.fpath] - invoke con_write_asciiz, ebx - invoke con_write_asciiz, str_newline - -; Start the search - invoke file.find.first, ebx, str_mask, FA_READONLY+FA_FOLDER+FA_ARCHIVED;+FA_NORMAL - test eax, eax - jz .nosuchdir - - lea edi, [ebp + thread_data.buffer] - .parse_file: - test eax, eax ; did we find a file? - jz .done - mov ebx, eax ; yes, save the descripter in ebx - -; first, convert the attributes - test [ebx + FileInfoA.Attributes], FA_FOLDER - jnz .folder - - test [ebx + FileInfoA.Attributes], FA_READONLY - jnz .readonly - - mov eax, '-rw-' - stosd - jmp .attr - - .folder: - mov eax, 'drwx' - stosd - jmp .attr - - .readonly: - mov eax, '-r--' - stosd - - .attr: - mov eax, 'rw-r' - stosd - mov ax, 'w-' - stosw - mov al, ' ' - stosb - -; now.. - mov ax, '1 ' - stosw - -; now write owner, everything is owned by FTP, woohoo! - mov eax, 'FTP ' - stosd - stosd - -; now the filesize in ascii - mov eax, dword[ebx + FileInfoA.FileSize] - call dword_to_ascii - mov al, ' ' - stosb - -; then date (month/day/year) - movzx eax, [ebx + FileInfoA.DateModify.month] - cmp eax, 12 - ja @f - mov eax, [months - 4 + 4*eax] - stosd - @@: - - movzx eax, [ebx + FileInfoA.DateModify.day] - call dword_to_ascii - mov al, ' ' - stosb - - movzx eax, [ebx + FileInfoA.DateModify.year] - call dword_to_ascii - mov al, ' ' - stosb - -; and last but not least, filename - lea esi, [ebx + FileInfoA.FileName] - mov ecx, 264 - .nameloop: - lodsb - test al, al - jz .namedone - stosb - loop .nameloop - -; insert a cr lf - .namedone: - mov ax, 0x0a0d - stosw - - test [ebp + thread_data.permissions], ABORT ; Did we receive ABOR command from client? - jnz abort_transfer - -; check next file - invoke file.find.next, ebx - jmp .parse_file - -; close file desc - .done: - invoke file.find.close, ebx ; ebx is descriptor of last file, eax will be -1 !! - -; append the string with a 0 - xor al, al - stosb - -; Warn the client we're about to send the data - push edi - sendFTP "150 Here it comes.." - pop esi - -; and send it to the client - mov ecx, [ebp + thread_data.datasocketnum] ; socket num - lea edx, [ebp + thread_data.buffer] ; buffer ptr - sub esi, edx ; length - xor edi, edi ; flags - mcall send - -; close the data socket.. - mov [ebp + thread_data.mode], MODE_NOTREADY - mcall close, [ebp + thread_data.datasocketnum] - - sendFTP "226 List OK" - ret - - .nosuchdir: - sendFTP "550 Directory does not exist" - ret - -;------------------------------------------------ -; "NLST" -; -; List the filenames of the files in the current working directory. -; -;------------------------------------------------ -align 4 -cmdNLST: - - test [ebp + thread_data.permissions], PERMISSION_EXEC - jz permission_denied - - ; TODO: same as list but simpler output format - - ret - -;------------------------------------------------ -; "NOOP" -; -; No operation, just keep the connection alive. -; -;------------------------------------------------ -align 4 -cmdNOOP: - - sendFTP "200 Command OK" - ret - -;------------------------------------------------ -; "PASS" -; -; Second phase of login process, client provides password. -; -;------------------------------------------------ -align 4 -cmdPASS: - -; read the password from users.ini - lea edi, [ebp + thread_data.buffer + 512] ; temp pass - lea ebx, [ebp + thread_data.fpath] ; temp username - invoke ini.get_str, path2, ebx, str_pass, edi, 512, str_infinity - test eax, eax ; unable to read password? fail! - jnz .incorrect - cmp dword [edi], -1 ; no key, section or file found.. fail! - je .incorrect - cmp byte [edi], 0 ; zero password? ok! - je .ok - - add esi, 5 - sub ecx, 5 - jbe .incorrect ; no password given? but hey, we need one! fail.. - -; compare with received password - repe cmpsb - cmp byte [esi-1], 0x20 ; printeable characters left? - jae .incorrect - cmp byte [edi-1], 0 - jne .incorrect - - .ok: - invoke ini.get_int, path2, ebx, str_mode, 0 - mov [ebp + thread_data.permissions], eax - - invoke con_write_asciiz, str_pass_ok - mov [ebp + thread_data.state], STATE_ACTIVE - sendFTP "230 You are now logged in" - ret - - .2: - .incorrect: - invoke con_write_asciiz, str_pass_err - mov [ebp + thread_data.state], STATE_CONNECTED ; reset state - sendFTP "530 Login incorrect" - ret - - .0: - sendFTP "503 Login with USER first" - ret - - .3: - sendFTP "230 Already logged in" - ret - -;------------------------------------------------ -; "PASV" -; -; Initiate a passive dataconnection. -; -;------------------------------------------------ -align 4 -cmdPASV: - -; cmp [ebp + thread_data.passivesocknum], -1 -; je @f -; mcall close, [ebp + thread_data.passivesocknum] ; if there is still a socket open, close it -; @@: - -; Open a new TCP socket - mcall socket, AF_INET4, SOCK_STREAM, 0 - cmp eax, -1 - je socketerror - mov [ebp + thread_data.passivesocknum], eax - -; Bind it to a known local port - mov [ebp + thread_data.datasock.sin_family], AF_INET4 - mov [ebp + thread_data.datasock.sin_addr], 0 - - mov ecx, eax ; passivesocketnum - lea edx, [ebp + thread_data.datasock] - mov esi, sizeof.thread_data.datasock - - .next_port: ; TODO: break the endless loop - call nextpasvport - mov ax, [pasv_port] - xchg al, ah - mov [ebp + thread_data.datasock.sin_port], ax - - mcall bind - cmp eax, -1 - je .next_port - -; And set it to listen! - mcall listen, , 1 - cmp eax, -1 - je socketerror - -; Tell our thread we are ready to accept incoming calls - mov [ebp + thread_data.mode], MODE_PASSIVE_WAIT - -; Now tell the client where to connect to in this format: -; 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2) -; where a1.a2.a3.a4 is the IP address and p1*256+p2 is the port number. - -; '227 Entering passive mode (' - lea edi, [ebp + thread_data.buffer] - mov ecx, str_227.length - mov esi, str_227 - rep movsb -; ip - movzx eax, byte [serverip] - call dword_to_ascii - mov al, ',' - stosb - movzx eax, byte [serverip+1] - call dword_to_ascii - mov al, ',' - stosb - movzx eax, byte [serverip+2] - call dword_to_ascii - mov al, ',' - stosb - movzx eax, byte [serverip+3] - call dword_to_ascii - mov al, ',' - stosb -; port - movzx eax, byte [ebp + thread_data.datasock.sin_port] - call dword_to_ascii - mov al, ',' - stosb - movzx eax, byte [ebp + thread_data.datasock.sin_port+1] - call dword_to_ascii -; ')', 13, 10, 0 - mov eax, ')' + (0x000a0d shl 8) - stosd - - lea esi, [edi - thread_data.buffer - 1] ; calculate length, do not cound the trailing 0 byte - sub esi, ebp - mov ecx, [ebp + thread_data.socketnum] - lea edx, [ebp + thread_data.buffer] - xor edi, edi - mcall send - - invoke con_write_asciiz, edx - - ret - - -iglobal - str_227 db "227 Entering passive mode (" - .length = $ - str_227 -endg - -;------------------------------------------------ -; "PWD" -; -; Print the current working directory. -; -;------------------------------------------------ -align 4 -cmdPWD: - - mov dword [ebp + thread_data.buffer], '257 ' - mov byte [ebp + thread_data.buffer+4], '"' - - lea edi, [ebp + thread_data.buffer+5] - lea esi, [ebp + thread_data.work_dir] - mov ecx, 1024 - .loop: - lodsb - or al, al - jz .ok - stosb - dec ecx - jnz .loop - - .ok: - mov dword [edi], '"' + 0x000a0d00 ; '"',13,10,0 - lea esi, [edi - thread_data.buffer + 3] - sub esi, ebp - mov ecx, [ebp + thread_data.socketnum] - lea edx, [ebp + thread_data.buffer] - xor edi, edi - mcall send - -; Print the new working dir on the console - lea eax, [ebp + thread_data.work_dir] - invoke con_write_asciiz, eax - invoke con_write_asciiz, str_newline - - ret - -;------------------------------------------------ -; "PORT" -; -; Initiate an active dataconnection. -; -;------------------------------------------------ -align 4 -cmdPORT: - -; PORT a1,a2,a3,a4,p1,p2 -; IP address a1.a2.a3.a4, port p1*256+p2 - -; Convert the IP - lea esi, [esi+5] - mov cl, ',' - call ip_to_dword -; And put it in datasock - mov [ebp + thread_data.datasock.sin_addr], ebx - -; Now the same with portnumber - inc esi - call ascii_to_byte - mov byte[ebp + thread_data.datasock.sin_port], al - inc esi - call ascii_to_byte - mov byte[ebp + thread_data.datasock.sin_port+1], al - -; We will open the socket, but do not connect yet! - mov [ebp + thread_data.datasock.sin_family], AF_INET4 - mcall socket, AF_INET4, SOCK_STREAM, 0 - cmp eax, -1 - je socketerror - - mov [ebp + thread_data.datasocketnum], eax - mov [ebp + thread_data.mode], MODE_ACTIVE - - sendFTP "225 Data connection open" - ret - -;------------------------------------------------ -; "QUIT" -; -; Close the connection with client. -; -;------------------------------------------------ -align 4 -cmdQUIT: - - sendFTP "221 Bye!" - mcall close, [ebp + thread_data.datasocketnum] - mcall close, [ebp + thread_data.socketnum] - - add esp, 4 ; get rid of call return address - jmp thread_exit ; now close this thread - - -;------------------------------------------------ -; "RETR" -; -; Retrieve a file from the ftp server. -; -;------------------------------------------------ -align 4 -cmdRETR: - - test [ebp + thread_data.permissions], PERMISSION_READ - jz permission_denied - - cmp ecx, 1024 + 5 - jae .cannot_open - - sub ecx, 5 - jb .cannot_open - - call open_datasock - - call create_path - dec edi - lea esi, [ebp + thread_data.buffer + 5] - mov ecx, 1024 - cmp byte [esi], '/' - jne .loop - inc esi - .loop: - lodsb - cmp al, 0x20 - jl .done - stosb - loop .loop - .done: - xor al, al - stosb - - lea ebx, [ebp + thread_data.fpath] - invoke con_write_asciiz, ebx - invoke con_write_asciiz, str_newline - - invoke file.open, ebx, O_READ - test eax, eax - jz .cannot_open - - push eax - sendFTP "150 Here it comes.." - pop ebx - - .read_more: - test [ebp + thread_data.permissions], ABORT - jnz abort_transfer - - lea eax, [ebp + thread_data.buffer] ; FIXME: use another buffer!! if we receive something on control connection now, we screw up! - invoke file.read, ebx, eax, BUFFERSIZE - cmp eax, -1 - je .cannot_open ; FIXME: this is not the correct error - - invoke con_write_asciiz, str2 - - push eax ebx - mov esi, eax - mov ecx, [ebp + thread_data.datasocketnum] - lea edx, [ebp + thread_data.buffer] - xor edi, edi - mcall send - pop ebx ecx - cmp eax, -1 - je socketerror ; FIXME: not the correct error - -; cmp eax, ecx -; jne not_all_byes_sent ; TODO - - cmp ecx, BUFFERSIZE - je .read_more - - invoke file.close, ebx - - invoke con_write_asciiz, str2b - - mov [ebp + thread_data.mode], MODE_NOTREADY - mcall close, [ebp + thread_data.datasocketnum] - - sendFTP "226 Transfer OK, closing connection" - ret - - .cannot_open: - invoke con_set_flags, 0x0c - invoke con_write_asciiz, str_notfound - invoke con_set_flags, 0x07 - - sendFTP "550 No such file" - ret - - - -;------------------------------------------------ -; "STOR" -; -; Store a file on the server. -; -;------------------------------------------------ -align 4 -cmdSTOR: - - test [ebp + thread_data.permissions], PERMISSION_WRITE - jz permission_denied - - ;sendFTP " Ready to receive" - ; open datasocket - cmp ecx, 1024 + 5 - jae .cannot_open - - sub ecx, 5 - jb .cannot_open - - call open_datasock - - ; creat path - call create_path - dec edi - lea esi, [ebp + thread_data.buffer + 5] - mov ecx, 1024 - cmp byte [esi], '/' - jne .loop - inc esi - .loop: - lodsb - cmp al, 0x20 - jl .done - stosb - loop .loop - .done: - xor al, al - stosb - - lea ebx, [ebp + thread_data.fpath] - invoke con_write_asciiz, ebx - invoke con_write_asciiz, str_newline - - ; open file - invoke file.open, ebx, O_CREATE + O_WRITE - test eax, eax - jz .cannot_open - - push eax - sendFTP "150 Here it comes.." - pop ebx - - .write_more: - test [ebp + thread_data.permissions], ABORT - jnz abort_transfer - - push eax ebx - mov esi, BUFFERSIZE ; eax - mov ecx, [ebp + thread_data.datasocketnum] - lea edx, [ebp + thread_data.buffer] - xor edi, edi - mcall recv - pop ebx ecx - cmp eax, -1 - je socketerror ; FIXME: not the correct error - - test eax, eax - jz @f - - push edx - mov edx, eax - lea eax, [ebp + thread_data.buffer] ; FIXME: use another buffer!! if we receive something on control connection now, we screw up! - invoke file.write, ebx, eax, edx - pop edx - - cmp eax, -1 - je .cannot_open ; FIXME: this is not the correct error - - invoke con_write_asciiz, str2 - -; cmp eax, ecx -; jne not_all_byes_sent ; TODO - - ;cmp ecx, BUFFERSIZE - ;je .write_more - jmp .write_more -@@: - - invoke file.close, ebx - - invoke con_write_asciiz, str2b - - mov [ebp + thread_data.mode], MODE_NOTREADY - mcall close, [ebp + thread_data.datasocketnum] - - - -;;;; TODO -; -; test [ebp + thread_data.permissions], ABORT -; jnz abort_transfer -; -;;;; - - sendFTP "226 Transfer OK" - ret - -.cannot_open: - sendFTP "550 No create file" - ret - -;------------------------------------------------ -; "SYST" -; -; Send information about the system. -; -;------------------------------------------------ -align 4 -cmdSYST: - - sendFTP "215 UNIX type: L8" - ret - -;------------------------------------------------ -; "TYPE" -; -; Choose the file transfer type. -; -;------------------------------------------------ -align 4 -cmdTYPE: - - cmp ecx, 6 - jb parse_cmd.error - - mov al, byte[esi+5] - and al, not 0x20 - - cmp al, 'A' - je .ascii - cmp al, 'E' - je .ebdic - cmp al, 'I' - je .image - cmp al, 'L' - je .local - - jmp parse_cmd.error - - .ascii: - mov [ebp + thread_data.type], TYPE_ASCII - jmp .subtype - - .ebdic: - mov [ebp + thread_data.type], TYPE_EBDIC - - .subtype: - cmp ecx, 8 - jb .non_print - - mov al, byte[esi+7] - and al, not 0x20 - - cmp al, 'N' - je .non_print - cmp al, 'T' - je .telnet - cmp al, 'C' - je .asacc - cmp al, 0x20 - jb .non_print - - jmp parse_cmd.error - - .non_print: - or [ebp + thread_data.type], TYPE_NP - jmp .ok - - .telnet: - or [ebp + thread_data.type], TYPE_TELNET - jmp .ok - - .asacc: - or [ebp + thread_data.type], TYPE_ASA - jmp .ok - - .image: - mov [ebp + thread_data.type], TYPE_IMAGE - jmp .ok - - .local: - cmp ecx, 8 - jb parse_cmd.error - - mov al, byte[esi+7] - sub al, '0' - jb parse_cmd.error ; FIXME: this is not the correct errormessage - cmp al, 9 - ja parse_cmd.error ; FIXME - or al, TYPE_LOCAL - mov [ebp + thread_data.type], al - - .ok: - sendFTP "200 Command ok" - ret - -;------------------------------------------------ -; "USER" -; -; Login to the server, step one of two. ;;; TODO: prevent buffer overflow! -; -;------------------------------------------------ -align 4 -cmdUSER: - - lea esi, [esi + 5] - lea edi, [ebp + thread_data.fpath] ; temp buffer for username - .loop: - lodsb - stosb - cmp al, 0x20 - jae .loop - mov byte [edi-1], 0 - - lea esi, [ebp + thread_data.fpath] - lea eax, [ebp + thread_data.home_dir] - invoke ini.get_str, path2, esi, str_home, eax, 1024, str_infinity - cmp eax, -1 - je .login_fail - cmp dword [esi], -1 - je .login_fail - - mov word [ebp + thread_data.work_dir], "/" ; "/", 0 - - invoke con_write_asciiz, str_logged_in - mov [ebp + thread_data.state], STATE_LOGIN - .sendstr: - sendFTP "331 Please specify the password" - ret - - .login_fail: - invoke con_write_asciiz, str_pass_err - mov [ebp + thread_data.state], STATE_LOGIN_FAIL - jmp .sendstr - -align 4 - .2: - sendFTP "530 Can't change to another user" - ret + + +struct thread_data + rb 1024 + stack rb 0 + + home_dir rb 1024 ; home directory in which the user is locked, asciiz + work_dir rb 1024 ; working directory, must at all times begin and end with a '/', asciiz + fpath rb 1024*3 ; file path, combination of home_dir, work_dir and filename + ; Will also be used to temporarily store username + + type db ? ; ASCII/EBDIC/IMAGE/.. + mode db ? ; active/passive + socketnum dd ? ; Commands socket + state dd ? ; disconnected/logging in/logged in/.. + passivesocknum dd ? ; when in passive mode, this is the listening socket + datasocketnum dd ? ; socket used for data transfers + permissions dd ? ; read/write/execute/.... + buffer_ptr dd ? + pid dd ? ; Process id of the current thread + + datasock sockaddr_in + + buffer rb BUFFERSIZE +ends + +;------------------------------------------------ +; parse_cmd +; +; Internal function which uses the 'commands' +; table to call an appropriate cmd_xx function. +; +; input: esi = ptr to ascii commands +; ecx = number of bytes input +; ebp = pointer to thread_data structure +; +; output: none +; +;------------------------------------------------ +align 4 +parse_cmd: ; esi must point to command + + cmp byte [esi], 0x20 ; skip all leading characters + ja .ok + inc esi + dec ecx + cmp ecx, 3 + jb .error + jmp parse_cmd + .ok: + cmp byte [esi+3], 0x20 + ja @f + mov byte [esi+3], 0 + @@: + + mov eax, [esi] + and eax, not 0x20202020 ; convert to upper case + mov edi, commands ; list of commands to scan + .scanloop: + cmp eax, [edi] + je .got_it + + add edi, 5*4 + cmp byte [edi], 0 + jne .scanloop + + .error: + cmp [ebp + thread_data.state], STATE_ACTIVE + jb login_first + sendFTP "500 Unsupported command" + ret + + .got_it: + mov eax, [ebp + thread_data.state] + jmp dword [edi + 4 + eax] + + +align 4 +iglobal +commands: ; all commands must be in uppercase + + dd 'ABOR', login_first, login_first, login_first, cmdABOR +; dd 'ACCT', login_first, login_first, login_first, cmd_ACCT +; dd 'APPE', login_first, login_first, login_first, cmd_APPE + dd 'CDUP', login_first, login_first, login_first, cmdCDUP + dd 'CWD', login_first, login_first, login_first, cmdCWD + dd 'DELE', login_first, login_first, login_first, cmdDELE +; dd 'HELP', login_first, login_first, login_first, cmd_HELP + dd 'LIST', login_first, login_first, login_first, cmdLIST +; dd 'MDTM', login_first, login_first, login_first, cmd_MDTM +; dd 'MKD', login_first, login_first, login_first, cmd_MKD +; dd 'MODE', login_first, login_first, login_first, cmd_MODE +; dd 'NLST', login_first, login_first, login_first, cmdNLST + dd 'NOOP', login_first, login_first, login_first, cmdNOOP + dd 'PASS', cmdPASS.0, cmdPASS , cmdPASS.2, cmdPASS.3 + dd 'PASV', login_first, login_first, login_first, cmdPASV + dd 'PORT', login_first, login_first, login_first, cmdPORT + dd 'PWD', login_first, login_first, login_first, cmdPWD + dd 'QUIT', cmdQUIT, cmdQUIT, cmdQUIT, cmdQUIT +; dd 'REIN', login_first, login_first, login_first, cmd_REIN +; dd 'REST', login_first, login_first, login_first, cmd_REST + dd 'RETR', login_first, login_first, login_first, cmdRETR +; dd 'RMD', login_first, login_first, login_first, cmd_RMD +; dd 'RNFR', login_first, login_first, login_first, cmd_RNFR +; dd 'RNTO', login_first, login_first, login_first, cmd_RNTO +; dd 'SITE', login_first, login_first, login_first, cmd_SITE +; dd 'SIZE', login_first, login_first, login_first, cmd_SIZE +; dd 'STAT', login_first, login_first, login_first, cmd_STAT + dd 'STOR', login_first, login_first, login_first, cmdSTOR +; dd 'STOU', login_first, login_first, login_first, cmd_STOU +; dd 'STRU', login_first, login_first, login_first, cmd_STRU + dd 'SYST', login_first, login_first, login_first, cmdSYST + dd 'TYPE', login_first, login_first, login_first, cmdTYPE + dd 'USER', cmdUSER, cmdUSER, cmdUSER, cmdUSER.2 + db 0 ; end marker +endg + +align 4 +login_first: + sendFTP "530 Please login with USER and PASS" + ret + +align 4 +permission_denied: + sendFTP "550 Permission denied" + ret + +align 4 +socketerror: + invoke con_set_flags, 0x0c + invoke con_write_asciiz, str_sockerr + invoke con_set_flags, 0x07 + + sendFTP "425 Can't open data connection" + ret + +align 4 +abort_transfer: + and [ebp + thread_data.permissions], not ABORT + mov [ebp + thread_data.mode], MODE_NOTREADY + invoke file.close, ebx + mcall close, [ebp + thread_data.datasocketnum] + + sendFTP "530 Transfer aborted" + ret + +align 4 +ip_to_dword: ; esi = ptr to str, cl = separator ('.', ',') + + call ascii_to_byte + mov bl, al + cmp byte [esi], cl + jne .err + inc esi + + call ascii_to_byte + mov bh, al + cmp byte [esi], cl + jne .err + inc esi + + shl ebx, 16 + + call ascii_to_byte + mov bl, al + cmp byte [esi], cl + jne .err + inc esi + + call ascii_to_byte + mov bh, al + + ror ebx, 16 + ret + + .err: + xor ebx, ebx + ret + +align 4 ; esi = ptr to str, output in eax +ascii_to_byte: + + xor eax, eax + push ebx + + .loop: + movzx ebx, byte[esi] + sub bl, '0' + jb .done + cmp bl, 9 + ja .done + lea eax, [eax*4 + eax] ; + shl eax, 1 ; eax = eax * 10 + add eax, ebx + inc esi + + jmp .loop + + .done: + pop ebx + ret + +align 4 +dword_to_ascii: ; edi = ptr where to write, eax is number + + push edx ebx ecx + mov ebx, 10 + xor ecx, ecx + + @@: + xor edx, edx + div ebx + add edx, '0' + pushw dx + inc ecx + test eax, eax + jnz @r + + @@: + popw ax + stosb + dec ecx + jnz @r + + pop ecx ebx edx + ret + +align 4 +create_path: ; combine home_dir and work_dir strings into fpath + + lea edi, [ebp + thread_data.fpath] + lea esi, [ebp + thread_data.home_dir] + mov ecx, 1024 + .loop1: + lodsb + cmp al, 0x20 + jb .next + stosb + loop .loop1 + .next: + + cmp byte[edi-1], '/' + jne @f + dec edi + @@: + + lea esi, [ebp + thread_data.work_dir] + mov ecx, 1024 + .loop2: + lodsb + cmp al, 0x20 + jb .done + stosb + loop .loop2 + + .done: + xor al, al + stosb + ret + + +align 4 +nextpasvport: + + inc [pasv_port] + + mov ax, [pasv_port] + cmp ax, [pasv_start] + jb .restart + cmp ax, [pasv_end] + ja .restart + + ret + + .restart: + pushw [pasv_start] + popw [pasv_port] + + ret + + +align 4 +open_datasock: + + cmp [ebp + thread_data.mode], MODE_PASSIVE_OK + je .already_open + +; If we are in active mode, it's time to open the data socket.. + cmp [ebp + thread_data.mode], MODE_ACTIVE + jne .not_active + mov ecx, [ebp + thread_data.datasocketnum] + lea edx, [ebp + thread_data.datasock] + mov esi, sizeof.thread_data.datasock + mcall connect + cmp eax, -1 + je .socketerror + invoke con_write_asciiz, str_datasock2 + ret + + .already_open: + invoke con_write_asciiz, str_alopen + ret + + .socketerror: + add esp, 4 + jmp socketerror + +; If we are still in passive_wait, it's time we accept an incoming call... + .not_active: + cmp [ebp + thread_data.mode], MODE_PASSIVE_WAIT + jne .socketerror + + .try_now: + mov ecx, [ebp + thread_data.passivesocknum] + lea edx, [ebp + thread_data.datasock] + mov esi, sizeof.thread_data.datasock + mcall accept + cmp eax, -1 + jne .pasv_ok + mov [ebp + thread_data.mode], MODE_PASSIVE_FAILED ; assume that we will fail + mcall 23, 200 + mcall accept + cmp eax, -1 + je .socketerror + .pasv_ok: + mov [ebp + thread_data.datasocketnum], eax + mov [ebp + thread_data.mode], MODE_PASSIVE_OK + mcall close ; [ebp + thread_data.passivesocknum] + mov [ebp + thread_data.passivesocknum], -1 + invoke con_write_asciiz, str_datasock + + ret + + +;------------------------------------------------ +; "ABOR" +; +; This command aborts the current filetransfer. +; +;------------------------------------------------ +align 4 +cmdABOR: + + or [ebp + thread_data.permissions], ABORT + sendFTP "250 Command succesul" + ret + +;------------------------------------------------ +; "CDUP" +; +; Change the directory to move up one level. +; +;------------------------------------------------ +align 4 +cmdCDUP: + + test [ebp + thread_data.permissions], PERMISSION_CD + jz permission_denied + + cmp byte [ebp + thread_data.work_dir+1], 0 ; are we in "/" ? + je .done ; if so, we can't go up. + +; find the end of asciiz string work_dir + mov ecx, 1024 + xor al, al + lea edi, [ebp + thread_data.work_dir] + repne scasb +; return 2 characters (right before last /) + sub edi, 3 +; and now search backwards, for a '/' + mov al,'/' + neg ecx + add ecx, 1024 + std + repne scasb + cld +; terminate the string here + mov byte[edi+2], 0 + + .done: +; Print the new working dir on the console + lea eax, [ebp + thread_data.work_dir] + invoke con_write_asciiz, eax + invoke con_write_asciiz, str_newline + + sendFTP "250 Command succesul" + ret + +;------------------------------------------------ +; "CWD" +; +; Change Working Directory. +; +;------------------------------------------------ +align 4 +cmdCWD: + + test [ebp + thread_data.permissions], PERMISSION_CD + jz permission_denied + +; do we have enough parameters? + sub ecx, 4 + jbe .err + +; get ready to copy the path + add esi, 4 + mov ecx, 1024 + lea edi, [ebp + thread_data.work_dir] + +; if received dir starts with '/', we will simply copy it +; If not, we will append the current path with the received path. + cmp byte [esi], '/' + je .copyloop + +; Find the end of work_dir string. + xor al, al + .find_zero: + repne scasb + dec edi + +; and now append work_dir with received string + mov ecx, 1024 + +; scan for end byte, or '.' + .copyloop: + lodsb + cmp al, 0x20 + jb .done +;;; cmp al, '.' ; '..' means we must go up one dir TODO +;;; je .up + stosb + loop .copyloop + +; now, now make sure it ends with '/', 0 + .done: + cmp byte [edi-1], '/' + je @f + mov byte [edi], '/' + inc edi + @@: + mov byte [edi], 0 + +; TODO: Check directory on disk + + +; Print the new working dir on the console + lea eax, [ebp + thread_data.work_dir] + invoke con_write_asciiz, eax + invoke con_write_asciiz, str_newline + + sendFTP "250 Command succesful" + ret + + .err: + sendFTP "550 Directory does not exist" + ret + +;------------------------------------------------ +; "DELE" +; +; Delete a file from the server. +; +;------------------------------------------------ +align 4 +cmdDELE: + + test [ebp + thread_data.permissions], PERMISSION_DELETE + jz permission_denied + + ; Create path + cmp ecx, 1024 + 5 + jae .err + + sub ecx, 5 + jb .err + + call create_path + dec edi + lea esi, [ebp + thread_data.buffer + 5] + mov ecx, 1024 + cmp byte [esi], '/' + jne .loop + inc esi + .loop: + lodsb + cmp al, 0x20 + jl .done + stosb + loop .loop + .done: + xor al, al + stosb + + lea ebx, [ebp + thread_data.fpath] + invoke con_write_asciiz, ebx + invoke con_write_asciiz, str_newline + ; called fs function + push ebx + dec esp + mov byte[esp], 0 + push dword 0 + push dword 0 + push dword 0 + push dword 0 + push dword 8 + mov ebx, esp + mcall 70 + add esp, 6*4 + 1 + + test eax, eax + jnz .err + + sendFTP "250 Command succesful" + ret +.err: + sendFTP "550 No such file" + ret + +;------------------------------------------------ +; "LIST" +; +; List the files in the current working directory. +; +;------------------------------------------------ +align 4 +cmdLIST: + + test [ebp + thread_data.permissions], PERMISSION_EXEC + jz permission_denied + + call open_datasock + +; Create fpath from home_dir and work_dir + call create_path + + lea ebx, [ebp + thread_data.fpath] + invoke con_write_asciiz, ebx + invoke con_write_asciiz, str_newline + +; Start the search + invoke file.find.first, ebx, str_mask, FA_READONLY+FA_FOLDER+FA_ARCHIVED;+FA_NORMAL + test eax, eax + jz .nosuchdir + + lea edi, [ebp + thread_data.buffer] + .parse_file: + test eax, eax ; did we find a file? + jz .done + mov ebx, eax ; yes, save the descripter in ebx + +; first, convert the attributes + test [ebx + FileInfoA.Attributes], FA_FOLDER + jnz .folder + + test [ebx + FileInfoA.Attributes], FA_READONLY + jnz .readonly + + mov eax, '-rw-' + stosd + jmp .attr + + .folder: + mov eax, 'drwx' + stosd + jmp .attr + + .readonly: + mov eax, '-r--' + stosd + + .attr: + mov eax, 'rw-r' + stosd + mov ax, 'w-' + stosw + mov al, ' ' + stosb + +; now.. + mov ax, '1 ' + stosw + +; now write owner, everything is owned by FTP, woohoo! + mov eax, 'FTP ' + stosd + stosd + +; now the filesize in ascii + mov eax, dword[ebx + FileInfoA.FileSize] + call dword_to_ascii + mov al, ' ' + stosb + +; then date (month/day/year) + movzx eax, [ebx + FileInfoA.DateModify.month] + cmp eax, 12 + ja @f + mov eax, [months - 4 + 4*eax] + stosd + @@: + + movzx eax, [ebx + FileInfoA.DateModify.day] + call dword_to_ascii + mov al, ' ' + stosb + + movzx eax, [ebx + FileInfoA.DateModify.year] + call dword_to_ascii + mov al, ' ' + stosb + +; and last but not least, filename + lea esi, [ebx + FileInfoA.FileName] + mov ecx, 264 + .nameloop: + lodsb + test al, al + jz .namedone + stosb + loop .nameloop + +; insert a cr lf + .namedone: + mov ax, 0x0a0d + stosw + + test [ebp + thread_data.permissions], ABORT ; Did we receive ABOR command from client? + jnz abort_transfer + +; check next file + invoke file.find.next, ebx + jmp .parse_file + +; close file desc + .done: + invoke file.find.close, ebx ; ebx is descriptor of last file, eax will be -1 !! + +; append the string with a 0 + xor al, al + stosb + +; Warn the client we're about to send the data + push edi + sendFTP "150 Here it comes.." + pop esi + +; and send it to the client + mov ecx, [ebp + thread_data.datasocketnum] ; socket num + lea edx, [ebp + thread_data.buffer] ; buffer ptr + sub esi, edx ; length + xor edi, edi ; flags + mcall send + +; close the data socket.. + mov [ebp + thread_data.mode], MODE_NOTREADY + mcall close, [ebp + thread_data.datasocketnum] + + sendFTP "226 List OK" + ret + + .nosuchdir: + sendFTP "550 Directory does not exist" + ret + +;------------------------------------------------ +; "NLST" +; +; List the filenames of the files in the current working directory. +; +;------------------------------------------------ +align 4 +cmdNLST: + + test [ebp + thread_data.permissions], PERMISSION_EXEC + jz permission_denied + + ; TODO: same as list but simpler output format + + ret + +;------------------------------------------------ +; "NOOP" +; +; No operation, just keep the connection alive. +; +;------------------------------------------------ +align 4 +cmdNOOP: + + sendFTP "200 Command OK" + ret + +;------------------------------------------------ +; "PASS" +; +; Second phase of login process, client provides password. +; +;------------------------------------------------ +align 4 +cmdPASS: + +; read the password from users.ini + lea edi, [ebp + thread_data.buffer + 512] ; temp pass + lea ebx, [ebp + thread_data.fpath] ; temp username + invoke ini.get_str, path2, ebx, str_pass, edi, 512, str_infinity + test eax, eax ; unable to read password? fail! + jnz .incorrect + cmp dword [edi], -1 ; no key, section or file found.. fail! + je .incorrect + cmp byte [edi], 0 ; zero password? ok! + je .ok + + add esi, 5 + sub ecx, 5 + jbe .incorrect ; no password given? but hey, we need one! fail.. + +; compare with received password + repe cmpsb + cmp byte [esi-1], 0x20 ; printeable characters left? + jae .incorrect + cmp byte [edi-1], 0 + jne .incorrect + + .ok: + invoke ini.get_int, path2, ebx, str_mode, 0 + mov [ebp + thread_data.permissions], eax + + invoke con_write_asciiz, str_pass_ok + mov [ebp + thread_data.state], STATE_ACTIVE + sendFTP "230 You are now logged in" + ret + + .2: + .incorrect: + invoke con_write_asciiz, str_pass_err + mov [ebp + thread_data.state], STATE_CONNECTED ; reset state + sendFTP "530 Login incorrect" + ret + + .0: + sendFTP "503 Login with USER first" + ret + + .3: + sendFTP "230 Already logged in" + ret + +;------------------------------------------------ +; "PASV" +; +; Initiate a passive dataconnection. +; +;------------------------------------------------ +align 4 +cmdPASV: + +; cmp [ebp + thread_data.passivesocknum], -1 +; je @f +; mcall close, [ebp + thread_data.passivesocknum] ; if there is still a socket open, close it +; @@: + +; Open a new TCP socket + mcall socket, AF_INET4, SOCK_STREAM, 0 + cmp eax, -1 + je socketerror + mov [ebp + thread_data.passivesocknum], eax + +; Bind it to a known local port + mov [ebp + thread_data.datasock.sin_family], AF_INET4 + mov [ebp + thread_data.datasock.sin_addr], 0 + + mov ecx, eax ; passivesocketnum + lea edx, [ebp + thread_data.datasock] + mov esi, sizeof.thread_data.datasock + + .next_port: ; TODO: break the endless loop + call nextpasvport + mov ax, [pasv_port] + xchg al, ah + mov [ebp + thread_data.datasock.sin_port], ax + + mcall bind + cmp eax, -1 + je .next_port + +; And set it to listen! + mcall listen, , 1 + cmp eax, -1 + je socketerror + +; Tell our thread we are ready to accept incoming calls + mov [ebp + thread_data.mode], MODE_PASSIVE_WAIT + +; Now tell the client where to connect to in this format: +; 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2) +; where a1.a2.a3.a4 is the IP address and p1*256+p2 is the port number. + +; '227 Entering passive mode (' + lea edi, [ebp + thread_data.buffer] + mov ecx, str_227.length + mov esi, str_227 + rep movsb +; ip + movzx eax, byte [serverip] + call dword_to_ascii + mov al, ',' + stosb + movzx eax, byte [serverip+1] + call dword_to_ascii + mov al, ',' + stosb + movzx eax, byte [serverip+2] + call dword_to_ascii + mov al, ',' + stosb + movzx eax, byte [serverip+3] + call dword_to_ascii + mov al, ',' + stosb +; port + movzx eax, byte [ebp + thread_data.datasock.sin_port] + call dword_to_ascii + mov al, ',' + stosb + movzx eax, byte [ebp + thread_data.datasock.sin_port+1] + call dword_to_ascii +; ')', 13, 10, 0 + mov eax, ')' + (0x000a0d shl 8) + stosd + + lea esi, [edi - thread_data.buffer - 1] ; calculate length, do not cound the trailing 0 byte + sub esi, ebp + mov ecx, [ebp + thread_data.socketnum] + lea edx, [ebp + thread_data.buffer] + xor edi, edi + mcall send + + invoke con_write_asciiz, edx + + ret + + +iglobal + str_227 db "227 Entering passive mode (" + .length = $ - str_227 +endg + +;------------------------------------------------ +; "PWD" +; +; Print the current working directory. +; +;------------------------------------------------ +align 4 +cmdPWD: + + mov dword [ebp + thread_data.buffer], '257 ' + mov byte [ebp + thread_data.buffer+4], '"' + + lea edi, [ebp + thread_data.buffer+5] + lea esi, [ebp + thread_data.work_dir] + mov ecx, 1024 + .loop: + lodsb + or al, al + jz .ok + stosb + dec ecx + jnz .loop + + .ok: + mov dword [edi], '"' + 0x000a0d00 ; '"',13,10,0 + lea esi, [edi - thread_data.buffer + 3] + sub esi, ebp + mov ecx, [ebp + thread_data.socketnum] + lea edx, [ebp + thread_data.buffer] + xor edi, edi + mcall send + +; Print the new working dir on the console + lea eax, [ebp + thread_data.work_dir] + invoke con_write_asciiz, eax + invoke con_write_asciiz, str_newline + + ret + +;------------------------------------------------ +; "PORT" +; +; Initiate an active dataconnection. +; +;------------------------------------------------ +align 4 +cmdPORT: + +; PORT a1,a2,a3,a4,p1,p2 +; IP address a1.a2.a3.a4, port p1*256+p2 + +; Convert the IP + lea esi, [esi+5] + mov cl, ',' + call ip_to_dword +; And put it in datasock + mov [ebp + thread_data.datasock.sin_addr], ebx + +; Now the same with portnumber + inc esi + call ascii_to_byte + mov byte[ebp + thread_data.datasock.sin_port], al + inc esi + call ascii_to_byte + mov byte[ebp + thread_data.datasock.sin_port+1], al + +; We will open the socket, but do not connect yet! + mov [ebp + thread_data.datasock.sin_family], AF_INET4 + mcall socket, AF_INET4, SOCK_STREAM, 0 + cmp eax, -1 + je socketerror + + mov [ebp + thread_data.datasocketnum], eax + mov [ebp + thread_data.mode], MODE_ACTIVE + + sendFTP "225 Data connection open" + ret + +;------------------------------------------------ +; "QUIT" +; +; Close the connection with client. +; +;------------------------------------------------ +align 4 +cmdQUIT: + + sendFTP "221 Bye!" + mcall close, [ebp + thread_data.datasocketnum] + mcall close, [ebp + thread_data.socketnum] + + add esp, 4 ; get rid of call return address + jmp thread_exit ; now close this thread + + +;------------------------------------------------ +; "RETR" +; +; Retrieve a file from the ftp server. +; +;------------------------------------------------ +align 4 +cmdRETR: + + test [ebp + thread_data.permissions], PERMISSION_READ + jz permission_denied + + cmp ecx, 1024 + 5 + jae .cannot_open + + sub ecx, 5 + jb .cannot_open + + call open_datasock + + call create_path + dec edi + lea esi, [ebp + thread_data.buffer + 5] + mov ecx, 1024 + cmp byte [esi], '/' + jne .loop + inc esi + .loop: + lodsb + cmp al, 0x20 + jl .done + stosb + loop .loop + .done: + xor al, al + stosb + + lea ebx, [ebp + thread_data.fpath] + invoke con_write_asciiz, ebx + invoke con_write_asciiz, str_newline + + invoke file.open, ebx, O_READ + test eax, eax + jz .cannot_open + + push eax + sendFTP "150 Here it comes.." + pop ebx + + .read_more: + test [ebp + thread_data.permissions], ABORT + jnz abort_transfer + + lea eax, [ebp + thread_data.buffer] ; FIXME: use another buffer!! if we receive something on control connection now, we screw up! + invoke file.read, ebx, eax, BUFFERSIZE + cmp eax, -1 + je .cannot_open ; FIXME: this is not the correct error + + invoke con_write_asciiz, str2 + + push eax ebx + mov esi, eax + mov ecx, [ebp + thread_data.datasocketnum] + lea edx, [ebp + thread_data.buffer] + xor edi, edi + mcall send + pop ebx ecx + cmp eax, -1 + je socketerror ; FIXME: not the correct error + +; cmp eax, ecx +; jne not_all_byes_sent ; TODO + + cmp ecx, BUFFERSIZE + je .read_more + + invoke file.close, ebx + + invoke con_write_asciiz, str2b + + mov [ebp + thread_data.mode], MODE_NOTREADY + mcall close, [ebp + thread_data.datasocketnum] + + sendFTP "226 Transfer OK, closing connection" + ret + + .cannot_open: + invoke con_set_flags, 0x0c + invoke con_write_asciiz, str_notfound + invoke con_set_flags, 0x07 + + sendFTP "550 No such file" + ret + + + +;------------------------------------------------ +; "STOR" +; +; Store a file on the server. +; +;------------------------------------------------ +align 4 +cmdSTOR: + + test [ebp + thread_data.permissions], PERMISSION_WRITE + jz permission_denied + + ;sendFTP " Ready to receive" + ; open datasocket + cmp ecx, 1024 + 5 + jae .cannot_open + + sub ecx, 5 + jb .cannot_open + + call open_datasock + + ; creat path + call create_path + dec edi + lea esi, [ebp + thread_data.buffer + 5] + mov ecx, 1024 + cmp byte [esi], '/' + jne .loop + inc esi + .loop: + lodsb + cmp al, 0x20 + jl .done + stosb + loop .loop + .done: + xor al, al + stosb + + lea ebx, [ebp + thread_data.fpath] + invoke con_write_asciiz, ebx + invoke con_write_asciiz, str_newline + + ; open file + invoke file.open, ebx, O_CREATE + O_WRITE + test eax, eax + jz .cannot_open + + push eax + sendFTP "150 Here it comes.." + pop ebx + + .write_more: + test [ebp + thread_data.permissions], ABORT + jnz abort_transfer + + push eax ebx + mov esi, BUFFERSIZE ; eax + mov ecx, [ebp + thread_data.datasocketnum] + lea edx, [ebp + thread_data.buffer] + xor edi, edi + mcall recv + pop ebx ecx + cmp eax, -1 + je socketerror ; FIXME: not the correct error + + test eax, eax + jz @f + + push edx + mov edx, eax + lea eax, [ebp + thread_data.buffer] ; FIXME: use another buffer!! if we receive something on control connection now, we screw up! + invoke file.write, ebx, eax, edx + pop edx + + cmp eax, -1 + je .cannot_open ; FIXME: this is not the correct error + + invoke con_write_asciiz, str2 + +; cmp eax, ecx +; jne not_all_byes_sent ; TODO + + ;cmp ecx, BUFFERSIZE + ;je .write_more + jmp .write_more +@@: + + invoke file.close, ebx + + invoke con_write_asciiz, str2b + + mov [ebp + thread_data.mode], MODE_NOTREADY + mcall close, [ebp + thread_data.datasocketnum] + + + +;;;; TODO +; +; test [ebp + thread_data.permissions], ABORT +; jnz abort_transfer +; +;;;; + + sendFTP "226 Transfer OK" + ret + +.cannot_open: + sendFTP "550 No create file" + ret + +;------------------------------------------------ +; "SYST" +; +; Send information about the system. +; +;------------------------------------------------ +align 4 +cmdSYST: + + sendFTP "215 UNIX type: L8" + ret + +;------------------------------------------------ +; "TYPE" +; +; Choose the file transfer type. +; +;------------------------------------------------ +align 4 +cmdTYPE: + + cmp ecx, 6 + jb parse_cmd.error + + mov al, byte[esi+5] + and al, not 0x20 + + cmp al, 'A' + je .ascii + cmp al, 'E' + je .ebdic + cmp al, 'I' + je .image + cmp al, 'L' + je .local + + jmp parse_cmd.error + + .ascii: + mov [ebp + thread_data.type], TYPE_ASCII + jmp .subtype + + .ebdic: + mov [ebp + thread_data.type], TYPE_EBDIC + + .subtype: + cmp ecx, 8 + jb .non_print + + mov al, byte[esi+7] + and al, not 0x20 + + cmp al, 'N' + je .non_print + cmp al, 'T' + je .telnet + cmp al, 'C' + je .asacc + cmp al, 0x20 + jb .non_print + + jmp parse_cmd.error + + .non_print: + or [ebp + thread_data.type], TYPE_NP + jmp .ok + + .telnet: + or [ebp + thread_data.type], TYPE_TELNET + jmp .ok + + .asacc: + or [ebp + thread_data.type], TYPE_ASA + jmp .ok + + .image: + mov [ebp + thread_data.type], TYPE_IMAGE + jmp .ok + + .local: + cmp ecx, 8 + jb parse_cmd.error + + mov al, byte[esi+7] + sub al, '0' + jb parse_cmd.error ; FIXME: this is not the correct errormessage + cmp al, 9 + ja parse_cmd.error ; FIXME + or al, TYPE_LOCAL + mov [ebp + thread_data.type], al + + .ok: + sendFTP "200 Command ok" + ret + +;------------------------------------------------ +; "USER" +; +; Login to the server, step one of two. ;;; TODO: prevent buffer overflow! +; +;------------------------------------------------ +align 4 +cmdUSER: + + lea esi, [esi + 5] + lea edi, [ebp + thread_data.fpath] ; temp buffer for username + .loop: + lodsb + stosb + cmp al, 0x20 + jae .loop + mov byte [edi-1], 0 + + lea esi, [ebp + thread_data.fpath] + lea eax, [ebp + thread_data.home_dir] + invoke ini.get_str, path2, esi, str_home, eax, 1024, str_infinity + cmp eax, -1 + je .login_fail + cmp dword [esi], -1 + je .login_fail + + mov word [ebp + thread_data.work_dir], "/" ; "/", 0 + + invoke con_write_asciiz, str_logged_in + mov [ebp + thread_data.state], STATE_LOGIN + .sendstr: + sendFTP "331 Please specify the password" + ret + + .login_fail: + invoke con_write_asciiz, str_pass_err + mov [ebp + thread_data.state], STATE_LOGIN_FAIL + jmp .sendstr + +align 4 + .2: + sendFTP "530 Can't change to another user" + ret diff --git a/programs/network/ftpd/ftpd.asm b/programs/network/ftpd/ftpd.asm index 4aa30946b..1a1614eb6 100644 --- a/programs/network/ftpd/ftpd.asm +++ b/programs/network/ftpd/ftpd.asm @@ -1,467 +1,467 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; ;; -;; Copyright (C) KolibriOS team 2010-2017. All rights reserved. ;; -;; Distributed under terms of the GNU General Public License ;; -;; ;; -;; ftpd.asm - FTP Daemon for KolibriOS ;; -;; ;; -;; Written by hidnplayr@kolibrios.org ;; -;; ;; -;; GNU GENERAL PUBLIC LICENSE ;; -;; Version 2, June 1991 ;; -;; ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -DEBUG = 0 ; if set to one, program will run in a single thread - -BUFFERSIZE = 8192 - -; using multiple's of 4 -STATE_CONNECTED = 0*4 -STATE_LOGIN = 1*4 -STATE_LOGIN_FAIL = 2*4 ; When an invalid username was given -STATE_ACTIVE = 3*4 - -TYPE_UNDEF = 0 - -TYPE_ASCII = 00000100b -TYPE_EBDIC = 00001000b -; subtypes for ascii & ebdic (np = default) -TYPE_NP = 00000001b ; non printable -TYPE_TELNET = 00000010b -TYPE_ASA = 00000011b - -TYPE_IMAGE = 01000000b ; binary data -TYPE_LOCAL = 10000000b ; bits per byte must be specified - ; lower 4 bits will hold this value -MODE_NOTREADY = 0 -MODE_ACTIVE = 1 -MODE_PASSIVE_WAIT = 2 -MODE_PASSIVE_OK = 3 -MODE_PASSIVE_FAILED = 4 - -PERMISSION_EXEC = 1b ; LIST -PERMISSION_READ = 10b -PERMISSION_WRITE = 100b -PERMISSION_DELETE = 1000b -PERMISSION_CD = 10000b ; Change Directory - -ABORT = 1 shl 31 - -format binary as "" - -use32 - - org 0x0 - - db 'MENUET01' ; signature - dd 1 ; header version - dd start ; entry point - dd i_end ; initialized size - dd mem+0x1000 ; required memory - dd mem+0x1000 ; stack pointer - dd params ; parameters - dd path ; path - -include '../../macros.inc' -purge mov,add,sub -include '../../proc32.inc' -include '../../dll.inc' -include '../../struct.inc' -include '../../develop/libraries/libs-dev/libio/libio.inc' - -include '../../network.inc' - -macro sendFTP str { -local string, length - xor edi, edi - mcall send, [ebp + thread_data.socketnum], string, length - invoke con_write_asciiz, string - -iglobal -string db str, 13, 10, 0 -length = $ - string - 1 -\} -} - -include 'commands.inc' - -start: - mcall 68, 11 ; init heap - mcall 40, EVM_STACK ; we only want network events - -; load libraries - stdcall dll.Load, @IMPORT - test eax, eax - jnz exit - -; find path to main settings file (ftpd.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 - -; now create the second path (users.ini) - std - mov al, '/' - repne scasb - lea ecx, [edi - path + 2] - cld - mov esi, path - mov edi, path2 - rep movsb - mov esi, str_users - movsd - movsd - movsw - -; initialize console - invoke con_start, 1 - invoke con_init, -1, -1, -1, -1, title - -; get settings from ini - invoke ini.get_str, path, str_ftpd, str_ip, ini_buf, 16, 0 - mov esi, ini_buf - mov cl, '.' - call ip_to_dword - mov [serverip], ebx - - invoke ini.get_int, path, str_ftpd, str_port, 21 - xchg al, ah - mov [sockaddr1.port], ax - - xchg al, ah - invoke con_printf, str1, eax - add esp, 8 - -; open listening socket - mcall socket, AF_INET4, SOCK_STREAM, SO_NONBLOCK ; we dont want to block on accept - cmp eax, -1 - je sock_err - mov [socketnum], eax - - invoke con_write_asciiz, str2 - -; mcall setsockopt, [socketnum], SOL_SOCKET, SO_REUSEADDR, &yes, -; cmp eax, -1 -; je opt_err - - mcall bind, [socketnum], sockaddr1, sockaddr1.length - cmp eax, -1 - je bind_err - - invoke con_write_asciiz, str2 - - invoke ini.get_int, path, str_ftpd, str_conn, 1 ; Backlog (max connections) - mov edx, eax - - invoke con_write_asciiz, str2 - - mcall listen, [socketnum] - cmp eax, -1 - je listen_err - - invoke con_write_asciiz, str2b - - invoke ini.get_int, path, str_pasv, str_start, 2000 - mov [pasv_start], ax - invoke ini.get_int, path, str_pasv, str_end, 5000 - mov [pasv_end], ax - - mov [alive], 1 - -mainloop: - mcall 23, 100 ; Wait here for incoming connections on the base socket (socketnum) - ; One second timeout, we will use this to check if console is still working - - test eax, eax ; network event? - jz .checkconsole - -if DEBUG - jmp threadstart -else - mcall 51, 1, threadstart, 0 ; Start a new thread for every incoming connection - ; NOTE: upon initialisation of the thread, stack will not be available! -end if - jmp mainloop - - .checkconsole: - - invoke con_get_flags ; Is console still running? - test eax, 0x0200 - jz mainloop - mcall close, [socketnum] ; kill the listening socket - mov [alive], 0 - mcall -1 ; and exit - - diff16 "threadstart", 0, $ - -threadstart: -;;; mcall 68, 11 ; init heap - mcall 68, 12, sizeof.thread_data ; allocate the thread data struct - test eax, eax - je exit - - lea esp, [eax + thread_data.stack] ; init stack - mov ebp, eax - - mcall 40, EVM_STACK ; we only want network events for this thread - - lea ebx, [ebp + thread_data.buffer] ; get information about the current process - or ecx, -1 - mcall 9 - mov eax, dword [ebp + thread_data.buffer + 30] ; PID is at offset 30 - mov [ebp + thread_data.pid], eax - - invoke con_set_flags, 0x03 - invoke con_printf, str8, [ebp + thread_data.pid] ; print on the console that we have created the new thread successfully - add esp, 8 ; balance stack - invoke con_set_flags, 0x07 - - mcall accept, [socketnum], sockaddr1, sockaddr1.length ; time to accept the awaiting connection.. - cmp eax, -1 - je thread_exit - mov [ebp + thread_data.socketnum], eax - -if DEBUG - mcall close, [socketnum] ; close the listening socket -end if - - mov [ebp + thread_data.state], STATE_CONNECTED - mov [ebp + thread_data.permissions], 0 - mov [ebp + thread_data.mode], MODE_NOTREADY - lea eax, [ebp + thread_data.buffer] - mov [ebp + thread_data.buffer_ptr], eax - mov [ebp + thread_data.passivesocknum], -1 - - sendFTP " 220 Welcome to KolibriOS FTP daemon" ; fix output code - - diff16 "threadloop", 0, $ -threadloop: -;; Check if our socket is still connected -; mcall send, [ebp + thread_data.socketnum], 0, 0 ; Try to send zero bytes, if socket is closed, this will return -1 -; cmp eax, -1 -; je thread_exit - - cmp [alive], 0 ; Did main thread take a run for it? - je thread_exit - - mcall 23, 100 ; Wait for network event - test eax, eax - jz threadloop - - cmp [ebp + thread_data.mode], MODE_PASSIVE_WAIT - jne .not_passive - mov ecx, [ebp + thread_data.passivesocknum] - lea edx, [ebp + thread_data.datasock] - mov esi, sizeof.thread_data.datasock - mcall accept - cmp eax, -1 - je .not_passive - mov [ebp + thread_data.datasocketnum], eax - mov [ebp + thread_data.mode], MODE_PASSIVE_OK - mcall close ; [ebp + thread_data.passivesocknum] - mov [ebp + thread_data.passivesocknum], -1 - - invoke con_write_asciiz, str_datasock - .not_passive: - - mov ecx, [ebp + thread_data.socketnum] - mov edx, [ebp + thread_data.buffer_ptr] - mov esi, sizeof.thread_data.buffer ;;; FIXME - mov edi, MSG_DONTWAIT - mcall recv - inc eax ; error? (-1) - jz threadloop - dec eax ; 0 bytes read? - jz threadloop - - mov edi, [ebp + thread_data.buffer_ptr] - add [ebp + thread_data.buffer_ptr], eax - -; Check if we received a newline character, if not, wait for more data - mov ecx, eax - mov al, 10 - repne scasb - jne threadloop - - cmp word[edi-1], 0x0a0d - jne .got_command - dec edi - -; We got a command! - .got_command: - mov byte [edi], 0 ; append string with zero byte - lea esi, [ebp + thread_data.buffer] - mov ecx, [ebp + thread_data.buffer_ptr] - sub ecx, esi - mov [ebp + thread_data.buffer_ptr], esi ; reset buffer ptr - - invoke con_set_flags, 0x02 ; print received data to console (in green color) - invoke con_write_asciiz, str_newline - invoke con_write_asciiz, esi - invoke con_set_flags, 0x07 - - push threadloop - jmp parse_cmd - -listen_err: - invoke con_set_flags, 0x0c ; print errors in red - invoke con_write_asciiz, str3 - jmp done - -bind_err: - invoke con_set_flags, 0x0c ; print errors in red - invoke con_write_asciiz, str4 - jmp done - -sock_err: - invoke con_set_flags, 0x0c ; print errors in red - invoke con_write_asciiz, str6 - jmp done - -done: - invoke con_exit, 0 -exit: - mcall -1 - - -thread_exit: - invoke con_set_flags, 0x03 ; print thread info in blue - invoke con_printf, str_bye, [ebp + thread_data.pid] ; print on the console that we are about to kill the thread - add esp, 8 ; balance stack - mcall 68, 13, ebp ; free the memory - mcall -1 ; and kill the thread - - -; initialized data - -title db 'FTP daemon', 0 -str1 db 'Starting FTP daemon on port %u.', 0 -str2 db '.', 0 -str2b db ' OK!',10,0 -str3 db 'Listen error',10,0 -str4 db 10,'ERROR: local port is already in use.',10,0 -;str5 db 'Setsockopt error.',10,10,0 -str6 db 'ERROR: Could not open socket.',10,0 -str7 db 'Got data!',10,10,0 -str8 db 10,'Thread %d created',10,0 -str_bye db 10,'Thread %d killed',10,0 - -str_logged_in db 'Login ok',10,0 -str_pass_ok db 'Password ok',10,0 -str_pass_err db 'Password/Username incorrect',10,0 -str_pwd db 'Current directory is "%s"\n',0 -str_err2 db 'ERROR: cannot open the directory.',10,0 -str_datasock db 'Passive data socket connected.',10,0 -str_datasock2 db 'Active data socket connected.',10,0 -str_alopen db 'Data connection already open.',10,0 -str_notfound db 'ERROR: file not found.',10,0 -str_sockerr db 'ERROR: socket error.',10,0 - -str_newline db 10, 0 -str_mask db '*', 0 -str_infinity db 0xff, 0xff, 0xff, 0xff, 0 - -months dd 'Jan ' - dd 'Feb ' - dd 'Mar ' - dd 'Apr ' - dd 'May ' - dd 'Jun ' - dd 'Jul ' - dd 'Aug ' - dd 'Sep ' - dd 'Oct ' - dd 'Nov ' - dd 'Dec ' - -str_users db 'users' -str_ini db '.ini', 0 -str_port db 'port', 0 -str_ftpd db 'ftpd', 0 -str_conn db 'conn', 0 -str_ip db 'ip', 0 -str_pass db 'pass', 0 -str_home db 'home', 0 -str_mode db 'mode', 0 -str_pasv db 'pasv', 0 -str_start db 'start', 0 -str_end db 'end', 0 - - -sockaddr1: - dw AF_INET4 - .port dw 0 - .ip dd 0 - rb 10 - .length = $ - sockaddr1 - -; import - -align 4 -@IMPORT: - -diff16 "import", 0, $ - -library console, 'console.obj',\ - libini, 'libini.obj', \ - libio, 'libio.obj' - -import console,\ - con_start, 'START',\ - con_init, 'con_init',\ - con_write_asciiz, 'con_write_asciiz',\ - con_exit, 'con_exit',\ - con_gets, 'con_gets',\ - con_cls, 'con_cls',\ - con_printf, 'con_printf',\ - con_getch2, 'con_getch2',\ - con_set_cursor_pos, 'con_set_cursor_pos',\ - con_set_flags, 'con_set_flags',\ - con_get_flags, 'con_get_flags' - -import libini,\ - ini.get_str, 'ini_get_str',\ - ini.get_int, 'ini_get_int' - -import libio,\ - file.size, 'file_size',\ - file.open, 'file_open',\ - file.read, 'file_read',\ - file.write, 'file_write',\ - file.close, 'file_close',\ - file.find.first, 'file_find_first',\ - file.find.next, 'file_find_next',\ - file.find.close, 'file_find_close' - - -IncludeIGlobals - - -i_end: - -diff16 "i_end", 0, $ - -; uninitialised data - - socketnum dd ? - path rb 1024 - path2 rb 1024 - params rb 1024 - serverip dd ? - pasv_start dw ? - pasv_end dw ? - pasv_port dw ? - - ini_buf rb 3*4+3+1 - - alive db ? - -mem: - - +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2010-2024. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; ftpd.asm - FTP Daemon for KolibriOS ;; +;; ;; +;; Written by hidnplayr@kolibrios.org ;; +;; ;; +;; GNU GENERAL PUBLIC LICENSE ;; +;; Version 2, June 1991 ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +DEBUG = 0 ; if set to one, program will run in a single thread + +BUFFERSIZE = 8192 + +; using multiple's of 4 +STATE_CONNECTED = 0*4 +STATE_LOGIN = 1*4 +STATE_LOGIN_FAIL = 2*4 ; When an invalid username was given +STATE_ACTIVE = 3*4 + +TYPE_UNDEF = 0 + +TYPE_ASCII = 00000100b +TYPE_EBDIC = 00001000b +; subtypes for ascii & ebdic (np = default) +TYPE_NP = 00000001b ; non printable +TYPE_TELNET = 00000010b +TYPE_ASA = 00000011b + +TYPE_IMAGE = 01000000b ; binary data +TYPE_LOCAL = 10000000b ; bits per byte must be specified + ; lower 4 bits will hold this value +MODE_NOTREADY = 0 +MODE_ACTIVE = 1 +MODE_PASSIVE_WAIT = 2 +MODE_PASSIVE_OK = 3 +MODE_PASSIVE_FAILED = 4 + +PERMISSION_EXEC = 1b ; LIST +PERMISSION_READ = 10b +PERMISSION_WRITE = 100b +PERMISSION_DELETE = 1000b +PERMISSION_CD = 10000b ; Change Directory + +ABORT = 1 shl 31 + +format binary as "" + +use32 + + org 0x0 + + db 'MENUET01' ; signature + dd 1 ; header version + dd start ; entry point + dd i_end ; initialized size + dd mem+0x1000 ; required memory + dd mem+0x1000 ; stack pointer + dd params ; parameters + dd path ; path + +include '../../macros.inc' +purge mov,add,sub +include '../../proc32.inc' +include '../../dll.inc' +include '../../struct.inc' +include '../../develop/libraries/libs-dev/libio/libio.inc' + +include '../../network.inc' + +macro sendFTP str { +local string, length + xor edi, edi + mcall send, [ebp + thread_data.socketnum], string, length + invoke con_write_asciiz, string + +iglobal +string db str, 13, 10, 0 +length = $ - string - 1 +\} +} + +include 'commands.inc' + +start: + mcall 68, 11 ; init heap + mcall 40, EVM_STACK ; we only want network events + +; load libraries + stdcall dll.Load, @IMPORT + test eax, eax + jnz exit + +; find path to main settings file (ftpd.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 + +; now create the second path (users.ini) + std + mov al, '/' + repne scasb + lea ecx, [edi - path + 2] + cld + mov esi, path + mov edi, path2 + rep movsb + mov esi, str_users + movsd + movsd + movsw + +; initialize console + invoke con_start, 1 + invoke con_init, -1, -1, -1, -1, title + +; get settings from ini + invoke ini.get_str, path, str_ftpd, str_ip, ini_buf, 16, 0 + mov esi, ini_buf + mov cl, '.' + call ip_to_dword + mov [serverip], ebx + + invoke ini.get_int, path, str_ftpd, str_port, 21 + xchg al, ah + mov [sockaddr1.port], ax + + xchg al, ah + invoke con_printf, str1, eax + add esp, 8 + +; open listening socket + mcall socket, AF_INET4, SOCK_STREAM, SO_NONBLOCK ; we don't want to block on accept + cmp eax, -1 + je sock_err + mov [socketnum], eax + + invoke con_write_asciiz, str2 + +; mcall setsockopt, [socketnum], SOL_SOCKET, SO_REUSEADDR, &yes, +; cmp eax, -1 +; je opt_err + + mcall bind, [socketnum], sockaddr1, sockaddr1.length + cmp eax, -1 + je bind_err + + invoke con_write_asciiz, str2 + + invoke ini.get_int, path, str_ftpd, str_conn, 1 ; Backlog (max connections) + mov edx, eax + + invoke con_write_asciiz, str2 + + mcall listen, [socketnum] + cmp eax, -1 + je listen_err + + invoke con_write_asciiz, str2b + + invoke ini.get_int, path, str_pasv, str_start, 2000 + mov [pasv_start], ax + invoke ini.get_int, path, str_pasv, str_end, 5000 + mov [pasv_end], ax + + mov [alive], 1 + +mainloop: + mcall 23, 100 ; Wait here for incoming connections on the base socket (socketnum) + ; One second timeout, we will use this to check if console is still working + + test eax, eax ; network event? + jz .checkconsole + +if DEBUG + jmp threadstart +else + mcall 51, 1, threadstart, 0 ; Start a new thread for every incoming connection + ; NOTE: upon initialisation of the thread, stack will not be available! +end if + jmp mainloop + + .checkconsole: + + invoke con_get_flags ; Is console still running? + test eax, 0x0200 + jz mainloop + mcall close, [socketnum] ; kill the listening socket + mov [alive], 0 + mcall -1 ; and exit + + diff16 "threadstart", 0, $ + +threadstart: +;;; mcall 68, 11 ; init heap + mcall 68, 12, sizeof.thread_data ; allocate the thread data struct + test eax, eax + je exit + + lea esp, [eax + thread_data.stack] ; init stack + mov ebp, eax + + mcall 40, EVM_STACK ; we only want network events for this thread + + lea ebx, [ebp + thread_data.buffer] ; get information about the current process + or ecx, -1 + mcall 9 + mov eax, dword [ebp + thread_data.buffer + 30] ; PID is at offset 30 + mov [ebp + thread_data.pid], eax + + invoke con_set_flags, 0x03 + invoke con_printf, str8, [ebp + thread_data.pid] ; print on the console that we have created the new thread successfully + add esp, 8 ; balance stack + invoke con_set_flags, 0x07 + + mcall accept, [socketnum], sockaddr1, sockaddr1.length ; time to accept the awaiting connection.. + cmp eax, -1 + je thread_exit + mov [ebp + thread_data.socketnum], eax + +if DEBUG + mcall close, [socketnum] ; close the listening socket +end if + + mov [ebp + thread_data.state], STATE_CONNECTED + mov [ebp + thread_data.permissions], 0 + mov [ebp + thread_data.mode], MODE_NOTREADY + lea eax, [ebp + thread_data.buffer] + mov [ebp + thread_data.buffer_ptr], eax + mov [ebp + thread_data.passivesocknum], -1 + + sendFTP " 220 Welcome to KolibriOS FTP daemon" ; fix output code + + diff16 "threadloop", 0, $ +threadloop: +;; Check if our socket is still connected +; mcall send, [ebp + thread_data.socketnum], 0, 0 ; Try to send zero bytes, if socket is closed, this will return -1 +; cmp eax, -1 +; je thread_exit + + cmp [alive], 0 ; Did main thread take a run for it? + je thread_exit + + mcall 23, 100 ; Wait for network event + test eax, eax + jz threadloop + + cmp [ebp + thread_data.mode], MODE_PASSIVE_WAIT + jne .not_passive + mov ecx, [ebp + thread_data.passivesocknum] + lea edx, [ebp + thread_data.datasock] + mov esi, sizeof.thread_data.datasock + mcall accept + cmp eax, -1 + je .not_passive + mov [ebp + thread_data.datasocketnum], eax + mov [ebp + thread_data.mode], MODE_PASSIVE_OK + mcall close ; [ebp + thread_data.passivesocknum] + mov [ebp + thread_data.passivesocknum], -1 + + invoke con_write_asciiz, str_datasock + .not_passive: + + mov ecx, [ebp + thread_data.socketnum] + mov edx, [ebp + thread_data.buffer_ptr] + mov esi, sizeof.thread_data.buffer ;;; FIXME + mov edi, MSG_DONTWAIT + mcall recv + inc eax ; error? (-1) + jz threadloop + dec eax ; 0 bytes read? + jz threadloop + + mov edi, [ebp + thread_data.buffer_ptr] + add [ebp + thread_data.buffer_ptr], eax + +; Check if we received a newline character, if not, wait for more data + mov ecx, eax + mov al, 10 + repne scasb + jne threadloop + + cmp word[edi-1], 0x0a0d + jne .got_command + dec edi + +; We got a command! + .got_command: + mov byte [edi], 0 ; append string with zero byte + lea esi, [ebp + thread_data.buffer] + mov ecx, [ebp + thread_data.buffer_ptr] + sub ecx, esi + mov [ebp + thread_data.buffer_ptr], esi ; reset buffer ptr + + invoke con_set_flags, 0x02 ; print received data to console (in green color) + invoke con_write_asciiz, str_newline + invoke con_write_asciiz, esi + invoke con_set_flags, 0x07 + + push threadloop + jmp parse_cmd + +listen_err: + invoke con_set_flags, 0x0c ; print errors in red + invoke con_write_asciiz, str3 + jmp done + +bind_err: + invoke con_set_flags, 0x0c ; print errors in red + invoke con_write_asciiz, str4 + jmp done + +sock_err: + invoke con_set_flags, 0x0c ; print errors in red + invoke con_write_asciiz, str6 + jmp done + +done: + invoke con_exit, 0 +exit: + mcall -1 + + +thread_exit: + invoke con_set_flags, 0x03 ; print thread info in blue + invoke con_printf, str_bye, [ebp + thread_data.pid] ; print on the console that we are about to kill the thread + add esp, 8 ; balance stack + mcall 68, 13, ebp ; free the memory + mcall -1 ; and kill the thread + + +; initialized data + +title db 'FTP daemon', 0 +str1 db 'Starting FTP daemon on port %u.', 0 +str2 db '.', 0 +str2b db ' OK!',10,0 +str3 db 'Listen error',10,0 +str4 db 10,'ERROR: local port is already in use.',10,0 +;str5 db 'Setsockopt error.',10,10,0 +str6 db 'ERROR: Could not open socket.',10,0 +str7 db 'Got data!',10,10,0 +str8 db 10,'Thread %d created',10,0 +str_bye db 10,'Thread %d killed',10,0 + +str_logged_in db 'Login ok',10,0 +str_pass_ok db 'Password ok',10,0 +str_pass_err db 'Password/Username incorrect',10,0 +str_pwd db 'Current directory is "%s"\n',0 +str_err2 db 'ERROR: cannot open the directory.',10,0 +str_datasock db 'Passive data socket connected.',10,0 +str_datasock2 db 'Active data socket connected.',10,0 +str_alopen db 'Data connection already open.',10,0 +str_notfound db 'ERROR: file not found.',10,0 +str_sockerr db 'ERROR: socket error.',10,0 + +str_newline db 10, 0 +str_mask db '*', 0 +str_infinity db 0xff, 0xff, 0xff, 0xff, 0 + +months dd 'Jan ' + dd 'Feb ' + dd 'Mar ' + dd 'Apr ' + dd 'May ' + dd 'Jun ' + dd 'Jul ' + dd 'Aug ' + dd 'Sep ' + dd 'Oct ' + dd 'Nov ' + dd 'Dec ' + +str_users db 'users' +str_ini db '.ini', 0 +str_port db 'port', 0 +str_ftpd db 'ftpd', 0 +str_conn db 'conn', 0 +str_ip db 'ip', 0 +str_pass db 'pass', 0 +str_home db 'home', 0 +str_mode db 'mode', 0 +str_pasv db 'pasv', 0 +str_start db 'start', 0 +str_end db 'end', 0 + + +sockaddr1: + dw AF_INET4 + .port dw 0 + .ip dd 0 + rb 10 + .length = $ - sockaddr1 + +; import + +align 4 +@IMPORT: + +diff16 "import", 0, $ + +library console, 'console.obj',\ + libini, 'libini.obj', \ + libio, 'libio.obj' + +import console,\ + con_start, 'START',\ + con_init, 'con_init',\ + con_write_asciiz, 'con_write_asciiz',\ + con_exit, 'con_exit',\ + con_gets, 'con_gets',\ + con_cls, 'con_cls',\ + con_printf, 'con_printf',\ + con_getch2, 'con_getch2',\ + con_set_cursor_pos, 'con_set_cursor_pos',\ + con_set_flags, 'con_set_flags',\ + con_get_flags, 'con_get_flags' + +import libini,\ + ini.get_str, 'ini_get_str',\ + ini.get_int, 'ini_get_int' + +import libio,\ + file.size, 'file_size',\ + file.open, 'file_open',\ + file.read, 'file_read',\ + file.write, 'file_write',\ + file.close, 'file_close',\ + file.find.first, 'file_find_first',\ + file.find.next, 'file_find_next',\ + file.find.close, 'file_find_close' + + +IncludeIGlobals + + +i_end: + +diff16 "i_end", 0, $ + +; uninitialised data + + socketnum dd ? + path rb 1024 + path2 rb 1024 + params rb 1024 + serverip dd ? + pasv_start dw ? + pasv_end dw ? + pasv_port dw ? + + ini_buf rb 3*4+3+1 + + alive db ? + +mem: + + diff --git a/programs/network/ircc/gui.inc b/programs/network/ircc/gui.inc index c9e4eac35..71f694b6b 100644 --- a/programs/network/ircc/gui.inc +++ b/programs/network/ircc/gui.inc @@ -1,385 +1,385 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; ;; -;; Copyright (C) KolibriOS team 2004-2020. All rights reserved. ;; -;; Distributed under terms of the GNU General Public License ;; -;; ;; -;; Written by hidnplayr@kolibrios.org ;; -;; ;; -;; GNU GENERAL PUBLIC LICENSE ;; -;; Version 2, June 1991 ;; -;; ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - - -draw_window: ; Completely redraw the window, recalculate all coordinates and sizes - - pusha - -; get system colors - mcall 48, 3, colors, 40 - call set_edit_box_and_scrollbar_colors - - mcall 9, thread_info, -1 ; get information about this thread - mov eax, [thread_info.box.width] ; window xsize - mov ebx, [thread_info.box.height] ; ysize - mov edx, [thread_info.client_box.width] ; work area xsize - mov esi, [thread_info.client_box.height] ; ysize - sub eax, edx - sub ebx, esi - - cmp edx, WIN_MIN_X - jae .x_ok - mov edx, WIN_MIN_X - .x_ok: - mov [xsize], edx - add edx, eax - - cmp esi, WIN_MIN_Y - jae .y_ok - mov esi, WIN_MIN_Y - .y_ok: - mov [ysize], esi - add esi, ebx - mcall 67, -1, -1 ; set the new sizes - - popa - - .dont_resize: - - pusha - - mcall 12, 1 - xor eax, eax ; draw window - mov ebx, WIN_MIN_X+10 - mov ecx, WIN_MIN_Y+30 - mov edx, [colors.work] - add edx, 0x33000000 - mov edi, str_programname - mcall - - test [thread_info.wnd_state], 100b ; skip if window is rolled up - jne .exit - - cmp [window_active], 0 - je .no_window - -; calculate available space for textbox and coordinates for scrollbars - mov eax, [ysize] - sub eax, TOP_Y + INPUTBOX_HEIGHT - 1 - mov [scroll2.y_size], ax - mov [scroll1.y_size], ax - sub eax, 4 - xor edx, edx - mov ecx, FONT_HEIGHT - div ecx - mov [textbox_height], eax - mov [scroll2.cur_area], eax - mov [scroll1.cur_area], eax - - mov eax, [xsize] - sub eax, SCROLLBAR_WIDTH - mov [scroll1.x_pos], ax - mov edi, [window_active] - cmp [edi + window.type], WINDOWTYPE_CHANNEL - jne @f - sub eax, USERLIST_WIDTH + SCROLLBAR_WIDTH + 2 - @@: - mov [scroll2.x_pos], ax - sub eax, 10 - xor edx, edx - mov ecx, FONT_WIDTH - div ecx - mov [textbox_width], eax - -; recalculate text line breaks (because height/width might have changed..) -; meanwhile, recalculate line number of current line - mov esi, [edi + window.text_print] - mov al, byte[esi] - push eax - mov byte[esi], 0 - push esi - - mov esi, [edi + window.text_start] - call text_insert_newlines - mov [edi + window.text_lines], edx - mov [edi + window.text_scanned], esi - mov [edi + window.text_line_print], edx - - pop esi - pop eax - mov byte[esi], al - -; and redraw the textbox (and scrollbar if needed) - mov [scroll2.all_redraw], 1 - call draw_channel_text - -; Draw userlist if active window is a channel - mov edi, [window_active] - cmp [edi + window.type], WINDOWTYPE_CHANNEL - jne .not_channel - - mov [scroll1.all_redraw], 1 - call draw_user_list - -; draw a vertical separator line when there is no scrollbar - cmp [scroll2.all_redraw], 1 - jne .not_channel - - mov ebx, [xsize] - sub ebx, USERLIST_WIDTH + SCROLLBAR_WIDTH + 3 - push bx - shl ebx, 16 - pop bx - mov ecx, [ysize] - add ecx, TOP_Y shl 16 - (INPUTBOX_HEIGHT) - mov edx, [colors.work_graph] - mcall 38 - .not_channel: - .no_window: - -; draw editbox - mov eax, [ysize] - sub eax, INPUTBOX_HEIGHT - mov [edit1.top], eax - - mov eax, [xsize] - mov [edit1.width], eax - - invoke edit_box_draw, edit1 - -; draw tabs - call draw_window_tabs - - .exit: - mcall 12, 2 - popa - ret - - - -draw_user_list: - - pusha - - ; Do we need a scrollbar? - mov ebx, [window_active] - mov eax, [ebx + window.users] - mov [scroll1.max_area], eax - cmp [scroll1.cur_area], eax - jae .noscroll - - ; Is the current position greater then the max position? - cmp eax, [scroll1.position] - ja @f - mov [scroll1.position], eax - @@: - ; OK, draw the scrollbar - invoke scrollbar_draw, scroll1 - - ; dont redraw scrollbar completely next time, - ; unless draw_window asks us to by setting [scroll1.all_redraw] back to 1 - mov [scroll1.all_redraw], 0 - jmp .scroll_done - - .noscroll: - mov [scroll1.position], 0 - .scroll_done: - - ; draw an invisible button, where the usernames will go - mov ebx, [xsize] - sub ebx, USERLIST_WIDTH + SCROLLBAR_WIDTH - shl ebx, 16 - push ebx - mov bx, USERLIST_WIDTH - mov ecx, [ysize] - add ecx, TEXT_Y shl 16 - (TEXT_Y + 16) - push ecx ebx - mov edx, WINDOW_BTN_LIST + 1 shl 29 + 1 shl 30 - mcall 8 - - ; draw a filled rectangle to clear previously printed names - pop ebx ecx - mov edx, [colors.work] - mcall 13 - -; now, draw the names according to the scrollbar position and window size - mov eax, [scroll1.position] - xor edx, edx - mov ecx, MAX_NICK_LEN - mul ecx - mov edx, eax - mov eax, [window_active] - mov ebp, [eax + window.selected] - add edx, [eax + window.data_ptr] - sub ebp, [scroll1.position] - add edx, window_data.names - - pop ebx - mov bx, TEXT_Y - mov ecx, [colors.work_text] - or ecx, 0x90000000 ; 8x16 font, zero terminated string - mov eax, 4 ; draw text - - mov edi, [textbox_height] ; how many names will fit on screen - .loop: - cmp byte[edx], 0 ; end of list? - je .done - - dec ebp ; is this name selected? - jnz .nothighlight - ; yes, highlight it - pusha - mov cx, bx - mov bx, USERLIST_WIDTH - shl ecx, 16 - mov cx, FONT_HEIGHT - mov edx, 0x00000055 ; blue! - mcall 13 - popa - - mov ecx, 0x9000ffff ; cyan! - mcall - - mov ecx, [colors.work_text] - or ecx, 0x90000000 ; 8x16 font, zero terminated string - jmp .next - - .nothighlight: - mcall - - .next: - add edx, MAX_NICK_LEN - add ebx, FONT_HEIGHT - dec edi - jnz .loop - - .done: - popa - - ret - - -draw_window_tabs: - -; Draw horizontal line - - mov ebx, [xsize] - mov edx, [colors.work_graph] - mov ecx, TOP_Y SHL 16 + TOP_Y - mcall 38 - -; Create the buttons - - mov eax, 8 - mov ebx, TAB_WIDTH - mov ecx, TOP_SPACE shl 16 + TAB_HEIGHT - mov edx, WINDOW_BTN_START - mov edi, windows - .more_btn: - mov esi, [colors.work] - cmp [window_active], edi - jne @f - ;not esi - ;and esi, 0x00ffffff - mov esi, [colors.work_light] - @@: - mcall - inc edx - add ebx, (TAB_WIDTH + TAB_SPACE) shl 16 - add edi, sizeof.window - cmp [edi + window.data_ptr], 0 - jne .more_btn - -; Draw the close window button - mov edi, [window_active] - cmp [edi + window.type], WINDOWTYPE_SERVER ; dont let the user close server window - je @f - -; mov eax, 8 - mov ebx, [xsize] - sub ebx, SCROLLBAR_WIDTH - shl ebx, 16 - mov bx, SCROLLBAR_WIDTH - mov ecx, TOP_SPACE shl 16 + TAB_HEIGHT - 1 - mov edx, WINDOW_BTN_CLOSE - mov esi, 0x00aa0000 ; red ! - mcall - - pusha - ; write closing cross - mov ebx, [xsize] - sub ebx, 9 - shl ebx, 16 - add ebx, TOP_SPACE+3 - mov ecx, 0x80FFFfff - mov edx, closing_cross - mcall 4 - popa - @@: - -; Draw the windownames onto the buttons - - mov eax, 4 - mov ebx, 5 shl 16 + TOP_SPACE + 4 ;;;; - mov esi, MAX_WINDOWS - mov edi, windows - .more: - mov ecx, [colors.work_text] - test [edi + window.flags], FLAG_UPDATED - jz @f - mov ecx, 0x00aa0000 ; RED! - @@: - or ecx, 0x80000000 ; ASCIIZ string - lea edx, [edi + window.name] - mcall - add edi, sizeof.window ; get ptr to next window - cmp [edi + window.data_ptr], 0 - je .enough - add ebx, (TAB_WIDTH + TAB_SPACE) shl 16 - dec esi - jnz .more - .enough: - - ret - - - -highlight_updated_tabs: - mov eax, 4 - mov ebx, 5 shl 16 + TOP_SPACE + 4 ;;;; - mov ecx, 0x80aa0000 - mov esi, MAX_WINDOWS - mov edi, windows - .more_: - test [edi + window.flags], FLAG_UPDATED - jz .next - lea edx, [edi + window.name] - mcall - .next: - add edi, sizeof.window ; get ptr to next window - cmp [edi + window.data_ptr], 0 - je .enough_ - add ebx, (TAB_WIDTH + TAB_SPACE) shl 16 - dec esi - jnz .more_ - .enough_: - - ret - -set_edit_box_and_scrollbar_colors: - mov eax, [colors.work] - mov [scroll1.bg_color], eax - mov [scroll2.bg_color], eax - - mov eax, [colors.work_button] - mov [scroll1.front_color], eax - mov [scroll2.front_color], eax - - mov eax, [colors.work_text] - mov [scroll1.line_color], eax - mov [scroll2.line_color], eax - - mov [scroll1.type], 0 ; 0 = simple, 1 = skinned - mov [scroll2.type], 0 - - ret \ No newline at end of file +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2024. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; Written by hidnplayr@kolibrios.org ;; +;; ;; +;; GNU GENERAL PUBLIC LICENSE ;; +;; Version 2, June 1991 ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +draw_window: ; Completely redraw the window, recalculate all coordinates and sizes + + pusha + +; get system colors + mcall 48, 3, colors, 40 + call set_edit_box_and_scrollbar_colors + + mcall 9, thread_info, -1 ; get information about this thread + mov eax, [thread_info.box.width] ; window xsize + mov ebx, [thread_info.box.height] ; ysize + mov edx, [thread_info.client_box.width] ; work area xsize + mov esi, [thread_info.client_box.height] ; ysize + sub eax, edx + sub ebx, esi + + cmp edx, WIN_MIN_X + jae .x_ok + mov edx, WIN_MIN_X + .x_ok: + mov [xsize], edx + add edx, eax + + cmp esi, WIN_MIN_Y + jae .y_ok + mov esi, WIN_MIN_Y + .y_ok: + mov [ysize], esi + add esi, ebx + mcall 67, -1, -1 ; set the new sizes + + popa + + .dont_resize: + + pusha + + mcall 12, 1 + xor eax, eax ; draw window + mov ebx, WIN_MIN_X+10 + mov ecx, WIN_MIN_Y+30 + mov edx, [colors.work] + add edx, 0x33000000 + mov edi, str_programname + mcall + + test [thread_info.wnd_state], 100b ; skip if window is rolled up + jne .exit + + cmp [window_active], 0 + je .no_window + +; calculate available space for textbox and coordinates for scrollbars + mov eax, [ysize] + sub eax, TOP_Y + INPUTBOX_HEIGHT - 1 + mov [scroll2.y_size], ax + mov [scroll1.y_size], ax + sub eax, 4 + xor edx, edx + mov ecx, FONT_HEIGHT + div ecx + mov [textbox_height], eax + mov [scroll2.cur_area], eax + mov [scroll1.cur_area], eax + + mov eax, [xsize] + sub eax, SCROLLBAR_WIDTH + mov [scroll1.x_pos], ax + mov edi, [window_active] + cmp [edi + window.type], WINDOWTYPE_CHANNEL + jne @f + sub eax, USERLIST_WIDTH + SCROLLBAR_WIDTH + 2 + @@: + mov [scroll2.x_pos], ax + sub eax, 10 + xor edx, edx + mov ecx, FONT_WIDTH + div ecx + mov [textbox_width], eax + +; recalculate text line breaks (because height/width might have changed..) +; meanwhile, recalculate line number of current line + mov esi, [edi + window.text_print] + mov al, byte[esi] + push eax + mov byte[esi], 0 + push esi + + mov esi, [edi + window.text_start] + call text_insert_newlines + mov [edi + window.text_lines], edx + mov [edi + window.text_scanned], esi + mov [edi + window.text_line_print], edx + + pop esi + pop eax + mov byte[esi], al + +; and redraw the textbox (and scrollbar if needed) + mov [scroll2.all_redraw], 1 + call draw_channel_text + +; Draw userlist if active window is a channel + mov edi, [window_active] + cmp [edi + window.type], WINDOWTYPE_CHANNEL + jne .not_channel + + mov [scroll1.all_redraw], 1 + call draw_user_list + +; draw a vertical separator line when there is no scrollbar + cmp [scroll2.all_redraw], 1 + jne .not_channel + + mov ebx, [xsize] + sub ebx, USERLIST_WIDTH + SCROLLBAR_WIDTH + 3 + push bx + shl ebx, 16 + pop bx + mov ecx, [ysize] + add ecx, TOP_Y shl 16 - (INPUTBOX_HEIGHT) + mov edx, [colors.work_graph] + mcall 38 + .not_channel: + .no_window: + +; draw editbox + mov eax, [ysize] + sub eax, INPUTBOX_HEIGHT + mov [edit1.top], eax + + mov eax, [xsize] + mov [edit1.width], eax + + invoke edit_box_draw, edit1 + +; draw tabs + call draw_window_tabs + + .exit: + mcall 12, 2 + popa + ret + + + +draw_user_list: + + pusha + + ; Do we need a scrollbar? + mov ebx, [window_active] + mov eax, [ebx + window.users] + mov [scroll1.max_area], eax + cmp [scroll1.cur_area], eax + jae .noscroll + + ; Is the current position greater then the max position? + cmp eax, [scroll1.position] + ja @f + mov [scroll1.position], eax + @@: + ; OK, draw the scrollbar + invoke scrollbar_draw, scroll1 + + ; don't redraw scrollbar completely next time, + ; unless draw_window asks us to by setting [scroll1.all_redraw] back to 1 + mov [scroll1.all_redraw], 0 + jmp .scroll_done + + .noscroll: + mov [scroll1.position], 0 + .scroll_done: + + ; draw an invisible button, where the usernames will go + mov ebx, [xsize] + sub ebx, USERLIST_WIDTH + SCROLLBAR_WIDTH + shl ebx, 16 + push ebx + mov bx, USERLIST_WIDTH + mov ecx, [ysize] + add ecx, TEXT_Y shl 16 - (TEXT_Y + 16) + push ecx ebx + mov edx, WINDOW_BTN_LIST + 1 shl 29 + 1 shl 30 + mcall 8 + + ; draw a filled rectangle to clear previously printed names + pop ebx ecx + mov edx, [colors.work] + mcall 13 + +; now, draw the names according to the scrollbar position and window size + mov eax, [scroll1.position] + xor edx, edx + mov ecx, MAX_NICK_LEN + mul ecx + mov edx, eax + mov eax, [window_active] + mov ebp, [eax + window.selected] + add edx, [eax + window.data_ptr] + sub ebp, [scroll1.position] + add edx, window_data.names + + pop ebx + mov bx, TEXT_Y + mov ecx, [colors.work_text] + or ecx, 0x90000000 ; 8x16 font, zero terminated string + mov eax, 4 ; draw text + + mov edi, [textbox_height] ; how many names will fit on screen + .loop: + cmp byte[edx], 0 ; end of list? + je .done + + dec ebp ; is this name selected? + jnz .nothighlight + ; yes, highlight it + pusha + mov cx, bx + mov bx, USERLIST_WIDTH + shl ecx, 16 + mov cx, FONT_HEIGHT + mov edx, 0x00000055 ; blue! + mcall 13 + popa + + mov ecx, 0x9000ffff ; cyan! + mcall + + mov ecx, [colors.work_text] + or ecx, 0x90000000 ; 8x16 font, zero terminated string + jmp .next + + .nothighlight: + mcall + + .next: + add edx, MAX_NICK_LEN + add ebx, FONT_HEIGHT + dec edi + jnz .loop + + .done: + popa + + ret + + +draw_window_tabs: + +; Draw horizontal line + + mov ebx, [xsize] + mov edx, [colors.work_graph] + mov ecx, TOP_Y SHL 16 + TOP_Y + mcall 38 + +; Create the buttons + + mov eax, 8 + mov ebx, TAB_WIDTH + mov ecx, TOP_SPACE shl 16 + TAB_HEIGHT + mov edx, WINDOW_BTN_START + mov edi, windows + .more_btn: + mov esi, [colors.work] + cmp [window_active], edi + jne @f + ;not esi + ;and esi, 0x00ffffff + mov esi, [colors.work_light] + @@: + mcall + inc edx + add ebx, (TAB_WIDTH + TAB_SPACE) shl 16 + add edi, sizeof.window + cmp [edi + window.data_ptr], 0 + jne .more_btn + +; Draw the close window button + mov edi, [window_active] + cmp [edi + window.type], WINDOWTYPE_SERVER ; don't let the user close server window + je @f + +; mov eax, 8 + mov ebx, [xsize] + sub ebx, SCROLLBAR_WIDTH + shl ebx, 16 + mov bx, SCROLLBAR_WIDTH + mov ecx, TOP_SPACE shl 16 + TAB_HEIGHT - 1 + mov edx, WINDOW_BTN_CLOSE + mov esi, 0x00aa0000 ; red ! + mcall + + pusha + ; write closing cross + mov ebx, [xsize] + sub ebx, 9 + shl ebx, 16 + add ebx, TOP_SPACE+3 + mov ecx, 0x80FFFfff + mov edx, closing_cross + mcall 4 + popa + @@: + +; Draw the windownames onto the buttons + + mov eax, 4 + mov ebx, 5 shl 16 + TOP_SPACE + 4 ;;;; + mov esi, MAX_WINDOWS + mov edi, windows + .more: + mov ecx, [colors.work_text] + test [edi + window.flags], FLAG_UPDATED + jz @f + mov ecx, 0x00aa0000 ; RED! + @@: + or ecx, 0x80000000 ; ASCIIZ string + lea edx, [edi + window.name] + mcall + add edi, sizeof.window ; get ptr to next window + cmp [edi + window.data_ptr], 0 + je .enough + add ebx, (TAB_WIDTH + TAB_SPACE) shl 16 + dec esi + jnz .more + .enough: + + ret + + + +highlight_updated_tabs: + mov eax, 4 + mov ebx, 5 shl 16 + TOP_SPACE + 4 ;;;; + mov ecx, 0x80aa0000 + mov esi, MAX_WINDOWS + mov edi, windows + .more_: + test [edi + window.flags], FLAG_UPDATED + jz .next + lea edx, [edi + window.name] + mcall + .next: + add edi, sizeof.window ; get ptr to next window + cmp [edi + window.data_ptr], 0 + je .enough_ + add ebx, (TAB_WIDTH + TAB_SPACE) shl 16 + dec esi + jnz .more_ + .enough_: + + ret + +set_edit_box_and_scrollbar_colors: + mov eax, [colors.work] + mov [scroll1.bg_color], eax + mov [scroll2.bg_color], eax + + mov eax, [colors.work_button] + mov [scroll1.front_color], eax + mov [scroll2.front_color], eax + + mov eax, [colors.work_text] + mov [scroll1.line_color], eax + mov [scroll2.line_color], eax + + mov [scroll1.type], 0 ; 0 = simple, 1 = skinned + mov [scroll2.type], 0 + + ret diff --git a/programs/network/ircc/userparser.inc b/programs/network/ircc/userparser.inc index 3a1a2cb24..999513346 100644 --- a/programs/network/ircc/userparser.inc +++ b/programs/network/ircc/userparser.inc @@ -96,7 +96,7 @@ user_parser: .ret: ret -; Text begins with a '/' lets try to find the command in the lookup table. +; Text begins with a '/' let's try to find the command in the lookup table. .command: mov eax, dword[user_command+1] ; skip '/' or eax, 0x20202020 ; convert to lowercase @@ -309,7 +309,7 @@ cmd_usr_nick: .fail: ret -; We arent logged in yet, directly change user_nick field and print notification to user. +; We aren't logged in yet, directly change user_nick field and print notification to user. .dontsend: mov ecx, MAX_NICK_LEN mov esi, user_command+6 diff --git a/programs/network/vncc/network.inc b/programs/network/vncc/network.inc index 4542c37bb..8a68f8375 100644 --- a/programs/network/vncc/network.inc +++ b/programs/network/vncc/network.inc @@ -1,512 +1,512 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; ;; -;; Copyright (C) KolibriOS team 2010-2015. All rights reserved. ;; -;; Distributed under terms of the GNU General Public License ;; -;; ;; -;; VNC client for KolibriOS ;; -;; ;; -;; Written by hidnplayr@kolibrios.org ;; -;; ;; -;; GNU GENERAL PUBLIC LICENSE ;; -;; Version 2, June 1991 ;; -;; ;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -thread_start: - - mcall 40, 0 ; disable all events for this thread - -; Extract port number from server address - mov esi, serveraddr - @@: - lodsb - test al, al - jz .port_done - cmp al, ':' - jne @r - mov byte[esi-1], 0 ; replace colon with 0 byte, we dont want to upset getaddrinfo - xor eax, eax - xor ebx, ebx ; port number - @@: - lodsb - test al, al - jz @f - sub al, '0' - jb err_dns - cmp al, 9 - ja err_dns - lea ebx, [ebx*4+ebx] - lea ebx, [ebx*2+eax] - jmp @b - @@: - xchg bl, bh - mov [sockaddr1.port], bx - .port_done: - -; Resolve hostname - push esp ; reserve stack place - invoke getaddrinfo, serveraddr, 0, 0, esp - pop esi - test eax, eax - jnz err_dns - - mov eax, [esi+addrinfo.ai_addr] - mov eax, [eax+sockaddr_in.sin_addr] - mov [sockaddr1.ip], eax - invoke freeaddrinfo, esi - - DEBUGF 1, "Connecting to %u.%u.%u.%u:%u\n", \ - [sockaddr1.ip]:1, [sockaddr1.ip+1]:1, [sockaddr1.ip+2]:1, [sockaddr1.ip+3]:1, \ - [sockaddr1.port]:2 - -; Open socket - mcall socket, AF_INET4, SOCK_STREAM, 0 - cmp eax, -1 - je err_sock - mov [socketnum], eax - -; Connect to the server - mcall connect, [socketnum], sockaddr1, 18 - cmp eax, -1 - je err_connect - -; Verify handshake from server - call read_data - cmp eax, 12 - jb err_proto - cmp dword[esi], "RFB " - jne err_proto - add esi, 12 - -; Did we get an error message already? - cmp eax, 16 - jb @f - lodsd - test eax, eax - je err_handshake - @@: - -; Reply to handshake - DEBUGF 1, "Sending handshake\n" - mcall send, [socketnum], HandShake, 12, 0 - -; VNC 3.3 protocol: server decides security type - call read_data - cmp eax, 4 - jb err_proto - lodsd - cmp eax, 0x00000000 - je err_handshake - cmp eax, 0x01000000 ; no security - je initialize - cmp eax, 0x02000000 ; VNC security - je vnc_security - jmp err_proto - -vnc_security: - - lea eax, [esi+8] - cmp [datapointer], eax - jb err_proto - - push esi ; pointer to message - - mov dword[password], 0 - mov dword[password+4], 0 - - and [USERbox.flags], not ed_focus - or [USERbox.flags], ed_disabled - or [PASSbox.flags], ed_focus - - mov [status], STATUS_REQ_LOGIN - or [work], WORK_GUI - @@: - mcall 5, 10 - cmp [status], STATUS_LOGIN - je @f - cmp [status], STATUS_REQ_LOGIN - je @r - mcall -1 - @@: - DEBUGF 1, "VNC authentication\n" - -; Bit reverse the password and create DES keys - mov ebx, dword[password] - mov edx, ebx - and ebx, 0xf0f0f0f0 - shr ebx, 4 - and edx, 0x0f0f0f0f - shl edx, 4 - or ebx, edx - mov edx, ebx - and ebx, 0xCCCCCCCC - shr ebx, 2 - and edx, 0x33333333 - shl edx, 2 - or ebx, edx - mov edx, ebx - and ebx, 0xAAAAAAAA - shr ebx, 1 - and edx, 0x55555555 - shl edx, 1 - or ebx, edx - bswap ebx - - mov eax, dword[password+4] - mov edx, eax - and eax, 0xf0f0f0f0 - shr eax, 4 - and edx, 0x0f0f0f0f - shl edx, 4 - or eax, edx - mov edx, eax - and eax, 0xCCCCCCCC - shr eax, 2 - and edx, 0x33333333 - shl edx, 2 - or eax, edx - mov edx, eax - and eax, 0xAAAAAAAA - shr eax, 1 - and edx, 0x55555555 - shl edx, 1 - or edx, eax - bswap edx - - mov edi, keys - call DES_create_keys - -; Encrypt message with DES - mov esi, [esp] - mov ebx, dword[esi+0] - mov edx, dword[esi+4] - call encrypt_DES - mov esi, [esp] - mov dword[esi+0], ebx - mov dword[esi+4], edx - - mov ebx, dword[esi+8] - mov edx, dword[esi+12] - call encrypt_DES - mov esi, [esp] - mov dword[esi+8], ebx - mov dword[esi+12], edx - -; Blank out the password and key fields in RAM - mov edi, password - mov ecx, 384/4 - xor eax, eax - rep stosd - -; Send the authentication response to server - pop edx - mcall send, [socketnum], , 16, 0 - -securityresult: -; Wait for SecurityResult from server - call read_data - cmp eax, 4 - jb err_proto - cmp dword[esi], 0 ; OK - jne err_login - -initialize: - DEBUGF 1, "Sending ClientInit\n" - mcall send, [socketnum], ClientInit, 1, 0 - - call read_data ; now the server should send init message - cmp eax, ServerInit.name - jb err_proto - - DEBUGF 2, "Serverinit: bpp: %u depth: %u bigendian: %u truecolor: %u\n", \ - [esi+ServerInit.pixelformat.bpp]:1, \ - [esi+ServerInit.pixelformat.depth]:1, \ - [esi+ServerInit.pixelformat.big_endian]:1, \ - [esi+ServerInit.pixelformat.true_color]:1 - - mov eax, dword[esi+ServerInit.width] - mov dword[FramebufferUpdateRequest.width], eax - bswap eax - mov dword[screen], eax - DEBUGF 1, "Screen width=%u, height=%u\n", [screen.width]:2, [screen.height]:2 - -; Set main window caption to servername - mov ecx, dword[esi+ServerInit.name_length] - bswap ecx - add esi, ServerInit.name - lea eax, [esi+ecx] - cmp [datapointer], eax - jb err_proto - cmp ecx, 64 ; Limit name length to 64 chars - jbe @f - mov ecx, 64 - @@: - mov edi, servername - rep movsb - mov byte[edi], 0 - mov [name.dash], "-" - - DEBUGF 1, "Sending pixel format\n" - mcall send, [socketnum], SetPixelFormat, 20, 0 - - DEBUGF 1, "Sending encoding info\n" - mcall send, [socketnum], SetEncodings, SetEncodings.length, 0 - -; Tell the main thread we are ready for business! - mov [status], STATUS_CONNECTED - -; Request initial framebuffer update from server - mov [FramebufferUpdateRequest.inc], 0 - -request_fbu: - DEBUGF 1, "Requesting framebuffer update\n" - mcall send, [socketnum], FramebufferUpdateRequest, 10, 0 - mov [FramebufferUpdateRequest.inc], 1 - -thread_loop: - call read_data ; Read the data into the buffer - - lodsb - cmp al, 0 - je framebufferupdate - cmp al, 1 - je setcolourmapentries - cmp al, 2 - je bell - cmp al, 3 - je servercuttext - - DEBUGF 2, "Unknown server command: %u\n", al - jmp thread_loop - -framebufferupdate: - - @@: - lea eax, [esi+6] - cmp [datapointer], eax - jae @f - call read_data.more - jmp @b - @@: - - inc esi ; padding - lodsw - xchg al, ah - mov [rectangles], ax - DEBUGF 1, "Framebufferupdate: %u rectangles\n", ax - -rectangle_loop: - - @@: - lea eax, [esi+12] - cmp [datapointer], eax - jae @f - call read_data.more - jmp @b - @@: - - xor eax, eax - lodsw - xchg al, ah - mov [rectangle.x], eax - lodsw - xchg al, ah - mov [rectangle.y], eax - lodsw - xchg al, ah - mov [rectangle.width], eax - lodsw - xchg al, ah - mov [rectangle.height], eax - - lodsd ; encoding - bswap eax - DEBUGF 1, "Rectangle: x=%u y=%u width=%u height=%u encoding: ",\ - [rectangle.x]:2, [rectangle.y]:2, [rectangle.width]:2, [rectangle.height]:2 - - cmp eax, 0 - je encoding_raw - cmp eax, 1 - je encoding_CopyRect - cmp eax, 2 - je encoding_RRE - cmp eax, 15 - je encoding_TRLE - cmp eax, 16 - je encoding_ZRLE - cmp eax, 0xffffff11 - je encoding_cursor - - DEBUGF 2, "unknown encoding: %u\n", eax - jmp thread_loop - -next_rectangle: - or [work], WORK_FRAMEBUFFER - dec [rectangles] - jnz rectangle_loop - jmp request_fbu - - -setcolourmapentries: - - DEBUGF 1, "Server sent SetColourMapEntries message\n" - - @@: - lea eax, [esi+5] - cmp [datapointer], eax - jae @f - call read_data.more - jmp @b - @@: - - inc esi ; padding - - xor eax, eax - lodsw ; first color (just ignore for now) - - lodsw ; number of colors (use to find end of message) - xchg al, ah - lea eax, [eax*2+eax] - shl eax, 1 - @@: - push eax - add eax, esi - cmp [datapointer], eax - jae @f - call read_data.more - pop eax - jmp @b - @@: - pop eax - - add esi, eax ; Just skip it for now. - jmp thread_loop - - -bell: - mcall 55, 55, , , beep - jmp thread_loop - - -servercuttext: - - DEBUGF 1, "Server cut text\n" - - @@: - lea eax, [esi+7] - cmp [datapointer], eax - jae @f - call read_data.more - jmp @b - @@: - - add esi, 3 - lodsd - bswap eax - mov ecx, eax - - @@: - lea eax, [esi+ecx] - cmp [datapointer], eax - jae @f - call read_data.more - jmp @b - @@: - - ; TODO: paste text to clipboard - - DEBUGF 1, "%u bytes of text\n", ecx - add esi, ecx - jmp thread_loop - - -read_data: - mov [datapointer], receive_buffer - mov esi, receive_buffer - .more: - push ebx ecx edx esi edi - neg esi - add esi, receive_buffer + RECEIVE_BUFFER_SIZE - jz .buffer_end_reached - xor edi, edi - mcall recv, [socketnum], [datapointer] - pop edi esi edx ecx ebx - cmp eax, -1 - je err_sock - test eax, eax - jz err_disconnected - add [datapointer], eax - ret - - .buffer_end_reached: - DEBUGF 1, "end of buffer reached, re-organizing\n" - pop edi esi edx ecx ebx - ; Buffer is full, first needed data by program is pointed to by esi. - ; Move all usefull data to begin of buffer - cmp esi, receive_buffer - je err_proto - mov ecx, [datapointer] - sub ecx, esi - mov edi, receive_buffer - rep movsb - mov [datapointer], edi ; new end of data - mov esi, receive_buffer ; new start of data - jmp .more - - -err_disconnected: - mov [status], STATUS_DISCONNECTED - or [work], WORK_GUI - mcall -1 - -err_dns: - mov [status], STATUS_DNS_ERR - or [work], WORK_GUI - mcall -1 - -err_sock: -; TODO: distinguish between different socket errors! - DEBUGF 2, "Socket error: %u\n", ebx - mov [status], STATUS_SOCK_ERR - or [work], WORK_GUI - mcall -1 - -err_connect: - mov [status], STATUS_CONNECT_ERR - or [work], WORK_GUI - mcall -1 - ret - -err_proto: - mov [status], STATUS_PROTO_ERR - or [work], WORK_GUI - mcall -1 - ret - -err_handshake: - mov [status], STATUS_SECURITY_ERR - - lodsd ; Custom message from server? - test eax, eax - jz .no_msg - bswap eax - mov ecx, eax - cmp ecx, 512 - jb @f - mov ecx, 512 - @@: - mov edi, sz_err_security_c - rep movsb - mov byte[edi], 0 - mov [status], STATUS_SECURITY_ERR_C - .no_msg: - - or [work], WORK_GUI - mcall -1 - ret - -err_login: - mov [status], STATUS_LOGIN_FAILED - or [work], WORK_GUI - mcall -1 - ret +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2010-2024. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; VNC client for KolibriOS ;; +;; ;; +;; Written by hidnplayr@kolibrios.org ;; +;; ;; +;; GNU GENERAL PUBLIC LICENSE ;; +;; Version 2, June 1991 ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +thread_start: + + mcall 40, 0 ; disable all events for this thread + +; Extract port number from server address + mov esi, serveraddr + @@: + lodsb + test al, al + jz .port_done + cmp al, ':' + jne @r + mov byte[esi-1], 0 ; replace colon with 0 byte, we don't want to upset getaddrinfo + xor eax, eax + xor ebx, ebx ; port number + @@: + lodsb + test al, al + jz @f + sub al, '0' + jb err_dns + cmp al, 9 + ja err_dns + lea ebx, [ebx*4+ebx] + lea ebx, [ebx*2+eax] + jmp @b + @@: + xchg bl, bh + mov [sockaddr1.port], bx + .port_done: + +; Resolve hostname + push esp ; reserve stack place + invoke getaddrinfo, serveraddr, 0, 0, esp + pop esi + test eax, eax + jnz err_dns + + mov eax, [esi+addrinfo.ai_addr] + mov eax, [eax+sockaddr_in.sin_addr] + mov [sockaddr1.ip], eax + invoke freeaddrinfo, esi + + DEBUGF 1, "Connecting to %u.%u.%u.%u:%u\n", \ + [sockaddr1.ip]:1, [sockaddr1.ip+1]:1, [sockaddr1.ip+2]:1, [sockaddr1.ip+3]:1, \ + [sockaddr1.port]:2 + +; Open socket + mcall socket, AF_INET4, SOCK_STREAM, 0 + cmp eax, -1 + je err_sock + mov [socketnum], eax + +; Connect to the server + mcall connect, [socketnum], sockaddr1, 18 + cmp eax, -1 + je err_connect + +; Verify handshake from server + call read_data + cmp eax, 12 + jb err_proto + cmp dword[esi], "RFB " + jne err_proto + add esi, 12 + +; Did we get an error message already? + cmp eax, 16 + jb @f + lodsd + test eax, eax + je err_handshake + @@: + +; Reply to handshake + DEBUGF 1, "Sending handshake\n" + mcall send, [socketnum], HandShake, 12, 0 + +; VNC 3.3 protocol: server decides security type + call read_data + cmp eax, 4 + jb err_proto + lodsd + cmp eax, 0x00000000 + je err_handshake + cmp eax, 0x01000000 ; no security + je initialize + cmp eax, 0x02000000 ; VNC security + je vnc_security + jmp err_proto + +vnc_security: + + lea eax, [esi+8] + cmp [datapointer], eax + jb err_proto + + push esi ; pointer to message + + mov dword[password], 0 + mov dword[password+4], 0 + + and [USERbox.flags], not ed_focus + or [USERbox.flags], ed_disabled + or [PASSbox.flags], ed_focus + + mov [status], STATUS_REQ_LOGIN + or [work], WORK_GUI + @@: + mcall 5, 10 + cmp [status], STATUS_LOGIN + je @f + cmp [status], STATUS_REQ_LOGIN + je @r + mcall -1 + @@: + DEBUGF 1, "VNC authentication\n" + +; Bit reverse the password and create DES keys + mov ebx, dword[password] + mov edx, ebx + and ebx, 0xf0f0f0f0 + shr ebx, 4 + and edx, 0x0f0f0f0f + shl edx, 4 + or ebx, edx + mov edx, ebx + and ebx, 0xCCCCCCCC + shr ebx, 2 + and edx, 0x33333333 + shl edx, 2 + or ebx, edx + mov edx, ebx + and ebx, 0xAAAAAAAA + shr ebx, 1 + and edx, 0x55555555 + shl edx, 1 + or ebx, edx + bswap ebx + + mov eax, dword[password+4] + mov edx, eax + and eax, 0xf0f0f0f0 + shr eax, 4 + and edx, 0x0f0f0f0f + shl edx, 4 + or eax, edx + mov edx, eax + and eax, 0xCCCCCCCC + shr eax, 2 + and edx, 0x33333333 + shl edx, 2 + or eax, edx + mov edx, eax + and eax, 0xAAAAAAAA + shr eax, 1 + and edx, 0x55555555 + shl edx, 1 + or edx, eax + bswap edx + + mov edi, keys + call DES_create_keys + +; Encrypt message with DES + mov esi, [esp] + mov ebx, dword[esi+0] + mov edx, dword[esi+4] + call encrypt_DES + mov esi, [esp] + mov dword[esi+0], ebx + mov dword[esi+4], edx + + mov ebx, dword[esi+8] + mov edx, dword[esi+12] + call encrypt_DES + mov esi, [esp] + mov dword[esi+8], ebx + mov dword[esi+12], edx + +; Blank out the password and key fields in RAM + mov edi, password + mov ecx, 384/4 + xor eax, eax + rep stosd + +; Send the authentication response to server + pop edx + mcall send, [socketnum], , 16, 0 + +securityresult: +; Wait for SecurityResult from server + call read_data + cmp eax, 4 + jb err_proto + cmp dword[esi], 0 ; OK + jne err_login + +initialize: + DEBUGF 1, "Sending ClientInit\n" + mcall send, [socketnum], ClientInit, 1, 0 + + call read_data ; now the server should send init message + cmp eax, ServerInit.name + jb err_proto + + DEBUGF 2, "Serverinit: bpp: %u depth: %u bigendian: %u truecolor: %u\n", \ + [esi+ServerInit.pixelformat.bpp]:1, \ + [esi+ServerInit.pixelformat.depth]:1, \ + [esi+ServerInit.pixelformat.big_endian]:1, \ + [esi+ServerInit.pixelformat.true_color]:1 + + mov eax, dword[esi+ServerInit.width] + mov dword[FramebufferUpdateRequest.width], eax + bswap eax + mov dword[screen], eax + DEBUGF 1, "Screen width=%u, height=%u\n", [screen.width]:2, [screen.height]:2 + +; Set main window caption to servername + mov ecx, dword[esi+ServerInit.name_length] + bswap ecx + add esi, ServerInit.name + lea eax, [esi+ecx] + cmp [datapointer], eax + jb err_proto + cmp ecx, 64 ; Limit name length to 64 chars + jbe @f + mov ecx, 64 + @@: + mov edi, servername + rep movsb + mov byte[edi], 0 + mov [name.dash], "-" + + DEBUGF 1, "Sending pixel format\n" + mcall send, [socketnum], SetPixelFormat, 20, 0 + + DEBUGF 1, "Sending encoding info\n" + mcall send, [socketnum], SetEncodings, SetEncodings.length, 0 + +; Tell the main thread we are ready for business! + mov [status], STATUS_CONNECTED + +; Request initial framebuffer update from server + mov [FramebufferUpdateRequest.inc], 0 + +request_fbu: + DEBUGF 1, "Requesting framebuffer update\n" + mcall send, [socketnum], FramebufferUpdateRequest, 10, 0 + mov [FramebufferUpdateRequest.inc], 1 + +thread_loop: + call read_data ; Read the data into the buffer + + lodsb + cmp al, 0 + je framebufferupdate + cmp al, 1 + je setcolourmapentries + cmp al, 2 + je bell + cmp al, 3 + je servercuttext + + DEBUGF 2, "Unknown server command: %u\n", al + jmp thread_loop + +framebufferupdate: + + @@: + lea eax, [esi+6] + cmp [datapointer], eax + jae @f + call read_data.more + jmp @b + @@: + + inc esi ; padding + lodsw + xchg al, ah + mov [rectangles], ax + DEBUGF 1, "Framebufferupdate: %u rectangles\n", ax + +rectangle_loop: + + @@: + lea eax, [esi+12] + cmp [datapointer], eax + jae @f + call read_data.more + jmp @b + @@: + + xor eax, eax + lodsw + xchg al, ah + mov [rectangle.x], eax + lodsw + xchg al, ah + mov [rectangle.y], eax + lodsw + xchg al, ah + mov [rectangle.width], eax + lodsw + xchg al, ah + mov [rectangle.height], eax + + lodsd ; encoding + bswap eax + DEBUGF 1, "Rectangle: x=%u y=%u width=%u height=%u encoding: ",\ + [rectangle.x]:2, [rectangle.y]:2, [rectangle.width]:2, [rectangle.height]:2 + + cmp eax, 0 + je encoding_raw + cmp eax, 1 + je encoding_CopyRect + cmp eax, 2 + je encoding_RRE + cmp eax, 15 + je encoding_TRLE + cmp eax, 16 + je encoding_ZRLE + cmp eax, 0xffffff11 + je encoding_cursor + + DEBUGF 2, "unknown encoding: %u\n", eax + jmp thread_loop + +next_rectangle: + or [work], WORK_FRAMEBUFFER + dec [rectangles] + jnz rectangle_loop + jmp request_fbu + + +setcolourmapentries: + + DEBUGF 1, "Server sent SetColourMapEntries message\n" + + @@: + lea eax, [esi+5] + cmp [datapointer], eax + jae @f + call read_data.more + jmp @b + @@: + + inc esi ; padding + + xor eax, eax + lodsw ; first color (just ignore for now) + + lodsw ; number of colors (use to find end of message) + xchg al, ah + lea eax, [eax*2+eax] + shl eax, 1 + @@: + push eax + add eax, esi + cmp [datapointer], eax + jae @f + call read_data.more + pop eax + jmp @b + @@: + pop eax + + add esi, eax ; Just skip it for now. + jmp thread_loop + + +bell: + mcall 55, 55, , , beep + jmp thread_loop + + +servercuttext: + + DEBUGF 1, "Server cut text\n" + + @@: + lea eax, [esi+7] + cmp [datapointer], eax + jae @f + call read_data.more + jmp @b + @@: + + add esi, 3 + lodsd + bswap eax + mov ecx, eax + + @@: + lea eax, [esi+ecx] + cmp [datapointer], eax + jae @f + call read_data.more + jmp @b + @@: + + ; TODO: paste text to clipboard + + DEBUGF 1, "%u bytes of text\n", ecx + add esi, ecx + jmp thread_loop + + +read_data: + mov [datapointer], receive_buffer + mov esi, receive_buffer + .more: + push ebx ecx edx esi edi + neg esi + add esi, receive_buffer + RECEIVE_BUFFER_SIZE + jz .buffer_end_reached + xor edi, edi + mcall recv, [socketnum], [datapointer] + pop edi esi edx ecx ebx + cmp eax, -1 + je err_sock + test eax, eax + jz err_disconnected + add [datapointer], eax + ret + + .buffer_end_reached: + DEBUGF 1, "end of buffer reached, re-organizing\n" + pop edi esi edx ecx ebx + ; Buffer is full, first needed data by program is pointed to by esi. + ; Move all usefull data to begin of buffer + cmp esi, receive_buffer + je err_proto + mov ecx, [datapointer] + sub ecx, esi + mov edi, receive_buffer + rep movsb + mov [datapointer], edi ; new end of data + mov esi, receive_buffer ; new start of data + jmp .more + + +err_disconnected: + mov [status], STATUS_DISCONNECTED + or [work], WORK_GUI + mcall -1 + +err_dns: + mov [status], STATUS_DNS_ERR + or [work], WORK_GUI + mcall -1 + +err_sock: +; TODO: distinguish between different socket errors! + DEBUGF 2, "Socket error: %u\n", ebx + mov [status], STATUS_SOCK_ERR + or [work], WORK_GUI + mcall -1 + +err_connect: + mov [status], STATUS_CONNECT_ERR + or [work], WORK_GUI + mcall -1 + ret + +err_proto: + mov [status], STATUS_PROTO_ERR + or [work], WORK_GUI + mcall -1 + ret + +err_handshake: + mov [status], STATUS_SECURITY_ERR + + lodsd ; Custom message from server? + test eax, eax + jz .no_msg + bswap eax + mov ecx, eax + cmp ecx, 512 + jb @f + mov ecx, 512 + @@: + mov edi, sz_err_security_c + rep movsb + mov byte[edi], 0 + mov [status], STATUS_SECURITY_ERR_C + .no_msg: + + or [work], WORK_GUI + mcall -1 + ret + +err_login: + mov [status], STATUS_LOGIN_FAILED + or [work], WORK_GUI + mcall -1 + ret diff --git a/programs/network/whois/whois.c b/programs/network/whois/whois.c index 8b68b1b7c..2f1058df2 100644 --- a/programs/network/whois/whois.c +++ b/programs/network/whois/whois.c @@ -7,9 +7,9 @@ https://www.binarytides.com/whois-client-code-in-c-with-linux-sockets/ #include #include #include -#include -#include -#include +#include +#include +#include #include #include @@ -38,7 +38,7 @@ char* str_copy(char*); int main(int argc , char *argv[]) -{ +{ char *domain , *data = NULL; int f_flag=0; @@ -106,11 +106,11 @@ int get_whois_data(char *domain , char **data) // Next line please pch = strtok(NULL , "\n"); } - // Now we have the TLD whois server in wch , query again - //This will provide minimal whois information along with the parent whois server of the specific domain :) + // Now we have the TLD whois server in 'wch', query again + // This will provide minimal whois information along with the parent whois server of the specific domain :) wch = strdup(wch); free(response); - //This should not be necessary , but segmentation fault without this , why ? + // This should not be necessary, but segmentation fault without this, why ? response = NULL; if(wch != NULL){ fprintf(out,"\nTLD Whois server is : %s" , wch); @@ -122,9 +122,9 @@ int get_whois_data(char *domain , char **data) fprintf(out, "\nTLD whois server for %s not found\n" , ext); return EXIT_SUCCESS; } - + response_2 = strdup(response); - + // Again search for a whois server in this response. :) pch = strtok(response , "\n"); while(pch != NULL){ @@ -141,11 +141,11 @@ int get_whois_data(char *domain , char **data) If a registrar whois server is found then query it */ if(wch){ - // Now we have the registrar whois server , this has the direct full information of the particular domain - // so lets query again - + // Now we have the registrar whois server, this has the direct full information of the particular domain + // so let's query again + fprintf(out, "\nRegistrar Whois server is : %s" , wch); - + if( whois_query(wch , domain , &response) == EXIT_FAILURE ){ fprintf(out, "Whois query failed"); }else{ @@ -157,7 +157,7 @@ int get_whois_data(char *domain , char **data) */ else{ fprintf(out, "%s" , response_2); - } + } return 0; } @@ -170,22 +170,22 @@ int whois_query(char *server , char *query , char **response) int sock , read_size , total_size = 0; int WHOIS_PORT = 43; struct sockaddr dest; - + sock = socket(AF_INET4 , SOCK_STREAM , IPPROTO_TCP); - + //Prepare connection structures :) memset(&dest , 0 , sizeof(dest) ); dest.sin_family = AF_INET; server = str_copy(server); - + server[strcspn(server, "\r\n")] = '\0'; fprintf(out, "\nResolving: %s ...\n" , server); if(hostname_to_ip(server , ip) == EXIT_FAILURE ){ fprintf(out, "Failed\n"); return EXIT_FAILURE; } - - fprintf(out, "Found ip: %s \n" , ip); + + fprintf(out, "Found ip: %s \n" , ip); dest.sin_addr = inet_addr(ip); dest.sin_port = PORT(WHOIS_PORT); @@ -203,7 +203,7 @@ int whois_query(char *server , char *query , char **response) perror("send failed"); return EXIT_FAILURE; } - + //Now receive the response while((read_size = recv(sock, buffer, sizeof(buffer), 0))){ *response = realloc(*response , read_size + total_size); @@ -214,9 +214,9 @@ int whois_query(char *server , char *query , char **response) memcpy(*response + total_size , buffer , read_size); total_size += read_size; } - + fprintf(out, "Done\n"); - + *response = realloc(*response , total_size + 1); *(*response + total_size) = '\0'; close(sock); @@ -231,7 +231,7 @@ int hostname_to_ip(char *hostname , char *ip) char port_str[16]; sprintf(port_str, "%d", 80); struct addrinfo hints; memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; // IPv4 or IPv6 doesnt matter + hints.ai_family = AF_UNSPEC; // IPv4 or IPv6 doesn't matter hints.ai_socktype = SOCK_STREAM; // TCP stream sockets if (getaddrinfo(hostname, port_str, 0, &addr_info) != 0) { freeaddrinfo(addr_info); @@ -248,48 +248,48 @@ char *str_replace(char *search , char *replace , char *subject) { char *p = NULL , *old = NULL , *new_subject = NULL ; int c = 0 , search_size; - + search_size = strlen(search); - + //Count how many occurences for(p = strstr(subject , search) ; p != NULL ; p = strstr(p + search_size , search)){ c++; } //Final size c = ( strlen(replace) - search_size )*c + strlen(subject); - + //New subject with new size new_subject = malloc( c ); - + //Set it to blank strcpy(new_subject , ""); - + //The start position old = subject; - + for(p = strstr(subject , search) ; p != NULL ; p = strstr(p + search_size , search)){ //move ahead and copy some text from original subject , from a certain position strncpy(new_subject + strlen(new_subject) , old , p - old); - + //move ahead and copy the replacement text strcpy(new_subject + strlen(new_subject) , replace); - + //The new start position after this search match old = p + search_size; } - + //Copy the part after the last search match strcpy(new_subject + strlen(new_subject) , old); - + return new_subject; } char* str_copy(char *source) { - char *copy = malloc(strlen(source) + 1); - + char *copy = malloc(strlen(source) + 1); + if(copy){ strcpy(copy, source); } return copy; -} +}