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