15static const size_t FTP_SENT = (size_t)-1;
18static size_t ftp_emit(
char *buf,
size_t cap,
size_t n,
const char *s,
size_t slen)
23 if (n == FTP_SENT || n > cap || slen > cap - n)
27 memcpy(buf + n, s, slen);
32static size_t ftp_emit_uint(
char *buf,
size_t cap,
size_t n,
unsigned v)
43 rev[ri++] = (char)(
'0' + (v % 10));
46 if ((
size_t)ri > cap - n)
48 for (
int k = 0; k < ri; k++)
49 buf[n + k] = rev[ri - 1 - k];
50 return n + (size_t)ri;
54static size_t ftp_finish(
char *buf,
size_t cap,
size_t n)
56 if (n == FTP_SENT || n >= cap)
62size_t ftp_build_command(
char *buf,
size_t cap,
const char *verb,
const char *arg)
64 if (!buf || !verb || !verb[0])
67 n = ftp_emit(buf, cap, n, verb, strnlen(verb, cap));
70 n = ftp_emit(buf, cap, n,
" ", 1);
71 n = ftp_emit(buf, cap, n, arg, strnlen(arg, cap));
73 n = ftp_emit(buf, cap, n,
"\r\n", 2);
74 return ftp_finish(buf, cap, n);
77size_t ftp_build_port(
char *buf,
size_t cap,
const uint8_t ip[4], uint16_t port)
82 n = ftp_emit(buf, cap, n,
"PORT ", 5);
83 for (
int i = 0; i < 4; i++)
85 n = ftp_emit_uint(buf, cap, n, ip[i]);
86 n = ftp_emit(buf, cap, n,
",", 1);
88 n = ftp_emit_uint(buf, cap, n, (
unsigned)(port >> 8));
89 n = ftp_emit(buf, cap, n,
",", 1);
90 n = ftp_emit_uint(buf, cap, n, (
unsigned)(port & 0xFF));
91 n = ftp_emit(buf, cap, n,
"\r\n", 2);
92 return ftp_finish(buf, cap, n);
95size_t ftp_build_eprt(
char *buf,
size_t cap,
const char *ip_str,
bool ipv6, uint16_t port)
97 if (!buf || !ip_str || !ip_str[0])
100 n = ftp_emit(buf, cap, n,
"EPRT |", 6);
101 n = ftp_emit(buf, cap, n, ipv6 ?
"2" :
"1", 1);
102 n = ftp_emit(buf, cap, n,
"|", 1);
103 n = ftp_emit(buf, cap, n, ip_str, strnlen(ip_str, cap));
104 n = ftp_emit(buf, cap, n,
"|", 1);
105 n = ftp_emit_uint(buf, cap, n, port);
106 n = ftp_emit(buf, cap, n,
"|\r\n", 3);
107 return ftp_finish(buf, cap, n);
110static bool ftp_is_3digit(
const char *p)
112 return p[0] >=
'0' && p[0] <=
'9' && p[1] >=
'0' && p[1] <=
'9' && p[2] >=
'0' && p[2] <=
'9';
115static int ftp_code3(
const char *p)
117 return (p[0] -
'0') * 100 + (p[1] -
'0') * 10 + (p[2] -
'0');
121static size_t ftp_line_end(
const char *buf,
size_t len,
size_t start)
123 for (
size_t i = start; i < len; i++)
129bool ftp_parse_reply(
const char *buf,
size_t len,
int *code,
size_t *consumed)
131 if (!buf || len < 4 || !ftp_is_3digit(buf))
133 int first = ftp_code3(buf);
138 size_t eol = ftp_line_end(buf, len, 0);
149 size_t pos = ftp_line_end(buf, len, 0);
154 if (len - pos >= 4 && ftp_is_3digit(buf + pos) && ftp_code3(buf + pos) == first && buf[pos + 3] ==
' ')
156 size_t eol = ftp_line_end(buf, len, pos);
163 size_t eol = ftp_line_end(buf, len, pos);
171bool ftp_parse_pasv(
const char *buf,
size_t len, uint8_t ip[4], uint16_t *port)
173 if (!buf || !ip || !port)
176 while (i < len && buf[i] !=
'(')
183 for (
int ni = 0; ni < 6; ni++)
185 if (i >= len || buf[i] <
'0' || buf[i] >
'9')
188 while (i < len && buf[i] >=
'0' && buf[i] <=
'9')
190 v = v * 10 + (unsigned)(buf[i] -
'0');
198 if (i >= len || buf[i] !=
',')
203 ip[0] = (uint8_t)nums[0];
204 ip[1] = (uint8_t)nums[1];
205 ip[2] = (uint8_t)nums[2];
206 ip[3] = (uint8_t)nums[3];
207 *port = (uint16_t)(nums[4] * 256 + nums[5]);
211bool ftp_parse_epsv(
const char *buf,
size_t len, uint16_t *port)
216 while (i < len && buf[i] !=
'(')
227 while (i < len && seen < 3)
236 if (i >= len || buf[i] <
'0' || buf[i] >
'9')
239 while (i < len && buf[i] >=
'0' && buf[i] <=
'9')
241 v = v * 10 + (unsigned)(buf[i] -
'0');
FTP client wire codec (RFC 959 + RFC 2428 + RFC 3659), DETWS_ENABLE_FTP.