apps/ftpd: bound the pasv port search #710

Merged
Burer merged 3 commits from igorsh/kolibrios:ftpd-bound-the-PASV-port-search into main 2026-09-25 09:17:18 +00:00
3 Commits
Author SHA1 Message Date
Igor Shutrov 95cd348fff ftpd: close the listener on a failed listen, rename the PASV counter
Test PR / Build (es_ES) (pull_request) Successful in 2m10s
Test PR / Build (ru_RU) (pull_request) Successful in 2m13s
Test PR / Build (en_US) (pull_request) Successful in 2m16s
The per-thread counter counts down from the range size, so it holds the
number of ports left to try, not the number already tried: rename
pasv_tries to pasv_ports_left, which says what it counts.

Both ways out of the port search now share one exit. A failed listen
used to jump straight to socketerror and leave the socket it had just
bound open, while the exhausted-range path already closed it; the
cleanup moves to a .fail label that both paths reach. close_data_sock
is a no-op on this path (the mode is MODE_NOTREADY there, as cmdPASV
closes both sockets on entry and MODE_PASSIVE_WAIT is set only after a
successful listen) and stays only so the single exit drops whatever
socket this thread owns.

Found during the review of #710.

Assisted-by: ZCode:deepseek-flash
2026-09-25 09:14:23 +00:00
Igor Shutrov fc18d69327 Use close_pasv_sock 2026-09-25 09:14:23 +00:00
Igor Shutrov eb28ff4c90 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-25 09:14:23 +00:00