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