Compare commits

...
2 Commits
Author SHA1 Message Date
Igor Shutrov 3796635494 Use close_pasv_sock 2026-09-21 00:00:01 +05:00
Igor Shutrov 2091d65381 ftpd: bound the PASV port search instead of looping forever
On a failed bind cmdPASV jumped straight back to .next_port, and
nextpasvport wraps within [start, end], so a range with no usable port
spun forever without reading the control connection again: the client
hangs. The source itself said "TODO: break the endless loop".

Count the ports in the range (end - start + 1), decrement after a
failed bind and give up at zero, then close the listener, reset the
state and answer 425 Can't open data connection. The counter lives in a
new thread_data field rather than a register, because mcall bind
clobbers eax/ebx/ecx/edx/esi/edi and esi already carries the sockaddr
length for bind. Decrementing after the failed bind and not before the
next attempt is what keeps a single-port range (start = end) working:
it still gets its one attempt.

Also validate the pair read from the ini. With start > end the range
degenerates and no port is bindable, so fall back to 2000/5000.

Assisted-by: ZCode:deepseek-flash
2026-09-20 23:59:33 +05:00
2 changed files with 26 additions and 2 deletions

No files matched your search

+18 -2
View File
@@ -14,6 +14,7 @@ struct thread_data
socketnum dd ? ; Commands socket
state dd ? ; disconnected/logging in/logged in/..
passivesocknum dd ? ; when in passive mode, this is the listening socket
pasv_tries dd ? ; ports tried for the current PASV
datasocketnum dd ? ; socket used for data transfers
permissions dd ? ; read/write/execute/....
buffer_ptr dd ?
@@ -847,7 +848,13 @@ cmdPASV:
lea edx, [ebp + thread_data.datasock]
mov esi, sizeof.thread_data.datasock
.next_port: ; TODO: break the endless loop
movzx eax, word [pasv_end]
movzx ebx, word [pasv_start]
sub eax, ebx
inc eax
mov [ebp + thread_data.pasv_tries], eax ; number of ports in the range
.next_port:
call nextpasvport
mov ax, [pasv_port]
xchg al, ah
@@ -855,7 +862,16 @@ cmdPASV:
mcall bind
cmp eax, -1
je .next_port
jne .bound
dec dword [ebp + thread_data.pasv_tries] ; only count a port once bind failed
jnz .next_port
; The range is exhausted, do not spin here forever
call close_data_sock
call close_pasv_sock
jmp socketerror
.bound:
; And set it to listen!
mcall listen, , 1
+8
View File
@@ -172,6 +172,14 @@ start:
invoke ini.get_int, path, str_pasv, str_end, 5000
mov [pasv_end], ax
; an inverted range would leave no port to bind, fall back to the defaults
mov ax, [pasv_start]
cmp ax, [pasv_end]
jbe @f
mov word [pasv_start], 2000
mov word [pasv_end], 5000
@@:
mov [alive], 1
mainloop: