ftpcmd.y revision 1.62 1 1.62 lukem /* $NetBSD: ftpcmd.y,v 1.62 2001/04/12 02:28:59 lukem Exp $ */
2 1.41 lukem
3 1.41 lukem /*-
4 1.47 lukem * Copyright (c) 1997-2000 The NetBSD Foundation, Inc.
5 1.41 lukem * All rights reserved.
6 1.41 lukem *
7 1.41 lukem * This code is derived from software contributed to The NetBSD Foundation
8 1.41 lukem * by Luke Mewburn.
9 1.41 lukem *
10 1.41 lukem * Redistribution and use in source and binary forms, with or without
11 1.41 lukem * modification, are permitted provided that the following conditions
12 1.41 lukem * are met:
13 1.41 lukem * 1. Redistributions of source code must retain the above copyright
14 1.41 lukem * notice, this list of conditions and the following disclaimer.
15 1.41 lukem * 2. Redistributions in binary form must reproduce the above copyright
16 1.41 lukem * notice, this list of conditions and the following disclaimer in the
17 1.41 lukem * documentation and/or other materials provided with the distribution.
18 1.41 lukem * 3. All advertising materials mentioning features or use of this software
19 1.41 lukem * must display the following acknowledgement:
20 1.41 lukem * This product includes software developed by the NetBSD
21 1.41 lukem * Foundation, Inc. and its contributors.
22 1.41 lukem * 4. Neither the name of The NetBSD Foundation nor the names of its
23 1.41 lukem * contributors may be used to endorse or promote products derived
24 1.41 lukem * from this software without specific prior written permission.
25 1.41 lukem *
26 1.41 lukem * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 1.41 lukem * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 1.41 lukem * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 1.41 lukem * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 1.41 lukem * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 1.41 lukem * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 1.41 lukem * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 1.41 lukem * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 1.41 lukem * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 1.41 lukem * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 1.41 lukem * POSSIBILITY OF SUCH DAMAGE.
37 1.41 lukem */
38 1.5 cgd
39 1.1 cgd /*
40 1.4 deraadt * Copyright (c) 1985, 1988, 1993, 1994
41 1.4 deraadt * The Regents of the University of California. All rights reserved.
42 1.1 cgd *
43 1.1 cgd * Redistribution and use in source and binary forms, with or without
44 1.1 cgd * modification, are permitted provided that the following conditions
45 1.1 cgd * are met:
46 1.1 cgd * 1. Redistributions of source code must retain the above copyright
47 1.1 cgd * notice, this list of conditions and the following disclaimer.
48 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright
49 1.1 cgd * notice, this list of conditions and the following disclaimer in the
50 1.1 cgd * documentation and/or other materials provided with the distribution.
51 1.1 cgd * 3. All advertising materials mentioning features or use of this software
52 1.1 cgd * must display the following acknowledgement:
53 1.1 cgd * This product includes software developed by the University of
54 1.1 cgd * California, Berkeley and its contributors.
55 1.1 cgd * 4. Neither the name of the University nor the names of its contributors
56 1.1 cgd * may be used to endorse or promote products derived from this software
57 1.1 cgd * without specific prior written permission.
58 1.1 cgd *
59 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
60 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
61 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
62 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
63 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
64 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
65 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
66 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
67 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
68 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
69 1.1 cgd * SUCH DAMAGE.
70 1.1 cgd *
71 1.4 deraadt * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94
72 1.1 cgd */
73 1.1 cgd
74 1.1 cgd /*
75 1.1 cgd * Grammar for FTP commands.
76 1.1 cgd * See RFC 959.
77 1.1 cgd */
78 1.1 cgd
79 1.1 cgd %{
80 1.13 christos #include <sys/cdefs.h>
81 1.1 cgd
82 1.1 cgd #ifndef lint
83 1.5 cgd #if 0
84 1.4 deraadt static char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94";
85 1.5 cgd #else
86 1.62 lukem __RCSID("$NetBSD: ftpcmd.y,v 1.62 2001/04/12 02:28:59 lukem Exp $");
87 1.5 cgd #endif
88 1.1 cgd #endif /* not lint */
89 1.1 cgd
90 1.1 cgd #include <sys/param.h>
91 1.1 cgd #include <sys/socket.h>
92 1.1 cgd #include <sys/stat.h>
93 1.4 deraadt
94 1.1 cgd #include <netinet/in.h>
95 1.1 cgd #include <arpa/ftp.h>
96 1.15 mrg #include <arpa/inet.h>
97 1.4 deraadt
98 1.4 deraadt #include <ctype.h>
99 1.4 deraadt #include <errno.h>
100 1.4 deraadt #include <glob.h>
101 1.4 deraadt #include <pwd.h>
102 1.4 deraadt #include <setjmp.h>
103 1.1 cgd #include <signal.h>
104 1.4 deraadt #include <stdio.h>
105 1.4 deraadt #include <stdlib.h>
106 1.4 deraadt #include <string.h>
107 1.1 cgd #include <syslog.h>
108 1.1 cgd #include <time.h>
109 1.18 lukem #include <tzfile.h>
110 1.1 cgd #include <unistd.h>
111 1.32 itojun #include <netdb.h>
112 1.26 explorer
113 1.26 explorer #ifdef KERBEROS5
114 1.36 christos #include <krb5/krb5.h>
115 1.26 explorer #endif
116 1.4 deraadt
117 1.4 deraadt #include "extern.h"
118 1.45 lukem #include "version.h"
119 1.1 cgd
120 1.1 cgd static int cmd_type;
121 1.1 cgd static int cmd_form;
122 1.1 cgd static int cmd_bytesz;
123 1.44 lukem
124 1.60 aidan char cbuf[FTP_BUFLEN];
125 1.60 aidan char *cmdp;
126 1.1 cgd char *fromname;
127 1.24 lukem
128 1.1 cgd %}
129 1.1 cgd
130 1.4 deraadt %union {
131 1.4 deraadt int i;
132 1.4 deraadt char *s;
133 1.4 deraadt }
134 1.4 deraadt
135 1.1 cgd %token
136 1.1 cgd A B C E F I
137 1.1 cgd L N P R S T
138 1.1 cgd
139 1.4 deraadt SP CRLF COMMA
140 1.1 cgd
141 1.23 lukem USER PASS ACCT CWD CDUP SMNT
142 1.23 lukem QUIT REIN PORT PASV TYPE STRU
143 1.23 lukem MODE RETR STOR STOU APPE ALLO
144 1.23 lukem REST RNFR RNTO ABOR DELE RMD
145 1.23 lukem MKD PWD LIST NLST SITE SYST
146 1.23 lukem STAT HELP NOOP
147 1.23 lukem
148 1.25 lukem AUTH ADAT PROT PBSZ CCC MIC
149 1.25 lukem CONF ENC
150 1.25 lukem
151 1.23 lukem FEAT OPTS
152 1.21 lukem
153 1.47 lukem SIZE MDTM MLST MLSD
154 1.1 cgd
155 1.32 itojun LPRT LPSV EPRT EPSV
156 1.32 itojun
157 1.23 lukem MAIL MLFL MRCP MRSQ MSAM MSND
158 1.23 lukem MSOM
159 1.23 lukem
160 1.41 lukem CHMOD IDLE RATEGET RATEPUT UMASK
161 1.1 cgd
162 1.1 cgd LEXERR
163 1.1 cgd
164 1.4 deraadt %token <s> STRING
165 1.32 itojun %token <s> ALL
166 1.4 deraadt %token <i> NUMBER
167 1.4 deraadt
168 1.56 lukem %type <i> check_login octal_number byte_size
169 1.25 lukem %type <i> struct_code mode_code type_code form_code decimal_integer
170 1.4 deraadt %type <s> pathstring pathname password username
171 1.25 lukem %type <s> mechanism_name base64data prot_code
172 1.4 deraadt
173 1.60 aidan %start cmd_sel
174 1.1 cgd
175 1.1 cgd %%
176 1.1 cgd
177 1.60 aidan cmd_sel
178 1.60 aidan : cmd
179 1.4 deraadt {
180 1.22 lukem fromname = NULL;
181 1.1 cgd restart_point = (off_t) 0;
182 1.1 cgd }
183 1.23 lukem
184 1.60 aidan | rcmd
185 1.23 lukem
186 1.1 cgd ;
187 1.1 cgd
188 1.4 deraadt cmd
189 1.23 lukem /* RFC 959 */
190 1.4 deraadt : USER SP username CRLF
191 1.4 deraadt {
192 1.4 deraadt user($3);
193 1.4 deraadt free($3);
194 1.4 deraadt }
195 1.23 lukem
196 1.4 deraadt | PASS SP password CRLF
197 1.4 deraadt {
198 1.4 deraadt pass($3);
199 1.42 lukem memset($3, 0, strlen($3));
200 1.4 deraadt free($3);
201 1.1 cgd }
202 1.23 lukem
203 1.23 lukem | CWD check_login CRLF
204 1.23 lukem {
205 1.23 lukem if ($2)
206 1.51 lukem cwd(homedir);
207 1.23 lukem }
208 1.23 lukem
209 1.23 lukem | CWD check_login SP pathname CRLF
210 1.23 lukem {
211 1.23 lukem if ($2 && $4 != NULL)
212 1.23 lukem cwd($4);
213 1.23 lukem if ($4 != NULL)
214 1.23 lukem free($4);
215 1.23 lukem }
216 1.23 lukem
217 1.23 lukem | CDUP check_login CRLF
218 1.23 lukem {
219 1.23 lukem if ($2)
220 1.23 lukem cwd("..");
221 1.23 lukem }
222 1.23 lukem
223 1.23 lukem | QUIT CRLF
224 1.23 lukem {
225 1.27 lukem if (logged_in) {
226 1.49 sommerfe reply(-221, "%s", "");
227 1.48 lukem reply(0,
228 1.55 lukem "Data traffic for this session was " LLF " byte%s in " LLF " file%s.",
229 1.55 lukem (LLT)total_data, PLURAL(total_data),
230 1.55 lukem (LLT)total_files, PLURAL(total_files));
231 1.48 lukem reply(0,
232 1.55 lukem "Total traffic for this session was " LLF " byte%s in " LLF " transfer%s.",
233 1.55 lukem (LLT)total_bytes, PLURAL(total_bytes),
234 1.55 lukem (LLT)total_xfers, PLURAL(total_xfers));
235 1.31 lukem }
236 1.31 lukem reply(221,
237 1.31 lukem "Thank you for using the FTP service on %s.",
238 1.31 lukem hostname);
239 1.59 lukem if (logged_in && logging) {
240 1.27 lukem syslog(LOG_INFO,
241 1.55 lukem "Data traffic: " LLF " byte%s in " LLF " file%s",
242 1.55 lukem (LLT)total_data, PLURAL(total_data),
243 1.55 lukem (LLT)total_files, PLURAL(total_files));
244 1.27 lukem syslog(LOG_INFO,
245 1.55 lukem "Total traffic: " LLF " byte%s in " LLF " transfer%s",
246 1.55 lukem (LLT)total_bytes, PLURAL(total_bytes),
247 1.55 lukem (LLT)total_xfers, PLURAL(total_xfers));
248 1.27 lukem }
249 1.31 lukem
250 1.23 lukem dologout(0);
251 1.23 lukem }
252 1.23 lukem
253 1.15 mrg | PORT check_login SP host_port CRLF
254 1.4 deraadt {
255 1.52 lukem if ($2)
256 1.52 lukem port_check("PORT", AF_INET);
257 1.32 itojun }
258 1.32 itojun
259 1.34 itojun | LPRT check_login SP host_long_port4 CRLF
260 1.32 itojun {
261 1.52 lukem if ($2)
262 1.52 lukem port_check("LPRT", AF_INET);
263 1.34 itojun }
264 1.34 itojun
265 1.34 itojun | LPRT check_login SP host_long_port6 CRLF
266 1.34 itojun {
267 1.57 lukem #ifdef INET6
268 1.52 lukem if ($2)
269 1.52 lukem port_check("LPRT", AF_INET6);
270 1.57 lukem #else
271 1.57 lukem reply(500, "IPv6 support not available.");
272 1.57 lukem #endif
273 1.1 cgd }
274 1.23 lukem
275 1.32 itojun | EPRT check_login SP STRING CRLF
276 1.32 itojun {
277 1.42 lukem if ($2) {
278 1.54 itojun if (extended_port($4) == 0)
279 1.54 itojun port_check("EPRT", -1);
280 1.42 lukem }
281 1.42 lukem free($4);
282 1.32 itojun }
283 1.32 itojun
284 1.20 tv | PASV check_login CRLF
285 1.4 deraadt {
286 1.42 lukem if ($2) {
287 1.56 lukem if (CURCLASS_FLAGS_ISSET(passive))
288 1.42 lukem passive();
289 1.42 lukem else
290 1.42 lukem reply(500, "PASV mode not available.");
291 1.20 tv }
292 1.1 cgd }
293 1.23 lukem
294 1.42 lukem | LPSV check_login CRLF
295 1.32 itojun {
296 1.42 lukem if ($2) {
297 1.42 lukem if (epsvall)
298 1.42 lukem reply(501,
299 1.42 lukem "LPSV disallowed after EPSV ALL");
300 1.42 lukem else
301 1.42 lukem long_passive("LPSV", PF_UNSPEC);
302 1.42 lukem }
303 1.32 itojun }
304 1.32 itojun
305 1.42 lukem | EPSV check_login SP NUMBER CRLF
306 1.32 itojun {
307 1.54 itojun if ($2)
308 1.54 itojun long_passive("EPSV", epsvproto2af($4));
309 1.32 itojun }
310 1.32 itojun
311 1.42 lukem | EPSV check_login SP ALL CRLF
312 1.32 itojun {
313 1.42 lukem if ($2) {
314 1.32 itojun reply(200, "EPSV ALL command successful.");
315 1.32 itojun epsvall++;
316 1.32 itojun }
317 1.32 itojun }
318 1.32 itojun
319 1.42 lukem | EPSV check_login CRLF
320 1.32 itojun {
321 1.42 lukem if ($2)
322 1.42 lukem long_passive("EPSV", PF_UNSPEC);
323 1.32 itojun }
324 1.32 itojun
325 1.42 lukem | TYPE check_login SP type_code CRLF
326 1.4 deraadt {
327 1.42 lukem if ($2) {
328 1.42 lukem
329 1.1 cgd switch (cmd_type) {
330 1.1 cgd
331 1.1 cgd case TYPE_A:
332 1.1 cgd if (cmd_form == FORM_N) {
333 1.1 cgd reply(200, "Type set to A.");
334 1.1 cgd type = cmd_type;
335 1.1 cgd form = cmd_form;
336 1.1 cgd } else
337 1.1 cgd reply(504, "Form must be N.");
338 1.1 cgd break;
339 1.1 cgd
340 1.1 cgd case TYPE_E:
341 1.1 cgd reply(504, "Type E not implemented.");
342 1.1 cgd break;
343 1.1 cgd
344 1.1 cgd case TYPE_I:
345 1.1 cgd reply(200, "Type set to I.");
346 1.1 cgd type = cmd_type;
347 1.1 cgd break;
348 1.1 cgd
349 1.1 cgd case TYPE_L:
350 1.1 cgd #if NBBY == 8
351 1.1 cgd if (cmd_bytesz == 8) {
352 1.1 cgd reply(200,
353 1.1 cgd "Type set to L (byte size 8).");
354 1.1 cgd type = cmd_type;
355 1.1 cgd } else
356 1.1 cgd reply(504, "Byte size must be 8.");
357 1.1 cgd #else /* NBBY == 8 */
358 1.1 cgd UNIMPLEMENTED for NBBY != 8
359 1.1 cgd #endif /* NBBY == 8 */
360 1.1 cgd }
361 1.42 lukem
362 1.42 lukem }
363 1.1 cgd }
364 1.23 lukem
365 1.42 lukem | STRU check_login SP struct_code CRLF
366 1.4 deraadt {
367 1.42 lukem if ($2) {
368 1.42 lukem switch ($4) {
369 1.1 cgd
370 1.42 lukem case STRU_F:
371 1.42 lukem reply(200, "STRU F ok.");
372 1.42 lukem break;
373 1.1 cgd
374 1.42 lukem default:
375 1.42 lukem reply(504, "Unimplemented STRU type.");
376 1.42 lukem }
377 1.1 cgd }
378 1.1 cgd }
379 1.23 lukem
380 1.42 lukem | MODE check_login SP mode_code CRLF
381 1.4 deraadt {
382 1.42 lukem if ($2) {
383 1.42 lukem switch ($4) {
384 1.1 cgd
385 1.42 lukem case MODE_S:
386 1.42 lukem reply(200, "MODE S ok.");
387 1.42 lukem break;
388 1.1 cgd
389 1.42 lukem default:
390 1.42 lukem reply(502, "Unimplemented MODE type.");
391 1.42 lukem }
392 1.1 cgd }
393 1.1 cgd }
394 1.23 lukem
395 1.4 deraadt | RETR check_login SP pathname CRLF
396 1.4 deraadt {
397 1.1 cgd if ($2 && $4 != NULL)
398 1.22 lukem retrieve(NULL, $4);
399 1.1 cgd if ($4 != NULL)
400 1.4 deraadt free($4);
401 1.1 cgd }
402 1.23 lukem
403 1.56 lukem | STOR SP pathname CRLF
404 1.4 deraadt {
405 1.56 lukem if (check_write($3, 1))
406 1.56 lukem store($3, "w", 0);
407 1.56 lukem if ($3 != NULL)
408 1.56 lukem free($3);
409 1.1 cgd }
410 1.23 lukem
411 1.56 lukem | STOU SP pathname CRLF
412 1.4 deraadt {
413 1.56 lukem if (check_write($3, 1))
414 1.56 lukem store($3, "w", 1);
415 1.56 lukem if ($3 != NULL)
416 1.56 lukem free($3);
417 1.1 cgd }
418 1.23 lukem
419 1.56 lukem | APPE SP pathname CRLF
420 1.4 deraadt {
421 1.56 lukem if (check_write($3, 1))
422 1.56 lukem store($3, "a", 0);
423 1.56 lukem if ($3 != NULL)
424 1.56 lukem free($3);
425 1.1 cgd }
426 1.23 lukem
427 1.42 lukem | ALLO check_login SP NUMBER CRLF
428 1.4 deraadt {
429 1.42 lukem if ($2)
430 1.42 lukem reply(202, "ALLO command ignored.");
431 1.1 cgd }
432 1.23 lukem
433 1.42 lukem | ALLO check_login SP NUMBER SP R SP NUMBER CRLF
434 1.4 deraadt {
435 1.42 lukem if ($2)
436 1.42 lukem reply(202, "ALLO command ignored.");
437 1.1 cgd }
438 1.23 lukem
439 1.56 lukem | RNTO SP pathname CRLF
440 1.4 deraadt {
441 1.56 lukem if (check_write($3, 0)) {
442 1.42 lukem if (fromname) {
443 1.56 lukem renamecmd(fromname, $3);
444 1.42 lukem free(fromname);
445 1.42 lukem fromname = NULL;
446 1.42 lukem } else {
447 1.42 lukem reply(503, "Bad sequence of commands.");
448 1.42 lukem }
449 1.1 cgd }
450 1.56 lukem if ($3 != NULL)
451 1.56 lukem free($3);
452 1.1 cgd }
453 1.23 lukem
454 1.42 lukem | ABOR check_login CRLF
455 1.4 deraadt {
456 1.60 aidan if (is_oob)
457 1.60 aidan abor();
458 1.60 aidan else if ($2)
459 1.42 lukem reply(225, "ABOR command successful.");
460 1.1 cgd }
461 1.23 lukem
462 1.56 lukem | DELE SP pathname CRLF
463 1.4 deraadt {
464 1.56 lukem if (check_write($3, 0))
465 1.56 lukem delete($3);
466 1.56 lukem if ($3 != NULL)
467 1.56 lukem free($3);
468 1.1 cgd }
469 1.23 lukem
470 1.56 lukem | RMD SP pathname CRLF
471 1.4 deraadt {
472 1.56 lukem if (check_write($3, 0))
473 1.56 lukem removedir($3);
474 1.56 lukem if ($3 != NULL)
475 1.56 lukem free($3);
476 1.1 cgd }
477 1.1 cgd
478 1.56 lukem | MKD SP pathname CRLF
479 1.4 deraadt {
480 1.56 lukem if (check_write($3, 0))
481 1.56 lukem makedir($3);
482 1.56 lukem if ($3 != NULL)
483 1.56 lukem free($3);
484 1.1 cgd }
485 1.23 lukem
486 1.23 lukem | PWD check_login CRLF
487 1.23 lukem {
488 1.23 lukem if ($2)
489 1.23 lukem pwd();
490 1.23 lukem }
491 1.23 lukem
492 1.23 lukem | LIST check_login CRLF
493 1.23 lukem {
494 1.40 lukem char *argv[] = { INTERNAL_LS, "-lgA", NULL };
495 1.40 lukem
496 1.23 lukem if ($2)
497 1.40 lukem retrieve(argv, "");
498 1.23 lukem }
499 1.23 lukem
500 1.23 lukem | LIST check_login SP pathname CRLF
501 1.4 deraadt {
502 1.40 lukem char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL };
503 1.40 lukem
504 1.40 lukem if ($2 && $4 != NULL) {
505 1.40 lukem argv[2] = $4;
506 1.40 lukem retrieve(argv, $4);
507 1.40 lukem }
508 1.1 cgd if ($4 != NULL)
509 1.4 deraadt free($4);
510 1.1 cgd }
511 1.23 lukem
512 1.23 lukem | NLST check_login CRLF
513 1.4 deraadt {
514 1.1 cgd if ($2)
515 1.23 lukem send_file_list(".");
516 1.1 cgd }
517 1.23 lukem
518 1.51 lukem | NLST check_login SP pathname CRLF
519 1.4 deraadt {
520 1.42 lukem if ($2)
521 1.23 lukem send_file_list($4);
522 1.42 lukem free($4);
523 1.1 cgd }
524 1.23 lukem
525 1.4 deraadt | SITE SP HELP CRLF
526 1.4 deraadt {
527 1.22 lukem help(sitetab, NULL);
528 1.1 cgd }
529 1.23 lukem
530 1.56 lukem | SITE SP CHMOD SP octal_number SP pathname CRLF
531 1.4 deraadt {
532 1.56 lukem if (check_write($7, 0)) {
533 1.56 lukem if ($5 > 0777)
534 1.1 cgd reply(501,
535 1.1 cgd "CHMOD: Mode value must be between 0 and 0777");
536 1.56 lukem else if (chmod($7, $5) < 0)
537 1.56 lukem perror_reply(550, $7);
538 1.1 cgd else
539 1.1 cgd reply(200, "CHMOD command successful.");
540 1.1 cgd }
541 1.56 lukem if ($7 != NULL)
542 1.56 lukem free($7);
543 1.1 cgd }
544 1.23 lukem
545 1.41 lukem | SITE SP HELP SP STRING CRLF
546 1.41 lukem {
547 1.41 lukem help(sitetab, $5);
548 1.42 lukem free($5);
549 1.41 lukem }
550 1.41 lukem
551 1.42 lukem | SITE SP IDLE check_login CRLF
552 1.4 deraadt {
553 1.42 lukem if ($4) {
554 1.42 lukem reply(200,
555 1.1 cgd "Current IDLE time limit is %d seconds; max %d",
556 1.42 lukem curclass.timeout, curclass.maxtimeout);
557 1.42 lukem }
558 1.1 cgd }
559 1.23 lukem
560 1.42 lukem | SITE SP IDLE check_login SP NUMBER CRLF
561 1.4 deraadt {
562 1.42 lukem if ($4) {
563 1.42 lukem if ($6 < 30 || $6 > curclass.maxtimeout) {
564 1.42 lukem reply(501,
565 1.42 lukem "IDLE time limit must be between 30 and %d seconds",
566 1.42 lukem curclass.maxtimeout);
567 1.42 lukem } else {
568 1.42 lukem curclass.timeout = $6;
569 1.42 lukem (void) alarm(curclass.timeout);
570 1.42 lukem reply(200,
571 1.42 lukem "IDLE time limit set to %d seconds",
572 1.42 lukem curclass.timeout);
573 1.42 lukem }
574 1.1 cgd }
575 1.1 cgd }
576 1.23 lukem
577 1.42 lukem | SITE SP RATEGET check_login CRLF
578 1.41 lukem {
579 1.42 lukem if ($4) {
580 1.56 lukem reply(200,
581 1.56 lukem "Current RATEGET is " LLF " bytes/sec",
582 1.56 lukem (LLT)curclass.rateget);
583 1.42 lukem }
584 1.41 lukem }
585 1.41 lukem
586 1.42 lukem | SITE SP RATEGET check_login SP STRING CRLF
587 1.41 lukem {
588 1.42 lukem char *p = $6;
589 1.56 lukem LLT rate;
590 1.41 lukem
591 1.42 lukem if ($4) {
592 1.56 lukem rate = strsuftoll(p);
593 1.42 lukem if (rate == -1)
594 1.42 lukem reply(501, "Invalid RATEGET %s", p);
595 1.42 lukem else if (curclass.maxrateget &&
596 1.42 lukem rate > curclass.maxrateget)
597 1.42 lukem reply(501,
598 1.56 lukem "RATEGET " LLF " is larger than maximum RATEGET " LLF,
599 1.56 lukem (LLT)rate,
600 1.56 lukem (LLT)curclass.maxrateget);
601 1.42 lukem else {
602 1.42 lukem curclass.rateget = rate;
603 1.42 lukem reply(200,
604 1.56 lukem "RATEGET set to " LLF " bytes/sec",
605 1.56 lukem (LLT)curclass.rateget);
606 1.42 lukem }
607 1.41 lukem }
608 1.42 lukem free($6);
609 1.41 lukem }
610 1.41 lukem
611 1.42 lukem | SITE SP RATEPUT check_login CRLF
612 1.41 lukem {
613 1.42 lukem if ($4) {
614 1.56 lukem reply(200,
615 1.56 lukem "Current RATEPUT is " LLF " bytes/sec",
616 1.56 lukem (LLT)curclass.rateput);
617 1.42 lukem }
618 1.41 lukem }
619 1.41 lukem
620 1.42 lukem | SITE SP RATEPUT check_login SP STRING CRLF
621 1.41 lukem {
622 1.42 lukem char *p = $6;
623 1.56 lukem LLT rate;
624 1.41 lukem
625 1.42 lukem if ($4) {
626 1.56 lukem rate = strsuftoll(p);
627 1.42 lukem if (rate == -1)
628 1.42 lukem reply(501, "Invalid RATEPUT %s", p);
629 1.42 lukem else if (curclass.maxrateput &&
630 1.42 lukem rate > curclass.maxrateput)
631 1.42 lukem reply(501,
632 1.56 lukem "RATEPUT " LLF " is larger than maximum RATEPUT " LLF,
633 1.56 lukem (LLT)rate,
634 1.56 lukem (LLT)curclass.maxrateput);
635 1.42 lukem else {
636 1.42 lukem curclass.rateput = rate;
637 1.42 lukem reply(200,
638 1.56 lukem "RATEPUT set to " LLF " bytes/sec",
639 1.56 lukem (LLT)curclass.rateput);
640 1.42 lukem }
641 1.41 lukem }
642 1.42 lukem free($6);
643 1.41 lukem }
644 1.41 lukem
645 1.41 lukem | SITE SP UMASK check_login CRLF
646 1.41 lukem {
647 1.41 lukem int oldmask;
648 1.41 lukem
649 1.41 lukem if ($4) {
650 1.41 lukem oldmask = umask(0);
651 1.41 lukem (void) umask(oldmask);
652 1.41 lukem reply(200, "Current UMASK is %03o", oldmask);
653 1.41 lukem }
654 1.41 lukem }
655 1.41 lukem
656 1.56 lukem | SITE SP UMASK check_login SP octal_number CRLF
657 1.41 lukem {
658 1.41 lukem int oldmask;
659 1.41 lukem
660 1.56 lukem if ($4 && CURCLASS_FLAGS_ISSET(modify)) {
661 1.41 lukem if (($6 == -1) || ($6 > 0777)) {
662 1.41 lukem reply(501, "Bad UMASK value");
663 1.41 lukem } else {
664 1.41 lukem oldmask = umask($6);
665 1.41 lukem reply(200,
666 1.41 lukem "UMASK set to %03o (was %03o)",
667 1.41 lukem $6, oldmask);
668 1.41 lukem }
669 1.41 lukem }
670 1.41 lukem }
671 1.41 lukem
672 1.23 lukem | SYST CRLF
673 1.23 lukem {
674 1.52 lukem if (EMPTYSTR(version))
675 1.52 lukem reply(215, "UNIX Type: L%d", NBBY);
676 1.52 lukem else
677 1.52 lukem reply(215, "UNIX Type: L%d Version: %s", NBBY,
678 1.52 lukem version);
679 1.23 lukem }
680 1.23 lukem
681 1.23 lukem | STAT check_login SP pathname CRLF
682 1.4 deraadt {
683 1.1 cgd if ($2 && $4 != NULL)
684 1.23 lukem statfilecmd($4);
685 1.1 cgd if ($4 != NULL)
686 1.4 deraadt free($4);
687 1.1 cgd }
688 1.23 lukem
689 1.23 lukem | STAT CRLF
690 1.23 lukem {
691 1.60 aidan if (is_oob)
692 1.60 aidan statxfer();
693 1.60 aidan else
694 1.60 aidan statcmd();
695 1.23 lukem }
696 1.23 lukem
697 1.23 lukem | HELP CRLF
698 1.23 lukem {
699 1.23 lukem help(cmdtab, NULL);
700 1.23 lukem }
701 1.23 lukem
702 1.23 lukem | HELP SP STRING CRLF
703 1.23 lukem {
704 1.23 lukem char *cp = $3;
705 1.23 lukem
706 1.23 lukem if (strncasecmp(cp, "SITE", 4) == 0) {
707 1.23 lukem cp = $3 + 4;
708 1.23 lukem if (*cp == ' ')
709 1.23 lukem cp++;
710 1.23 lukem if (*cp)
711 1.23 lukem help(sitetab, cp);
712 1.23 lukem else
713 1.23 lukem help(sitetab, NULL);
714 1.23 lukem } else
715 1.23 lukem help(cmdtab, $3);
716 1.42 lukem free($3);
717 1.23 lukem }
718 1.23 lukem
719 1.23 lukem | NOOP CRLF
720 1.23 lukem {
721 1.23 lukem reply(200, "NOOP command successful.");
722 1.23 lukem }
723 1.23 lukem
724 1.25 lukem /* RFC 2228 */
725 1.25 lukem | AUTH SP mechanism_name CRLF
726 1.25 lukem {
727 1.25 lukem reply(502, "RFC 2228 authentication not implemented.");
728 1.42 lukem free($3);
729 1.25 lukem }
730 1.25 lukem
731 1.25 lukem | ADAT SP base64data CRLF
732 1.25 lukem {
733 1.25 lukem reply(503,
734 1.25 lukem "Please set authentication state with AUTH.");
735 1.42 lukem free($3);
736 1.25 lukem }
737 1.25 lukem
738 1.25 lukem | PROT SP prot_code CRLF
739 1.25 lukem {
740 1.25 lukem reply(503,
741 1.25 lukem "Please set protection buffer size with PBSZ.");
742 1.42 lukem free($3);
743 1.25 lukem }
744 1.25 lukem
745 1.25 lukem | PBSZ SP decimal_integer CRLF
746 1.25 lukem {
747 1.25 lukem reply(503,
748 1.25 lukem "Please set authentication state with AUTH.");
749 1.25 lukem }
750 1.25 lukem
751 1.25 lukem | CCC CRLF
752 1.25 lukem {
753 1.25 lukem reply(533, "No protection enabled.");
754 1.25 lukem }
755 1.25 lukem
756 1.25 lukem | MIC SP base64data CRLF
757 1.25 lukem {
758 1.25 lukem reply(502, "RFC 2228 authentication not implemented.");
759 1.42 lukem free($3);
760 1.25 lukem }
761 1.25 lukem
762 1.25 lukem | CONF SP base64data CRLF
763 1.25 lukem {
764 1.25 lukem reply(502, "RFC 2228 authentication not implemented.");
765 1.42 lukem free($3);
766 1.25 lukem }
767 1.25 lukem
768 1.25 lukem | ENC SP base64data CRLF
769 1.25 lukem {
770 1.25 lukem reply(502, "RFC 2228 authentication not implemented.");
771 1.42 lukem free($3);
772 1.25 lukem }
773 1.25 lukem
774 1.23 lukem /* RFC 2389 */
775 1.23 lukem | FEAT CRLF
776 1.23 lukem {
777 1.47 lukem
778 1.47 lukem feat();
779 1.23 lukem }
780 1.23 lukem
781 1.23 lukem | OPTS SP STRING CRLF
782 1.4 deraadt {
783 1.23 lukem
784 1.23 lukem opts($3);
785 1.42 lukem free($3);
786 1.1 cgd }
787 1.1 cgd
788 1.23 lukem
789 1.50 lukem /* extensions from draft-ietf-ftpext-mlst-11 */
790 1.23 lukem
791 1.1 cgd /*
792 1.1 cgd * Return size of file in a format suitable for
793 1.1 cgd * using with RESTART (we just count bytes).
794 1.1 cgd */
795 1.4 deraadt | SIZE check_login SP pathname CRLF
796 1.4 deraadt {
797 1.1 cgd if ($2 && $4 != NULL)
798 1.4 deraadt sizecmd($4);
799 1.1 cgd if ($4 != NULL)
800 1.4 deraadt free($4);
801 1.1 cgd }
802 1.1 cgd
803 1.1 cgd /*
804 1.1 cgd * Return modification time of file as an ISO 3307
805 1.1 cgd * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
806 1.1 cgd * where xxx is the fractional second (of any precision,
807 1.1 cgd * not necessarily 3 digits)
808 1.1 cgd */
809 1.4 deraadt | MDTM check_login SP pathname CRLF
810 1.4 deraadt {
811 1.1 cgd if ($2 && $4 != NULL) {
812 1.1 cgd struct stat stbuf;
813 1.4 deraadt if (stat($4, &stbuf) < 0)
814 1.22 lukem perror_reply(550, $4);
815 1.4 deraadt else if (!S_ISREG(stbuf.st_mode)) {
816 1.4 deraadt reply(550, "%s: not a plain file.", $4);
817 1.1 cgd } else {
818 1.4 deraadt struct tm *t;
819 1.47 lukem
820 1.1 cgd t = gmtime(&stbuf.st_mtime);
821 1.1 cgd reply(213,
822 1.7 jtc "%04d%02d%02d%02d%02d%02d",
823 1.18 lukem TM_YEAR_BASE + t->tm_year,
824 1.7 jtc t->tm_mon+1, t->tm_mday,
825 1.1 cgd t->tm_hour, t->tm_min, t->tm_sec);
826 1.1 cgd }
827 1.1 cgd }
828 1.1 cgd if ($4 != NULL)
829 1.4 deraadt free($4);
830 1.1 cgd }
831 1.23 lukem
832 1.47 lukem | MLST check_login SP pathname CRLF
833 1.47 lukem {
834 1.47 lukem if ($2 && $4 != NULL)
835 1.47 lukem mlst($4);
836 1.47 lukem if ($4 != NULL)
837 1.47 lukem free($4);
838 1.47 lukem }
839 1.47 lukem
840 1.48 lukem | MLST check_login CRLF
841 1.47 lukem {
842 1.47 lukem mlst(NULL);
843 1.47 lukem }
844 1.47 lukem
845 1.47 lukem | MLSD check_login SP pathname CRLF
846 1.47 lukem {
847 1.47 lukem if ($2 && $4 != NULL)
848 1.47 lukem mlsd($4);
849 1.47 lukem if ($4 != NULL)
850 1.47 lukem free($4);
851 1.47 lukem }
852 1.47 lukem
853 1.48 lukem | MLSD check_login CRLF
854 1.47 lukem {
855 1.47 lukem mlsd(NULL);
856 1.47 lukem }
857 1.47 lukem
858 1.4 deraadt | error CRLF
859 1.4 deraadt {
860 1.1 cgd yyerrok;
861 1.1 cgd }
862 1.1 cgd ;
863 1.21 lukem
864 1.4 deraadt rcmd
865 1.42 lukem : REST check_login SP byte_size CRLF
866 1.23 lukem {
867 1.42 lukem if ($2) {
868 1.42 lukem fromname = NULL;
869 1.59 lukem restart_point = $4; /* XXX: $4 is only "int" */
870 1.55 lukem reply(350,
871 1.55 lukem "Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.",
872 1.55 lukem (LLT)restart_point);
873 1.42 lukem }
874 1.23 lukem }
875 1.42 lukem
876 1.56 lukem | RNFR SP pathname CRLF
877 1.4 deraadt {
878 1.1 cgd restart_point = (off_t) 0;
879 1.56 lukem if (check_write($3, 0))
880 1.56 lukem fromname = renamefrom($3);
881 1.56 lukem if ($3 != NULL)
882 1.56 lukem free($3);
883 1.1 cgd }
884 1.1 cgd ;
885 1.4 deraadt
886 1.4 deraadt username
887 1.4 deraadt : STRING
888 1.1 cgd ;
889 1.1 cgd
890 1.4 deraadt password
891 1.4 deraadt : /* empty */
892 1.4 deraadt {
893 1.4 deraadt $$ = (char *)calloc(1, sizeof(char));
894 1.1 cgd }
895 1.23 lukem
896 1.4 deraadt | STRING
897 1.1 cgd ;
898 1.1 cgd
899 1.4 deraadt byte_size
900 1.4 deraadt : NUMBER
901 1.1 cgd ;
902 1.1 cgd
903 1.4 deraadt host_port
904 1.4 deraadt : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
905 1.1 cgd NUMBER COMMA NUMBER
906 1.4 deraadt {
907 1.4 deraadt char *a, *p;
908 1.1 cgd
909 1.55 lukem memset(&data_dest, 0, sizeof(data_dest));
910 1.32 itojun data_dest.su_len = sizeof(struct sockaddr_in);
911 1.32 itojun data_dest.su_family = AF_INET;
912 1.55 lukem p = (char *)&data_dest.su_port;
913 1.6 mycroft p[0] = $9; p[1] = $11;
914 1.55 lukem a = (char *)&data_dest.su_addr;
915 1.1 cgd a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
916 1.1 cgd }
917 1.1 cgd ;
918 1.1 cgd
919 1.34 itojun host_long_port4
920 1.34 itojun : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
921 1.34 itojun NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
922 1.34 itojun NUMBER
923 1.34 itojun {
924 1.34 itojun char *a, *p;
925 1.34 itojun
926 1.55 lukem memset(&data_dest, 0, sizeof(data_dest));
927 1.55 lukem data_dest.su_len = sizeof(struct sockaddr_in);
928 1.34 itojun data_dest.su_family = AF_INET;
929 1.34 itojun p = (char *)&data_dest.su_port;
930 1.34 itojun p[0] = $15; p[1] = $17;
931 1.55 lukem a = (char *)&data_dest.su_addr;
932 1.34 itojun a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11;
933 1.35 itojun
934 1.35 itojun /* reject invalid LPRT command */
935 1.35 itojun if ($1 != 4 || $3 != 4 || $13 != 2)
936 1.35 itojun memset(&data_dest, 0, sizeof(data_dest));
937 1.34 itojun }
938 1.34 itojun ;
939 1.34 itojun
940 1.34 itojun host_long_port6
941 1.32 itojun : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
942 1.32 itojun NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
943 1.32 itojun NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
944 1.32 itojun NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
945 1.32 itojun NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
946 1.32 itojun NUMBER
947 1.32 itojun {
948 1.53 christos #ifdef INET6
949 1.32 itojun char *a, *p;
950 1.32 itojun
951 1.55 lukem memset(&data_dest, 0, sizeof(data_dest));
952 1.55 lukem data_dest.su_len = sizeof(struct sockaddr_in6);
953 1.32 itojun data_dest.su_family = AF_INET6;
954 1.32 itojun p = (char *)&data_dest.su_port;
955 1.32 itojun p[0] = $39; p[1] = $41;
956 1.55 lukem a = (char *)&data_dest.si_su.su_sin6.sin6_addr;
957 1.32 itojun a[0] = $5; a[1] = $7; a[2] = $9; a[3] = $11;
958 1.32 itojun a[4] = $13; a[5] = $15; a[6] = $17; a[7] = $19;
959 1.32 itojun a[8] = $21; a[9] = $23; a[10] = $25; a[11] = $27;
960 1.32 itojun a[12] = $29; a[13] = $31; a[14] = $33; a[15] = $35;
961 1.37 itojun if (his_addr.su_family == AF_INET6) {
962 1.59 lukem /* XXX: more sanity checks! */
963 1.55 lukem data_dest.su_scope_id = his_addr.su_scope_id;
964 1.37 itojun }
965 1.53 christos #else
966 1.53 christos memset(&data_dest, 0, sizeof(data_dest));
967 1.55 lukem #endif /* INET6 */
968 1.35 itojun /* reject invalid LPRT command */
969 1.35 itojun if ($1 != 6 || $3 != 16 || $37 != 2)
970 1.35 itojun memset(&data_dest, 0, sizeof(data_dest));
971 1.32 itojun }
972 1.32 itojun ;
973 1.32 itojun
974 1.4 deraadt form_code
975 1.4 deraadt : N
976 1.4 deraadt {
977 1.4 deraadt $$ = FORM_N;
978 1.4 deraadt }
979 1.23 lukem
980 1.4 deraadt | T
981 1.4 deraadt {
982 1.4 deraadt $$ = FORM_T;
983 1.4 deraadt }
984 1.23 lukem
985 1.4 deraadt | C
986 1.4 deraadt {
987 1.4 deraadt $$ = FORM_C;
988 1.4 deraadt }
989 1.1 cgd ;
990 1.1 cgd
991 1.4 deraadt type_code
992 1.4 deraadt : A
993 1.4 deraadt {
994 1.4 deraadt cmd_type = TYPE_A;
995 1.4 deraadt cmd_form = FORM_N;
996 1.4 deraadt }
997 1.23 lukem
998 1.4 deraadt | A SP form_code
999 1.4 deraadt {
1000 1.4 deraadt cmd_type = TYPE_A;
1001 1.4 deraadt cmd_form = $3;
1002 1.4 deraadt }
1003 1.23 lukem
1004 1.4 deraadt | E
1005 1.4 deraadt {
1006 1.4 deraadt cmd_type = TYPE_E;
1007 1.4 deraadt cmd_form = FORM_N;
1008 1.4 deraadt }
1009 1.23 lukem
1010 1.4 deraadt | E SP form_code
1011 1.4 deraadt {
1012 1.4 deraadt cmd_type = TYPE_E;
1013 1.4 deraadt cmd_form = $3;
1014 1.4 deraadt }
1015 1.23 lukem
1016 1.4 deraadt | I
1017 1.4 deraadt {
1018 1.4 deraadt cmd_type = TYPE_I;
1019 1.4 deraadt }
1020 1.23 lukem
1021 1.4 deraadt | L
1022 1.4 deraadt {
1023 1.4 deraadt cmd_type = TYPE_L;
1024 1.4 deraadt cmd_bytesz = NBBY;
1025 1.4 deraadt }
1026 1.23 lukem
1027 1.4 deraadt | L SP byte_size
1028 1.4 deraadt {
1029 1.4 deraadt cmd_type = TYPE_L;
1030 1.4 deraadt cmd_bytesz = $3;
1031 1.4 deraadt }
1032 1.23 lukem
1033 1.4 deraadt /* this is for a bug in the BBN ftp */
1034 1.4 deraadt | L byte_size
1035 1.4 deraadt {
1036 1.4 deraadt cmd_type = TYPE_L;
1037 1.4 deraadt cmd_bytesz = $2;
1038 1.4 deraadt }
1039 1.1 cgd ;
1040 1.1 cgd
1041 1.4 deraadt struct_code
1042 1.4 deraadt : F
1043 1.4 deraadt {
1044 1.4 deraadt $$ = STRU_F;
1045 1.4 deraadt }
1046 1.23 lukem
1047 1.4 deraadt | R
1048 1.4 deraadt {
1049 1.4 deraadt $$ = STRU_R;
1050 1.4 deraadt }
1051 1.23 lukem
1052 1.4 deraadt | P
1053 1.4 deraadt {
1054 1.4 deraadt $$ = STRU_P;
1055 1.4 deraadt }
1056 1.1 cgd ;
1057 1.1 cgd
1058 1.4 deraadt mode_code
1059 1.4 deraadt : S
1060 1.4 deraadt {
1061 1.4 deraadt $$ = MODE_S;
1062 1.4 deraadt }
1063 1.23 lukem
1064 1.4 deraadt | B
1065 1.4 deraadt {
1066 1.4 deraadt $$ = MODE_B;
1067 1.4 deraadt }
1068 1.23 lukem
1069 1.4 deraadt | C
1070 1.4 deraadt {
1071 1.4 deraadt $$ = MODE_C;
1072 1.4 deraadt }
1073 1.1 cgd ;
1074 1.1 cgd
1075 1.4 deraadt pathname
1076 1.4 deraadt : pathstring
1077 1.4 deraadt {
1078 1.4 deraadt /*
1079 1.4 deraadt * Problem: this production is used for all pathname
1080 1.4 deraadt * processing, but only gives a 550 error reply.
1081 1.9 lukem * This is a valid reply in some cases but not in
1082 1.9 lukem * others.
1083 1.4 deraadt */
1084 1.4 deraadt if (logged_in && $1 && *$1 == '~') {
1085 1.4 deraadt glob_t gl;
1086 1.51 lukem int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE;
1087 1.4 deraadt
1088 1.9 lukem if ($1[1] == '\0')
1089 1.51 lukem $$ = xstrdup(homedir);
1090 1.9 lukem else {
1091 1.9 lukem memset(&gl, 0, sizeof(gl));
1092 1.9 lukem if (glob($1, flags, NULL, &gl) ||
1093 1.9 lukem gl.gl_pathc == 0) {
1094 1.9 lukem reply(550, "not found");
1095 1.9 lukem $$ = NULL;
1096 1.9 lukem } else
1097 1.23 lukem $$ = xstrdup(gl.gl_pathv[0]);
1098 1.9 lukem globfree(&gl);
1099 1.4 deraadt }
1100 1.4 deraadt free($1);
1101 1.4 deraadt } else
1102 1.4 deraadt $$ = $1;
1103 1.4 deraadt }
1104 1.1 cgd ;
1105 1.1 cgd
1106 1.4 deraadt pathstring
1107 1.4 deraadt : STRING
1108 1.1 cgd ;
1109 1.1 cgd
1110 1.4 deraadt octal_number
1111 1.4 deraadt : NUMBER
1112 1.4 deraadt {
1113 1.4 deraadt int ret, dec, multby, digit;
1114 1.1 cgd
1115 1.4 deraadt /*
1116 1.4 deraadt * Convert a number that was read as decimal number
1117 1.4 deraadt * to what it would be if it had been read as octal.
1118 1.4 deraadt */
1119 1.4 deraadt dec = $1;
1120 1.4 deraadt multby = 1;
1121 1.4 deraadt ret = 0;
1122 1.4 deraadt while (dec) {
1123 1.4 deraadt digit = dec%10;
1124 1.4 deraadt if (digit > 7) {
1125 1.4 deraadt ret = -1;
1126 1.4 deraadt break;
1127 1.4 deraadt }
1128 1.4 deraadt ret += digit * multby;
1129 1.4 deraadt multby *= 8;
1130 1.4 deraadt dec /= 10;
1131 1.1 cgd }
1132 1.4 deraadt $$ = ret;
1133 1.1 cgd }
1134 1.1 cgd ;
1135 1.1 cgd
1136 1.25 lukem mechanism_name
1137 1.25 lukem : STRING
1138 1.25 lukem ;
1139 1.25 lukem
1140 1.25 lukem base64data
1141 1.25 lukem : STRING
1142 1.25 lukem ;
1143 1.25 lukem
1144 1.25 lukem prot_code
1145 1.25 lukem : STRING
1146 1.25 lukem ;
1147 1.25 lukem
1148 1.25 lukem decimal_integer
1149 1.25 lukem : NUMBER
1150 1.25 lukem ;
1151 1.25 lukem
1152 1.4 deraadt check_login
1153 1.4 deraadt : /* empty */
1154 1.4 deraadt {
1155 1.4 deraadt if (logged_in)
1156 1.4 deraadt $$ = 1;
1157 1.4 deraadt else {
1158 1.4 deraadt reply(530, "Please login with USER and PASS.");
1159 1.4 deraadt $$ = 0;
1160 1.22 lukem hasyyerrored = 1;
1161 1.4 deraadt }
1162 1.1 cgd }
1163 1.1 cgd ;
1164 1.21 lukem
1165 1.1 cgd %%
1166 1.1 cgd
1167 1.1 cgd #define CMD 0 /* beginning of command */
1168 1.1 cgd #define ARGS 1 /* expect miscellaneous arguments */
1169 1.1 cgd #define STR1 2 /* expect SP followed by STRING */
1170 1.1 cgd #define STR2 3 /* expect STRING */
1171 1.1 cgd #define OSTR 4 /* optional SP then STRING */
1172 1.1 cgd #define ZSTR1 5 /* SP then optional STRING */
1173 1.1 cgd #define ZSTR2 6 /* optional STRING after SP */
1174 1.1 cgd #define SITECMD 7 /* SITE command */
1175 1.1 cgd #define NSTR 8 /* Number followed by a string */
1176 1.21 lukem #define NOARGS 9 /* No arguments allowed */
1177 1.60 aidan #define EOLN 10 /* End of line */
1178 1.1 cgd
1179 1.21 lukem struct tab cmdtab[] = {
1180 1.21 lukem /* From RFC 959, in order defined (5.3.1) */
1181 1.47 lukem { "USER", USER, STR1, 1, "<sp> username" },
1182 1.47 lukem { "PASS", PASS, ZSTR1, 1, "<sp> password" },
1183 1.47 lukem { "ACCT", ACCT, STR1, 0, "(specify account)" },
1184 1.47 lukem { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1185 1.47 lukem { "CDUP", CDUP, NOARGS, 1, "(change to parent directory)" },
1186 1.47 lukem { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
1187 1.47 lukem { "QUIT", QUIT, NOARGS, 1, "(terminate service)" },
1188 1.47 lukem { "REIN", REIN, NOARGS, 0, "(reinitialize server state)" },
1189 1.47 lukem { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
1190 1.47 lukem { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." },
1191 1.47 lukem { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" },
1192 1.47 lukem { "PASV", PASV, NOARGS, 1, "(set server in passive mode)" },
1193 1.47 lukem { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" },
1194 1.47 lukem { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" },
1195 1.47 lukem { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
1196 1.47 lukem { "STRU", STRU, ARGS, 1, "(specify file structure)" },
1197 1.47 lukem { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
1198 1.47 lukem { "RETR", RETR, STR1, 1, "<sp> file-name" },
1199 1.47 lukem { "STOR", STOR, STR1, 1, "<sp> file-name" },
1200 1.47 lukem { "STOU", STOU, STR1, 1, "<sp> file-name" },
1201 1.47 lukem { "APPE", APPE, STR1, 1, "<sp> file-name" },
1202 1.47 lukem { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
1203 1.47 lukem { "REST", REST, ARGS, 1, "<sp> offset (restart command)" },
1204 1.47 lukem { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
1205 1.47 lukem { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
1206 1.60 aidan { "ABOR", ABOR, NOARGS, 4, "(abort operation)" },
1207 1.47 lukem { "DELE", DELE, STR1, 1, "<sp> file-name" },
1208 1.47 lukem { "RMD", RMD, STR1, 1, "<sp> path-name" },
1209 1.47 lukem { "MKD", MKD, STR1, 1, "<sp> path-name" },
1210 1.47 lukem { "PWD", PWD, NOARGS, 1, "(return current directory)" },
1211 1.47 lukem { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
1212 1.47 lukem { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
1213 1.47 lukem { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
1214 1.47 lukem { "SYST", SYST, NOARGS, 1, "(get type of operating system)" },
1215 1.60 aidan { "STAT", STAT, OSTR, 4, "[ <sp> path-name ]" },
1216 1.47 lukem { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1217 1.47 lukem { "NOOP", NOOP, NOARGS, 2, "" },
1218 1.23 lukem
1219 1.25 lukem /* From RFC 2228, in order defined */
1220 1.47 lukem { "AUTH", AUTH, STR1, 1, "<sp> mechanism-name" },
1221 1.47 lukem { "ADAT", ADAT, STR1, 1, "<sp> base-64-data" },
1222 1.47 lukem { "PROT", PROT, STR1, 1, "<sp> prot-code" },
1223 1.47 lukem { "PBSZ", PBSZ, ARGS, 1, "<sp> decimal-integer" },
1224 1.47 lukem { "CCC", CCC, NOARGS, 1, "(Disable data protection)" },
1225 1.60 aidan { "MIC", MIC, STR1, 4, "<sp> base64data" },
1226 1.60 aidan { "CONF", CONF, STR1, 4, "<sp> base64data" },
1227 1.60 aidan { "ENC", ENC, STR1, 4, "<sp> base64data" },
1228 1.25 lukem
1229 1.25 lukem /* From RFC 2389, in order defined */
1230 1.47 lukem { "FEAT", FEAT, NOARGS, 1, "(display extended features)" },
1231 1.47 lukem { "OPTS", OPTS, STR1, 1, "<sp> command [ <sp> options ]" },
1232 1.23 lukem
1233 1.50 lukem /* from draft-ietf-ftpext-mlst-11 */
1234 1.47 lukem { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
1235 1.47 lukem { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
1236 1.47 lukem { "MLST", MLST, OSTR, 2, "[ <sp> path-name ]" },
1237 1.47 lukem { "MLSD", MLSD, OSTR, 1, "[ <sp> directory-name ]" },
1238 1.21 lukem
1239 1.21 lukem /* obsolete commands */
1240 1.47 lukem { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
1241 1.47 lukem { "MLFL", MLFL, OSTR, 0, "(mail file)" },
1242 1.47 lukem { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
1243 1.47 lukem { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
1244 1.47 lukem { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
1245 1.47 lukem { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
1246 1.47 lukem { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
1247 1.47 lukem { "XCUP", CDUP, NOARGS, 1, "(change to parent directory)" },
1248 1.47 lukem { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
1249 1.47 lukem { "XMKD", MKD, STR1, 1, "<sp> path-name" },
1250 1.47 lukem { "XPWD", PWD, NOARGS, 1, "(return current directory)" },
1251 1.47 lukem { "XRMD", RMD, STR1, 1, "<sp> path-name" },
1252 1.21 lukem
1253 1.47 lukem { NULL, 0, 0, 0, 0 }
1254 1.1 cgd };
1255 1.1 cgd
1256 1.1 cgd struct tab sitetab[] = {
1257 1.47 lukem { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
1258 1.47 lukem { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
1259 1.47 lukem { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
1260 1.47 lukem { "RATEGET", RATEGET,OSTR, 1, "[ <sp> get-throttle-rate ]" },
1261 1.47 lukem { "RATEPUT", RATEPUT,OSTR, 1, "[ <sp> put-throttle-rate ]" },
1262 1.47 lukem { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
1263 1.47 lukem { NULL, 0, 0, 0, NULL }
1264 1.1 cgd };
1265 1.1 cgd
1266 1.56 lukem static int check_write(const char *, int);
1267 1.56 lukem static void help(struct tab *, const char *);
1268 1.56 lukem static void port_check(const char *, int);
1269 1.56 lukem static void toolong(int);
1270 1.56 lukem static int yylex(void);
1271 1.4 deraadt
1272 1.32 itojun extern int epsvall;
1273 1.32 itojun
1274 1.56 lukem /*
1275 1.56 lukem * Check if a filename is allowed to be modified (isupload == 0) or
1276 1.56 lukem * uploaded (isupload == 1), and if necessary, check the filename is `sane'.
1277 1.56 lukem */
1278 1.56 lukem static int
1279 1.56 lukem check_write(const char *file, int isupload)
1280 1.56 lukem {
1281 1.56 lukem if (file == NULL)
1282 1.56 lukem return (0);
1283 1.56 lukem if (! logged_in) {
1284 1.56 lukem reply(530, "Please login with USER and PASS.");
1285 1.56 lukem return (0);
1286 1.56 lukem }
1287 1.56 lukem /* checking modify */
1288 1.56 lukem if (! isupload && ! CURCLASS_FLAGS_ISSET(modify)) {
1289 1.56 lukem reply(502, "No permission to use this command.");
1290 1.56 lukem return (0);
1291 1.56 lukem }
1292 1.56 lukem /* checking upload */
1293 1.56 lukem if (isupload && ! CURCLASS_FLAGS_ISSET(upload)) {
1294 1.56 lukem reply(502, "No permission to use this command.");
1295 1.56 lukem return (0);
1296 1.56 lukem }
1297 1.56 lukem /* checking sanenames */
1298 1.56 lukem if (CURCLASS_FLAGS_ISSET(sanenames)) {
1299 1.56 lukem const char *p;
1300 1.56 lukem
1301 1.56 lukem if (file[0] == '.')
1302 1.56 lukem goto insane_name;
1303 1.56 lukem for (p = file; *p; p++) {
1304 1.56 lukem if (isalnum(*p) || *p == '-' || *p == '+' ||
1305 1.56 lukem *p == ',' || *p == '.' || *p == '_')
1306 1.56 lukem continue;
1307 1.56 lukem insane_name:
1308 1.56 lukem reply(553, "File name `%s' not allowed.", file);
1309 1.56 lukem return (0);
1310 1.56 lukem }
1311 1.56 lukem }
1312 1.56 lukem return (1);
1313 1.56 lukem }
1314 1.56 lukem
1315 1.47 lukem struct tab *
1316 1.46 lukem lookup(struct tab *p, const char *cmd)
1317 1.1 cgd {
1318 1.1 cgd
1319 1.1 cgd for (; p->name != NULL; p++)
1320 1.23 lukem if (strcasecmp(cmd, p->name) == 0)
1321 1.1 cgd return (p);
1322 1.1 cgd return (0);
1323 1.1 cgd }
1324 1.1 cgd
1325 1.1 cgd #include <arpa/telnet.h>
1326 1.1 cgd
1327 1.1 cgd /*
1328 1.1 cgd * getline - a hacked up version of fgets to ignore TELNET escape codes.
1329 1.1 cgd */
1330 1.1 cgd char *
1331 1.46 lukem getline(char *s, int n, FILE *iop)
1332 1.1 cgd {
1333 1.4 deraadt int c;
1334 1.21 lukem char *cs;
1335 1.1 cgd
1336 1.1 cgd cs = s;
1337 1.1 cgd /* tmpline may contain saved command from urgent mode interruption */
1338 1.1 cgd for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
1339 1.1 cgd *cs++ = tmpline[c];
1340 1.1 cgd if (tmpline[c] == '\n') {
1341 1.1 cgd *cs++ = '\0';
1342 1.1 cgd if (debug)
1343 1.1 cgd syslog(LOG_DEBUG, "command: %s", s);
1344 1.1 cgd tmpline[0] = '\0';
1345 1.1 cgd return(s);
1346 1.1 cgd }
1347 1.1 cgd if (c == 0)
1348 1.1 cgd tmpline[0] = '\0';
1349 1.1 cgd }
1350 1.1 cgd while ((c = getc(iop)) != EOF) {
1351 1.27 lukem total_bytes++;
1352 1.27 lukem total_bytes_in++;
1353 1.1 cgd c &= 0377;
1354 1.1 cgd if (c == IAC) {
1355 1.1 cgd if ((c = getc(iop)) != EOF) {
1356 1.27 lukem total_bytes++;
1357 1.27 lukem total_bytes_in++;
1358 1.1 cgd c &= 0377;
1359 1.1 cgd switch (c) {
1360 1.1 cgd case WILL:
1361 1.1 cgd case WONT:
1362 1.1 cgd c = getc(iop);
1363 1.27 lukem total_bytes++;
1364 1.27 lukem total_bytes_in++;
1365 1.48 lukem cprintf(stdout, "%c%c%c", IAC, DONT, 0377&c);
1366 1.1 cgd (void) fflush(stdout);
1367 1.1 cgd continue;
1368 1.1 cgd case DO:
1369 1.1 cgd case DONT:
1370 1.1 cgd c = getc(iop);
1371 1.27 lukem total_bytes++;
1372 1.27 lukem total_bytes_in++;
1373 1.48 lukem cprintf(stdout, "%c%c%c", IAC, WONT, 0377&c);
1374 1.1 cgd (void) fflush(stdout);
1375 1.1 cgd continue;
1376 1.1 cgd case IAC:
1377 1.1 cgd break;
1378 1.1 cgd default:
1379 1.1 cgd continue; /* ignore command */
1380 1.1 cgd }
1381 1.1 cgd }
1382 1.1 cgd }
1383 1.1 cgd *cs++ = c;
1384 1.1 cgd if (--n <= 0 || c == '\n')
1385 1.1 cgd break;
1386 1.1 cgd }
1387 1.1 cgd if (c == EOF && cs == s)
1388 1.1 cgd return (NULL);
1389 1.1 cgd *cs++ = '\0';
1390 1.4 deraadt if (debug) {
1391 1.58 lukem if ((curclass.type != CLASS_GUEST &&
1392 1.58 lukem strncasecmp(s, "PASS ", 5) == 0) ||
1393 1.58 lukem strncasecmp(s, "ACCT ", 5) == 0) {
1394 1.4 deraadt /* Don't syslog passwords */
1395 1.58 lukem syslog(LOG_DEBUG, "command: %.4s ???", s);
1396 1.4 deraadt } else {
1397 1.21 lukem char *cp;
1398 1.21 lukem int len;
1399 1.4 deraadt
1400 1.4 deraadt /* Don't syslog trailing CR-LF */
1401 1.4 deraadt len = strlen(s);
1402 1.4 deraadt cp = s + len - 1;
1403 1.4 deraadt while (cp >= s && (*cp == '\n' || *cp == '\r')) {
1404 1.4 deraadt --cp;
1405 1.4 deraadt --len;
1406 1.4 deraadt }
1407 1.4 deraadt syslog(LOG_DEBUG, "command: %.*s", len, s);
1408 1.4 deraadt }
1409 1.4 deraadt }
1410 1.1 cgd return (s);
1411 1.1 cgd }
1412 1.1 cgd
1413 1.1 cgd static void
1414 1.46 lukem toolong(int signo)
1415 1.1 cgd {
1416 1.1 cgd
1417 1.1 cgd reply(421,
1418 1.12 lukem "Timeout (%d seconds): closing control connection.",
1419 1.12 lukem curclass.timeout);
1420 1.4 deraadt if (logging)
1421 1.4 deraadt syslog(LOG_INFO, "User %s timed out after %d seconds",
1422 1.41 lukem (pw ? pw->pw_name : "unknown"), curclass.timeout);
1423 1.1 cgd dologout(1);
1424 1.1 cgd }
1425 1.1 cgd
1426 1.60 aidan void
1427 1.60 aidan ftp_handle_line(char *cp)
1428 1.60 aidan {
1429 1.62 lukem
1430 1.60 aidan cmdp = cp;
1431 1.60 aidan yyparse();
1432 1.60 aidan }
1433 1.60 aidan
1434 1.60 aidan void
1435 1.60 aidan ftp_loop(void)
1436 1.60 aidan {
1437 1.62 lukem
1438 1.60 aidan while (1) {
1439 1.60 aidan (void) signal(SIGALRM, toolong);
1440 1.60 aidan (void) alarm(curclass.timeout);
1441 1.60 aidan if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
1442 1.60 aidan reply(221, "You could at least say goodbye.");
1443 1.60 aidan dologout(0);
1444 1.60 aidan }
1445 1.60 aidan (void) alarm(0);
1446 1.60 aidan ftp_handle_line(cbuf);
1447 1.60 aidan }
1448 1.60 aidan /*NOTREACHED*/
1449 1.60 aidan }
1450 1.60 aidan
1451 1.4 deraadt static int
1452 1.46 lukem yylex(void)
1453 1.1 cgd {
1454 1.1 cgd static int cpos, state;
1455 1.4 deraadt char *cp, *cp2;
1456 1.4 deraadt struct tab *p;
1457 1.22 lukem int n;
1458 1.4 deraadt char c;
1459 1.1 cgd
1460 1.23 lukem switch (state) {
1461 1.1 cgd
1462 1.23 lukem case CMD:
1463 1.23 lukem hasyyerrored = 0;
1464 1.60 aidan if ((cp = strchr(cmdp, '\r'))) {
1465 1.42 lukem *cp = '\0';
1466 1.58 lukem #if HAVE_SETPROCTITLE
1467 1.60 aidan if (strncasecmp(cmdp, "PASS", 4) != 0 &&
1468 1.60 aidan strncasecmp(cmdp, "ACCT", 4) != 0)
1469 1.60 aidan setproctitle("%s: %s", proctitle, cmdp);
1470 1.58 lukem #endif /* HAVE_SETPROCTITLE */
1471 1.23 lukem *cp++ = '\n';
1472 1.23 lukem *cp = '\0';
1473 1.23 lukem }
1474 1.60 aidan if ((cp = strpbrk(cmdp, " \n")))
1475 1.60 aidan cpos = cp - cmdp;
1476 1.23 lukem if (cpos == 0)
1477 1.23 lukem cpos = 4;
1478 1.60 aidan c = cmdp[cpos];
1479 1.60 aidan cmdp[cpos] = '\0';
1480 1.60 aidan p = lookup(cmdtab, cmdp);
1481 1.60 aidan cmdp[cpos] = c;
1482 1.23 lukem if (p != NULL) {
1483 1.60 aidan if (is_oob && ! CMD_OOB(p)) {
1484 1.60 aidan /* command will be handled in-band */
1485 1.60 aidan return (0);
1486 1.60 aidan } else if (! CMD_IMPLEMENTED(p)) {
1487 1.23 lukem reply(502, "%s command not implemented.",
1488 1.23 lukem p->name);
1489 1.23 lukem hasyyerrored = 1;
1490 1.23 lukem break;
1491 1.23 lukem }
1492 1.23 lukem state = p->state;
1493 1.23 lukem yylval.s = p->name;
1494 1.23 lukem return (p->token);
1495 1.23 lukem }
1496 1.23 lukem break;
1497 1.23 lukem
1498 1.23 lukem case SITECMD:
1499 1.60 aidan if (cmdp[cpos] == ' ') {
1500 1.23 lukem cpos++;
1501 1.23 lukem return (SP);
1502 1.23 lukem }
1503 1.60 aidan cp = &cmdp[cpos];
1504 1.23 lukem if ((cp2 = strpbrk(cp, " \n")))
1505 1.60 aidan cpos = cp2 - cmdp;
1506 1.60 aidan c = cmdp[cpos];
1507 1.60 aidan cmdp[cpos] = '\0';
1508 1.23 lukem p = lookup(sitetab, cp);
1509 1.60 aidan cmdp[cpos] = c;
1510 1.23 lukem if (p != NULL) {
1511 1.47 lukem if (!CMD_IMPLEMENTED(p)) {
1512 1.23 lukem reply(502, "SITE %s command not implemented.",
1513 1.23 lukem p->name);
1514 1.23 lukem hasyyerrored = 1;
1515 1.23 lukem break;
1516 1.23 lukem }
1517 1.23 lukem state = p->state;
1518 1.23 lukem yylval.s = p->name;
1519 1.23 lukem return (p->token);
1520 1.23 lukem }
1521 1.23 lukem break;
1522 1.23 lukem
1523 1.23 lukem case OSTR:
1524 1.60 aidan if (cmdp[cpos] == '\n') {
1525 1.60 aidan state = EOLN;
1526 1.23 lukem return (CRLF);
1527 1.23 lukem }
1528 1.23 lukem /* FALLTHROUGH */
1529 1.23 lukem
1530 1.23 lukem case STR1:
1531 1.23 lukem case ZSTR1:
1532 1.23 lukem dostr1:
1533 1.60 aidan if (cmdp[cpos] == ' ') {
1534 1.23 lukem cpos++;
1535 1.39 tron state = state == OSTR ? STR2 : state+1;
1536 1.23 lukem return (SP);
1537 1.23 lukem }
1538 1.23 lukem break;
1539 1.23 lukem
1540 1.23 lukem case ZSTR2:
1541 1.60 aidan if (cmdp[cpos] == '\n') {
1542 1.60 aidan state = EOLN;
1543 1.23 lukem return (CRLF);
1544 1.23 lukem }
1545 1.23 lukem /* FALLTHROUGH */
1546 1.23 lukem
1547 1.23 lukem case STR2:
1548 1.60 aidan cp = &cmdp[cpos];
1549 1.23 lukem n = strlen(cp);
1550 1.23 lukem cpos += n - 1;
1551 1.23 lukem /*
1552 1.23 lukem * Make sure the string is nonempty and \n terminated.
1553 1.23 lukem */
1554 1.60 aidan if (n > 1 && cmdp[cpos] == '\n') {
1555 1.60 aidan cmdp[cpos] = '\0';
1556 1.23 lukem yylval.s = xstrdup(cp);
1557 1.60 aidan cmdp[cpos] = '\n';
1558 1.23 lukem state = ARGS;
1559 1.23 lukem return (STRING);
1560 1.23 lukem }
1561 1.23 lukem break;
1562 1.23 lukem
1563 1.23 lukem case NSTR:
1564 1.60 aidan if (cmdp[cpos] == ' ') {
1565 1.23 lukem cpos++;
1566 1.23 lukem return (SP);
1567 1.23 lukem }
1568 1.60 aidan if (isdigit(cmdp[cpos])) {
1569 1.60 aidan cp = &cmdp[cpos];
1570 1.60 aidan while (isdigit(cmdp[++cpos]))
1571 1.23 lukem ;
1572 1.60 aidan c = cmdp[cpos];
1573 1.60 aidan cmdp[cpos] = '\0';
1574 1.23 lukem yylval.i = atoi(cp);
1575 1.60 aidan cmdp[cpos] = c;
1576 1.23 lukem state = STR1;
1577 1.23 lukem return (NUMBER);
1578 1.23 lukem }
1579 1.23 lukem state = STR1;
1580 1.23 lukem goto dostr1;
1581 1.1 cgd
1582 1.23 lukem case ARGS:
1583 1.60 aidan if (isdigit(cmdp[cpos])) {
1584 1.60 aidan cp = &cmdp[cpos];
1585 1.60 aidan while (isdigit(cmdp[++cpos]))
1586 1.23 lukem ;
1587 1.60 aidan c = cmdp[cpos];
1588 1.60 aidan cmdp[cpos] = '\0';
1589 1.23 lukem yylval.i = atoi(cp);
1590 1.60 aidan cmdp[cpos] = c;
1591 1.23 lukem return (NUMBER);
1592 1.32 itojun }
1593 1.60 aidan if (strncasecmp(&cmdp[cpos], "ALL", 3) == 0
1594 1.60 aidan && !isalnum(cmdp[cpos + 3])) {
1595 1.32 itojun yylval.s = xstrdup("ALL");
1596 1.32 itojun cpos += 3;
1597 1.32 itojun return ALL;
1598 1.23 lukem }
1599 1.60 aidan switch (cmdp[cpos++]) {
1600 1.23 lukem
1601 1.23 lukem case '\n':
1602 1.60 aidan state = EOLN;
1603 1.23 lukem return (CRLF);
1604 1.23 lukem
1605 1.23 lukem case ' ':
1606 1.23 lukem return (SP);
1607 1.23 lukem
1608 1.23 lukem case ',':
1609 1.23 lukem return (COMMA);
1610 1.23 lukem
1611 1.23 lukem case 'A':
1612 1.23 lukem case 'a':
1613 1.23 lukem return (A);
1614 1.23 lukem
1615 1.23 lukem case 'B':
1616 1.23 lukem case 'b':
1617 1.23 lukem return (B);
1618 1.23 lukem
1619 1.23 lukem case 'C':
1620 1.23 lukem case 'c':
1621 1.23 lukem return (C);
1622 1.23 lukem
1623 1.23 lukem case 'E':
1624 1.23 lukem case 'e':
1625 1.23 lukem return (E);
1626 1.23 lukem
1627 1.23 lukem case 'F':
1628 1.23 lukem case 'f':
1629 1.23 lukem return (F);
1630 1.23 lukem
1631 1.23 lukem case 'I':
1632 1.23 lukem case 'i':
1633 1.23 lukem return (I);
1634 1.1 cgd
1635 1.23 lukem case 'L':
1636 1.23 lukem case 'l':
1637 1.23 lukem return (L);
1638 1.1 cgd
1639 1.23 lukem case 'N':
1640 1.23 lukem case 'n':
1641 1.23 lukem return (N);
1642 1.1 cgd
1643 1.23 lukem case 'P':
1644 1.23 lukem case 'p':
1645 1.23 lukem return (P);
1646 1.1 cgd
1647 1.23 lukem case 'R':
1648 1.23 lukem case 'r':
1649 1.23 lukem return (R);
1650 1.1 cgd
1651 1.23 lukem case 'S':
1652 1.23 lukem case 's':
1653 1.23 lukem return (S);
1654 1.1 cgd
1655 1.23 lukem case 'T':
1656 1.23 lukem case 't':
1657 1.23 lukem return (T);
1658 1.1 cgd
1659 1.23 lukem }
1660 1.23 lukem break;
1661 1.21 lukem
1662 1.23 lukem case NOARGS:
1663 1.60 aidan if (cmdp[cpos] == '\n') {
1664 1.60 aidan state = EOLN;
1665 1.23 lukem return (CRLF);
1666 1.1 cgd }
1667 1.60 aidan c = cmdp[cpos];
1668 1.60 aidan cmdp[cpos] = '\0';
1669 1.60 aidan reply(501, "'%s' command does not take any arguments.", cmdp);
1670 1.23 lukem hasyyerrored = 1;
1671 1.60 aidan cmdp[cpos] = c;
1672 1.23 lukem break;
1673 1.23 lukem
1674 1.60 aidan case EOLN:
1675 1.60 aidan state = CMD;
1676 1.60 aidan return (0);
1677 1.60 aidan
1678 1.23 lukem default:
1679 1.23 lukem fatal("Unknown state in scanner.");
1680 1.1 cgd }
1681 1.23 lukem yyerror(NULL);
1682 1.23 lukem state = CMD;
1683 1.60 aidan is_oob = 0;
1684 1.23 lukem longjmp(errcatch, 0);
1685 1.23 lukem /* NOTREACHED */
1686 1.1 cgd }
1687 1.1 cgd
1688 1.22 lukem /* ARGSUSED */
1689 1.22 lukem void
1690 1.46 lukem yyerror(char *s)
1691 1.22 lukem {
1692 1.22 lukem char *cp;
1693 1.22 lukem
1694 1.60 aidan if (hasyyerrored || is_oob)
1695 1.22 lukem return;
1696 1.60 aidan if ((cp = strchr(cmdp,'\n')) != NULL)
1697 1.22 lukem *cp = '\0';
1698 1.60 aidan reply(500, "'%s': command not understood.", cmdp);
1699 1.22 lukem hasyyerrored = 1;
1700 1.22 lukem }
1701 1.22 lukem
1702 1.4 deraadt static void
1703 1.46 lukem help(struct tab *ctab, const char *s)
1704 1.1 cgd {
1705 1.4 deraadt struct tab *c;
1706 1.4 deraadt int width, NCMDS;
1707 1.1 cgd char *type;
1708 1.1 cgd
1709 1.1 cgd if (ctab == sitetab)
1710 1.1 cgd type = "SITE ";
1711 1.1 cgd else
1712 1.1 cgd type = "";
1713 1.1 cgd width = 0, NCMDS = 0;
1714 1.1 cgd for (c = ctab; c->name != NULL; c++) {
1715 1.1 cgd int len = strlen(c->name);
1716 1.1 cgd
1717 1.1 cgd if (len > width)
1718 1.1 cgd width = len;
1719 1.1 cgd NCMDS++;
1720 1.1 cgd }
1721 1.1 cgd width = (width + 8) &~ 7;
1722 1.1 cgd if (s == 0) {
1723 1.4 deraadt int i, j, w;
1724 1.1 cgd int columns, lines;
1725 1.1 cgd
1726 1.49 sommerfe reply(-214, "%s", "");
1727 1.48 lukem reply(0, "The following %scommands are recognized.", type);
1728 1.48 lukem reply(0, "(`-' = not implemented, `+' = supports options)");
1729 1.1 cgd columns = 76 / width;
1730 1.1 cgd if (columns == 0)
1731 1.1 cgd columns = 1;
1732 1.1 cgd lines = (NCMDS + columns - 1) / columns;
1733 1.1 cgd for (i = 0; i < lines; i++) {
1734 1.48 lukem cprintf(stdout, " ");
1735 1.1 cgd for (j = 0; j < columns; j++) {
1736 1.1 cgd c = ctab + j * lines + i;
1737 1.48 lukem cprintf(stdout, "%s", c->name);
1738 1.23 lukem w = strlen(c->name);
1739 1.47 lukem if (! CMD_IMPLEMENTED(c)) {
1740 1.48 lukem CPUTC('-', stdout);
1741 1.23 lukem w++;
1742 1.23 lukem }
1743 1.47 lukem if (CMD_HAS_OPTIONS(c)) {
1744 1.48 lukem CPUTC('+', stdout);
1745 1.23 lukem w++;
1746 1.23 lukem }
1747 1.1 cgd if (c + lines >= &ctab[NCMDS])
1748 1.1 cgd break;
1749 1.1 cgd while (w < width) {
1750 1.48 lukem CPUTC(' ', stdout);
1751 1.1 cgd w++;
1752 1.1 cgd }
1753 1.1 cgd }
1754 1.48 lukem cprintf(stdout, "\r\n");
1755 1.1 cgd }
1756 1.1 cgd (void) fflush(stdout);
1757 1.1 cgd reply(214, "Direct comments to ftp-bugs@%s.", hostname);
1758 1.1 cgd return;
1759 1.1 cgd }
1760 1.1 cgd c = lookup(ctab, s);
1761 1.1 cgd if (c == (struct tab *)0) {
1762 1.1 cgd reply(502, "Unknown command %s.", s);
1763 1.1 cgd return;
1764 1.1 cgd }
1765 1.47 lukem if (CMD_IMPLEMENTED(c))
1766 1.1 cgd reply(214, "Syntax: %s%s %s", type, c->name, c->help);
1767 1.1 cgd else
1768 1.23 lukem reply(214, "%s%-*s\t%s; not implemented.", type, width,
1769 1.1 cgd c->name, c->help);
1770 1.52 lukem }
1771 1.52 lukem
1772 1.52 lukem /*
1773 1.52 lukem * Check that the structures used for a PORT, LPRT or EPRT command are
1774 1.52 lukem * valid (data_dest, his_addr), and if necessary, detect ftp bounce attacks.
1775 1.52 lukem * If family != -1 check that his_addr.su_family == family.
1776 1.52 lukem */
1777 1.52 lukem static void
1778 1.52 lukem port_check(const char *cmd, int family)
1779 1.52 lukem {
1780 1.61 itojun char h1[NI_MAXHOST], h2[NI_MAXHOST];
1781 1.61 itojun char s1[NI_MAXHOST], s2[NI_MAXHOST];
1782 1.61 itojun #ifdef NI_WITHSCOPEID
1783 1.61 itojun const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
1784 1.61 itojun #else
1785 1.61 itojun const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
1786 1.61 itojun #endif
1787 1.52 lukem
1788 1.52 lukem if (epsvall) {
1789 1.52 lukem reply(501, "%s disallowed after EPSV ALL", cmd);
1790 1.52 lukem return;
1791 1.52 lukem }
1792 1.52 lukem
1793 1.52 lukem if (family != -1 && his_addr.su_family != family) {
1794 1.52 lukem port_check_fail:
1795 1.52 lukem reply(500, "Illegal %s command rejected", cmd);
1796 1.52 lukem return;
1797 1.52 lukem }
1798 1.52 lukem
1799 1.52 lukem if (data_dest.su_family != his_addr.su_family)
1800 1.52 lukem goto port_check_fail;
1801 1.52 lukem
1802 1.52 lukem /* be paranoid, if told so */
1803 1.56 lukem if (CURCLASS_FLAGS_ISSET(checkportcmd)) {
1804 1.61 itojun #ifdef INET6
1805 1.61 itojun /*
1806 1.61 itojun * be paranoid, there are getnameinfo implementation that does
1807 1.61 itojun * not present scopeid portion
1808 1.61 itojun */
1809 1.61 itojun if (data_dest.su_family == AF_INET6 &&
1810 1.61 itojun data_dest.su_scope_id != his_addr.su_scope_id)
1811 1.52 lukem goto port_check_fail;
1812 1.53 christos #endif
1813 1.61 itojun
1814 1.61 itojun if (getnameinfo((struct sockaddr *)&data_dest, data_dest.su_len,
1815 1.61 itojun h1, sizeof(h1), s1, sizeof(s1), niflags))
1816 1.61 itojun goto port_check_fail;
1817 1.61 itojun if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len,
1818 1.61 itojun h2, sizeof(h2), s2, sizeof(s2), niflags))
1819 1.61 itojun goto port_check_fail;
1820 1.61 itojun
1821 1.61 itojun if (atoi(s1) < IPPORT_RESERVED || strcmp(h1, h2) != 0)
1822 1.52 lukem goto port_check_fail;
1823 1.52 lukem }
1824 1.52 lukem
1825 1.52 lukem usedefault = 0;
1826 1.52 lukem if (pdata >= 0) {
1827 1.52 lukem (void) close(pdata);
1828 1.52 lukem pdata = -1;
1829 1.52 lukem }
1830 1.52 lukem reply(200, "%s command successful.", cmd);
1831 1.23 lukem }
1832