From 8deda5c565f12831d895b5a3b2bada34756aceef Mon Sep 17 00:00:00 2001 From: DNT Date: Tue, 4 Aug 2026 12:44:02 +0700 Subject: [PATCH] fix: allow REST in ASCII mode when ASCII conversion is disabled When DisableASCIIConversion is enabled, transfers are byte-for-byte even in ASCII (TYPE A) mode, so REST is safe and should be accepted. Previously handleREST always rejected REST in ASCII mode regardless of the setting, which broke resume support for clients that use TYPE A + REST (e.g. IoT devices behind NAT). Aligns with the existing behavior of handleSIZE, which already allows SIZE in ASCII mode when conversion is disabled. --- handle_files.go | 2 +- handle_files_test.go | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/handle_files.go b/handle_files.go index 485ff0cf..64a41c36 100644 --- a/handle_files.go +++ b/handle_files.go @@ -521,7 +521,7 @@ func (c *clientHandler) handleALLO(param string) error { func (c *clientHandler) handleREST(param string) error { if size, err := strconv.ParseInt(param, 10, 0); err == nil { - if c.currentTransferType == TransferTypeASCII { + if c.currentTransferType == TransferTypeASCII && !c.server.settings.DisableASCIIConversion { c.writeMessage(StatusSyntaxErrorParameters, "Resuming transfers not allowed in ASCII mode") return nil diff --git a/handle_files_test.go b/handle_files_test.go index eabaf4b1..ff788280 100644 --- a/handle_files_test.go +++ b/handle_files_test.go @@ -856,6 +856,43 @@ func TestREST(t *testing.T) { require.True(t, strings.HasPrefix(response, "Couldn't parse size")) } +func TestRESTInASCIIWithConversionDisabled(t *testing.T) { + driver := &TestServerDriver{ + Settings: &Settings{DisableASCIIConversion: true}, + } + server := NewTestServerWithTestDriver(t, driver) + conf := goftp.Config{ + User: authUser, + Password: authPass, + } + + client, err := goftp.DialConfig(conf, server.Addr()) + require.NoError(t, err, "Couldn't connect") + + defer func() { panicOnError(client.Close()) }() + + raw, err := client.OpenRawConn() + require.NoError(t, err, "Couldn't open raw connection") + + defer func() { require.NoError(t, raw.Close()) }() + + // With DisableASCIIConversion transfers are byte-for-byte even in ASCII + // mode, so REST must be accepted. + returnCode, response, err := raw.SendCommand("TYPE A") + require.NoError(t, err) + require.Equal(t, StatusOK, returnCode, response) + + returnCode, response, err = raw.SendCommand("REST 10") + require.NoError(t, err) + require.Equal(t, StatusFileActionPending, returnCode, response) + + // Invalid argument is still rejected regardless of conversion setting. + returnCode, response, err = raw.SendCommand("REST a") + require.NoError(t, err) + require.Equal(t, StatusActionNotTaken, returnCode, response) + require.True(t, strings.HasPrefix(response, "Couldn't parse size")) +} + func TestSIZE(t *testing.T) { server := NewTestServer(t, false) conf := goftp.Config{