curl 的 `--tftp-no-options` 选项:彻底禁用 TFTP 选项协商以兼容老旧服务器
2026/9/10 7:05:47 网站建设 项目流程

curl 的--tftp-no-options选项:彻底禁用 TFTP 选项协商以兼容老旧服务器

【免费下载链接】curlA command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. libcurl offers a myriad of powerful features项目地址: https://gitcode.com/GitHub_Trending/cu/curl

--tftp-no-options是 curl 命令行工具中用于 TFTP 协议的开关选项(自 7.48.0 起提供),它让客户端在发送 RRQ(读请求)与 WRQ(写请求)时完全不携带任何 TFTP 扩展选项,从而绕开部分老旧服务器对选项协商支持不完整导致的传输失败。本文以 tftp-no-options.md 文档为骨架,结合 lib/tftp.c 的实现、CURLOPT_TFTP_NO_OPTIONS编程接口以及 tests/data/test1242、tests/data/test1243 测试用例,说明该选项的使用方法、底层原理与适用场景。

选项速览

在 curl 的命令行手册中,--tftp-no-options的元数据定义如下(摘自 docs/cmdline-opts/tftp-no-options.md):

  • 长选项名--tftp-no-options
  • 帮助文本Do not send any TFTP options(不发送任何 TFTP 选项)
  • 适用协议:仅 TFTP
  • 引入版本:7.48.0
  • 参数类型:boolean(布尔开关,不需要携带值)
  • 取值语义(Multi):boolean,即使用一次即生效,重复指定不改变行为

使用方式非常简单,直接在 TFTP URL 前加上该开关即可:

curl --tftp-no-options tftp://192.168.0.1/

--tftp-blksize不同,它不需要跟随任何数值参数,因为它表达的是一个"关闭某能力"的语义,而不是"设置某个值"。

该选项解决什么问题

TFTP 协议(RFC 1350)的报文格式非常精简:RRQ/WRQ 请求包由opcode + 文件名 + 0 + 传输模式(octet/netascii) + 0构成。在此基础上,RFC 2347、RFC 2348、RFC 2349 引入了选项协商机制:客户端可以在请求包中追加形如option\0value\0的键值对,常见的选项包括:

选项名来源 RFC作用
blksizeRFC 2348协商数据块大小(默认 512 字节,可增大以减少包数)
tsizeRFC 2349协商传输文件大小(下载时用于进度显示)
timeoutRFC 2349协商重传超时时间(秒)

这些选项的实现可见于 lib/tftp.c 中的宏定义:

#define TFTP_OPTION_BLKSIZE "blksize" #define TFTP_OPTION_TSIZE "tsize" #define TFTP_OPTION_INTERVAL "timeout"

问题在于:并非所有 TFTP 服务器都完整实现了选项协商。一些老旧的嵌入式设备固件或最小化实现的服务端,要么不返回 OACK(Option Acknowledgment,选项确认包),要么对未知选项返回 ERROR 包,导致本来可以正常完成的文件传输直接失败。--tftp-no-options正是为应对这类互操作性问题而生:关闭选项发送,让 curl 退回到最朴素的、所有 TFTP 服务器都支持的 RFC 1350 基础行为

--tftp-blksize的联动关系

两个选项高度相关,且官方文档明确指出了它们的优先级关系:

When this option is used --tftp-blksize is ignored. (使用本选项时,--tftp-blksize将被忽略。)

也就是说:

# 即便同时指定了 blksize,由于 tftp-no-options 存在,blksize 不生效 curl --tftp-no-options --tftp-blksize 1024 tftp://example.com/file

此时 curl 仍会以默认的 512 字节块大小进行传输。这符合逻辑:既然请求包中不带任何选项(包括blksize),那么客户端必须按 RFC 1350 的默认值 512 字节收发数据,服务器端也不会通过 OACK 告知新的块大小。

关于--tftp-blksize本身的约束("必须大于等于 512"),参见 docs/cmdline-opts/tftp-blksize.md。

源码实现:选项是如何被"跳过"的

