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