ftpcmd.y revision 110036
1121054Semax/* 2121054Semax * Copyright (c) 1985, 1988, 1993, 1994 3121054Semax * The Regents of the University of California. All rights reserved. 4121054Semax * 5121054Semax * Redistribution and use in source and binary forms, with or without 6121054Semax * modification, are permitted provided that the following conditions 7121054Semax * are met: 8121054Semax * 1. Redistributions of source code must retain the above copyright 9121054Semax * notice, this list of conditions and the following disclaimer. 10121054Semax * 2. Redistributions in binary form must reproduce the above copyright 11121054Semax * notice, this list of conditions and the following disclaimer in the 12121054Semax * documentation and/or other materials provided with the distribution. 13121054Semax * 3. All advertising materials mentioning features or use of this software 14121054Semax * must display the following acknowledgement: 15121054Semax * This product includes software developed by the University of 16121054Semax * California, Berkeley and its contributors. 17121054Semax * 4. Neither the name of the University nor the names of its contributors 18121054Semax * may be used to endorse or promote products derived from this software 19121054Semax * without specific prior written permission. 20121054Semax * 21121054Semax * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22121054Semax * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23121054Semax * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24121054Semax * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25121054Semax * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26121054Semax * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27121054Semax * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28121054Semax * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29121054Semax * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30121054Semax * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31121054Semax * SUCH DAMAGE. 32146691Semax * 33121054Semax * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 34121054Semax */ 35121054Semax 36121054Semax/* 37121054Semax * Grammar for FTP commands. 38121054Semax * See RFC 959. 39121054Semax */ 40121054Semax 41124317Semax%{ 42121054Semax 43121054Semax#ifndef lint 44121054Semax#if 0 45121054Semaxstatic char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; 46121054Semax#endif 47121054Semaxstatic const char rcsid[] = 48121054Semax "$FreeBSD: head/libexec/ftpd/ftpcmd.y 110036 2003-01-29 10:07:27Z yar $"; 49121054Semax#endif /* not lint */ 50121054Semax 51121054Semax#include <sys/param.h> 52121054Semax#include <sys/socket.h> 53121054Semax#include <sys/stat.h> 54121054Semax 55121054Semax#include <netinet/in.h> 56121054Semax#include <arpa/ftp.h> 57124317Semax 58121054Semax#include <ctype.h> 59121054Semax#include <errno.h> 60121054Semax#include <glob.h> 61121054Semax#include <libutil.h> 62121054Semax#include <limits.h> 63121054Semax#include <md5.h> 64121054Semax#include <netdb.h> 65121054Semax#include <pwd.h> 66121054Semax#include <signal.h> 67121054Semax#include <stdio.h> 68121054Semax#include <stdlib.h> 69121054Semax#include <string.h> 70121054Semax#include <syslog.h> 71121054Semax#include <time.h> 72121054Semax#include <unistd.h> 73121054Semax 74124317Semax#include "extern.h" 75121054Semax#include "pathnames.h" 76124317Semax 77121054Semaxextern union sockunion data_dest, his_addr; 78121054Semaxextern int logged_in; 79121054Semaxextern struct passwd *pw; 80128076Semaxextern int guest; 81121054Semaxextern char *homedir; 82121054Semaxextern int paranoid; 83121054Semaxextern int logging; 84121054Semaxextern int type; 85121054Semaxextern int form; 86121054Semaxextern int ftpdebug; 87121054Semaxextern int timeout; 88121054Semaxextern int maxtimeout; 89121054Semaxextern int pdata; 90121054Semaxextern char *hostname; 91121054Semaxextern char remotehost[]; 92121054Semaxextern char proctitle[]; 93121054Semaxextern int usedefault; 94121054Semaxextern int transflag; 95121054Semaxextern char tmpline[]; 96121054Semaxextern int readonly; 97121054Semaxextern int noepsv; 98121054Semaxextern int noretr; 99121054Semaxextern int noguestretr; 100121054Semaxextern char *typenames[]; /* defined in <arpa/ftp.h> included from ftpd.c */ 101121054Semax 102121054Semaxoff_t restart_point; 103121054Semax 104121054Semaxstatic int cmd_type; 105290395Semaxstatic int cmd_form; 106290395Semaxstatic int cmd_bytesz; 107290395Semaxstatic int state; 108290395Semaxchar cbuf[512]; 109290395Semaxchar *fromname = (char *) 0; 110290395Semax 111121054Semaxextern int epsvall; 112121054Semax 113121054Semax%} 114121054Semax 115121054Semax%union { 116121054Semax struct { 117121054Semax off_t o; 118121054Semax int i; 119121054Semax } u; 120121054Semax char *s; 121121054Semax} 122121054Semax 123121054Semax%token 124121054Semax A B C E F I 125121054Semax L N P R S T 126121054Semax ALL 127121054Semax 128146691Semax SP CRLF COMMA 129121054Semax 130146691Semax USER PASS ACCT REIN QUIT PORT 131146691Semax PASV TYPE STRU MODE RETR STOR 132146691Semax APPE MLFL MAIL MSND MSOM MSAM 133146691Semax MRSQ MRCP ALLO REST RNFR RNTO 134146691Semax ABOR DELE CWD LIST NLST SITE 135146691Semax STAT HELP NOOP MKD RMD PWD 136121054Semax CDUP STOU SMNT SYST SIZE MDTM 137121054Semax LPRT LPSV EPRT EPSV 138121054Semax 139121054Semax UMASK IDLE CHMOD MDFIVE 140121054Semax 141121054Semax LEXERR NOTIMPL 142121054Semax 143121054Semax%token <s> STRING 144121054Semax%token <u> NUMBER 145121054Semax 146121054Semax%type <u.i> check_login octal_number byte_size 147121054Semax%type <u.i> check_login_ro check_login_epsv 148121054Semax%type <u.i> struct_code mode_code type_code form_code 149121054Semax%type <s> pathstring pathname password username 150121054Semax%type <s> ALL NOTIMPL 151121054Semax 152121054Semax%start cmd_list 153121054Semax 154121054Semax%% 155121054Semax 156121054Semaxcmd_list 157121054Semax : /* empty */ 158121054Semax | cmd_list cmd 159121054Semax { 160121054Semax if (fromname) 161121054Semax free(fromname); 162121054Semax fromname = (char *) 0; 163124317Semax restart_point = (off_t) 0; 164121054Semax } 165121054Semax | cmd_list rcmd 166121054Semax ; 167121054Semax 168121054Semaxcmd 169121054Semax : USER SP username CRLF 170121054Semax { 171121054Semax user($3); 172124317Semax free($3); 173121054Semax } 174121054Semax | PASS SP password CRLF 175121054Semax { 176121054Semax pass($3); 177121054Semax free($3); 178121054Semax } 179121054Semax | PASS CRLF 180121054Semax { 181121054Semax pass(""); 182121054Semax } 183121054Semax | PORT check_login SP host_port CRLF 184121054Semax { 185121054Semax if (epsvall) { 186121054Semax reply(501, "no PORT allowed after EPSV ALL"); 187121054Semax goto port_done; 188121054Semax } 189146691Semax if (!$2) 190121054Semax goto port_done; 191146691Semax if (port_check("PORT") == 1) 192146691Semax goto port_done; 193146691Semax#ifdef INET6 194146691Semax if ((his_addr.su_family != AF_INET6 || 195146691Semax !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) { 196146691Semax /* shoud never happen */ 197121054Semax usedefault = 1; 198121054Semax reply(500, "Invalid address rejected."); 199121054Semax goto port_done; 200121054Semax } 201121054Semax port_check_v6("pcmd"); 202121054Semax#endif 203121054Semax port_done: 204121054Semax } 205121054Semax | LPRT check_login SP host_long_port CRLF 206121054Semax { 207121054Semax if (epsvall) { 208121054Semax reply(501, "no LPRT allowed after EPSV ALL"); 209121054Semax goto lprt_done; 210121054Semax } 211121054Semax if (!$2) 212121054Semax goto lprt_done; 213121054Semax if (port_check("LPRT") == 1) 214121054Semax goto lprt_done; 215121054Semax#ifdef INET6 216121054Semax if (his_addr.su_family != AF_INET6) { 217121054Semax usedefault = 1; 218121054Semax reply(500, "Invalid address rejected."); 219121054Semax goto lprt_done; 220121054Semax } 221121054Semax if (port_check_v6("LPRT") == 1) 222121054Semax goto lprt_done; 223121054Semax#endif 224121054Semax lprt_done: 225121054Semax } 226121054Semax | EPRT check_login SP STRING CRLF 227121054Semax { 228121054Semax char delim; 229121054Semax char *tmp = NULL; 230121054Semax char *p, *q; 231121054Semax char *result[3]; 232121054Semax struct addrinfo hints; 233121054Semax struct addrinfo *res; 234121054Semax int i; 235121054Semax 236121054Semax if (epsvall) { 237121054Semax reply(501, "no EPRT allowed after EPSV ALL"); 238121054Semax goto eprt_done; 239121054Semax } 240128076Semax if (!$2) 241121054Semax goto eprt_done; 242121054Semax 243121054Semax memset(&data_dest, 0, sizeof(data_dest)); 244121054Semax tmp = strdup($4); 245121054Semax if (ftpdebug) 246146691Semax syslog(LOG_DEBUG, "%s", tmp); 247124317Semax if (!tmp) { 248146691Semax fatalerror("not enough core"); 249146691Semax /*NOTREACHED*/ 250124317Semax } 251121054Semax p = tmp; 252121054Semax delim = p[0]; 253146691Semax p++; 254146691Semax memset(result, 0, sizeof(result)); 255146691Semax for (i = 0; i < 3; i++) { 256146691Semax q = strchr(p, delim); 257146691Semax if (!q || *q != delim) { 258146691Semax parsefail: 259146691Semax reply(500, 260146691Semax "Invalid argument, rejected."); 261146691Semax if (tmp) 262146691Semax free(tmp); 263146691Semax usedefault = 1; 264121054Semax goto eprt_done; 265121054Semax } 266121054Semax *q++ = '\0'; 267290395Semax result[i] = p; 268290395Semax if (ftpdebug) 269290395Semax syslog(LOG_DEBUG, "%d: %s", i, p); 270121054Semax p = q; 271121054Semax } 272121054Semax 273121054Semax /* some more sanity check */ 274121054Semax p = result[0]; 275290395Semax while (*p) { 276290395Semax if (!isdigit(*p)) 277290395Semax goto parsefail; 278121054Semax p++; 279121054Semax } 280121054Semax p = result[2]; 281121054Semax while (*p) { 282121054Semax if (!isdigit(*p)) 283290395Semax goto parsefail; 284290395Semax p++; 285290395Semax } 286121054Semax 287121054Semax /* grab address */ 288121054Semax memset(&hints, 0, sizeof(hints)); 289121054Semax if (atoi(result[0]) == 1) 290121054Semax hints.ai_family = PF_INET; 291290395Semax#ifdef INET6 292121054Semax else if (atoi(result[0]) == 2) 293121054Semax hints.ai_family = PF_INET6; 294121054Semax#endif 295121054Semax else 296121054Semax hints.ai_family = PF_UNSPEC; /*XXX*/ 297121054Semax hints.ai_socktype = SOCK_STREAM; 298121054Semax i = getaddrinfo(result[1], result[2], &hints, &res); 299290395Semax if (i) 300121054Semax goto parsefail; 301121054Semax memcpy(&data_dest, res->ai_addr, res->ai_addrlen); 302121054Semax#ifdef INET6 303121054Semax if (his_addr.su_family == AF_INET6 304121054Semax && data_dest.su_family == AF_INET6) { 305121054Semax /* XXX more sanity checks! */ 306121054Semax data_dest.su_sin6.sin6_scope_id = 307290395Semax his_addr.su_sin6.sin6_scope_id; 308121054Semax } 309121054Semax#endif 310121054Semax free(tmp); 311121054Semax tmp = NULL; 312121054Semax 313121054Semax if (port_check("EPRT") == 1) 314121054Semax goto eprt_done; 315121054Semax#ifdef INET6 316121054Semax if (his_addr.su_family != AF_INET6) { 317121054Semax usedefault = 1; 318121054Semax reply(500, "Invalid address rejected."); 319121054Semax goto eprt_done; 320121054Semax } 321121054Semax if (port_check_v6("EPRT") == 1) 322124317Semax goto eprt_done; 323121054Semax#endif 324124317Semax eprt_done: 325121054Semax free($4); 326121054Semax } 327121054Semax | PASV check_login CRLF 328128076Semax { 329121054Semax if (epsvall) 330121054Semax reply(501, "no PASV allowed after EPSV ALL"); 331121054Semax else if ($2) 332121054Semax passive(); 333121054Semax } 334121054Semax | LPSV check_login CRLF 335121054Semax { 336121054Semax if (epsvall) 337121054Semax reply(501, "no LPSV allowed after EPSV ALL"); 338121054Semax else if ($2) 339121054Semax long_passive("LPSV", PF_UNSPEC); 340121054Semax } 341121054Semax | EPSV check_login_epsv SP NUMBER CRLF 342121054Semax { 343121054Semax if ($2) { 344121054Semax int pf; 345121054Semax switch ($4.i) { 346121054Semax case 1: 347121054Semax pf = PF_INET; 348121054Semax break; 349121054Semax#ifdef INET6 350121054Semax case 2: 351121054Semax pf = PF_INET6; 352121054Semax break; 353290395Semax#endif 354290395Semax default: 355290395Semax pf = -1; /*junk value*/ 356290395Semax break; 357290395Semax } 358290395Semax long_passive("EPSV", pf); 359121054Semax } 360121054Semax } 361121054Semax | EPSV check_login_epsv SP ALL CRLF 362121054Semax { 363121054Semax if ($2) { 364121054Semax reply(200, 365121054Semax "EPSV ALL command successful."); 366121054Semax epsvall++; 367121054Semax } 368121054Semax } 369121054Semax | EPSV check_login_epsv CRLF 370121054Semax { 371121054Semax if ($2) 372121054Semax long_passive("EPSV", PF_UNSPEC); 373121054Semax } 374121054Semax | TYPE check_login SP type_code CRLF 375121054Semax { 376121054Semax if ($2) { 377121054Semax switch (cmd_type) { 378121054Semax 379121054Semax case TYPE_A: 380121054Semax if (cmd_form == FORM_N) { 381290395Semax reply(200, "Type set to A."); 382290395Semax type = cmd_type; 383290395Semax form = cmd_form; 384290395Semax } else 385290395Semax reply(504, "Form must be N."); 386290395Semax break; 387121054Semax 388121054Semax case TYPE_E: 389121054Semax reply(504, "Type E not implemented."); 390121054Semax break; 391121054Semax 392121054Semax case TYPE_I: 393121054Semax reply(200, "Type set to I."); 394121054Semax type = cmd_type; 395121054Semax break; 396121054Semax 397121054Semax case TYPE_L: 398121054Semax#if CHAR_BIT == 8 399121054Semax if (cmd_bytesz == 8) { 400121054Semax reply(200, 401121054Semax "Type set to L (byte size 8)."); 402121054Semax type = cmd_type; 403121054Semax } else 404121054Semax reply(504, "Byte size must be 8."); 405121054Semax#else /* CHAR_BIT == 8 */ 406121054Semax UNIMPLEMENTED for CHAR_BIT != 8 407121054Semax#endif /* CHAR_BIT == 8 */ 408124317Semax } 409121054Semax } 410124317Semax } 411121054Semax | STRU check_login SP struct_code CRLF 412121054Semax { 413121054Semax if ($2) { 414128076Semax switch ($4) { 415121054Semax 416121054Semax case STRU_F: 417121054Semax reply(200, "STRU F ok."); 418121054Semax break; 419121054Semax 420121054Semax default: 421121054Semax reply(504, "Unimplemented STRU type."); 422121054Semax } 423121054Semax } 424121054Semax } 425121054Semax | MODE check_login SP mode_code CRLF 426121054Semax { 427121054Semax if ($2) { 428121054Semax switch ($4) { 429121054Semax 430121054Semax case MODE_S: 431121054Semax reply(200, "MODE S ok."); 432121054Semax break; 433121054Semax 434121054Semax default: 435121054Semax reply(502, "Unimplemented MODE type."); 436121054Semax } 437121054Semax } 438121054Semax } 439290395Semax | ALLO check_login SP NUMBER CRLF 440290395Semax { 441290395Semax if ($2) { 442290395Semax reply(202, "ALLO command ignored."); 443290395Semax } 444290395Semax } 445121054Semax | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 446121054Semax { 447121054Semax if ($2) { 448121054Semax reply(202, "ALLO command ignored."); 449121054Semax } 450121054Semax } 451121054Semax | RETR check_login SP pathname CRLF 452121054Semax { 453121054Semax if (noretr || (guest && noguestretr)) 454121054Semax reply(500, "RETR command is disabled"); 455121054Semax else if ($2 && $4 != NULL) 456121054Semax retrieve((char *) 0, $4); 457121054Semax 458121054Semax if ($4 != NULL) 459121054Semax free($4); 460121054Semax } 461121054Semax | STOR check_login_ro SP pathname CRLF 462121054Semax { 463121054Semax if ($2 && $4 != NULL) 464121054Semax store($4, "w", 0); 465121054Semax if ($4 != NULL) 466121054Semax free($4); 467121054Semax } 468290395Semax | APPE check_login_ro SP pathname CRLF 469290395Semax { 470290395Semax if ($2 && $4 != NULL) 471290395Semax store($4, "a", 0); 472290395Semax if ($4 != NULL) 473290395Semax free($4); 474290395Semax } 475121054Semax | NLST check_login CRLF 476121054Semax { 477121054Semax if ($2) 478121054Semax send_file_list("."); 479121054Semax } 480121054Semax | NLST check_login SP pathstring CRLF 481121054Semax { 482121054Semax if ($2) 483121054Semax send_file_list($4); 484121054Semax free($4); 485121054Semax } 486121054Semax | LIST check_login CRLF 487121054Semax { 488121054Semax if ($2) 489121054Semax retrieve(_PATH_LS " -lgA", ""); 490121054Semax } 491121054Semax | LIST check_login SP pathstring CRLF 492146691Semax { 493121054Semax if ($2) 494146691Semax retrieve(_PATH_LS " -lgA %s", $4); 495146691Semax free($4); 496146691Semax } 497146691Semax | STAT check_login SP pathname CRLF 498146691Semax { 499146691Semax if ($2 && $4 != NULL) 500121054Semax statfilecmd($4); 501121054Semax if ($4 != NULL) 502121054Semax free($4); 503121054Semax } 504121054Semax | STAT check_login CRLF 505121054Semax { 506121054Semax if ($2) { 507121054Semax statcmd(); 508121054Semax } 509121054Semax } 510121054Semax | DELE check_login_ro SP pathname CRLF 511121054Semax { 512121054Semax if ($2 && $4 != NULL) 513121054Semax delete($4); 514121054Semax if ($4 != NULL) 515121054Semax free($4); 516121054Semax } 517121054Semax | RNTO check_login_ro SP pathname CRLF 518121054Semax { 519121054Semax if ($2 && $4 != NULL) { 520121054Semax if (fromname) { 521121054Semax renamecmd(fromname, $4); 522121054Semax free(fromname); 523121054Semax fromname = (char *) 0; 524121054Semax } else { 525121054Semax reply(503, "Bad sequence of commands."); 526121054Semax } 527121054Semax } 528121054Semax if ($4 != NULL) 529121054Semax free($4); 530121054Semax } 531124317Semax | ABOR check_login CRLF 532121054Semax { 533121054Semax if ($2) 534121054Semax reply(225, "ABOR command successful."); 535121054Semax } 536121054Semax | CWD check_login CRLF 537121054Semax { 538121054Semax if ($2) { 539121054Semax cwd(homedir); 540121054Semax } 541121054Semax } 542121054Semax | CWD check_login SP pathname CRLF 543121054Semax { 544121054Semax if ($2 && $4 != NULL) 545121054Semax cwd($4); 546121054Semax if ($4 != NULL) 547121054Semax free($4); 548121054Semax } 549121054Semax | HELP CRLF 550121054Semax { 551121054Semax help(cmdtab, (char *) 0); 552121054Semax } 553121054Semax | HELP SP STRING CRLF 554121054Semax { 555121054Semax char *cp = $3; 556121054Semax 557121054Semax if (strncasecmp(cp, "SITE", 4) == 0) { 558121054Semax cp = $3 + 4; 559121054Semax if (*cp == ' ') 560121054Semax cp++; 561121054Semax if (*cp) 562121054Semax help(sitetab, cp); 563121054Semax else 564121054Semax help(sitetab, (char *) 0); 565121054Semax } else 566121054Semax help(cmdtab, $3); 567121054Semax free($3); 568121054Semax } 569121054Semax | NOOP CRLF 570121054Semax { 571121054Semax reply(200, "NOOP command successful."); 572121054Semax } 573121054Semax | MKD check_login_ro SP pathname CRLF 574121054Semax { 575121054Semax if ($2 && $4 != NULL) 576121054Semax makedir($4); 577121054Semax if ($4 != NULL) 578121054Semax free($4); 579121054Semax } 580121054Semax | RMD check_login_ro SP pathname CRLF 581121054Semax { 582121054Semax if ($2 && $4 != NULL) 583121054Semax removedir($4); 584121054Semax if ($4 != NULL) 585121054Semax free($4); 586121054Semax } 587121054Semax | PWD check_login CRLF 588121054Semax { 589121054Semax if ($2) 590121054Semax pwd(); 591121054Semax } 592121054Semax | CDUP check_login CRLF 593121054Semax { 594121054Semax if ($2) 595121054Semax cwd(".."); 596121054Semax } 597121054Semax | SITE SP HELP CRLF 598121054Semax { 599121054Semax help(sitetab, (char *) 0); 600121054Semax } 601121054Semax | SITE SP HELP SP STRING CRLF 602121054Semax { 603121054Semax help(sitetab, $5); 604121054Semax free($5); 605121054Semax } 606121054Semax | SITE SP MDFIVE check_login SP pathname CRLF 607121054Semax { 608121054Semax char p[64], *q; 609121054Semax 610121054Semax if ($4 && $6) { 611121054Semax q = MD5File($6, p); 612121054Semax if (q != NULL) 613121054Semax reply(200, "MD5(%s) = %s", $6, p); 614121054Semax else 615121054Semax perror_reply(550, $6); 616124317Semax } 617121054Semax if ($6) 618121054Semax free($6); 619121054Semax } 620121054Semax | SITE SP UMASK check_login CRLF 621121054Semax { 622121054Semax int oldmask; 623121054Semax 624121054Semax if ($4) { 625121054Semax oldmask = umask(0); 626121054Semax (void) umask(oldmask); 627121054Semax reply(200, "Current UMASK is %03o", oldmask); 628121054Semax } 629121054Semax } 630121054Semax | SITE SP UMASK check_login SP octal_number CRLF 631121054Semax { 632121054Semax int oldmask; 633121054Semax 634121054Semax if ($4) { 635121054Semax if (($6 == -1) || ($6 > 0777)) { 636121054Semax reply(501, "Bad UMASK value"); 637121054Semax } else { 638121054Semax oldmask = umask($6); 639121054Semax reply(200, 640121054Semax "UMASK set to %03o (was %03o)", 641121054Semax $6, oldmask); 642121054Semax } 643121054Semax } 644121054Semax } 645121054Semax | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF 646121054Semax { 647121054Semax if ($4 && ($8 != NULL)) { 648121054Semax if (($6 == -1 ) || ($6 > 0777)) 649121054Semax reply(501, "Bad mode value"); 650121054Semax else if (chmod($8, $6) < 0) 651121054Semax perror_reply(550, $8); 652121054Semax else 653121054Semax reply(200, "CHMOD command successful."); 654121054Semax } 655121054Semax if ($8 != NULL) 656121054Semax free($8); 657121054Semax } 658121054Semax | SITE SP check_login IDLE CRLF 659121054Semax { 660121054Semax if ($3) 661121054Semax reply(200, 662121054Semax "Current IDLE time limit is %d seconds; max %d", 663121054Semax timeout, maxtimeout); 664121054Semax } 665121054Semax | SITE SP check_login IDLE SP NUMBER CRLF 666121054Semax { 667121054Semax if ($3) { 668121054Semax if ($6.i < 30 || $6.i > maxtimeout) { 669121054Semax reply(501, 670121054Semax "Maximum IDLE time must be between 30 and %d seconds", 671121054Semax maxtimeout); 672121054Semax } else { 673121054Semax timeout = $6.i; 674121054Semax (void) alarm((unsigned) timeout); 675121054Semax reply(200, 676121054Semax "Maximum IDLE time set to %d seconds", 677121054Semax timeout); 678121054Semax } 679121054Semax } 680121054Semax } 681121054Semax | STOU check_login_ro SP pathname CRLF 682121054Semax { 683121054Semax if ($2 && $4 != NULL) 684121054Semax store($4, "w", 1); 685121054Semax if ($4 != NULL) 686121054Semax free($4); 687121054Semax } 688121054Semax | SYST check_login CRLF 689121054Semax { 690121054Semax if ($2) 691121054Semax#ifdef unix 692121054Semax#ifdef BSD 693121054Semax reply(215, "UNIX Type: L%d Version: BSD-%d", 694121054Semax CHAR_BIT, BSD); 695121054Semax#else /* BSD */ 696121054Semax reply(215, "UNIX Type: L%d", CHAR_BIT); 697121054Semax#endif /* BSD */ 698121054Semax#else /* unix */ 699121054Semax reply(215, "UNKNOWN Type: L%d", CHAR_BIT); 700121054Semax#endif /* unix */ 701121054Semax } 702121054Semax 703121054Semax /* 704121054Semax * SIZE is not in RFC959, but Postel has blessed it and 705121054Semax * it will be in the updated RFC. 706121054Semax * 707121054Semax * Return size of file in a format suitable for 708121054Semax * using with RESTART (we just count bytes). 709121054Semax */ 710121054Semax | SIZE check_login SP pathname CRLF 711121054Semax { 712121054Semax if ($2 && $4 != NULL) 713121054Semax sizecmd($4); 714121054Semax if ($4 != NULL) 715121054Semax free($4); 716121054Semax } 717121054Semax 718121054Semax /* 719121054Semax * MDTM is not in RFC959, but Postel has blessed it and 720121054Semax * it will be in the updated RFC. 721121054Semax * 722121054Semax * Return modification time of file as an ISO 3307 723121054Semax * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 724121054Semax * where xxx is the fractional second (of any precision, 725121054Semax * not necessarily 3 digits) 726121054Semax */ 727121054Semax | MDTM check_login SP pathname CRLF 728121054Semax { 729121054Semax if ($2 && $4 != NULL) { 730121054Semax struct stat stbuf; 731121054Semax if (stat($4, &stbuf) < 0) 732121054Semax reply(550, "%s: %s", 733121054Semax $4, strerror(errno)); 734121054Semax else if (!S_ISREG(stbuf.st_mode)) { 735121054Semax reply(550, "%s: not a plain file.", $4); 736121054Semax } else { 737121054Semax struct tm *t; 738121054Semax t = gmtime(&stbuf.st_mtime); 739121054Semax reply(213, 740121054Semax "%04d%02d%02d%02d%02d%02d", 741121054Semax 1900 + t->tm_year, 742121054Semax t->tm_mon+1, t->tm_mday, 743121054Semax t->tm_hour, t->tm_min, t->tm_sec); 744121054Semax } 745121054Semax } 746121054Semax if ($4 != NULL) 747121054Semax free($4); 748 } 749 | QUIT CRLF 750 { 751 reply(221, "Goodbye."); 752 dologout(0); 753 } 754 | NOTIMPL 755 { 756 nack($1); 757 } 758 | error 759 { 760 yyclearin; /* discard lookahead data */ 761 yyerrok; /* clear error condition */ 762 state = CMD; /* reset lexer state */ 763 } 764 ; 765rcmd 766 : RNFR check_login_ro SP pathname CRLF 767 { 768 restart_point = (off_t) 0; 769 if ($2 && $4) { 770 if (fromname) 771 free(fromname); 772 fromname = (char *) 0; 773 if (renamefrom($4)) 774 fromname = $4; 775 else 776 free($4); 777 } else if ($4) { 778 free($4); 779 } 780 } 781 | REST check_login SP NUMBER CRLF 782 { 783 if ($2) { 784 if (fromname) 785 free(fromname); 786 fromname = (char *) 0; 787 restart_point = $4.o; 788 reply(350, "Restarting at %llu. %s", 789 restart_point, 790 "Send STORE or RETRIEVE to initiate transfer."); 791 } 792 } 793 ; 794 795username 796 : STRING 797 ; 798 799password 800 : /* empty */ 801 { 802 $$ = (char *)calloc(1, sizeof(char)); 803 } 804 | STRING 805 ; 806 807byte_size 808 : NUMBER 809 { 810 $$ = $1.i; 811 } 812 ; 813 814host_port 815 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 816 NUMBER COMMA NUMBER 817 { 818 char *a, *p; 819 820 data_dest.su_len = sizeof(struct sockaddr_in); 821 data_dest.su_family = AF_INET; 822 p = (char *)&data_dest.su_sin.sin_port; 823 p[0] = $9.i; p[1] = $11.i; 824 a = (char *)&data_dest.su_sin.sin_addr; 825 a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i; 826 } 827 ; 828 829host_long_port 830 : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 831 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 832 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 833 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 834 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 835 NUMBER 836 { 837 char *a, *p; 838 839 memset(&data_dest, 0, sizeof(data_dest)); 840 data_dest.su_len = sizeof(struct sockaddr_in6); 841 data_dest.su_family = AF_INET6; 842 p = (char *)&data_dest.su_port; 843 p[0] = $39.i; p[1] = $41.i; 844 a = (char *)&data_dest.su_sin6.sin6_addr; 845 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 846 a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i; 847 a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i; 848 a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i; 849 if (his_addr.su_family == AF_INET6) { 850 /* XXX more sanity checks! */ 851 data_dest.su_sin6.sin6_scope_id = 852 his_addr.su_sin6.sin6_scope_id; 853 } 854 if ($1.i != 6 || $3.i != 16 || $37.i != 2) 855 memset(&data_dest, 0, sizeof(data_dest)); 856 } 857 | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 858 NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 859 NUMBER 860 { 861 char *a, *p; 862 863 memset(&data_dest, 0, sizeof(data_dest)); 864 data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); 865 data_dest.su_family = AF_INET; 866 p = (char *)&data_dest.su_port; 867 p[0] = $15.i; p[1] = $17.i; 868 a = (char *)&data_dest.su_sin.sin_addr; 869 a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 870 if ($1.i != 4 || $3.i != 4 || $13.i != 2) 871 memset(&data_dest, 0, sizeof(data_dest)); 872 } 873 ; 874 875form_code 876 : N 877 { 878 $$ = FORM_N; 879 } 880 | T 881 { 882 $$ = FORM_T; 883 } 884 | C 885 { 886 $$ = FORM_C; 887 } 888 ; 889 890type_code 891 : A 892 { 893 cmd_type = TYPE_A; 894 cmd_form = FORM_N; 895 } 896 | A SP form_code 897 { 898 cmd_type = TYPE_A; 899 cmd_form = $3; 900 } 901 | E 902 { 903 cmd_type = TYPE_E; 904 cmd_form = FORM_N; 905 } 906 | E SP form_code 907 { 908 cmd_type = TYPE_E; 909 cmd_form = $3; 910 } 911 | I 912 { 913 cmd_type = TYPE_I; 914 } 915 | L 916 { 917 cmd_type = TYPE_L; 918 cmd_bytesz = CHAR_BIT; 919 } 920 | L SP byte_size 921 { 922 cmd_type = TYPE_L; 923 cmd_bytesz = $3; 924 } 925 /* this is for a bug in the BBN ftp */ 926 | L byte_size 927 { 928 cmd_type = TYPE_L; 929 cmd_bytesz = $2; 930 } 931 ; 932 933struct_code 934 : F 935 { 936 $$ = STRU_F; 937 } 938 | R 939 { 940 $$ = STRU_R; 941 } 942 | P 943 { 944 $$ = STRU_P; 945 } 946 ; 947 948mode_code 949 : S 950 { 951 $$ = MODE_S; 952 } 953 | B 954 { 955 $$ = MODE_B; 956 } 957 | C 958 { 959 $$ = MODE_C; 960 } 961 ; 962 963pathname 964 : pathstring 965 { 966 /* 967 * Problem: this production is used for all pathname 968 * processing, but only gives a 550 error reply. 969 * This is a valid reply in some cases but not in others. 970 */ 971 if (logged_in && $1) { 972 glob_t gl; 973 char *p, **pp; 974 int flags = 975 GLOB_BRACE|GLOB_NOCHECK|GLOB_TILDE; 976 int n; 977 978 memset(&gl, 0, sizeof(gl)); 979 flags |= GLOB_LIMIT; 980 gl.gl_matchc = MAXGLOBARGS; 981 if (glob($1, flags, NULL, &gl) || 982 gl.gl_pathc == 0) { 983 reply(550, "wildcard expansion error"); 984 $$ = NULL; 985 } else { 986 n = 0; 987 for (pp = gl.gl_pathv; *pp; pp++) 988 if (strcspn(*pp, "\r\n") == 989 strlen(*pp)) { 990 p = *pp; 991 n++; 992 } 993 if (n == 0) 994 $$ = strdup($1); 995 else if (n == 1) 996 $$ = strdup(p); 997 else { 998 reply(550, "ambiguous"); 999 $$ = NULL; 1000 } 1001 } 1002 globfree(&gl); 1003 free($1); 1004 } else 1005 $$ = $1; 1006 } 1007 ; 1008 1009pathstring 1010 : STRING 1011 ; 1012 1013octal_number 1014 : NUMBER 1015 { 1016 int ret, dec, multby, digit; 1017 1018 /* 1019 * Convert a number that was read as decimal number 1020 * to what it would be if it had been read as octal. 1021 */ 1022 dec = $1.i; 1023 multby = 1; 1024 ret = 0; 1025 while (dec) { 1026 digit = dec%10; 1027 if (digit > 7) { 1028 ret = -1; 1029 break; 1030 } 1031 ret += digit * multby; 1032 multby *= 8; 1033 dec /= 10; 1034 } 1035 $$ = ret; 1036 } 1037 ; 1038 1039 1040check_login 1041 : /* empty */ 1042 { 1043 $$ = check_login1(); 1044 } 1045 ; 1046 1047check_login_epsv 1048 : /* empty */ 1049 { 1050 if (noepsv) { 1051 reply(500, "EPSV command disabled"); 1052 $$ = 0; 1053 } 1054 else 1055 $$ = check_login1(); 1056 } 1057 ; 1058 1059check_login_ro 1060 : /* empty */ 1061 { 1062 if (readonly) { 1063 reply(550, "Permission denied."); 1064 $$ = 0; 1065 } 1066 else 1067 $$ = check_login1(); 1068 } 1069 ; 1070 1071%% 1072 1073#define CMD 0 /* beginning of command */ 1074#define ARGS 1 /* expect miscellaneous arguments */ 1075#define STR1 2 /* expect SP followed by STRING */ 1076#define STR2 3 /* expect STRING */ 1077#define OSTR 4 /* optional SP then STRING */ 1078#define ZSTR1 5 /* optional SP then optional STRING */ 1079#define ZSTR2 6 /* optional STRING after SP */ 1080#define SITECMD 7 /* SITE command */ 1081#define NSTR 8 /* Number followed by a string */ 1082 1083#define MAXGLOBARGS 1000 1084 1085#define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */ 1086 1087struct tab { 1088 char *name; 1089 short token; 1090 short state; 1091 short implemented; /* 1 if command is implemented */ 1092 char *help; 1093}; 1094 1095struct tab cmdtab[] = { /* In order defined in RFC 765 */ 1096 { "USER", USER, STR1, 1, "<sp> username" }, 1097 { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" }, 1098 { "ACCT", ACCT, STR1, 0, "(specify account)" }, 1099 { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 1100 { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 1101 { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 1102 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" }, 1103 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 1104 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 1105 { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 1106 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 1107 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 1108 { "TYPE", TYPE, ARGS, 1, "<sp> { A | E | I | L }" }, 1109 { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 1110 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 1111 { "RETR", RETR, STR1, 1, "<sp> file-name" }, 1112 { "STOR", STOR, STR1, 1, "<sp> file-name" }, 1113 { "APPE", APPE, STR1, 1, "<sp> file-name" }, 1114 { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 1115 { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 1116 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 1117 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 1118 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 1119 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 1120 { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 1121 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 1122 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 1123 { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 1124 { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 1125 { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 1126 { "DELE", DELE, STR1, 1, "<sp> file-name" }, 1127 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 1128 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 1129 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 1130 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 1131 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 1132 { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 1133 { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 1134 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 1135 { "NOOP", NOOP, ARGS, 1, "" }, 1136 { "MKD", MKD, STR1, 1, "<sp> path-name" }, 1137 { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 1138 { "RMD", RMD, STR1, 1, "<sp> path-name" }, 1139 { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 1140 { "PWD", PWD, ARGS, 1, "(return current directory)" }, 1141 { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 1142 { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 1143 { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 1144 { "STOU", STOU, STR1, 1, "<sp> file-name" }, 1145 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 1146 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 1147 { NULL, 0, 0, 0, 0 } 1148}; 1149 1150struct tab sitetab[] = { 1151 { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" }, 1152 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 1153 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 1154 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 1155 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 1156 { NULL, 0, 0, 0, 0 } 1157}; 1158 1159static char *copy(char *); 1160static void help(struct tab *, char *); 1161static struct tab * 1162 lookup(struct tab *, char *); 1163static int port_check(const char *); 1164static int port_check_v6(const char *); 1165static void sizecmd(char *); 1166static void toolong(int); 1167static void v4map_data_dest(void); 1168static int yylex(void); 1169 1170static struct tab * 1171lookup(struct tab *p, char *cmd) 1172{ 1173 1174 for (; p->name != NULL; p++) 1175 if (strcmp(cmd, p->name) == 0) 1176 return (p); 1177 return (0); 1178} 1179 1180#include <arpa/telnet.h> 1181 1182/* 1183 * getline - a hacked up version of fgets to ignore TELNET escape codes. 1184 */ 1185char * 1186getline(char *s, int n, FILE *iop) 1187{ 1188 int c; 1189 register char *cs; 1190 1191 cs = s; 1192/* tmpline may contain saved command from urgent mode interruption */ 1193 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 1194 *cs++ = tmpline[c]; 1195 if (tmpline[c] == '\n') { 1196 *cs++ = '\0'; 1197 if (ftpdebug) 1198 syslog(LOG_DEBUG, "command: %s", s); 1199 tmpline[0] = '\0'; 1200 return(s); 1201 } 1202 if (c == 0) 1203 tmpline[0] = '\0'; 1204 } 1205 while ((c = getc(iop)) != EOF) { 1206 c &= 0377; 1207 if (c == IAC) { 1208 if ((c = getc(iop)) != EOF) { 1209 c &= 0377; 1210 switch (c) { 1211 case WILL: 1212 case WONT: 1213 c = getc(iop); 1214 printf("%c%c%c", IAC, DONT, 0377&c); 1215 (void) fflush(stdout); 1216 continue; 1217 case DO: 1218 case DONT: 1219 c = getc(iop); 1220 printf("%c%c%c", IAC, WONT, 0377&c); 1221 (void) fflush(stdout); 1222 continue; 1223 case IAC: 1224 break; 1225 default: 1226 continue; /* ignore command */ 1227 } 1228 } 1229 } 1230 *cs++ = c; 1231 if (--n <= 0 || c == '\n') 1232 break; 1233 } 1234 if (c == EOF && cs == s) 1235 return (NULL); 1236 *cs++ = '\0'; 1237 if (ftpdebug) { 1238 if (!guest && strncasecmp("pass ", s, 5) == 0) { 1239 /* Don't syslog passwords */ 1240 syslog(LOG_DEBUG, "command: %.5s ???", s); 1241 } else { 1242 register char *cp; 1243 register int len; 1244 1245 /* Don't syslog trailing CR-LF */ 1246 len = strlen(s); 1247 cp = s + len - 1; 1248 while (cp >= s && (*cp == '\n' || *cp == '\r')) { 1249 --cp; 1250 --len; 1251 } 1252 syslog(LOG_DEBUG, "command: %.*s", len, s); 1253 } 1254 } 1255 return (s); 1256} 1257 1258static void 1259toolong(int signo) 1260{ 1261 1262 reply(421, 1263 "Timeout (%d seconds): closing control connection.", timeout); 1264 if (logging) 1265 syslog(LOG_INFO, "User %s timed out after %d seconds", 1266 (pw ? pw -> pw_name : "unknown"), timeout); 1267 dologout(1); 1268} 1269 1270static int 1271yylex(void) 1272{ 1273 static int cpos; 1274 char *cp, *cp2; 1275 struct tab *p; 1276 int n; 1277 char c; 1278 1279 for (;;) { 1280 switch (state) { 1281 1282 case CMD: 1283 (void) signal(SIGALRM, toolong); 1284 (void) alarm((unsigned) timeout); 1285 if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) { 1286 reply(221, "You could at least say goodbye."); 1287 dologout(0); 1288 } 1289 (void) alarm(0); 1290#ifdef SETPROCTITLE 1291 if (strncasecmp(cbuf, "PASS", 4) != 0) 1292 setproctitle("%s: %s", proctitle, cbuf); 1293#endif /* SETPROCTITLE */ 1294 if ((cp = strchr(cbuf, '\r'))) { 1295 *cp++ = '\n'; 1296 *cp = '\0'; 1297 } 1298 if ((cp = strpbrk(cbuf, " \n"))) 1299 cpos = cp - cbuf; 1300 if (cpos == 0) 1301 cpos = 4; 1302 c = cbuf[cpos]; 1303 cbuf[cpos] = '\0'; 1304 upper(cbuf); 1305 p = lookup(cmdtab, cbuf); 1306 cbuf[cpos] = c; 1307 if (p != 0) { 1308 yylval.s = p->name; 1309 if (!p->implemented) 1310 return (NOTIMPL); /* state remains CMD */ 1311 state = p->state; 1312 return (p->token); 1313 } 1314 break; 1315 1316 case SITECMD: 1317 if (cbuf[cpos] == ' ') { 1318 cpos++; 1319 return (SP); 1320 } 1321 cp = &cbuf[cpos]; 1322 if ((cp2 = strpbrk(cp, " \n"))) 1323 cpos = cp2 - cbuf; 1324 c = cbuf[cpos]; 1325 cbuf[cpos] = '\0'; 1326 upper(cp); 1327 p = lookup(sitetab, cp); 1328 cbuf[cpos] = c; 1329 if (guest == 0 && p != 0) { 1330 yylval.s = p->name; 1331 if (!p->implemented) { 1332 state = CMD; 1333 return (NOTIMPL); 1334 } 1335 state = p->state; 1336 return (p->token); 1337 } 1338 state = CMD; 1339 break; 1340 1341 case ZSTR1: 1342 case OSTR: 1343 if (cbuf[cpos] == '\n') { 1344 state = CMD; 1345 return (CRLF); 1346 } 1347 /* FALLTHROUGH */ 1348 1349 case STR1: 1350 dostr1: 1351 if (cbuf[cpos] == ' ') { 1352 cpos++; 1353 state = state == OSTR ? STR2 : state+1; 1354 return (SP); 1355 } 1356 break; 1357 1358 case ZSTR2: 1359 if (cbuf[cpos] == '\n') { 1360 state = CMD; 1361 return (CRLF); 1362 } 1363 /* FALLTHROUGH */ 1364 1365 case STR2: 1366 cp = &cbuf[cpos]; 1367 n = strlen(cp); 1368 cpos += n - 1; 1369 /* 1370 * Make sure the string is nonempty and \n terminated. 1371 */ 1372 if (n > 1 && cbuf[cpos] == '\n') { 1373 cbuf[cpos] = '\0'; 1374 yylval.s = copy(cp); 1375 cbuf[cpos] = '\n'; 1376 state = ARGS; 1377 return (STRING); 1378 } 1379 break; 1380 1381 case NSTR: 1382 if (cbuf[cpos] == ' ') { 1383 cpos++; 1384 return (SP); 1385 } 1386 if (isdigit(cbuf[cpos])) { 1387 cp = &cbuf[cpos]; 1388 while (isdigit(cbuf[++cpos])) 1389 ; 1390 c = cbuf[cpos]; 1391 cbuf[cpos] = '\0'; 1392 yylval.u.i = atoi(cp); 1393 cbuf[cpos] = c; 1394 state = STR1; 1395 return (NUMBER); 1396 } 1397 state = STR1; 1398 goto dostr1; 1399 1400 case ARGS: 1401 if (isdigit(cbuf[cpos])) { 1402 cp = &cbuf[cpos]; 1403 while (isdigit(cbuf[++cpos])) 1404 ; 1405 c = cbuf[cpos]; 1406 cbuf[cpos] = '\0'; 1407 yylval.u.i = atoi(cp); 1408 yylval.u.o = strtoull(cp, (char **)NULL, 10); 1409 cbuf[cpos] = c; 1410 return (NUMBER); 1411 } 1412 if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 1413 && !isalnum(cbuf[cpos + 3])) { 1414 cpos += 3; 1415 return ALL; 1416 } 1417 switch (cbuf[cpos++]) { 1418 1419 case '\n': 1420 state = CMD; 1421 return (CRLF); 1422 1423 case ' ': 1424 return (SP); 1425 1426 case ',': 1427 return (COMMA); 1428 1429 case 'A': 1430 case 'a': 1431 return (A); 1432 1433 case 'B': 1434 case 'b': 1435 return (B); 1436 1437 case 'C': 1438 case 'c': 1439 return (C); 1440 1441 case 'E': 1442 case 'e': 1443 return (E); 1444 1445 case 'F': 1446 case 'f': 1447 return (F); 1448 1449 case 'I': 1450 case 'i': 1451 return (I); 1452 1453 case 'L': 1454 case 'l': 1455 return (L); 1456 1457 case 'N': 1458 case 'n': 1459 return (N); 1460 1461 case 'P': 1462 case 'p': 1463 return (P); 1464 1465 case 'R': 1466 case 'r': 1467 return (R); 1468 1469 case 'S': 1470 case 's': 1471 return (S); 1472 1473 case 'T': 1474 case 't': 1475 return (T); 1476 1477 } 1478 break; 1479 1480 default: 1481 fatalerror("Unknown state in scanner."); 1482 } 1483 state = CMD; 1484 return (LEXERR); 1485 } 1486} 1487 1488void 1489upper(char *s) 1490{ 1491 while (*s != '\0') { 1492 if (islower(*s)) 1493 *s = toupper(*s); 1494 s++; 1495 } 1496} 1497 1498static char * 1499copy(char *s) 1500{ 1501 char *p; 1502 1503 p = malloc((unsigned) strlen(s) + 1); 1504 if (p == NULL) 1505 fatalerror("Ran out of memory."); 1506 (void) strcpy(p, s); 1507 return (p); 1508} 1509 1510static void 1511help(struct tab *ctab, char *s) 1512{ 1513 struct tab *c; 1514 int width, NCMDS; 1515 char *type; 1516 1517 if (ctab == sitetab) 1518 type = "SITE "; 1519 else 1520 type = ""; 1521 width = 0, NCMDS = 0; 1522 for (c = ctab; c->name != NULL; c++) { 1523 int len = strlen(c->name); 1524 1525 if (len > width) 1526 width = len; 1527 NCMDS++; 1528 } 1529 width = (width + 8) &~ 7; 1530 if (s == 0) { 1531 int i, j, w; 1532 int columns, lines; 1533 1534 lreply(214, "The following %scommands are recognized %s.", 1535 type, "(* =>'s unimplemented)"); 1536 columns = 76 / width; 1537 if (columns == 0) 1538 columns = 1; 1539 lines = (NCMDS + columns - 1) / columns; 1540 for (i = 0; i < lines; i++) { 1541 printf(" "); 1542 for (j = 0; j < columns; j++) { 1543 c = ctab + j * lines + i; 1544 printf("%s%c", c->name, 1545 c->implemented ? ' ' : '*'); 1546 if (c + lines >= &ctab[NCMDS]) 1547 break; 1548 w = strlen(c->name) + 1; 1549 while (w < width) { 1550 putchar(' '); 1551 w++; 1552 } 1553 } 1554 printf("\r\n"); 1555 } 1556 (void) fflush(stdout); 1557 reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1558 return; 1559 } 1560 upper(s); 1561 c = lookup(ctab, s); 1562 if (c == (struct tab *)0) { 1563 reply(502, "Unknown command %s.", s); 1564 return; 1565 } 1566 if (c->implemented) 1567 reply(214, "Syntax: %s%s %s", type, c->name, c->help); 1568 else 1569 reply(214, "%s%-*s\t%s; unimplemented.", type, width, 1570 c->name, c->help); 1571} 1572 1573static void 1574sizecmd(char *filename) 1575{ 1576 switch (type) { 1577 case TYPE_L: 1578 case TYPE_I: { 1579 struct stat stbuf; 1580 if (stat(filename, &stbuf) < 0) 1581 perror_reply(550, filename); 1582 else if (!S_ISREG(stbuf.st_mode)) 1583 reply(550, "%s: not a plain file.", filename); 1584 else 1585 reply(213, "%qu", stbuf.st_size); 1586 break; } 1587 case TYPE_A: { 1588 FILE *fin; 1589 int c; 1590 off_t count; 1591 struct stat stbuf; 1592 fin = fopen(filename, "r"); 1593 if (fin == NULL) { 1594 perror_reply(550, filename); 1595 return; 1596 } 1597 if (fstat(fileno(fin), &stbuf) < 0) { 1598 perror_reply(550, filename); 1599 (void) fclose(fin); 1600 return; 1601 } else if (!S_ISREG(stbuf.st_mode)) { 1602 reply(550, "%s: not a plain file.", filename); 1603 (void) fclose(fin); 1604 return; 1605 } else if (stbuf.st_size > MAXASIZE) { 1606 reply(550, "%s: too large for type A SIZE.", filename); 1607 (void) fclose(fin); 1608 return; 1609 } 1610 1611 count = 0; 1612 while((c=getc(fin)) != EOF) { 1613 if (c == '\n') /* will get expanded to \r\n */ 1614 count++; 1615 count++; 1616 } 1617 (void) fclose(fin); 1618 1619 reply(213, "%qd", count); 1620 break; } 1621 default: 1622 reply(504, "SIZE not implemented for type %s.", 1623 typenames[type]); 1624 } 1625} 1626 1627/* Return 1, if port check is done. Return 0, if not yet. */ 1628static int 1629port_check(const char *pcmd) 1630{ 1631 if (his_addr.su_family == AF_INET) { 1632 if (data_dest.su_family != AF_INET) { 1633 usedefault = 1; 1634 reply(500, "Invalid address rejected."); 1635 return 1; 1636 } 1637 if (paranoid && 1638 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 1639 memcmp(&data_dest.su_sin.sin_addr, 1640 &his_addr.su_sin.sin_addr, 1641 sizeof(data_dest.su_sin.sin_addr)))) { 1642 usedefault = 1; 1643 reply(500, "Illegal PORT range rejected."); 1644 } else { 1645 usedefault = 0; 1646 if (pdata >= 0) { 1647 (void) close(pdata); 1648 pdata = -1; 1649 } 1650 reply(200, "%s command successful.", pcmd); 1651 } 1652 return 1; 1653 } 1654 return 0; 1655} 1656 1657static int 1658check_login1(void) 1659{ 1660 if (logged_in) 1661 return 1; 1662 else { 1663 reply(530, "Please login with USER and PASS."); 1664 return 0; 1665 } 1666} 1667 1668#ifdef INET6 1669/* Return 1, if port check is done. Return 0, if not yet. */ 1670static int 1671port_check_v6(const char *pcmd) 1672{ 1673 if (his_addr.su_family == AF_INET6) { 1674 if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) 1675 /* Convert data_dest into v4 mapped sockaddr.*/ 1676 v4map_data_dest(); 1677 if (data_dest.su_family != AF_INET6) { 1678 usedefault = 1; 1679 reply(500, "Invalid address rejected."); 1680 return 1; 1681 } 1682 if (paranoid && 1683 ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 1684 memcmp(&data_dest.su_sin6.sin6_addr, 1685 &his_addr.su_sin6.sin6_addr, 1686 sizeof(data_dest.su_sin6.sin6_addr)))) { 1687 usedefault = 1; 1688 reply(500, "Illegal PORT range rejected."); 1689 } else { 1690 usedefault = 0; 1691 if (pdata >= 0) { 1692 (void) close(pdata); 1693 pdata = -1; 1694 } 1695 reply(200, "%s command successful.", pcmd); 1696 } 1697 return 1; 1698 } 1699 return 0; 1700} 1701 1702static void 1703v4map_data_dest(void) 1704{ 1705 struct in_addr savedaddr; 1706 int savedport; 1707 1708 if (data_dest.su_family != AF_INET) { 1709 usedefault = 1; 1710 reply(500, "Invalid address rejected."); 1711 return; 1712 } 1713 1714 savedaddr = data_dest.su_sin.sin_addr; 1715 savedport = data_dest.su_port; 1716 1717 memset(&data_dest, 0, sizeof(data_dest)); 1718 data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); 1719 data_dest.su_sin6.sin6_family = AF_INET6; 1720 data_dest.su_sin6.sin6_port = savedport; 1721 memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); 1722 memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], 1723 (caddr_t)&savedaddr, sizeof(savedaddr)); 1724} 1725#endif 1726