curl 的 TFTP 协议实现在 lib/tftp.c 中,首次请求包的构造位于tftp_send_first()函数(lib/tftp.c#L651-L742)。

关键逻辑在第 713 行:

/* optional addition of TFTP options */ if(!data->set.tftp_no_options) { char buf[64]; /* add tsize option */ ... result = tftp_option_add(state, &sbytes, sbytes, TFTP_OPTION_TSIZE); ... /* add blksize option */ ... /* add timeout option */ ... }

从源码结构可以清晰看到:

  1. 无论是否禁用选项,请求包的基础部分(opcode、文件名、octet/netascii模式)都会先被构造出来;
  2. 只有当data->set.tftp_no_options为假(即未指定该选项)时,才会依次调用tftp_option_add()(lib/tftp.c#L332-L345)追加tsizeblksizetimeout三组选项;
  3. 指定--tftp-no-options后,这段代码整体被跳过,发出的就是一个最原始的 RRQ/WRQ 包。

tftp_option_add()的实现也值得注意:它会校验剩余缓冲空间是否足够容纳选项名与值(长度不足时返回CURLE_TFTP_ILLEGAL),这正是选项协商需要额外缓冲区的根源——禁用选项后,请求包体积固定为最小形态,天然规避了"缓冲区过小"这类边界问题。

另外,即便关闭了客户端侧发送选项,tftp_parse_option_ack()(lib/tftp.c#L259-L329)仍保留了 OACK 解析能力:它会把blksize复位为默认值 512(state->blksize = TFTP_BLKSIZE_DEFAULT;),并校验服务器返回的blksize不超过客户端请求值、tsize用于下载进度等。这表明该开关的语义是"客户端不主动发起协商",而非"完全拒绝理解选项报文"。

命令行层面的解析

--tftp-no-options在命令行工具中的注册位于 src/tool_getparam.c:

{"tftp-no-options", ARG_BOOL, ' ', C_TFTP_NO_OPTIONS},

ARG_BOOL表示这是一个布尔型开关,不需要额外取值。其处理分支在 src/tool_getparam.c:

case C_TFTP_NO_OPTIONS: /* --tftp-no-options */ config->tftp_no_options = toggle;

布尔型参数支持--tftp-no-options--no-tftp-no-options两种写法,后者用于显式关闭该行为(对应 curl 布尔选项的通用约定)。解析得到的值随后在运行时被传递到 libcurl 的CURLOPT_TFTP_NO_OPTIONS选项(参见 lib/setopt.c 的s->tftp_no_options = enabled;),最终被 lib/tftp.c 消费。

libcurl 编程接口:CURLOPT_TFTP_NO_OPTIONS

对于使用 libcurl 库的开发者,对应的接口是CURLOPT_TFTP_NO_OPTIONS,其完整说明见 docs/libcurl/opts/CURLOPT_TFTP_NO_OPTIONS.md。

函数原型:

#include <curl/curl.h> CURLcode curl_easy_setopt(CURL *handle, CURLOPT_TFTP_NO_OPTIONS, long onoff);
  • 参数语义:设置onoff1L时,从读写请求中排除 RFC 2347、RFC 2348 与 RFC 2349 定义的所有 TFTP 选项;设置为0L(默认值)时恢复选项协商。
  • 默认值0,即默认开启选项协商。

官方文档给出了一段完整的下载示例(整理如下):

static size_t write_callback(char *ptr, size_t size, size_t nmemb, void *fp) { return fwrite(ptr, size, nmemb, (FILE *)fp); } int main(void) { CURL *curl = curl_easy_init(); if(curl) { CURLcode result = CURLE_OK; FILE *fp = fopen("foo.bin", "wb"); if(fp) { curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)fp); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_URL, "tftp://example.com/foo.bin"); /* do not send TFTP options requests */ curl_easy_setopt(curl, CURLOPT_TFTP_NO_OPTIONS, 1L); /* Perform the request */ result = curl_easy_perform(curl); fclose(fp); } curl_easy_cleanup(curl); } }

返回值与所有curl_easy_setopt一致:CURLE_OK (0)表示设置成功,非零表示出错(错误码含义见 docs/libcurl/curl_easy_setopt.md)。该选项在include/curl/curl.h与 lib/easyoptions.c 中均有注册,属于稳定的公共 API。

测试用例验证

curl 自带的集成测试直接验证了该选项的行为,测试数据位于 tests/data/test1242(下载场景)与 tests/data/test1243(上传场景)。

test1242 —— 不带选项的 RRQ(下载)

<command> tftp://%HOSTIP:%TFTPPORT//%TESTNUMBER --tftp-no-options </command> ... <protocol> opcode = 1 mode = octet filename = /%TESTNUMBER </protocol>

验证伪协议明确要求请求包中只有opcode = 1(RRQ)、mode = octet、文件名三项内容,没有任何选项键值对。

test1243 —— 不带选项的 WRQ(上传)

<command> -T %LOGDIR/test%TESTNUMBER.txt tftp://%HOSTIP:%TFTPPORT// --tftp-no-options </command> ... <protocol> opcode = 2 mode = octet filename = /test%TESTNUMBER.txt </protocol>

同样要求opcode = 2(WRQ)报文中不携带任何选项。这两个用例从下载与上传两个方向共同保证了--tftp-no-options的行为契约:请求包退化为纯 RFC 1350 形态

使用建议与注意事项

  • 什么时候该用:当连接某些老旧 TFTP 服务器出现"握手后无响应"、"服务器返回 ERROR"或"超时重传"等异常,且排除了网络原因时,可优先尝试--tftp-no-options。它牺牲的是块大小、超时、文件大小等优化能力,换来的是最大化的兼容性。
  • 代价:禁用选项后块大小固定为 512 字节,大文件传输的包数量显著增加,传输效率会下降;同时--tftp-blksize的设置会被忽略。
  • 与进度显示的关系tsize选项被禁用后,下载侧无法在传输前获知文件大小(源码中Curl_pgrsSetDownloadSize()仅在 OACK 携带tsize时被调用),因此进度条可能无法显示总字节数。
  • 适用范围:该选项仅对 TFTP 协议有意义,对其他协议(HTTP、FTP 等)无效;在非 TFTP 请求中指定会被忽略。

延伸阅读

  • 命令行选项完整说明:docs/cmdline-opts/tftp-no-options.md、docs/cmdline-opts/tftp-blksize.md
  • libcurl 编程接口:docs/libcurl/opts/CURLOPT_TFTP_NO_OPTIONS.md、docs/libcurl/curl_easy_setopt.md
  • 核心实现:lib/tftp.c(请求构造tftp_send_first、选项追加tftp_option_add、OACK 解析tftp_parse_option_ack
  • 选项注册:src/tool_getparam.c、lib/setopt.c
  • 集成测试:tests/data/test1242、tests/data/test1243

【免费下载链接】curlA command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. libcurl offers a myriad of powerful features项目地址: https://gitcode.com/GitHub_Trending/cu/curl

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询