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