ftpcmd.y revision 29965
1210389Sgabor/* ftpcmd.y: yacc parser for the FTP daemon. 2210389Sgabor 3210389Sgabor%%% portions-copyright-cmetz-96 4211496SdesPortions of this software are Copyright 1996-1997 by Craig Metz, All Rights 5210389SgaborReserved. The Inner Net License Version 2 applies to these portions of 6210389Sgaborthe software. 7210389SgaborYou should have received a copy of the license with this software. If 8210389Sgaboryou didn't get a copy, you may request one from <license@inner.net>. 9210389Sgabor 10210389Sgabor History: 11210389Sgabor 12210389Sgabor Modified by cmetz for OPIE 2.3. Moved LS_COMMAND here. 13210389Sgabor Modified by cmetz for OPIE 2.2. Fixed a *lot* of warnings. 14210389Sgabor Use FUNCTION declaration et al. Removed useless strings. 15210389Sgabor Changed some char []s to char *s. Deleted comment address. 16210389Sgabor Changed tmpline references to be more pure-pointer 17210389Sgabor references. Changed tmpline declaration back to char []. 18210389Sgabor Modified at NRL for OPIE 2.1. Minor changes for autoconf. 19210389Sgabor Modified at NRL for OPIE 2.01. Added forward declaration for sitetab[] 20210389Sgabor -- fixes problems experienced by bison users. Merged in new 21210389Sgabor PORT attack fixes from Hobbit. 22210389Sgabor Modified at NRL for OPIE 2.0. 23210389Sgabor Originally from BSD. 24210389Sgabor*/ 25210389Sgabor/* 26210389Sgabor * Copyright (c) 1985, 1988 Regents of the University of California. 27210389Sgabor * All rights reserved. 28210389Sgabor * 29210389Sgabor * Redistribution and use in source and binary forms, with or without 30210389Sgabor * modification, are permitted provided that the following conditions 31210389Sgabor * are met: 32210389Sgabor * 1. Redistributions of source code must retain the above copyright 33210389Sgabor * notice, this list of conditions and the following disclaimer. 34210389Sgabor * 2. Redistributions in binary form must reproduce the above copyright 35210389Sgabor * notice, this list of conditions and the following disclaimer in the 36210389Sgabor * documentation and/or other materials provided with the distribution. 37210389Sgabor * 3. All advertising materials mentioning features or use of this software 38210389Sgabor * must display the following acknowledgement: 39210389Sgabor * This product includes software developed by the University of 40210389Sgabor * California, Berkeley and its contributors. 41210389Sgabor * 4. Neither the name of the University nor the names of its contributors 42210578Sgabor * may be used to endorse or promote products derived from this software 43210389Sgabor * without specific prior written permission. 44210389Sgabor * 45210389Sgabor * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 46210389Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 47210389Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 48210389Sgabor * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 49210389Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 50210389Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 51210389Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 52210389Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 53210389Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 54210389Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 55210578Sgabor * SUCH DAMAGE. 56210578Sgabor * 57210578Sgabor * @(#)ftpcmd.y 5.24 (Berkeley) 2/25/91 58220421Sgabor */ 59210578Sgabor 60210578Sgabor/* 61210578Sgabor * Grammar for FTP commands. 62220421Sgabor * See RFC 959. 63210578Sgabor */ 64210578Sgabor 65220421Sgabor%{ 66220421Sgabor#include "opie_cfg.h" 67210578Sgabor 68210578Sgabor#include <sys/param.h> 69210578Sgabor#include <sys/types.h> 70210578Sgabor#include <sys/socket.h> 71210578Sgabor#include <sys/stat.h> 72210578Sgabor#include <netinet/in.h> 73210578Sgabor#include <arpa/ftp.h> 74210578Sgabor#include <signal.h> 75210578Sgabor#include <setjmp.h> 76211364Sgabor#include <syslog.h> 77210578Sgabor#if TM_IN_SYS_TIME 78210578Sgabor#include <sys/time.h> 79210578Sgabor#else /* TM_IN_SYS_TIME */ 80210578Sgabor#include <time.h> 81210578Sgabor#endif /* TM_IN_SYS_TIME */ 82210578Sgabor#include <pwd.h> 83210578Sgabor#include <unistd.h> 84210578Sgabor#include <stdio.h> 85210578Sgabor#include <ctype.h> 86210578Sgabor#include <stdlib.h> 87210578Sgabor#include <string.h> 88210578Sgabor 89210578Sgabor#include "opie.h" 90210578Sgabor 91210578Sgabor#if HAVE_LS_G_FLAG 92210578Sgabor#define LS_COMMAND "/bin/ls -lgA" 93210578Sgabor#else /* HAVE_LS_G_FLAG */ 94210578Sgabor#define LS_COMMAND "/bin/ls -lA" 95210389Sgabor#endif /* HAVE_LS_G_FLAG */ 96210389Sgabor 97210389Sgaborextern struct sockaddr_in data_dest; 98210389Sgaborextern struct sockaddr_in his_addr; 99210389Sgaborextern int logged_in; 100210389Sgaborextern struct passwd *pw; 101210389Sgaborextern int guest; 102210389Sgaborextern int type; 103210389Sgaborextern int form; 104210430Sdelphijextern int debug; 105210389Sgaborextern int timeout; 106210389Sgaborextern int maxtimeout; 107210389Sgaborextern int pdata; 108210389Sgaborextern char *remotehost; 109210389Sgaborextern char *proctitle; 110210389Sgaborextern char *globerr; 111210389Sgaborextern int usedefault; 112210389Sgaborextern int transflag; 113210389Sgaborextern char tmpline[]; 114210389Sgaborchar **ftpglob(); 115210389Sgabor 116210389SgaborVOIDRET dologout __P((int)); 117210389SgaborVOIDRET upper __P((char *)); 118210389SgaborVOIDRET nack __P((char *)); 119210389SgaborVOIDRET opiefatal __P((char *)); 120210389Sgabor 121210389SgaborVOIDRET pass __P((char *)); 122210389Sgaborint user __P((char *)); 123210389SgaborVOIDRET passive __P((void)); 124210389SgaborVOIDRET retrieve __P((char *, char *)); 125210430SdelphijVOIDRET store __P((char *, char *, int)); 126210389SgaborVOIDRET send_file_list __P((char *)); 127210389SgaborVOIDRET statfilecmd __P((char *)); 128210389SgaborVOIDRET statcmd __P((void)); 129210389SgaborVOIDRET delete __P((char *)); 130210389SgaborVOIDRET renamecmd __P((char *, char *)); 131210389SgaborVOIDRET cwd __P((char *)); 132210389SgaborVOIDRET makedir __P((char *)); 133210389SgaborVOIDRET removedir __P((char *)); 134210389SgaborVOIDRET pwd __P((void)); 135210389Sgabor 136210389SgaborVOIDRET sizecmd __P((char *)); 137210389Sgabor 138210389Sgaboroff_t restart_point; 139210389Sgabor 140210389Sgaborstatic int cmd_type; 141210389Sgaborstatic int cmd_form; 142210389Sgaborstatic int cmd_bytesz; 143210389Sgaborstatic unsigned short cliport = 0; 144210389Sgaborchar cbuf[512]; 145210578Sgaborchar *fromname; 146210430Sdelphij 147210430Sdelphijstruct tab { 148210430Sdelphij char *name; 149211364Sgabor short token; 150211364Sgabor short state; 151211364Sgabor short implemented; /* 1 if command is implemented */ 152210430Sdelphij char *help; 153210578Sgabor}; 154210430Sdelphij 155210430SdelphijVOIDRET help __P((struct tab *, char *)); 156210389Sgabor 157210578Sgaborstruct tab cmdtab[], sitetab[]; 158210578Sgabor 159210389Sgabor%} 160210389Sgabor 161210389Sgabor%token 162210389Sgabor A B C E F I 163210389Sgabor L N P R S T 164210389Sgabor 165210389Sgabor SP CRLF COMMA STRING NUMBER 166210430Sdelphij 167210389Sgabor USER PASS ACCT REIN QUIT PORT 168210389Sgabor PASV TYPE STRU MODE RETR STOR 169210389Sgabor APPE MLFL MAIL MSND MSOM MSAM 170210389Sgabor MRSQ MRCP ALLO REST RNFR RNTO 171210389Sgabor ABOR DELE CWD LIST NLST SITE 172210389Sgabor STAT HELP NOOP MKD RMD PWD 173210389Sgabor CDUP STOU SMNT SYST SIZE MDTM 174210389Sgabor 175210389Sgabor UMASK IDLE CHMOD 176210389Sgabor 177210389Sgabor LEXERR 178210389Sgabor 179210389Sgabor%start cmd_list 180210389Sgabor 181210389Sgabor%% 182210389Sgabor 183210389Sgaborcmd_list: /* empty */ 184210389Sgabor | cmd_list cmd 185210389Sgabor = { 186210389Sgabor fromname = (char *) 0; 187210389Sgabor restart_point = (off_t) 0; 188211463Sgabor } 189210389Sgabor | cmd_list rcmd 190210389Sgabor ; 191210389Sgabor 192210389Sgaborcmd: USER SP username CRLF 193210389Sgabor = { 194210389Sgabor user((char *) $3); 195210389Sgabor free((char *) $3); 196210389Sgabor } 197210389Sgabor | PASS SP password CRLF 198210389Sgabor = { 199210389Sgabor pass((char *) $3); 200210389Sgabor free((char *) $3); 201210389Sgabor } 202210389Sgabor | PORT check_login SP host_port CRLF 203210389Sgabor = { 204210389Sgabor usedefault = 0; 205210389Sgabor if (pdata >= 0) { 206210389Sgabor (void) close(pdata); 207210389Sgabor pdata = -1; 208210389Sgabor } 209210389Sgabor/* H* port fix, part B: admonish the twit. 210210389Sgabor Also require login before PORT works */ 211210389Sgabor if ($2) { 212210389Sgabor if ((cliport > 1023) && (data_dest.sin_addr.s_addr > 0)) { 213210389Sgabor reply(200, "PORT command successful."); 214210389Sgabor } else { 215210389Sgabor syslog (LOG_WARNING, "refused %s from %s", 216210389Sgabor cbuf, remotehost); 217210389Sgabor reply(500, "You've GOT to be joking."); 218210389Sgabor } 219211463Sgabor } 220210389Sgabor } 221210389Sgabor/* | PASV CRLF 222210389Sgabor = { 223210389Sgabor passive(); 224210389Sgabor } */ 225210389Sgabor | PASV check_login CRLF 226210389Sgabor = { 227210389Sgabor/* Require login for PASV, too. This actually fixes a bug -- telnet to an 228210389Sgabor unfixed wu-ftpd and type PASV first off, and it crashes! */ 229210389Sgabor if ($2) { 230210389Sgabor passive(); 231210389Sgabor } 232210430Sdelphij } 233210389Sgabor | TYPE SP type_code CRLF 234210389Sgabor = { 235210389Sgabor switch (cmd_type) { 236210389Sgabor 237210389Sgabor case TYPE_A: 238210389Sgabor if (cmd_form == FORM_N) { 239210389Sgabor reply(200, "Type set to A."); 240210389Sgabor type = cmd_type; 241210389Sgabor form = cmd_form; 242210389Sgabor } else 243210389Sgabor reply(504, "Form must be N."); 244210389Sgabor break; 245210389Sgabor 246210389Sgabor case TYPE_E: 247210389Sgabor reply(504, "Type E not implemented."); 248210389Sgabor break; 249210389Sgabor 250210389Sgabor case TYPE_I: 251210389Sgabor reply(200, "Type set to I."); 252210389Sgabor type = cmd_type; 253210389Sgabor break; 254210389Sgabor 255210389Sgabor case TYPE_L: 256210389Sgabor#if NBBY == 8 257210389Sgabor if (cmd_bytesz == 8) { 258210389Sgabor reply(200, 259210461Sgabor "Type set to L (byte size 8)."); 260210389Sgabor type = cmd_type; 261210461Sgabor } else 262210389Sgabor reply(504, "Byte size must be 8."); 263210389Sgabor#else /* NBBY == 8 */ 264210389Sgabor UNIMPLEMENTED for NBBY != 8 265210622Sgabor#endif /* NBBY == 8 */ 266210389Sgabor } 267210430Sdelphij } 268210389Sgabor | STRU SP struct_code CRLF 269210389Sgabor = { 270210389Sgabor switch ($3) { 271210389Sgabor 272210389Sgabor case STRU_F: 273210389Sgabor reply(200, "STRU F ok."); 274210389Sgabor break; 275210389Sgabor 276210389Sgabor default: 277210389Sgabor reply(504, "Unimplemented STRU type."); 278210389Sgabor } 279210389Sgabor } 280210389Sgabor | MODE SP mode_code CRLF 281220421Sgabor = { 282210389Sgabor switch ($3) { 283210389Sgabor 284210389Sgabor case MODE_S: 285210389Sgabor reply(200, "MODE S ok."); 286210389Sgabor break; 287210389Sgabor 288210389Sgabor default: 289210389Sgabor reply(502, "Unimplemented MODE type."); 290210389Sgabor } 291210389Sgabor } 292210389Sgabor | ALLO SP NUMBER CRLF 293210389Sgabor = { 294210389Sgabor reply(202, "ALLO command ignored."); 295210389Sgabor } 296210389Sgabor | ALLO SP NUMBER SP R SP NUMBER CRLF 297210389Sgabor = { 298210389Sgabor reply(202, "ALLO command ignored."); 299210389Sgabor } 300210389Sgabor | RETR check_login SP pathname CRLF 301210389Sgabor = { 302210389Sgabor if ($2 && $4) 303210389Sgabor retrieve((char *) 0, (char *) $4); 304210389Sgabor if ($4) 305210389Sgabor free((char *) $4); 306210389Sgabor } 307210389Sgabor | STOR check_login SP pathname CRLF 308210389Sgabor = { 309210389Sgabor if ($2 && $4) 310210389Sgabor store((char *) $4, "w", 0); 311210389Sgabor if ($4) 312210389Sgabor free((char *) $4); 313210389Sgabor } 314210389Sgabor | APPE check_login SP pathname CRLF 315210389Sgabor = { 316210389Sgabor if ($2 && $4) 317210389Sgabor store((char *) $4, "a", 0); 318210389Sgabor if ($4) 319210389Sgabor free((char *) $4); 320210389Sgabor } 321210389Sgabor | NLST check_login CRLF 322220421Sgabor = { 323220421Sgabor if ($2) 324211364Sgabor send_file_list("."); 325210389Sgabor } 326211364Sgabor | NLST check_login SP STRING CRLF 327211364Sgabor = { 328211364Sgabor if ($2 && $4) 329211364Sgabor send_file_list((char *) $4); 330210389Sgabor if ($4) 331211364Sgabor free((char *) $4); 332211364Sgabor } 333211364Sgabor | LIST check_login CRLF 334211364Sgabor = { 335211364Sgabor if ($2) 336211364Sgabor retrieve(LS_COMMAND, ""); 337210389Sgabor } 338210389Sgabor | LIST check_login SP pathname CRLF 339210389Sgabor = { 340210389Sgabor if ($2 && $4) 341210389Sgabor { 342210389Sgabor char buffer[sizeof(LS_COMMAND)+3]; 343210389Sgabor strcpy(buffer, LS_COMMAND); 344210461Sgabor strcat(buffer, " %s"); 345210461Sgabor retrieve(buffer, (char *) $4); 346210389Sgabor } 347210389Sgabor if ($4) 348210389Sgabor free((char *) $4); 349210389Sgabor } 350210389Sgabor | STAT check_login SP pathname CRLF 351210389Sgabor = { 352210389Sgabor if ($2 && $4) 353210389Sgabor statfilecmd((char *) $4); 354210461Sgabor if ($4) 355210389Sgabor free((char *) $4); 356210389Sgabor } 357210389Sgabor | STAT CRLF 358210389Sgabor = { 359210389Sgabor statcmd(); 360210389Sgabor } 361210389Sgabor | DELE check_login SP pathname CRLF 362210389Sgabor = { 363210389Sgabor if ($2 && $4) 364210389Sgabor delete((char *) $4); 365210389Sgabor if ($4) 366210389Sgabor free((char *) $4); 367210479Sgabor } 368210389Sgabor | RNTO SP pathname CRLF 369210389Sgabor = { 370210389Sgabor if (fromname) { 371210389Sgabor renamecmd(fromname, (char *) $3); 372210389Sgabor free(fromname); 373210389Sgabor fromname = (char *) 0; 374210389Sgabor } else { 375210389Sgabor reply(503, "Bad sequence of commands."); 376210389Sgabor } 377210389Sgabor free((char *) $3); 378210389Sgabor } 379210389Sgabor | ABOR CRLF 380210389Sgabor = { 381210389Sgabor reply(225, "ABOR command successful."); 382210389Sgabor } 383210389Sgabor | CWD check_login CRLF 384210389Sgabor = { 385210389Sgabor if ($2) 386210389Sgabor cwd(pw->pw_dir); 387210389Sgabor } 388210389Sgabor | CWD check_login SP pathname CRLF 389210389Sgabor = { 390210389Sgabor if ($2 && $4) 391210389Sgabor cwd((char *) $4); 392210389Sgabor if ($4) 393210389Sgabor free((char *) $4); 394210389Sgabor } 395210389Sgabor | HELP CRLF 396210389Sgabor = { 397210389Sgabor help(cmdtab, (char *) 0); 398210389Sgabor } 399210389Sgabor | HELP SP STRING CRLF 400210389Sgabor = { 401210389Sgabor register char *cp = (char *)$3; 402210389Sgabor 403210389Sgabor if (strncasecmp(cp, "SITE", 4) == 0) { 404210389Sgabor cp = (char *)$3 + 4; 405210389Sgabor if (*cp == ' ') 406210389Sgabor cp++; 407210389Sgabor if (*cp) 408210389Sgabor help(sitetab, cp); 409210389Sgabor else 410210389Sgabor help(sitetab, (char *) 0); 411210389Sgabor } else 412210389Sgabor help(cmdtab, (char *) $3); 413210389Sgabor } 414210389Sgabor | NOOP CRLF 415210389Sgabor = { 416210389Sgabor reply(200, "NOOP command successful."); 417210389Sgabor } 418210389Sgabor | MKD check_login SP pathname CRLF 419210389Sgabor = { 420210389Sgabor if ($2 && $4) 421210389Sgabor makedir((char *) $4); 422210389Sgabor if ($4) 423210389Sgabor free((char *) $4); 424210389Sgabor } 425210389Sgabor | RMD check_login SP pathname CRLF 426210389Sgabor = { 427210389Sgabor if ($2 && $4) 428210389Sgabor removedir((char *) $4); 429210389Sgabor if ($4) 430210389Sgabor free((char *) $4); 431210389Sgabor } 432210389Sgabor | PWD check_login CRLF 433210578Sgabor = { 434210578Sgabor if ($2) 435210578Sgabor pwd(); 436210578Sgabor } 437210578Sgabor | CDUP check_login CRLF 438210578Sgabor = { 439210578Sgabor if ($2) 440210578Sgabor cwd(".."); 441210578Sgabor } 442210578Sgabor | SITE SP HELP CRLF 443210578Sgabor = { 444210578Sgabor help(sitetab, (char *) 0); 445210578Sgabor } 446210389Sgabor | SITE SP HELP SP STRING CRLF 447210389Sgabor = { 448210389Sgabor help(sitetab, (char *) $5); 449210389Sgabor } 450210389Sgabor | SITE SP UMASK check_login CRLF 451210389Sgabor = { 452210389Sgabor int oldmask; 453210389Sgabor 454210389Sgabor if ($4) { 455210389Sgabor oldmask = umask(0); 456210389Sgabor (void) umask(oldmask); 457210389Sgabor reply(200, "Current UMASK is %03o", oldmask); 458210389Sgabor } 459210389Sgabor } 460210389Sgabor | SITE SP UMASK check_login SP octal_number CRLF 461210389Sgabor = { 462210389Sgabor int oldmask; 463210389Sgabor 464210389Sgabor if ($4) { 465210389Sgabor if (($6 == -1) || ($6 > 0777)) { 466210389Sgabor reply(501, "Bad UMASK value"); 467210389Sgabor } else { 468210389Sgabor oldmask = umask($6); 469210389Sgabor reply(200, 470210389Sgabor "UMASK set to %03o (was %03o)", 471210389Sgabor $6, oldmask); 472210389Sgabor } 473210389Sgabor } 474210389Sgabor } 475210389Sgabor | SITE SP CHMOD check_login SP octal_number SP pathname CRLF 476210389Sgabor = { 477210389Sgabor if ($4 && $8) { 478210389Sgabor if ($6 > 0777) 479210389Sgabor reply(501, 480210389Sgabor "CHMOD: Mode value must be between 0 and 0777"); 481210389Sgabor else if (chmod((char *) $8, $6) < 0) 482210389Sgabor perror_reply(550, (char *) $8); 483210389Sgabor else 484210389Sgabor reply(200, "CHMOD command successful."); 485210389Sgabor } 486210389Sgabor if ($8) 487210389Sgabor free((char *) $8); 488210389Sgabor } 489210389Sgabor | SITE SP IDLE CRLF 490210389Sgabor = { 491210389Sgabor reply(200, 492210389Sgabor "Current IDLE time limit is %d seconds; max %d", 493210389Sgabor timeout, maxtimeout); 494210389Sgabor } 495210389Sgabor | SITE SP IDLE SP NUMBER CRLF 496210389Sgabor = { 497210389Sgabor if ($5 < 30 || $5 > maxtimeout) { 498210389Sgabor reply(501, 499210389Sgabor "Maximum IDLE time must be between 30 and %d seconds", 500210389Sgabor maxtimeout); 501210389Sgabor } else { 502210389Sgabor timeout = $5; 503210389Sgabor (void) alarm((unsigned) timeout); 504210389Sgabor reply(200, 505 "Maximum IDLE time set to %d seconds", 506 timeout); 507 } 508 } 509 | STOU check_login SP pathname CRLF 510 = { 511 if ($2 && $4) 512 store((char *) $4, "w", 1); 513 if ($4) 514 free((char *) $4); 515 } 516 | SYST CRLF 517 = { 518#ifdef unix 519#ifdef BSD 520 reply(215, "UNIX Type: L%d Version: BSD-%d", 521 NBBY, BSD); 522#else /* BSD */ 523 reply(215, "UNIX Type: L%d", NBBY); 524#endif /* BSD */ 525#else /* unix */ 526 reply(215, "UNKNOWN Type: L%d", NBBY); 527#endif /* unix */ 528 } 529 530 /* 531 * SIZE is not in RFC959, but Postel has blessed it and 532 * it will be in the updated RFC. 533 * 534 * Return size of file in a format suitable for 535 * using with RESTART (we just count bytes). 536 */ 537 | SIZE check_login SP pathname CRLF 538 = { 539 if ($2 && $4) 540 sizecmd((char *) $4); 541 if ($4) 542 free((char *) $4); 543 } 544 545 /* 546 * MDTM is not in RFC959, but Postel has blessed it and 547 * it will be in the updated RFC. 548 * 549 * Return modification time of file as an ISO 3307 550 * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 551 * where xxx is the fractional second (of any precision, 552 * not necessarily 3 digits) 553 */ 554 | MDTM check_login SP pathname CRLF 555 = { 556 if ($2 && $4) { 557 struct stat stbuf; 558 if (stat((char *) $4, &stbuf) < 0) 559 perror_reply(550, (char *) $4); 560 else if ((stbuf.st_mode&S_IFMT) != S_IFREG) { 561 reply(550, "%s: not a plain file.", 562 (char *) $4); 563 } else { 564 register struct tm *t; 565 struct tm *gmtime(); 566 t = gmtime(&stbuf.st_mtime); 567 reply(213, 568 "19%02d%02d%02d%02d%02d%02d", 569 t->tm_year, t->tm_mon+1, t->tm_mday, 570 t->tm_hour, t->tm_min, t->tm_sec); 571 } 572 } 573 if ($4) 574 free((char *) $4); 575 } 576 | QUIT CRLF 577 = { 578 reply(221, "Goodbye."); 579 dologout(0); 580 } 581 | error CRLF 582 = { 583 yyerrok; 584 } 585 ; 586rcmd: RNFR check_login SP pathname CRLF 587 = { 588 char *renamefrom(); 589 590 restart_point = (off_t) 0; 591 if ($2 && $4) { 592 fromname = renamefrom((char *) $4); 593 if (fromname == (char *) 0 && $4) { 594 free((char *) $4); 595 } 596 } 597 } 598 | REST SP byte_size CRLF 599 = { 600 long atol(); 601 602 fromname = (char *) 0; 603 restart_point = $3; 604 reply(350, "Restarting at %ld. %s", restart_point, 605 "Send STORE or RETRIEVE to initiate transfer."); 606 } 607 ; 608 609username: STRING 610 ; 611 612password: /* empty */ 613 = { 614 *(char **)&($$) = (char *)calloc(1, sizeof(char)); 615 } 616 | STRING 617 ; 618 619byte_size: NUMBER 620 ; 621 622host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 623 NUMBER COMMA NUMBER 624 = { 625 register char *a, *p; 626 627 a = (char *)&data_dest.sin_addr; 628 a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7; 629 630/* H* port fix, part A-1: Check the args against the client addr */ 631 p = (char *)&his_addr.sin_addr; 632 if (memcmp (a, p, sizeof (data_dest.sin_addr))) 633 memset (a, 0, sizeof (data_dest.sin_addr)); /* XXX */ 634 635 p = (char *)&data_dest.sin_port; 636 637/* H* port fix, part A-2: only allow client ports in "user space" */ 638 p[0] = 0; p[1] = 0; 639 cliport = ($9 << 8) + $11; 640 if (cliport > 1023) { 641 p[0] = $9; p[1] = $11; 642 } 643 644 p[0] = $9; p[1] = $11; 645 data_dest.sin_family = AF_INET; 646 } 647 ; 648 649form_code: N 650 = { 651 $$ = FORM_N; 652 } 653 | T 654 = { 655 $$ = FORM_T; 656 } 657 | C 658 = { 659 $$ = FORM_C; 660 } 661 ; 662 663type_code: A 664 = { 665 cmd_type = TYPE_A; 666 cmd_form = FORM_N; 667 } 668 | A SP form_code 669 = { 670 cmd_type = TYPE_A; 671 cmd_form = $3; 672 } 673 | E 674 = { 675 cmd_type = TYPE_E; 676 cmd_form = FORM_N; 677 } 678 | E SP form_code 679 = { 680 cmd_type = TYPE_E; 681 cmd_form = $3; 682 } 683 | I 684 = { 685 cmd_type = TYPE_I; 686 } 687 | L 688 = { 689 cmd_type = TYPE_L; 690 cmd_bytesz = NBBY; 691 } 692 | L SP byte_size 693 = { 694 cmd_type = TYPE_L; 695 cmd_bytesz = $3; 696 } 697 /* this is for a bug in the BBN ftp */ 698 | L byte_size 699 = { 700 cmd_type = TYPE_L; 701 cmd_bytesz = $2; 702 } 703 ; 704 705struct_code: F 706 = { 707 $$ = STRU_F; 708 } 709 | R 710 = { 711 $$ = STRU_R; 712 } 713 | P 714 = { 715 $$ = STRU_P; 716 } 717 ; 718 719mode_code: S 720 = { 721 $$ = MODE_S; 722 } 723 | B 724 = { 725 $$ = MODE_B; 726 } 727 | C 728 = { 729 $$ = MODE_C; 730 } 731 ; 732 733pathname: pathstring 734 = { 735 /* 736 * Problem: this production is used for all pathname 737 * processing, but only gives a 550 error reply. 738 * This is a valid reply in some cases but not in others. 739 */ 740 if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) { 741 *(char **)&($$) = *ftpglob((char *) $1); 742 if (globerr != NULL) { 743 reply(550, globerr); 744/* $$ = NULL; */ 745 $$ = 0; 746 } 747 free((char *) $1); 748 } else 749 $$ = $1; 750 } 751 ; 752 753pathstring: STRING 754 ; 755 756octal_number: NUMBER 757 = { 758 register int ret, dec, multby, digit; 759 760 /* 761 * Convert a number that was read as decimal number 762 * to what it would be if it had been read as octal. 763 */ 764 dec = $1; 765 multby = 1; 766 ret = 0; 767 while (dec) { 768 digit = dec%10; 769 if (digit > 7) { 770 ret = -1; 771 break; 772 } 773 ret += digit * multby; 774 multby *= 8; 775 dec /= 10; 776 } 777 $$ = ret; 778 } 779 ; 780 781check_login: /* empty */ 782 = { 783 if (logged_in) 784 $$ = 1; 785 else { 786 reply(530, "Please login with USER and PASS."); 787 $$ = 0; 788 } 789 } 790 ; 791 792%% 793 794extern jmp_buf errcatch; 795 796#define CMD 0 /* beginning of command */ 797#define ARGS 1 /* expect miscellaneous arguments */ 798#define STR1 2 /* expect SP followed by STRING */ 799#define STR2 3 /* expect STRING */ 800#define OSTR 4 /* optional SP then STRING */ 801#define ZSTR1 5 /* SP then optional STRING */ 802#define ZSTR2 6 /* optional STRING after SP */ 803#define SITECMD 7 /* SITE command */ 804#define NSTR 8 /* Number followed by a string */ 805 806struct tab cmdtab[] = { /* In order defined in RFC 765 */ 807 { "USER", USER, STR1, 1, "<sp> username" }, 808 { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 809 { "ACCT", ACCT, STR1, 0, "(specify account)" }, 810 { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 811 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 812 { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 813 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" }, 814 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 815 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 816 { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 817 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 818 { "RETR", RETR, STR1, 1, "<sp> file-name" }, 819 { "STOR", STOR, STR1, 1, "<sp> file-name" }, 820 { "APPE", APPE, STR1, 1, "<sp> file-name" }, 821 { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 822 { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 823 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 824 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 825 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 826 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 827 { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 828 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 829 { "REST", REST, ARGS, 1, "(restart command)" }, 830 { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 831 { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 832 { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 833 { "DELE", DELE, STR1, 1, "<sp> file-name" }, 834 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 835 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 836 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 837 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 838 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 839 { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 840 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 841 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 842 { "NOOP", NOOP, ARGS, 1, "" }, 843 { "MKD", MKD, STR1, 1, "<sp> path-name" }, 844 { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 845 { "RMD", RMD, STR1, 1, "<sp> path-name" }, 846 { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 847 { "PWD", PWD, ARGS, 1, "(return current directory)" }, 848 { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 849 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 850 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 851 { "STOU", STOU, STR1, 1, "<sp> file-name" }, 852 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 853 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 854 { NULL, 0, 0, 0, 0 } 855}; 856 857struct tab sitetab[] = { 858 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 859 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 860 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 861 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 862 { NULL, 0, 0, 0, 0 } 863}; 864 865struct tab *lookup FUNCTION((p, cmd), register struct tab *p AND char *cmd) 866{ 867 868 for (; p->name != NULL; p++) 869 if (strcmp(cmd, p->name) == 0) 870 return (p); 871 return (0); 872} 873 874#include <arpa/telnet.h> 875 876/* 877 * getline - a hacked up version of fgets to ignore TELNET escape codes. 878 */ 879char *getline FUNCTION((s, n, iop), char *s AND int n AND FILE *iop) 880{ 881 register c; 882 register char *cs; 883 884 cs = s; 885/* tmpline may contain saved command from urgent mode interruption */ 886 for (c = 0; *(tmpline + c) && --n > 0; ++c) { 887 *cs++ = *(tmpline + c); 888 if (*(tmpline + c) == '\n') { 889 *cs++ = '\0'; 890 if (debug) 891 syslog(LOG_DEBUG, "command: %s", s); 892 *tmpline = '\0'; 893 return(s); 894 } 895 if (c == 0) 896 *tmpline = '\0'; 897 } 898 while ((c = getc(iop)) != EOF) { 899 c &= 0377; 900 if (c == IAC) { 901 if ((c = getc(iop)) != EOF) { 902 c &= 0377; 903 switch (c) { 904 case WILL: 905 case WONT: 906 c = getc(iop); 907 printf("%c%c%c", IAC, DONT, 0377&c); 908 (void) fflush(stdout); 909 continue; 910 case DO: 911 case DONT: 912 c = getc(iop); 913 printf("%c%c%c", IAC, WONT, 0377&c); 914 (void) fflush(stdout); 915 continue; 916 case IAC: 917 break; 918 default: 919 continue; /* ignore command */ 920 } 921 } 922 } 923 *cs++ = c; 924 if (--n <= 0 || c == '\n') 925 break; 926 } 927 if (c == EOF && cs == s) 928 return (NULL); 929 *cs++ = '\0'; 930 if (debug) 931 syslog(LOG_DEBUG, "command: %s", s); 932 return (s); 933} 934 935static VOIDRET toolong FUNCTION((input), int input) 936{ 937 time_t now; 938 939 reply(421, "Timeout (%d seconds): closing control connection.", timeout); 940 (void) time(&now); 941 syslog(LOG_INFO, "User %s timed out after %d seconds at %s", 942 (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now)); 943 dologout(1); 944} 945 946int yylex FUNCTION_NOARGS 947{ 948 static int cpos, state; 949 register char *cp, *cp2; 950 register struct tab *p; 951 int n; 952 char c, *copy(); 953 954 for (;;) { 955 switch (state) { 956 957 case CMD: 958 (void) signal(SIGALRM, toolong); 959 (void) alarm((unsigned) timeout); 960 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 961 reply(221, "You could at least say goodbye."); 962 dologout(0); 963 } 964 (void) alarm(0); 965#ifdef SETPROCTITLE 966 if (strncasecmp(cbuf, "PASS", 4) != NULL) 967 setproctitle("%s: %s", proctitle, cbuf); 968#endif /* SETPROCTITLE */ 969 if ((cp = strchr(cbuf, '\r'))) { 970 *cp++ = '\n'; 971 *cp = '\0'; 972 } 973 if ((cp = strpbrk(cbuf, " \n"))) 974 cpos = cp - cbuf; 975 if (cpos == 0) 976 cpos = 4; 977 c = cbuf[cpos]; 978 cbuf[cpos] = '\0'; 979 upper(cbuf); 980 p = lookup(cmdtab, cbuf); 981 cbuf[cpos] = c; 982 if (p != 0) { 983 if (p->implemented == 0) { 984 nack(p->name); 985 longjmp(errcatch,0); 986 /* NOTREACHED */ 987 } 988 state = p->state; 989 *(char **)&yylval = p->name; 990 return (p->token); 991 } 992 break; 993 994 case SITECMD: 995 if (cbuf[cpos] == ' ') { 996 cpos++; 997 return (SP); 998 } 999 cp = &cbuf[cpos]; 1000 if ((cp2 = strpbrk(cp, " \n"))) 1001 cpos = cp2 - cbuf; 1002 c = cbuf[cpos]; 1003 cbuf[cpos] = '\0'; 1004 upper(cp); 1005 p = lookup(sitetab, cp); 1006 cbuf[cpos] = c; 1007 if (p != 0) { 1008 if (p->implemented == 0) { 1009 state = CMD; 1010 nack(p->name); 1011 longjmp(errcatch,0); 1012 /* NOTREACHED */ 1013 } 1014 state = p->state; 1015 *(char **)&yylval = p->name; 1016 return (p->token); 1017 } 1018 state = CMD; 1019 break; 1020 1021 case OSTR: 1022 if (cbuf[cpos] == '\n') { 1023 state = CMD; 1024 return (CRLF); 1025 } 1026 /* FALLTHROUGH */ 1027 1028 case STR1: 1029 case ZSTR1: 1030 dostr1: 1031 if (cbuf[cpos] == ' ') { 1032 cpos++; 1033 state = state == OSTR ? STR2 : ++state; 1034 return (SP); 1035 } 1036 break; 1037 1038 case ZSTR2: 1039 if (cbuf[cpos] == '\n') { 1040 state = CMD; 1041 return (CRLF); 1042 } 1043 /* FALLTHROUGH */ 1044 1045 case STR2: 1046 cp = &cbuf[cpos]; 1047 n = strlen(cp); 1048 cpos += n - 1; 1049 /* 1050 * Make sure the string is nonempty and \n terminated. 1051 */ 1052 if (n > 1 && cbuf[cpos] == '\n') { 1053 cbuf[cpos] = '\0'; 1054 *(char **)&yylval = copy(cp); 1055 cbuf[cpos] = '\n'; 1056 state = ARGS; 1057 return (STRING); 1058 } 1059 break; 1060 1061 case NSTR: 1062 if (cbuf[cpos] == ' ') { 1063 cpos++; 1064 return (SP); 1065 } 1066 if (isdigit(cbuf[cpos])) { 1067 cp = &cbuf[cpos]; 1068 while (isdigit(cbuf[++cpos])) 1069 ; 1070 c = cbuf[cpos]; 1071 cbuf[cpos] = '\0'; 1072 yylval = atoi(cp); 1073 cbuf[cpos] = c; 1074 state = STR1; 1075 return (NUMBER); 1076 } 1077 state = STR1; 1078 goto dostr1; 1079 1080 case ARGS: 1081 if (isdigit(cbuf[cpos])) { 1082 cp = &cbuf[cpos]; 1083 while (isdigit(cbuf[++cpos])) 1084 ; 1085 c = cbuf[cpos]; 1086 cbuf[cpos] = '\0'; 1087 yylval = atoi(cp); 1088 cbuf[cpos] = c; 1089 return (NUMBER); 1090 } 1091 switch (cbuf[cpos++]) { 1092 1093 case '\n': 1094 state = CMD; 1095 return (CRLF); 1096 1097 case ' ': 1098 return (SP); 1099 1100 case ',': 1101 return (COMMA); 1102 1103 case 'A': 1104 case 'a': 1105 return (A); 1106 1107 case 'B': 1108 case 'b': 1109 return (B); 1110 1111 case 'C': 1112 case 'c': 1113 return (C); 1114 1115 case 'E': 1116 case 'e': 1117 return (E); 1118 1119 case 'F': 1120 case 'f': 1121 return (F); 1122 1123 case 'I': 1124 case 'i': 1125 return (I); 1126 1127 case 'L': 1128 case 'l': 1129 return (L); 1130 1131 case 'N': 1132 case 'n': 1133 return (N); 1134 1135 case 'P': 1136 case 'p': 1137 return (P); 1138 1139 case 'R': 1140 case 'r': 1141 return (R); 1142 1143 case 'S': 1144 case 's': 1145 return (S); 1146 1147 case 'T': 1148 case 't': 1149 return (T); 1150 1151 } 1152 break; 1153 1154 default: 1155 opiefatal("Unknown state in scanner."); 1156 } 1157 yyerror((char *) 0); 1158 state = CMD; 1159 longjmp(errcatch,0); 1160 } 1161} 1162 1163VOIDRET upper FUNCTION((s), char *s) 1164{ 1165 while (*s != '\0') { 1166 if (islower(*s)) 1167 *s = toupper(*s); 1168 s++; 1169 } 1170} 1171 1172char *copy FUNCTION((s), char *s) 1173{ 1174 char *p; 1175 1176 p = malloc((unsigned) strlen(s) + 1); 1177 if (p == NULL) 1178 opiefatal("Ran out of memory."); 1179 (void) strcpy(p, s); 1180 return (p); 1181} 1182 1183VOIDRET help FUNCTION((ctab, s), struct tab *ctab AND char *s) 1184{ 1185 register struct tab *c; 1186 register int width, NCMDS; 1187 char *type; 1188 1189 if (ctab == sitetab) 1190 type = "SITE "; 1191 else 1192 type = ""; 1193 width = 0, NCMDS = 0; 1194 for (c = ctab; c->name != NULL; c++) { 1195 int len = strlen(c->name); 1196 1197 if (len > width) 1198 width = len; 1199 NCMDS++; 1200 } 1201 width = (width + 8) &~ 7; 1202 if (s == 0) { 1203 register int i, j, w; 1204 int columns, lines; 1205 1206 lreply(214, "The following %scommands are recognized %s.", 1207 type, "(* =>'s unimplemented)"); 1208 columns = 76 / width; 1209 if (columns == 0) 1210 columns = 1; 1211 lines = (NCMDS + columns - 1) / columns; 1212 for (i = 0; i < lines; i++) { 1213 printf(" "); 1214 for (j = 0; j < columns; j++) { 1215 c = ctab + j * lines + i; 1216 printf("%s%c", c->name, 1217 c->implemented ? ' ' : '*'); 1218 if (c + lines >= &ctab[NCMDS]) 1219 break; 1220 w = strlen(c->name) + 1; 1221 while (w < width) { 1222 putchar(' '); 1223 w++; 1224 } 1225 } 1226 printf("\r\n"); 1227 } 1228 (void) fflush(stdout); 1229 reply(214, " "); 1230 return; 1231 } 1232 upper(s); 1233 c = lookup(ctab, s); 1234 if (c == (struct tab *)0) { 1235 reply(502, "Unknown command %s.", s); 1236 return; 1237 } 1238 if (c->implemented) 1239 reply(214, "Syntax: %s%s %s", type, c->name, c->help); 1240 else 1241 reply(214, "%s%-*s\t%s; unimplemented.", type, width, 1242 c->name, c->help); 1243} 1244 1245VOIDRET sizecmd FUNCTION((filename), char *filename) 1246{ 1247 switch (type) { 1248 case TYPE_L: 1249 case TYPE_I: { 1250 struct stat stbuf; 1251 if (stat(filename, &stbuf) < 0 || 1252 (stbuf.st_mode&S_IFMT) != S_IFREG) 1253 reply(550, "%s: not a plain file.", filename); 1254 else 1255 reply(213, "%lu", stbuf.st_size); 1256 break;} 1257 case TYPE_A: { 1258 FILE *fin; 1259 register int c; 1260 register long count; 1261 struct stat stbuf; 1262 fin = fopen(filename, "r"); 1263 if (fin == NULL) { 1264 perror_reply(550, filename); 1265 return; 1266 } 1267 if (fstat(fileno(fin), &stbuf) < 0 || 1268 (stbuf.st_mode&S_IFMT) != S_IFREG) { 1269 reply(550, "%s: not a plain file.", filename); 1270 (void) fclose(fin); 1271 return; 1272 } 1273 1274 count = 0; 1275 while((c=getc(fin)) != EOF) { 1276 if (c == '\n') /* will get expanded to \r\n */ 1277 count++; 1278 count++; 1279 } 1280 (void) fclose(fin); 1281 1282 reply(213, "%ld", count); 1283 break;} 1284 default: 1285 reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]); 1286 } 1287} 1288