ftp: fix 0-length last write on upload from stdin
When uploading FTP with unknown length, we write a last 0-length chunk with the EOS flag set. OpenSSL's SSL_write() errors on such a write. Skip writing 0-length data to TLS backends instead. Add test in FTPS for such uploads to verify. Fixes #15101 Reported-by: Denis Goleshchikhin Closes #15102
This commit is contained in:
parent
68c358619f
commit
72d2090fc2
@ -1743,13 +1743,17 @@ static ssize_t ssl_cf_send(struct Curl_cfilter *cf,
|
||||
bool eos, CURLcode *err)
|
||||
{
|
||||
struct cf_call_data save;
|
||||
ssize_t nwritten;
|
||||
ssize_t nwritten = 0;
|
||||
|
||||
(void)eos; /* unused */
|
||||
CF_DATA_SAVE(save, cf, data);
|
||||
(void)eos;
|
||||
/* OpenSSL and maybe other TLS libs do not like 0-length writes. Skip. */
|
||||
*err = CURLE_OK;
|
||||
nwritten = Curl_ssl->send_plain(cf, data, buf, len, err);
|
||||
CF_DATA_RESTORE(cf, save);
|
||||
if(len > 0) {
|
||||
CF_DATA_SAVE(save, cf, data);
|
||||
*err = CURLE_OK;
|
||||
nwritten = Curl_ssl->send_plain(cf, data, buf, len, err);
|
||||
CF_DATA_RESTORE(cf, save);
|
||||
}
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
|
||||
@ -220,6 +220,23 @@ class TestVsFTPD:
|
||||
r.check_stats(count=count, http_status=226)
|
||||
self.check_upload(env, vsftpds, docname=docname)
|
||||
|
||||
@pytest.mark.parametrize("indata", [
|
||||
'1234567890', ''
|
||||
])
|
||||
def test_31_10_upload_stdin(self, env: Env, vsftpds: VsFTPD, indata):
|
||||
curl = CurlClient(env=env)
|
||||
docname = "upload_31_10"
|
||||
dstfile = os.path.join(vsftpds.docs_dir, docname)
|
||||
self._rmf(dstfile)
|
||||
count = 1
|
||||
url = f'ftp://{env.ftp_domain}:{vsftpds.port}/{docname}'
|
||||
r = curl.ftp_ssl_upload(urls=[url], updata=indata, with_stats=True)
|
||||
r.check_stats(count=count, http_status=226)
|
||||
assert os.path.exists(dstfile)
|
||||
destdata = open(dstfile).readlines()
|
||||
expdata = [indata] if len(indata) else []
|
||||
assert expdata == destdata, f'exected: {expdata}, got: {destdata}'
|
||||
|
||||
def check_downloads(self, client, srcfile: str, count: int,
|
||||
complete: bool = True):
|
||||
for i in range(count):
|
||||
|
||||
@ -696,27 +696,39 @@ class CurlClient:
|
||||
with_tcpdump=with_tcpdump,
|
||||
extra_args=extra_args)
|
||||
|
||||
def ftp_upload(self, urls: List[str], fupload,
|
||||
def ftp_upload(self, urls: List[str],
|
||||
fupload: Optional[Any] = None,
|
||||
updata: Optional[str] = None,
|
||||
with_stats: bool = True,
|
||||
with_profile: bool = False,
|
||||
with_tcpdump: bool = False,
|
||||
extra_args: List[str] = None):
|
||||
if extra_args is None:
|
||||
extra_args = []
|
||||
extra_args.extend([
|
||||
'--upload-file', fupload
|
||||
])
|
||||
if fupload is not None:
|
||||
extra_args.extend([
|
||||
'--upload-file', fupload
|
||||
])
|
||||
elif updata is not None:
|
||||
extra_args.extend([
|
||||
'--upload-file', '-'
|
||||
])
|
||||
else:
|
||||
raise Exception('need either file or data to upload')
|
||||
if with_stats:
|
||||
extra_args.extend([
|
||||
'-w', '%{json}\\n'
|
||||
])
|
||||
return self._raw(urls, options=extra_args,
|
||||
intext=updata,
|
||||
with_stats=with_stats,
|
||||
with_headers=False,
|
||||
with_profile=with_profile,
|
||||
with_tcpdump=with_tcpdump)
|
||||
|
||||
def ftp_ssl_upload(self, urls: List[str], fupload,
|
||||
def ftp_ssl_upload(self, urls: List[str],
|
||||
fupload: Optional[Any] = None,
|
||||
updata: Optional[str] = None,
|
||||
with_stats: bool = True,
|
||||
with_profile: bool = False,
|
||||
with_tcpdump: bool = False,
|
||||
@ -726,7 +738,7 @@ class CurlClient:
|
||||
extra_args.extend([
|
||||
'--ssl-reqd',
|
||||
])
|
||||
return self.ftp_upload(urls=urls, fupload=fupload,
|
||||
return self.ftp_upload(urls=urls, fupload=fupload, updata=updata,
|
||||
with_stats=with_stats, with_profile=with_profile,
|
||||
with_tcpdump=with_tcpdump,
|
||||
extra_args=extra_args)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user