1234313Sbapt/* $NetBSD: ftpcmd.y,v 1.84 2006/02/01 14:20:12 christos Exp $ */ 2263020Sbapt 3257353Sbdrewery/*- 4234313Sbapt * Copyright (c) 1997-2005 The NetBSD Foundation, Inc. 5234313Sbapt * All rights reserved. 6234313Sbapt * 7234313Sbapt * This code is derived from software contributed to The NetBSD Foundation 8234313Sbapt * by Luke Mewburn. 9234313Sbapt * 10234313Sbapt * Redistribution and use in source and binary forms, with or without 11234313Sbapt * modification, are permitted provided that the following conditions 12234313Sbapt * are met: 13234313Sbapt * 1. Redistributions of source code must retain the above copyright 14234313Sbapt * notice, this list of conditions and the following disclaimer. 15234313Sbapt * 2. Redistributions in binary form must reproduce the above copyright 16234313Sbapt * notice, this list of conditions and the following disclaimer in the 17234313Sbapt * documentation and/or other materials provided with the distribution. 18234313Sbapt * 3. All advertising materials mentioning features or use of this software 19234313Sbapt * must display the following acknowledgement: 20234313Sbapt * This product includes software developed by the NetBSD 21234313Sbapt * Foundation, Inc. and its contributors. 22234313Sbapt * 4. Neither the name of The NetBSD Foundation nor the names of its 23234313Sbapt * contributors may be used to endorse or promote products derived 24234313Sbapt * from this software without specific prior written permission. 25234313Sbapt * 26234313Sbapt * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27234313Sbapt * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28234313Sbapt * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29234313Sbapt * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30234313Sbapt * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31234313Sbapt * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32257353Sbdrewery * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33257353Sbdrewery * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34257353Sbdrewery * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35234322Sbapt * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36234313Sbapt * POSSIBILITY OF SUCH DAMAGE. 37257353Sbdrewery */ 38234313Sbapt 39234313Sbapt/* 40257353Sbdrewery * Copyright (c) 1985, 1988, 1993, 1994 41234313Sbapt * The Regents of the University of California. All rights reserved. 42234313Sbapt * 43257309Sbapt * Redistribution and use in source and binary forms, with or without 44234322Sbapt * modification, are permitted provided that the following conditions 45234351Sbapt * are met: 46247841Sbapt * 1. Redistributions of source code must retain the above copyright 47234313Sbapt * notice, this list of conditions and the following disclaimer. 48234313Sbapt * 2. Redistributions in binary form must reproduce the above copyright 49234313Sbapt * notice, this list of conditions and the following disclaimer in the 50234313Sbapt * documentation and/or other materials provided with the distribution. 51263020Sbapt * 3. Neither the name of the University nor the names of its contributors 52234313Sbapt * may be used to endorse or promote products derived from this software 53257353Sbdrewery * without specific prior written permission. 54257353Sbdrewery * 55257353Sbdrewery * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 56243883Sbapt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 57247841Sbapt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 58234313Sbapt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 59257353Sbdrewery * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 60257353Sbdrewery * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 61257353Sbdrewery * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 62257353Sbdrewery * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 63257353Sbdrewery * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 64257353Sbdrewery * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 65257353Sbdrewery * SUCH DAMAGE. 66257353Sbdrewery * 67257353Sbdrewery * @(#)ftpcmd.y 8.3 (Berkeley) 4/6/94 68287872Sdelphij */ 69287872Sdelphij 70287872Sdelphij/* 71287872Sdelphij * Grammar for FTP commands. 72287872Sdelphij * See RFC 959. 73257353Sbdrewery */ 74257353Sbdrewery 75257353Sbdrewery%{ 76257353Sbdrewery#include <sys/cdefs.h> 77257353Sbdrewery 78257353Sbdrewery#ifndef lint 79257353Sbdrewery#if 0 80257353Sbdrewerystatic char sccsid[] = "@(#)ftpcmd.y 8.3 (Berkeley) 4/6/94"; 81257353Sbdrewery#else 82257353Sbdrewery__RCSID("$NetBSD: ftpcmd.y,v 1.84 2006/02/01 14:20:12 christos Exp $"); 83257353Sbdrewery#endif 84257353Sbdrewery#endif /* not lint */ 85257353Sbdrewery 86257353Sbdrewery#include <sys/param.h> 87234313Sbapt#include <sys/socket.h> 88234313Sbapt#include <sys/stat.h> 89234313Sbapt 90234313Sbapt#include <netinet/in.h> 91234313Sbapt#include <arpa/ftp.h> 92234313Sbapt#include <arpa/inet.h> 93234313Sbapt 94234313Sbapt#include <ctype.h> 95234351Sbapt#include <errno.h> 96234313Sbapt#include <pwd.h> 97234351Sbapt#include <stdio.h> 98234351Sbapt#include <stdlib.h> 99234351Sbapt#include <string.h> 100234351Sbapt#include <syslog.h> 101247060Sbapt#include <time.h> 102234313Sbapt#include <tzfile.h> 103234313Sbapt#include <unistd.h> 104234351Sbapt#include <netdb.h> 105234351Sbapt 106234351Sbapt#ifdef KERBEROS5 107234351Sbapt#include <krb5/krb5.h> 108234313Sbapt#endif 109234313Sbapt 110234351Sbapt#include "extern.h" 111234313Sbapt#include "version.h" 112234313Sbapt 113234313Sbaptstatic int cmd_type; 114234313Sbaptstatic int cmd_form; 115234313Sbaptstatic int cmd_bytesz; 116234313Sbapt 117234313Sbaptchar cbuf[FTP_BUFLEN]; 118234313Sbaptchar *cmdp; 119234313Sbaptchar *fromname; 120234313Sbapt 121234313Sbaptextern int epsvall; 122234322Sbaptstruct tab sitetab[]; 123234322Sbapt 124234322Sbaptstatic int check_write(const char *, int); 125234351Sbaptstatic void help(struct tab *, const char *); 126234313Sbaptstatic void port_check(const char *, int); 127234313Sbapt int yylex(void); 128234313Sbapt 129234313Sbapt%} 130234351Sbapt 131234351Sbapt%union { 132234351Sbapt struct { 133269937Sgavin LLT ll; 134269937Sgavin int i; 135234313Sbapt } u; 136234313Sbapt char *s; 137247060Sbapt} 138234322Sbapt 139234313Sbapt%token 140234313Sbapt A B C E F I 141234313Sbapt L N P R S T 142234313Sbapt 143257632Sbdrewery SP CRLF COMMA ALL 144234313Sbapt 145234313Sbapt USER PASS ACCT CWD CDUP SMNT 146234313Sbapt QUIT REIN PORT PASV TYPE STRU 147234313Sbapt MODE RETR STOR STOU APPE ALLO 148234313Sbapt REST RNFR RNTO ABOR DELE RMD 149234351Sbapt MKD PWD LIST NLST SITE SYST 150234351Sbapt STAT HELP NOOP 151234351Sbapt 152257632Sbdrewery AUTH ADAT PROT PBSZ CCC MIC 153257632Sbdrewery CONF ENC 154257632Sbdrewery 155257632Sbdrewery FEAT OPTS 156257632Sbdrewery 157257632Sbdrewery SIZE MDTM MLST MLSD 158234351Sbapt 159234351Sbapt LPRT LPSV EPRT EPSV 160234351Sbapt 161234313Sbapt MAIL MLFL MRCP MRSQ MSAM MSND 162234313Sbapt MSOM 163234351Sbapt 164234313Sbapt CHMOD IDLE RATEGET RATEPUT UMASK 165234313Sbapt 166234313Sbapt LEXERR 167234351Sbapt 168234351Sbapt%token <s> STRING 169234351Sbapt%token <u> NUMBER 170234351Sbapt 171234351Sbapt%type <u.i> check_login octal_number byte_size 172234313Sbapt%type <u.i> struct_code mode_code type_code form_code decimal_integer 173234313Sbapt%type <s> pathstring pathname password username 174234313Sbapt%type <s> mechanism_name base64data prot_code 175257353Sbdrewery 176234313Sbapt%start cmd_sel 177243883Sbapt 178257353Sbdrewery%% 179257353Sbdrewery 180234313Sbaptcmd_sel 181243883Sbapt : cmd 182257353Sbdrewery { 183257353Sbdrewery REASSIGN(fromname, NULL); 184283793Sbapt restart_point = (off_t) 0; 185257353Sbdrewery } 186243883Sbapt 187257353Sbdrewery | rcmd 188234313Sbapt 189243883Sbapt ; 190257353Sbdrewery 191234313Sbaptcmd 192234313Sbapt /* RFC 959 */ 193257353Sbdrewery : USER SP username CRLF 194257353Sbdrewery { 195247841Sbapt user($3); 196247841Sbapt free($3); 197247841Sbapt } 198257328Sbdrewery 199257353Sbdrewery | PASS SP password CRLF 200234313Sbapt { 201234322Sbapt pass($3); 202234313Sbapt memset($3, 0, strlen($3)); 203234313Sbapt free($3); 204243883Sbapt } 205234313Sbapt 206278564Sbapt | CWD check_login CRLF 207278564Sbapt { 208278564Sbapt if ($2) 209278564Sbapt cwd(homedir); 210278564Sbapt } 211243883Sbapt 212243883Sbapt | CWD check_login SP pathname CRLF 213247841Sbapt { 214247841Sbapt if ($2 && $4 != NULL) 215243883Sbapt cwd($4); 216243883Sbapt if ($4 != NULL) 217243883Sbapt free($4); 218243883Sbapt } 219243883Sbapt 220243883Sbapt | CDUP check_login CRLF 221243883Sbapt { 222257309Sbapt if ($2) 223243883Sbapt cwd(".."); 224257309Sbapt } 225257309Sbapt 226243883Sbapt | QUIT CRLF 227243883Sbapt { 228243883Sbapt if (logged_in) { 229243883Sbapt reply(-221, "%s", ""); 230243883Sbapt reply(0, 231243883Sbapt "Data traffic for this session was " LLF " byte%s in " LLF " file%s.", 232243883Sbapt (LLT)total_data, PLURAL(total_data), 233243883Sbapt (LLT)total_files, PLURAL(total_files)); 234243883Sbapt reply(0, 235243883Sbapt "Total traffic for this session was " LLF " byte%s in " LLF " transfer%s.", 236243883Sbapt (LLT)total_bytes, PLURAL(total_bytes), 237243883Sbapt (LLT)total_xfers, PLURAL(total_xfers)); 238243883Sbapt } 239243883Sbapt reply(221, 240243883Sbapt "Thank you for using the FTP service on %s.", 241243883Sbapt hostname); 242283793Sbapt if (logged_in && logging) { 243234313Sbapt syslog(LOG_INFO, 244234313Sbapt "Data traffic: " LLF " byte%s in " LLF " file%s", 245257353Sbdrewery (LLT)total_data, PLURAL(total_data), 246234313Sbapt (LLT)total_files, PLURAL(total_files)); 247283793Sbapt syslog(LOG_INFO, 248234313Sbapt "Total traffic: " LLF " byte%s in " LLF " transfer%s", 249283793Sbapt (LLT)total_bytes, PLURAL(total_bytes), 250283793Sbapt (LLT)total_xfers, PLURAL(total_xfers)); 251283793Sbapt } 252234313Sbapt 253234313Sbapt dologout(0); 254234351Sbapt } 255234351Sbapt 256234313Sbapt | PORT check_login SP host_port CRLF 257257353Sbdrewery { 258257353Sbdrewery if ($2) 259257353Sbdrewery port_check("PORT", AF_INET); 260257353Sbdrewery } 261257353Sbdrewery 262257353Sbdrewery | LPRT check_login SP host_long_port4 CRLF 263257353Sbdrewery { 264257353Sbdrewery if ($2) 265257353Sbdrewery port_check("LPRT", AF_INET); 266257353Sbdrewery } 267257353Sbdrewery 268257353Sbdrewery | LPRT check_login SP host_long_port6 CRLF 269257353Sbdrewery { 270257353Sbdrewery#ifdef INET6 271257353Sbdrewery if ($2) 272257353Sbdrewery port_check("LPRT", AF_INET6); 273257353Sbdrewery#else 274263020Sbapt reply(500, "IPv6 support not available."); 275257353Sbdrewery#endif 276268896Sbapt } 277263020Sbapt 278263020Sbapt | EPRT check_login SP STRING CRLF 279257353Sbdrewery { 280257353Sbdrewery if ($2) { 281257353Sbdrewery if (extended_port($4) == 0) 282257353Sbdrewery port_check("EPRT", -1); 283257353Sbdrewery } 284263020Sbapt free($4); 285263020Sbapt } 286263020Sbapt 287257353Sbdrewery | PASV check_login CRLF 288263020Sbapt { 289263020Sbapt if ($2) { 290263020Sbapt if (CURCLASS_FLAGS_ISSET(passive)) 291257353Sbdrewery passive(); 292263020Sbapt else 293263020Sbapt reply(500, "PASV mode not available."); 294257353Sbdrewery } 295257353Sbdrewery } 296257353Sbdrewery 297257353Sbdrewery | LPSV check_login CRLF 298257353Sbdrewery { 299257353Sbdrewery if ($2) { 300257353Sbdrewery if (CURCLASS_FLAGS_ISSET(passive)) { 301257353Sbdrewery if (epsvall) 302257353Sbdrewery reply(501, 303257353Sbdrewery "LPSV disallowed after EPSV ALL"); 304257353Sbdrewery else 305263020Sbapt long_passive("LPSV", PF_UNSPEC); 306257353Sbdrewery } else 307257353Sbdrewery reply(500, "LPSV mode not available."); 308257353Sbdrewery } 309257353Sbdrewery } 310257353Sbdrewery 311257353Sbdrewery | EPSV check_login SP NUMBER CRLF 312257353Sbdrewery { 313257353Sbdrewery if ($2) { 314257353Sbdrewery if (CURCLASS_FLAGS_ISSET(passive)) 315257353Sbdrewery long_passive("EPSV", 316257353Sbdrewery epsvproto2af($4.i)); 317257353Sbdrewery else 318257353Sbdrewery reply(500, "EPSV mode not available."); 319258126Sglebius } 320257353Sbdrewery } 321258126Sglebius 322283788Sbapt | EPSV check_login SP ALL CRLF 323257353Sbdrewery { 324257353Sbdrewery if ($2) { 325257353Sbdrewery if (CURCLASS_FLAGS_ISSET(passive)) { 326257353Sbdrewery reply(200, 327257353Sbdrewery "EPSV ALL command successful."); 328257353Sbdrewery epsvall++; 329257353Sbdrewery } else 330257353Sbdrewery reply(500, "EPSV mode not available."); 331263020Sbapt } 332263020Sbapt } 333257353Sbdrewery 334257353Sbdrewery | EPSV check_login CRLF 335257353Sbdrewery { 336257353Sbdrewery if ($2) { 337257353Sbdrewery if (CURCLASS_FLAGS_ISSET(passive)) 338257353Sbdrewery long_passive("EPSV", PF_UNSPEC); 339257353Sbdrewery else 340263020Sbapt reply(500, "EPSV mode not available."); 341263020Sbapt } 342263020Sbapt } 343263020Sbapt 344257353Sbdrewery | TYPE check_login SP type_code CRLF 345263020Sbapt { 346257353Sbdrewery if ($2) { 347263020Sbapt 348257353Sbdrewery switch (cmd_type) { 349263020Sbapt 350263020Sbapt case TYPE_A: 351257353Sbdrewery if (cmd_form == FORM_N) { 352263020Sbapt reply(200, "Type set to A."); 353263020Sbapt type = cmd_type; 354257353Sbdrewery form = cmd_form; 355268896Sbapt } else 356263020Sbapt reply(504, "Form must be N."); 357257353Sbdrewery break; 358257353Sbdrewery 359257353Sbdrewery case TYPE_E: 360257353Sbdrewery reply(504, "Type E not implemented."); 361257353Sbdrewery break; 362257353Sbdrewery 363257353Sbdrewery case TYPE_I: 364257353Sbdrewery reply(200, "Type set to I."); 365257353Sbdrewery type = cmd_type; 366257353Sbdrewery break; 367257353Sbdrewery 368257353Sbdrewery case TYPE_L: 369257353Sbdrewery#if NBBY == 8 370257353Sbdrewery if (cmd_bytesz == 8) { 371257353Sbdrewery reply(200, 372257353Sbdrewery "Type set to L (byte size 8)."); 373257353Sbdrewery type = cmd_type; 374257353Sbdrewery } else 375257353Sbdrewery reply(504, "Byte size must be 8."); 376278563Sbapt#else /* NBBY == 8 */ 377278563Sbapt UNIMPLEMENTED for NBBY != 8 378278563Sbapt#endif /* NBBY == 8 */ 379257353Sbdrewery } 380278563Sbapt 381257353Sbdrewery } 382257353Sbdrewery } 383257353Sbdrewery 384257353Sbdrewery | STRU check_login SP struct_code CRLF 385257353Sbdrewery { 386257353Sbdrewery if ($2) { 387257353Sbdrewery switch ($4) { 388257353Sbdrewery 389257353Sbdrewery case STRU_F: 390257353Sbdrewery reply(200, "STRU F ok."); 391257353Sbdrewery break; 392257353Sbdrewery 393257353Sbdrewery default: 394257353Sbdrewery reply(504, "Unimplemented STRU type."); 395257353Sbdrewery } 396257353Sbdrewery } 397257353Sbdrewery } 398257353Sbdrewery 399257353Sbdrewery | MODE check_login SP mode_code CRLF 400257353Sbdrewery { 401257353Sbdrewery if ($2) { 402257353Sbdrewery switch ($4) { 403257353Sbdrewery 404257353Sbdrewery case MODE_S: 405257353Sbdrewery reply(200, "MODE S ok."); 406257353Sbdrewery break; 407257353Sbdrewery 408257353Sbdrewery default: 409257353Sbdrewery reply(502, "Unimplemented MODE type."); 410257353Sbdrewery } 411257353Sbdrewery } 412257353Sbdrewery } 413257353Sbdrewery 414257353Sbdrewery | RETR check_login SP pathname CRLF 415257353Sbdrewery { 416257353Sbdrewery if ($2 && $4 != NULL) 417257353Sbdrewery retrieve(NULL, $4); 418257353Sbdrewery if ($4 != NULL) 419257353Sbdrewery free($4); 420257353Sbdrewery } 421257353Sbdrewery 422257353Sbdrewery | STOR SP pathname CRLF 423257353Sbdrewery { 424257353Sbdrewery if (check_write($3, 1)) 425257353Sbdrewery store($3, "w", 0); 426257353Sbdrewery if ($3 != NULL) 427257353Sbdrewery free($3); 428257353Sbdrewery } 429257353Sbdrewery 430257353Sbdrewery | STOU SP pathname CRLF 431257353Sbdrewery { 432257353Sbdrewery if (check_write($3, 1)) 433257353Sbdrewery store($3, "w", 1); 434257353Sbdrewery if ($3 != NULL) 435257353Sbdrewery free($3); 436257353Sbdrewery } 437257353Sbdrewery 438257353Sbdrewery | APPE SP pathname CRLF 439257353Sbdrewery { 440257353Sbdrewery if (check_write($3, 1)) 441257353Sbdrewery store($3, "a", 0); 442257353Sbdrewery if ($3 != NULL) 443257353Sbdrewery free($3); 444257353Sbdrewery } 445257353Sbdrewery 446257353Sbdrewery | ALLO check_login SP NUMBER CRLF 447257353Sbdrewery { 448257353Sbdrewery if ($2) 449257353Sbdrewery reply(202, "ALLO command ignored."); 450257353Sbdrewery } 451257353Sbdrewery 452257353Sbdrewery | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 453257353Sbdrewery { 454257353Sbdrewery if ($2) 455257353Sbdrewery reply(202, "ALLO command ignored."); 456257353Sbdrewery } 457257353Sbdrewery 458257353Sbdrewery | RNTO SP pathname CRLF 459257353Sbdrewery { 460257353Sbdrewery if (check_write($3, 0)) { 461257353Sbdrewery if (fromname) { 462257353Sbdrewery renamecmd(fromname, $3); 463257353Sbdrewery REASSIGN(fromname, NULL); 464257353Sbdrewery } else { 465257353Sbdrewery reply(503, "Bad sequence of commands."); 466257353Sbdrewery } 467257353Sbdrewery } 468257353Sbdrewery if ($3 != NULL) 469257353Sbdrewery free($3); 470257353Sbdrewery } 471257353Sbdrewery 472257353Sbdrewery | ABOR check_login CRLF 473257353Sbdrewery { 474257353Sbdrewery if (is_oob) 475257353Sbdrewery abor(); 476257353Sbdrewery else if ($2) 477257353Sbdrewery reply(225, "ABOR command successful."); 478287872Sdelphij } 479287872Sdelphij 480287872Sdelphij | DELE SP pathname CRLF 481287872Sdelphij { 482287872Sdelphij if (check_write($3, 0)) 483287872Sdelphij delete($3); 484287872Sdelphij if ($3 != NULL) 485287872Sdelphij free($3); 486287872Sdelphij } 487287872Sdelphij 488287872Sdelphij | RMD SP pathname CRLF 489287872Sdelphij { 490287872Sdelphij if (check_write($3, 0)) 491287872Sdelphij removedir($3); 492287872Sdelphij if ($3 != NULL) 493287872Sdelphij free($3); 494287872Sdelphij } 495287872Sdelphij 496287872Sdelphij | MKD SP pathname CRLF 497257353Sbdrewery { 498257353Sbdrewery if (check_write($3, 0)) 499257353Sbdrewery makedir($3); 500257353Sbdrewery if ($3 != NULL) 501257353Sbdrewery free($3); 502257353Sbdrewery } 503257353Sbdrewery 504257353Sbdrewery | PWD check_login CRLF 505257353Sbdrewery { 506257353Sbdrewery if ($2) 507257353Sbdrewery pwd(); 508257353Sbdrewery } 509257353Sbdrewery 510257353Sbdrewery | LIST check_login CRLF 511257353Sbdrewery { 512257353Sbdrewery char *argv[] = { INTERNAL_LS, "-lgA", NULL }; 513257353Sbdrewery 514287872Sdelphij if (CURCLASS_FLAGS_ISSET(hidesymlinks)) 515287872Sdelphij argv[1] = "-LlgA"; 516257353Sbdrewery if ($2) 517257353Sbdrewery retrieve(argv, ""); 518257353Sbdrewery } 519257353Sbdrewery 520257353Sbdrewery | LIST check_login SP pathname CRLF 521257353Sbdrewery { 522257353Sbdrewery char *argv[] = { INTERNAL_LS, "-lgA", NULL, NULL }; 523257353Sbdrewery 524257353Sbdrewery if (CURCLASS_FLAGS_ISSET(hidesymlinks)) 525257353Sbdrewery argv[1] = "-LlgA"; 526257353Sbdrewery if ($2 && $4 != NULL) { 527287872Sdelphij argv[2] = $4; 528287872Sdelphij retrieve(argv, $4); 529257353Sbdrewery } 530257353Sbdrewery if ($4 != NULL) 531257353Sbdrewery free($4); 532257353Sbdrewery } 533257353Sbdrewery 534257353Sbdrewery | NLST check_login CRLF 535257353Sbdrewery { 536257353Sbdrewery if ($2) 537257353Sbdrewery send_file_list("."); 538257353Sbdrewery } 539287872Sdelphij 540287872Sdelphij | NLST check_login SP pathname CRLF 541287872Sdelphij { 542287872Sdelphij if ($2) 543287872Sdelphij send_file_list($4); 544287872Sdelphij free($4); 545287872Sdelphij } 546287872Sdelphij 547287872Sdelphij | SITE SP HELP CRLF 548287872Sdelphij { 549257353Sbdrewery help(sitetab, NULL); 550257353Sbdrewery } 551257353Sbdrewery 552257353Sbdrewery | SITE SP CHMOD SP octal_number SP pathname CRLF 553257353Sbdrewery { 554257353Sbdrewery if (check_write($7, 0)) { 555257353Sbdrewery if (($5 == -1) || ($5 > 0777)) 556257353Sbdrewery reply(501, 557257353Sbdrewery "CHMOD: Mode value must be between 0 and 0777"); 558257353Sbdrewery else if (chmod($7, $5) < 0) 559257353Sbdrewery perror_reply(550, $7); 560257353Sbdrewery else 561257353Sbdrewery reply(200, "CHMOD command successful."); 562257353Sbdrewery } 563257353Sbdrewery if ($7 != NULL) 564257353Sbdrewery free($7); 565257353Sbdrewery } 566257353Sbdrewery 567257353Sbdrewery | SITE SP HELP SP STRING CRLF 568257353Sbdrewery { 569257353Sbdrewery help(sitetab, $5); 570257353Sbdrewery free($5); 571257353Sbdrewery } 572257353Sbdrewery 573257353Sbdrewery | SITE SP IDLE check_login CRLF 574257353Sbdrewery { 575257353Sbdrewery if ($4) { 576257353Sbdrewery reply(200, 577257353Sbdrewery "Current IDLE time limit is " LLF 578257353Sbdrewery " seconds; max " LLF, 579257353Sbdrewery (LLT)curclass.timeout, 580257353Sbdrewery (LLT)curclass.maxtimeout); 581257353Sbdrewery } 582257353Sbdrewery } 583257353Sbdrewery 584257353Sbdrewery | SITE SP IDLE check_login SP NUMBER CRLF 585257353Sbdrewery { 586257353Sbdrewery if ($4) { 587257353Sbdrewery if ($6.i < 30 || $6.i > curclass.maxtimeout) { 588287872Sdelphij reply(501, 589287872Sdelphij "IDLE time limit must be between 30 and " 590287872Sdelphij LLF " seconds", 591287872Sdelphij (LLT)curclass.maxtimeout); 592287872Sdelphij } else { 593287872Sdelphij curclass.timeout = $6.i; 594287872Sdelphij (void) alarm(curclass.timeout); 595287872Sdelphij reply(200, 596287872Sdelphij "IDLE time limit set to " 597287872Sdelphij LLF " seconds", 598287872Sdelphij (LLT)curclass.timeout); 599287872Sdelphij } 600287872Sdelphij } 601287872Sdelphij } 602287872Sdelphij 603287872Sdelphij | SITE SP RATEGET check_login CRLF 604287872Sdelphij { 605287872Sdelphij if ($4) { 606287872Sdelphij reply(200, 607287872Sdelphij "Current RATEGET is " LLF " bytes/sec", 608287872Sdelphij (LLT)curclass.rateget); 609287872Sdelphij } 610287872Sdelphij } 611287872Sdelphij 612287872Sdelphij | SITE SP RATEGET check_login SP STRING CRLF 613287872Sdelphij { 614287872Sdelphij char errbuf[100]; 615287872Sdelphij char *p = $6; 616287872Sdelphij LLT rate; 617257353Sbdrewery 618257353Sbdrewery if ($4) { 619257353Sbdrewery rate = strsuftollx("RATEGET", p, 0, 620257353Sbdrewery curclass.maxrateget 621257353Sbdrewery ? curclass.maxrateget 622257353Sbdrewery : LLTMAX, errbuf, sizeof(errbuf)); 623257353Sbdrewery if (errbuf[0]) 624257353Sbdrewery reply(501, "%s", errbuf); 625257353Sbdrewery else { 626257353Sbdrewery curclass.rateget = rate; 627257353Sbdrewery reply(200, 628257353Sbdrewery "RATEGET set to " LLF " bytes/sec", 629257353Sbdrewery (LLT)curclass.rateget); 630257353Sbdrewery } 631257353Sbdrewery } 632257353Sbdrewery free($6); 633257353Sbdrewery } 634257353Sbdrewery 635257353Sbdrewery | SITE SP RATEPUT check_login CRLF 636257353Sbdrewery { 637257353Sbdrewery if ($4) { 638257353Sbdrewery reply(200, 639257353Sbdrewery "Current RATEPUT is " LLF " bytes/sec", 640257353Sbdrewery (LLT)curclass.rateput); 641257353Sbdrewery } 642257353Sbdrewery } 643257353Sbdrewery 644257353Sbdrewery | SITE SP RATEPUT check_login SP STRING CRLF 645257353Sbdrewery { 646257353Sbdrewery char errbuf[100]; 647257353Sbdrewery char *p = $6; 648257353Sbdrewery LLT rate; 649257353Sbdrewery 650257353Sbdrewery if ($4) { 651257353Sbdrewery rate = strsuftollx("RATEPUT", p, 0, 652257353Sbdrewery curclass.maxrateput 653257353Sbdrewery ? curclass.maxrateput 654257353Sbdrewery : LLTMAX, errbuf, sizeof(errbuf)); 655257353Sbdrewery if (errbuf[0]) 656257353Sbdrewery reply(501, "%s", errbuf); 657257353Sbdrewery else { 658257353Sbdrewery curclass.rateput = rate; 659257353Sbdrewery reply(200, 660257353Sbdrewery "RATEPUT set to " LLF " bytes/sec", 661257353Sbdrewery (LLT)curclass.rateput); 662257353Sbdrewery } 663257353Sbdrewery } 664257353Sbdrewery free($6); 665257353Sbdrewery } 666257353Sbdrewery 667257353Sbdrewery | SITE SP UMASK check_login CRLF 668257353Sbdrewery { 669257353Sbdrewery int oldmask; 670257353Sbdrewery 671257353Sbdrewery if ($4) { 672257353Sbdrewery oldmask = umask(0); 673257353Sbdrewery (void) umask(oldmask); 674257353Sbdrewery reply(200, "Current UMASK is %03o", oldmask); 675257353Sbdrewery } 676257353Sbdrewery } 677257353Sbdrewery 678257353Sbdrewery | SITE SP UMASK check_login SP octal_number CRLF 679257353Sbdrewery { 680257353Sbdrewery int oldmask; 681257353Sbdrewery 682257353Sbdrewery if ($4 && check_write("", 0)) { 683257353Sbdrewery if (($6 == -1) || ($6 > 0777)) { 684257353Sbdrewery reply(501, "Bad UMASK value"); 685257353Sbdrewery } else { 686257353Sbdrewery oldmask = umask($6); 687257353Sbdrewery reply(200, 688257353Sbdrewery "UMASK set to %03o (was %03o)", 689257353Sbdrewery $6, oldmask); 690287872Sdelphij } 691287872Sdelphij } 692287872Sdelphij } 693287872Sdelphij 694287872Sdelphij | SYST CRLF 695287872Sdelphij { 696287872Sdelphij if (EMPTYSTR(version)) 697287872Sdelphij reply(215, "UNIX Type: L%d", NBBY); 698287872Sdelphij else 699287872Sdelphij reply(215, "UNIX Type: L%d Version: %s", NBBY, 700287872Sdelphij version); 701287872Sdelphij } 702287872Sdelphij 703287872Sdelphij | STAT check_login SP pathname CRLF 704287872Sdelphij { 705287872Sdelphij if ($2 && $4 != NULL) 706287872Sdelphij statfilecmd($4); 707287872Sdelphij if ($4 != NULL) 708287872Sdelphij free($4); 709287872Sdelphij } 710287872Sdelphij 711287872Sdelphij | STAT CRLF 712287872Sdelphij { 713287872Sdelphij if (is_oob) 714287872Sdelphij statxfer(); 715287872Sdelphij else 716287872Sdelphij statcmd(); 717287872Sdelphij } 718287872Sdelphij 719287872Sdelphij | HELP CRLF 720287872Sdelphij { 721287872Sdelphij help(cmdtab, NULL); 722287872Sdelphij } 723287872Sdelphij 724287872Sdelphij | HELP SP STRING CRLF 725287872Sdelphij { 726287872Sdelphij char *cp = $3; 727287872Sdelphij 728287872Sdelphij if (strncasecmp(cp, "SITE", 4) == 0) { 729257353Sbdrewery cp = $3 + 4; 730257353Sbdrewery if (*cp == ' ') 731257353Sbdrewery cp++; 732257353Sbdrewery if (*cp) 733257353Sbdrewery help(sitetab, cp); 734257353Sbdrewery else 735257353Sbdrewery help(sitetab, NULL); 736257353Sbdrewery } else 737257353Sbdrewery help(cmdtab, $3); 738257353Sbdrewery free($3); 739257353Sbdrewery } 740257353Sbdrewery 741257353Sbdrewery | NOOP CRLF 742257353Sbdrewery { 743257353Sbdrewery reply(200, "NOOP command successful."); 744257353Sbdrewery } 745257353Sbdrewery 746257353Sbdrewery /* RFC 2228 */ 747257353Sbdrewery | AUTH SP mechanism_name CRLF 748257353Sbdrewery { 749257353Sbdrewery reply(502, "RFC 2228 authentication not implemented."); 750257353Sbdrewery free($3); 751257353Sbdrewery } 752257353Sbdrewery 753257353Sbdrewery | ADAT SP base64data CRLF 754257353Sbdrewery { 755257353Sbdrewery reply(503, 756257353Sbdrewery "Please set authentication state with AUTH."); 757257353Sbdrewery free($3); 758257353Sbdrewery } 759257353Sbdrewery 760257353Sbdrewery | PROT SP prot_code CRLF 761257353Sbdrewery { 762257353Sbdrewery reply(503, 763257353Sbdrewery "Please set protection buffer size with PBSZ."); 764257353Sbdrewery free($3); 765257353Sbdrewery } 766257353Sbdrewery 767257353Sbdrewery | PBSZ SP decimal_integer CRLF 768257353Sbdrewery { 769257353Sbdrewery reply(503, 770257353Sbdrewery "Please set authentication state with AUTH."); 771257353Sbdrewery } 772257353Sbdrewery 773257353Sbdrewery | CCC CRLF 774257353Sbdrewery { 775257353Sbdrewery reply(533, "No protection enabled."); 776257353Sbdrewery } 777257353Sbdrewery 778257353Sbdrewery | MIC SP base64data CRLF 779257353Sbdrewery { 780257353Sbdrewery reply(502, "RFC 2228 authentication not implemented."); 781257353Sbdrewery free($3); 782257353Sbdrewery } 783257353Sbdrewery 784257353Sbdrewery | CONF SP base64data CRLF 785257353Sbdrewery { 786257353Sbdrewery reply(502, "RFC 2228 authentication not implemented."); 787257353Sbdrewery free($3); 788257353Sbdrewery } 789257353Sbdrewery 790257353Sbdrewery | ENC SP base64data CRLF 791257353Sbdrewery { 792257353Sbdrewery reply(502, "RFC 2228 authentication not implemented."); 793257353Sbdrewery free($3); 794257353Sbdrewery } 795257353Sbdrewery 796257353Sbdrewery /* RFC 2389 */ 797257353Sbdrewery | FEAT CRLF 798257353Sbdrewery { 799257353Sbdrewery 800257353Sbdrewery feat(); 801257353Sbdrewery } 802257353Sbdrewery 803257353Sbdrewery | OPTS SP STRING CRLF 804257353Sbdrewery { 805257353Sbdrewery 806287872Sdelphij opts($3); 807257353Sbdrewery free($3); 808257353Sbdrewery } 809257353Sbdrewery 810257353Sbdrewery 811257353Sbdrewery /* extensions from draft-ietf-ftpext-mlst-11 */ 812257353Sbdrewery 813257353Sbdrewery /* 814257353Sbdrewery * Return size of file in a format suitable for 815257353Sbdrewery * using with RESTART (we just count bytes). 816257353Sbdrewery */ 817257353Sbdrewery | SIZE check_login SP pathname CRLF 818257353Sbdrewery { 819257353Sbdrewery if ($2 && $4 != NULL) 820283788Sbapt sizecmd($4); 821283788Sbapt if ($4 != NULL) 822283788Sbapt free($4); 823257353Sbdrewery } 824257353Sbdrewery 825257353Sbdrewery /* 826257353Sbdrewery * Return modification time of file as an ISO 3307 827257353Sbdrewery * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 828257353Sbdrewery * where xxx is the fractional second (of any precision, 829257353Sbdrewery * not necessarily 3 digits) 830257632Sbdrewery */ 831257353Sbdrewery | MDTM check_login SP pathname CRLF 832257353Sbdrewery { 833257353Sbdrewery if ($2 && $4 != NULL) { 834257353Sbdrewery struct stat stbuf; 835257353Sbdrewery if (stat($4, &stbuf) < 0) 836257353Sbdrewery perror_reply(550, $4); 837257353Sbdrewery else if (!S_ISREG(stbuf.st_mode)) { 838257353Sbdrewery reply(550, "%s: not a plain file.", $4); 839257353Sbdrewery } else { 840257353Sbdrewery struct tm *t; 841257353Sbdrewery 842257353Sbdrewery t = gmtime(&stbuf.st_mtime); 843257353Sbdrewery reply(213, 844257353Sbdrewery "%04d%02d%02d%02d%02d%02d", 845257353Sbdrewery TM_YEAR_BASE + t->tm_year, 846257353Sbdrewery t->tm_mon+1, t->tm_mday, 847257353Sbdrewery t->tm_hour, t->tm_min, t->tm_sec); 848257353Sbdrewery } 849257353Sbdrewery } 850257353Sbdrewery if ($4 != NULL) 851257353Sbdrewery free($4); 852257353Sbdrewery } 853257353Sbdrewery 854257353Sbdrewery | MLST check_login SP pathname CRLF 855257353Sbdrewery { 856257353Sbdrewery if ($2 && $4 != NULL) 857257353Sbdrewery mlst($4); 858257353Sbdrewery if ($4 != NULL) 859257353Sbdrewery free($4); 860257353Sbdrewery } 861257353Sbdrewery 862257353Sbdrewery | MLST check_login CRLF 863257353Sbdrewery { 864257353Sbdrewery mlst(NULL); 865257353Sbdrewery } 866257353Sbdrewery 867257353Sbdrewery | MLSD check_login SP pathname CRLF 868257353Sbdrewery { 869257353Sbdrewery if ($2 && $4 != NULL) 870257353Sbdrewery mlsd($4); 871287145Sdelphij if ($4 != NULL) 872287872Sdelphij free($4); 873287872Sdelphij } 874287872Sdelphij 875287872Sdelphij | MLSD check_login CRLF 876287872Sdelphij { 877287872Sdelphij mlsd(NULL); 878287872Sdelphij } 879287872Sdelphij 880287872Sdelphij | error CRLF 881287872Sdelphij { 882287872Sdelphij yyerrok; 883287872Sdelphij } 884287872Sdelphij ; 885287872Sdelphij 886287872Sdelphijrcmd 887287872Sdelphij : REST check_login SP NUMBER CRLF 888287872Sdelphij { 889287872Sdelphij if ($2) { 890287872Sdelphij REASSIGN(fromname, NULL); 891287872Sdelphij restart_point = (off_t)$4.ll; 892287872Sdelphij reply(350, 893287872Sdelphij "Restarting at " LLF ". Send STORE or RETRIEVE to initiate transfer.", 894287872Sdelphij (LLT)restart_point); 895287872Sdelphij } 896287872Sdelphij } 897287872Sdelphij 898287872Sdelphij | RNFR SP pathname CRLF 899287872Sdelphij { 900287872Sdelphij restart_point = (off_t) 0; 901287872Sdelphij if (check_write($3, 0)) { 902287872Sdelphij REASSIGN(fromname, NULL); 903287872Sdelphij fromname = renamefrom($3); 904287145Sdelphij } 905287145Sdelphij if ($3 != NULL) 906287145Sdelphij free($3); 907287145Sdelphij } 908257353Sbdrewery ; 909257353Sbdrewery 910257353Sbdreweryusername 911257632Sbdrewery : STRING 912234313Sbapt ; 913234351Sbapt 914234351Sbaptpassword 915234351Sbapt : /* empty */ 916234351Sbapt { 917257353Sbdrewery $$ = (char *)calloc(1, sizeof(char)); 918257353Sbdrewery } 919257353Sbdrewery 920257353Sbdrewery | STRING 921234351Sbapt ; 922234313Sbapt 923257353Sbdrewerybyte_size 924257353Sbdrewery : NUMBER 925257353Sbdrewery { 926257353Sbdrewery $$ = $1.i; 927234313Sbapt } 928278563Sbapt ; 929278563Sbapt 930278563Sbapthost_port 931278563Sbapt : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 932278563Sbapt NUMBER COMMA NUMBER 933234351Sbapt { 934234313Sbapt char *a, *p; 935234313Sbapt 936238461Skan memset(&data_dest, 0, sizeof(data_dest)); 937238461Skan data_dest.su_len = sizeof(struct sockaddr_in); 938238461Skan data_dest.su_family = AF_INET; 939238461Skan p = (char *)&data_dest.su_port; 940283790Sbapt p[0] = $9.i; p[1] = $11.i; 941283790Sbapt a = (char *)&data_dest.su_addr; 942283790Sbapt a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i; 943283790Sbapt } 944283790Sbapt ; 945238461Skan 946238461Skanhost_long_port4 947238461Skan : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 948238461Skan NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 949238461Skan NUMBER 950238461Skan { 951238461Skan char *a, *p; 952238461Skan 953238461Skan memset(&data_dest, 0, sizeof(data_dest)); 954238461Skan data_dest.su_len = sizeof(struct sockaddr_in); 955238461Skan data_dest.su_family = AF_INET; 956238461Skan p = (char *)&data_dest.su_port; 957238461Skan p[0] = $15.i; p[1] = $17.i; 958238461Skan a = (char *)&data_dest.su_addr; 959238461Skan a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 960238461Skan 961238461Skan /* reject invalid LPRT command */ 962238461Skan if ($1.i != 4 || $3.i != 4 || $13.i != 2) 963257353Sbdrewery memset(&data_dest, 0, sizeof(data_dest)); 964257632Sbdrewery } 965257353Sbdrewery ; 966257353Sbdrewery 967257353Sbdreweryhost_long_port6 968257353Sbdrewery : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 969257353Sbdrewery NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 970257353Sbdrewery NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 971257353Sbdrewery NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 972257353Sbdrewery NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 973257353Sbdrewery NUMBER 974257353Sbdrewery { 975257353Sbdrewery#ifdef INET6 976257353Sbdrewery char *a, *p; 977257353Sbdrewery 978257353Sbdrewery memset(&data_dest, 0, sizeof(data_dest)); 979257353Sbdrewery data_dest.su_len = sizeof(struct sockaddr_in6); 980278563Sbapt data_dest.su_family = AF_INET6; 981257353Sbdrewery p = (char *)&data_dest.su_port; 982257353Sbdrewery p[0] = $39.i; p[1] = $41.i; 983287145Sdelphij a = (char *)&data_dest.si_su.su_sin6.sin6_addr; 984287872Sdelphij a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 985287872Sdelphij a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i; 986287872Sdelphij a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i; 987287872Sdelphij a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i; 988287872Sdelphij if (his_addr.su_family == AF_INET6) { 989287872Sdelphij /* XXX: more sanity checks! */ 990287872Sdelphij data_dest.su_scope_id = his_addr.su_scope_id; 991287872Sdelphij } 992287872Sdelphij#else 993287872Sdelphij memset(&data_dest, 0, sizeof(data_dest)); 994287872Sdelphij#endif /* INET6 */ 995287872Sdelphij /* reject invalid LPRT command */ 996287872Sdelphij if ($1.i != 6 || $3.i != 16 || $37.i != 2) 997287872Sdelphij memset(&data_dest, 0, sizeof(data_dest)); 998287872Sdelphij } 999287872Sdelphij ; 1000287872Sdelphij 1001287872Sdelphijform_code 1002287872Sdelphij : N 1003287872Sdelphij { 1004287872Sdelphij $$ = FORM_N; 1005287872Sdelphij } 1006287872Sdelphij 1007287872Sdelphij | T 1008287872Sdelphij { 1009287872Sdelphij $$ = FORM_T; 1010287872Sdelphij } 1011287145Sdelphij 1012287145Sdelphij | C 1013287145Sdelphij { 1014287145Sdelphij $$ = FORM_C; 1015257353Sbdrewery } 1016257353Sbdrewery ; 1017257353Sbdrewery 1018257632Sbdrewerytype_code 1019257353Sbdrewery : A 1020257353Sbdrewery { 1021257353Sbdrewery cmd_type = TYPE_A; 1022257353Sbdrewery cmd_form = FORM_N; 1023257353Sbdrewery } 1024257353Sbdrewery 1025257353Sbdrewery | A SP form_code 1026257353Sbdrewery { 1027257353Sbdrewery cmd_type = TYPE_A; 1028234313Sbapt cmd_form = $3; 1029283789Sbapt } 1030234313Sbapt 1031234313Sbapt | E 1032257632Sbdrewery { 1033257632Sbdrewery cmd_type = TYPE_E; 1034234313Sbapt cmd_form = FORM_N; 1035257632Sbdrewery } 1036257632Sbdrewery 1037257632Sbdrewery | E SP form_code 1038257632Sbdrewery { 1039257632Sbdrewery cmd_type = TYPE_E; 1040234313Sbapt cmd_form = $3; 1041234322Sbapt } 1042234313Sbapt 1043257632Sbdrewery | I 1044257632Sbdrewery { 1045257632Sbdrewery cmd_type = TYPE_I; 1046257632Sbdrewery } 1047257632Sbdrewery 1048257632Sbdrewery | L 1049257632Sbdrewery { 1050244553Smatthew cmd_type = TYPE_L; 1051244594Smatthew cmd_bytesz = NBBY; 1052244553Smatthew } 1053244553Smatthew 1054244553Smatthew | L SP byte_size 1055244639Smatthew { 1056244639Smatthew cmd_type = TYPE_L; 1057244553Smatthew cmd_bytesz = $3; 1058257353Sbdrewery } 1059257353Sbdrewery 1060257632Sbdrewery /* this is for a bug in the BBN ftp */ 1061257632Sbdrewery | L byte_size 1062257632Sbdrewery { 1063257632Sbdrewery cmd_type = TYPE_L; 1064257632Sbdrewery cmd_bytesz = $2; 1065257632Sbdrewery } 1066257632Sbdrewery ; 1067257632Sbdrewery 1068257309Sbaptstruct_code 1069257632Sbdrewery : F 1070257632Sbdrewery { 1071257632Sbdrewery $$ = STRU_F; 1072257632Sbdrewery } 1073257632Sbdrewery 1074257632Sbdrewery | R 1075257632Sbdrewery { 1076257309Sbapt $$ = STRU_R; 1077257309Sbapt } 1078238461Skan 1079238461Skan | P 1080238461Skan { 1081238461Skan $$ = STRU_P; 1082238461Skan } 1083247841Sbapt ; 1084247841Sbapt 1085283790Sbaptmode_code 1086283790Sbapt : S 1087238461Skan { 1088283790Sbapt $$ = MODE_S; 1089239664Sbapt } 1090283790Sbapt 1091239664Sbapt | B 1092239663Sbapt { 1093238461Skan $$ = MODE_B; 1094257632Sbdrewery } 1095234351Sbapt 1096247841Sbapt | C 1097257571Sbdrewery { 1098257632Sbdrewery $$ = MODE_C; 1099257571Sbdrewery } 1100257632Sbdrewery ; 1101257632Sbdrewery 1102257632Sbdrewerypathname 1103238461Skan : pathstring 1104234313Sbapt { 1105234313Sbapt /* 1106234313Sbapt * Problem: this production is used for all pathname 1107234351Sbapt * processing, but only gives a 550 error reply. 1108234322Sbapt * This is a valid reply in some cases but not in 1109234313Sbapt * others. 1110 */ 1111 if (logged_in && $1 && *$1 == '~') { 1112 char *path, *home, *result; 1113 size_t len; 1114 1115 path = strchr($1 + 1, '/'); 1116 if (path != NULL) 1117 *path++ = '\0'; 1118 if ($1[1] == '\0') 1119 home = homedir; 1120 else { 1121 struct passwd *hpw; 1122 1123 if ((hpw = getpwnam($1 + 1)) != NULL) 1124 home = hpw->pw_dir; 1125 else 1126 home = $1; 1127 } 1128 len = strlen(home) + 1; 1129 if (path != NULL) 1130 len += strlen(path) + 1; 1131 if ((result = malloc(len)) == NULL) 1132 fatal("Local resource failure: malloc"); 1133 strlcpy(result, home, len); 1134 if (path != NULL) { 1135 strlcat(result, "/", len); 1136 strlcat(result, path, len); 1137 } 1138 $$ = result; 1139 free($1); 1140 } else 1141 $$ = $1; 1142 } 1143 ; 1144 1145pathstring 1146 : STRING 1147 ; 1148 1149octal_number 1150 : NUMBER 1151 { 1152 int ret, dec, multby, digit; 1153 1154 /* 1155 * Convert a number that was read as decimal number 1156 * to what it would be if it had been read as octal. 1157 */ 1158 dec = $1.i; 1159 multby = 1; 1160 ret = 0; 1161 while (dec) { 1162 digit = dec%10; 1163 if (digit > 7) { 1164 ret = -1; 1165 break; 1166 } 1167 ret += digit * multby; 1168 multby *= 8; 1169 dec /= 10; 1170 } 1171 $$ = ret; 1172 } 1173 ; 1174 1175mechanism_name 1176 : STRING 1177 ; 1178 1179base64data 1180 : STRING 1181 ; 1182 1183prot_code 1184 : STRING 1185 ; 1186 1187decimal_integer 1188 : NUMBER 1189 { 1190 $$ = $1.i; 1191 } 1192 ; 1193 1194check_login 1195 : /* empty */ 1196 { 1197 if (logged_in) 1198 $$ = 1; 1199 else { 1200 reply(530, "Please login with USER and PASS."); 1201 $$ = 0; 1202 hasyyerrored = 1; 1203 } 1204 } 1205 ; 1206 1207%% 1208 1209#define CMD 0 /* beginning of command */ 1210#define ARGS 1 /* expect miscellaneous arguments */ 1211#define STR1 2 /* expect SP followed by STRING */ 1212#define STR2 3 /* expect STRING */ 1213#define OSTR 4 /* optional SP then STRING */ 1214#define ZSTR1 5 /* SP then optional STRING */ 1215#define ZSTR2 6 /* optional STRING after SP */ 1216#define SITECMD 7 /* SITE command */ 1217#define NSTR 8 /* Number followed by a string */ 1218#define NOARGS 9 /* No arguments allowed */ 1219#define EOLN 10 /* End of line */ 1220 1221struct tab cmdtab[] = { 1222 /* From RFC 959, in order defined (5.3.1) */ 1223 { "USER", USER, STR1, 1, "<sp> username" }, 1224 { "PASS", PASS, ZSTR1, 1, "<sp> password" }, 1225 { "ACCT", ACCT, STR1, 0, "(specify account)" }, 1226 { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 1227 { "CDUP", CDUP, NOARGS, 1, "(change to parent directory)" }, 1228 { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 1229 { "QUIT", QUIT, NOARGS, 1, "(terminate service)" }, 1230 { "REIN", REIN, NOARGS, 0, "(reinitialize server state)" }, 1231 { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" }, 1232 { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 1233 { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 1234 { "PASV", PASV, NOARGS, 1, "(set server in passive mode)" }, 1235 { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 1236 { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 1237 { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" }, 1238 { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 1239 { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 1240 { "RETR", RETR, STR1, 1, "<sp> file-name" }, 1241 { "STOR", STOR, STR1, 1, "<sp> file-name" }, 1242 { "STOU", STOU, STR1, 1, "<sp> file-name" }, 1243 { "APPE", APPE, STR1, 1, "<sp> file-name" }, 1244 { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 1245 { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 1246 { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 1247 { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 1248 { "ABOR", ABOR, NOARGS, 4, "(abort operation)" }, 1249 { "DELE", DELE, STR1, 1, "<sp> file-name" }, 1250 { "RMD", RMD, STR1, 1, "<sp> path-name" }, 1251 { "MKD", MKD, STR1, 1, "<sp> path-name" }, 1252 { "PWD", PWD, NOARGS, 1, "(return current directory)" }, 1253 { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 1254 { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 1255 { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 1256 { "SYST", SYST, NOARGS, 1, "(get type of operating system)" }, 1257 { "STAT", STAT, OSTR, 4, "[ <sp> path-name ]" }, 1258 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 1259 { "NOOP", NOOP, NOARGS, 2, "" }, 1260 1261 /* From RFC 2228, in order defined */ 1262 { "AUTH", AUTH, STR1, 1, "<sp> mechanism-name" }, 1263 { "ADAT", ADAT, STR1, 1, "<sp> base-64-data" }, 1264 { "PROT", PROT, STR1, 1, "<sp> prot-code" }, 1265 { "PBSZ", PBSZ, ARGS, 1, "<sp> decimal-integer" }, 1266 { "CCC", CCC, NOARGS, 1, "(Disable data protection)" }, 1267 { "MIC", MIC, STR1, 4, "<sp> base64data" }, 1268 { "CONF", CONF, STR1, 4, "<sp> base64data" }, 1269 { "ENC", ENC, STR1, 4, "<sp> base64data" }, 1270 1271 /* From RFC 2389, in order defined */ 1272 { "FEAT", FEAT, NOARGS, 1, "(display extended features)" }, 1273 { "OPTS", OPTS, STR1, 1, "<sp> command [ <sp> options ]" }, 1274 1275 /* from draft-ietf-ftpext-mlst-11 */ 1276 { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 1277 { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 1278 { "MLST", MLST, OSTR, 2, "[ <sp> path-name ]" }, 1279 { "MLSD", MLSD, OSTR, 1, "[ <sp> directory-name ]" }, 1280 1281 /* obsolete commands */ 1282 { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 1283 { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 1284 { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 1285 { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 1286 { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 1287 { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 1288 { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 1289 { "XCUP", CDUP, NOARGS, 1, "(change to parent directory)" }, 1290 { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 1291 { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 1292 { "XPWD", PWD, NOARGS, 1, "(return current directory)" }, 1293 { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 1294 1295 { NULL, 0, 0, 0, 0 } 1296}; 1297 1298struct tab sitetab[] = { 1299 { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 1300 { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 1301 { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 1302 { "RATEGET", RATEGET,OSTR, 1, "[ <sp> get-throttle-rate ]" }, 1303 { "RATEPUT", RATEPUT,OSTR, 1, "[ <sp> put-throttle-rate ]" }, 1304 { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 1305 { NULL, 0, 0, 0, NULL } 1306}; 1307 1308/* 1309 * Check if a filename is allowed to be modified (isupload == 0) or 1310 * uploaded (isupload == 1), and if necessary, check the filename is `sane'. 1311 * If the filename is NULL, fail. 1312 * If the filename is "", don't do the sane name check. 1313 */ 1314static int 1315check_write(const char *file, int isupload) 1316{ 1317 if (file == NULL) 1318 return (0); 1319 if (! logged_in) { 1320 reply(530, "Please login with USER and PASS."); 1321 return (0); 1322 } 1323 /* checking modify */ 1324 if (! isupload && ! CURCLASS_FLAGS_ISSET(modify)) { 1325 reply(502, "No permission to use this command."); 1326 return (0); 1327 } 1328 /* checking upload */ 1329 if (isupload && ! CURCLASS_FLAGS_ISSET(upload)) { 1330 reply(502, "No permission to use this command."); 1331 return (0); 1332 } 1333 1334 /* checking sanenames */ 1335 if (file[0] != '\0' && CURCLASS_FLAGS_ISSET(sanenames)) { 1336 const char *p; 1337 1338 if (file[0] == '.') 1339 goto insane_name; 1340 for (p = file; *p; p++) { 1341 if (isalnum((unsigned char)*p) || *p == '-' || *p == '+' || 1342 *p == ',' || *p == '.' || *p == '_') 1343 continue; 1344 insane_name: 1345 reply(553, "File name `%s' not allowed.", file); 1346 return (0); 1347 } 1348 } 1349 return (1); 1350} 1351 1352struct tab * 1353lookup(struct tab *p, const char *cmd) 1354{ 1355 1356 for (; p->name != NULL; p++) 1357 if (strcasecmp(cmd, p->name) == 0) 1358 return (p); 1359 return (0); 1360} 1361 1362#include <arpa/telnet.h> 1363 1364/* 1365 * getline - a hacked up version of fgets to ignore TELNET escape codes. 1366 * `s' is the buffer to read into. 1367 * `n' is the 1 less than the size of the buffer, to allow trailing NUL 1368 * `iop' is the FILE to read from. 1369 * Returns 0 on success, -1 on EOF, -2 if the command was too long. 1370 */ 1371int 1372getline(char *s, int n, FILE *iop) 1373{ 1374 int c; 1375 char *cs; 1376 1377 cs = s; 1378/* tmpline may contain saved command from urgent mode interruption */ 1379 for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 1380 *cs++ = tmpline[c]; 1381 if (tmpline[c] == '\n') { 1382 *cs++ = '\0'; 1383 if (ftpd_debug) 1384 syslog(LOG_DEBUG, "command: %s", s); 1385 tmpline[0] = '\0'; 1386 return(0); 1387 } 1388 if (c == 0) 1389 tmpline[0] = '\0'; 1390 } 1391 while ((c = getc(iop)) != EOF) { 1392 total_bytes++; 1393 total_bytes_in++; 1394 c &= 0377; 1395 if (c == IAC) { 1396 if ((c = getc(iop)) != EOF) { 1397 total_bytes++; 1398 total_bytes_in++; 1399 c &= 0377; 1400 switch (c) { 1401 case WILL: 1402 case WONT: 1403 c = getc(iop); 1404 total_bytes++; 1405 total_bytes_in++; 1406 cprintf(stdout, "%c%c%c", IAC, DONT, 0377&c); 1407 (void) fflush(stdout); 1408 continue; 1409 case DO: 1410 case DONT: 1411 c = getc(iop); 1412 total_bytes++; 1413 total_bytes_in++; 1414 cprintf(stdout, "%c%c%c", IAC, WONT, 0377&c); 1415 (void) fflush(stdout); 1416 continue; 1417 case IAC: 1418 break; 1419 default: 1420 continue; /* ignore command */ 1421 } 1422 } 1423 } 1424 *cs++ = c; 1425 if (--n <= 0) { 1426 /* 1427 * If command doesn't fit into buffer, discard the 1428 * rest of the command and indicate truncation. 1429 * This prevents the command to be split up into 1430 * multiple commands. 1431 */ 1432 if (ftpd_debug) 1433 syslog(LOG_DEBUG, 1434 "command too long, last char: %d", c); 1435 while (c != '\n' && (c = getc(iop)) != EOF) 1436 continue; 1437 return (-2); 1438 } 1439 if (c == '\n') 1440 break; 1441 } 1442 if (c == EOF && cs == s) 1443 return (-1); 1444 *cs++ = '\0'; 1445 if (ftpd_debug) { 1446 if ((curclass.type != CLASS_GUEST && 1447 strncasecmp(s, "PASS ", 5) == 0) || 1448 strncasecmp(s, "ACCT ", 5) == 0) { 1449 /* Don't syslog passwords */ 1450 syslog(LOG_DEBUG, "command: %.4s ???", s); 1451 } else { 1452 char *cp; 1453 int len; 1454 1455 /* Don't syslog trailing CR-LF */ 1456 len = strlen(s); 1457 cp = s + len - 1; 1458 while (cp >= s && (*cp == '\n' || *cp == '\r')) { 1459 --cp; 1460 --len; 1461 } 1462 syslog(LOG_DEBUG, "command: %.*s", len, s); 1463 } 1464 } 1465 return (0); 1466} 1467 1468void 1469ftp_handle_line(char *cp) 1470{ 1471 1472 cmdp = cp; 1473 yyparse(); 1474} 1475 1476void 1477ftp_loop(void) 1478{ 1479 int ret; 1480 1481 while (1) { 1482 (void) alarm(curclass.timeout); 1483 ret = getline(cbuf, sizeof(cbuf)-1, stdin); 1484 (void) alarm(0); 1485 if (ret == -1) { 1486 reply(221, "You could at least say goodbye."); 1487 dologout(0); 1488 } else if (ret == -2) { 1489 reply(500, "Command too long."); 1490 } else { 1491 ftp_handle_line(cbuf); 1492 } 1493 } 1494 /*NOTREACHED*/ 1495} 1496 1497int 1498yylex(void) 1499{ 1500 static int cpos, state; 1501 char *cp, *cp2; 1502 struct tab *p; 1503 int n; 1504 char c; 1505 1506 switch (state) { 1507 1508 case CMD: 1509 hasyyerrored = 0; 1510 if ((cp = strchr(cmdp, '\r'))) { 1511 *cp = '\0'; 1512#if HAVE_SETPROCTITLE 1513 if (strncasecmp(cmdp, "PASS", 4) != 0 && 1514 strncasecmp(cmdp, "ACCT", 4) != 0) 1515 setproctitle("%s: %s", proctitle, cmdp); 1516#endif /* HAVE_SETPROCTITLE */ 1517 *cp++ = '\n'; 1518 *cp = '\0'; 1519 } 1520 if ((cp = strpbrk(cmdp, " \n"))) 1521 cpos = cp - cmdp; 1522 if (cpos == 0) 1523 cpos = 4; 1524 c = cmdp[cpos]; 1525 cmdp[cpos] = '\0'; 1526 p = lookup(cmdtab, cmdp); 1527 cmdp[cpos] = c; 1528 if (p != NULL) { 1529 if (is_oob && ! CMD_OOB(p)) { 1530 /* command will be handled in-band */ 1531 return (0); 1532 } else if (! CMD_IMPLEMENTED(p)) { 1533 reply(502, "%s command not implemented.", 1534 p->name); 1535 hasyyerrored = 1; 1536 break; 1537 } 1538 state = p->state; 1539 yylval.s = p->name; 1540 return (p->token); 1541 } 1542 break; 1543 1544 case SITECMD: 1545 if (cmdp[cpos] == ' ') { 1546 cpos++; 1547 return (SP); 1548 } 1549 cp = &cmdp[cpos]; 1550 if ((cp2 = strpbrk(cp, " \n"))) 1551 cpos = cp2 - cmdp; 1552 c = cmdp[cpos]; 1553 cmdp[cpos] = '\0'; 1554 p = lookup(sitetab, cp); 1555 cmdp[cpos] = c; 1556 if (p != NULL) { 1557 if (!CMD_IMPLEMENTED(p)) { 1558 reply(502, "SITE %s command not implemented.", 1559 p->name); 1560 hasyyerrored = 1; 1561 break; 1562 } 1563 state = p->state; 1564 yylval.s = p->name; 1565 return (p->token); 1566 } 1567 break; 1568 1569 case OSTR: 1570 if (cmdp[cpos] == '\n') { 1571 state = EOLN; 1572 return (CRLF); 1573 } 1574 /* FALLTHROUGH */ 1575 1576 case STR1: 1577 case ZSTR1: 1578 dostr1: 1579 if (cmdp[cpos] == ' ') { 1580 cpos++; 1581 state = state == OSTR ? STR2 : state+1; 1582 return (SP); 1583 } 1584 break; 1585 1586 case ZSTR2: 1587 if (cmdp[cpos] == '\n') { 1588 state = EOLN; 1589 return (CRLF); 1590 } 1591 /* FALLTHROUGH */ 1592 1593 case STR2: 1594 cp = &cmdp[cpos]; 1595 n = strlen(cp); 1596 cpos += n - 1; 1597 /* 1598 * Make sure the string is nonempty and \n terminated. 1599 */ 1600 if (n > 1 && cmdp[cpos] == '\n') { 1601 cmdp[cpos] = '\0'; 1602 yylval.s = ftpd_strdup(cp); 1603 cmdp[cpos] = '\n'; 1604 state = ARGS; 1605 return (STRING); 1606 } 1607 break; 1608 1609 case NSTR: 1610 if (cmdp[cpos] == ' ') { 1611 cpos++; 1612 return (SP); 1613 } 1614 if (isdigit((unsigned char)cmdp[cpos])) { 1615 cp = &cmdp[cpos]; 1616 while (isdigit((unsigned char)cmdp[++cpos])) 1617 ; 1618 c = cmdp[cpos]; 1619 cmdp[cpos] = '\0'; 1620 yylval.u.i = atoi(cp); 1621 cmdp[cpos] = c; 1622 state = STR1; 1623 return (NUMBER); 1624 } 1625 state = STR1; 1626 goto dostr1; 1627 1628 case ARGS: 1629 if (isdigit((unsigned char)cmdp[cpos])) { 1630 cp = &cmdp[cpos]; 1631 while (isdigit((unsigned char)cmdp[++cpos])) 1632 ; 1633 c = cmdp[cpos]; 1634 cmdp[cpos] = '\0'; 1635 yylval.u.i = atoi(cp); 1636 yylval.u.ll = STRTOLL(cp, (char **)NULL, 10); 1637 cmdp[cpos] = c; 1638 return (NUMBER); 1639 } 1640 if (strncasecmp(&cmdp[cpos], "ALL", 3) == 0 1641 && !isalnum((unsigned char)cmdp[cpos + 3])) { 1642 cpos += 3; 1643 return (ALL); 1644 } 1645 switch (cmdp[cpos++]) { 1646 1647 case '\n': 1648 state = EOLN; 1649 return (CRLF); 1650 1651 case ' ': 1652 return (SP); 1653 1654 case ',': 1655 return (COMMA); 1656 1657 case 'A': 1658 case 'a': 1659 return (A); 1660 1661 case 'B': 1662 case 'b': 1663 return (B); 1664 1665 case 'C': 1666 case 'c': 1667 return (C); 1668 1669 case 'E': 1670 case 'e': 1671 return (E); 1672 1673 case 'F': 1674 case 'f': 1675 return (F); 1676 1677 case 'I': 1678 case 'i': 1679 return (I); 1680 1681 case 'L': 1682 case 'l': 1683 return (L); 1684 1685 case 'N': 1686 case 'n': 1687 return (N); 1688 1689 case 'P': 1690 case 'p': 1691 return (P); 1692 1693 case 'R': 1694 case 'r': 1695 return (R); 1696 1697 case 'S': 1698 case 's': 1699 return (S); 1700 1701 case 'T': 1702 case 't': 1703 return (T); 1704 1705 } 1706 break; 1707 1708 case NOARGS: 1709 if (cmdp[cpos] == '\n') { 1710 state = EOLN; 1711 return (CRLF); 1712 } 1713 c = cmdp[cpos]; 1714 cmdp[cpos] = '\0'; 1715 reply(501, "'%s' command does not take any arguments.", cmdp); 1716 hasyyerrored = 1; 1717 cmdp[cpos] = c; 1718 break; 1719 1720 case EOLN: 1721 state = CMD; 1722 return (0); 1723 1724 default: 1725 fatal("Unknown state in scanner."); 1726 } 1727 yyerror(NULL); 1728 state = CMD; 1729 return (0); 1730} 1731 1732/* ARGSUSED */ 1733void 1734yyerror(char *s) 1735{ 1736 char *cp; 1737 1738 if (hasyyerrored || is_oob) 1739 return; 1740 if ((cp = strchr(cmdp,'\n')) != NULL) 1741 *cp = '\0'; 1742 reply(500, "'%s': command not understood.", cmdp); 1743 hasyyerrored = 1; 1744} 1745 1746static void 1747help(struct tab *ctab, const char *s) 1748{ 1749 struct tab *c; 1750 int width, NCMDS; 1751 char *htype; 1752 1753 if (ctab == sitetab) 1754 htype = "SITE "; 1755 else 1756 htype = ""; 1757 width = 0, NCMDS = 0; 1758 for (c = ctab; c->name != NULL; c++) { 1759 int len = strlen(c->name); 1760 1761 if (len > width) 1762 width = len; 1763 NCMDS++; 1764 } 1765 width = (width + 8) &~ 7; 1766 if (s == 0) { 1767 int i, j, w; 1768 int columns, lines; 1769 1770 reply(-214, "%s", ""); 1771 reply(0, "The following %scommands are recognized.", htype); 1772 reply(0, "(`-' = not implemented, `+' = supports options)"); 1773 columns = 76 / width; 1774 if (columns == 0) 1775 columns = 1; 1776 lines = (NCMDS + columns - 1) / columns; 1777 for (i = 0; i < lines; i++) { 1778 cprintf(stdout, " "); 1779 for (j = 0; j < columns; j++) { 1780 c = ctab + j * lines + i; 1781 cprintf(stdout, "%s", c->name); 1782 w = strlen(c->name); 1783 if (! CMD_IMPLEMENTED(c)) { 1784 CPUTC('-', stdout); 1785 w++; 1786 } 1787 if (CMD_HAS_OPTIONS(c)) { 1788 CPUTC('+', stdout); 1789 w++; 1790 } 1791 if (c + lines >= &ctab[NCMDS]) 1792 break; 1793 while (w < width) { 1794 CPUTC(' ', stdout); 1795 w++; 1796 } 1797 } 1798 cprintf(stdout, "\r\n"); 1799 } 1800 (void) fflush(stdout); 1801 reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1802 return; 1803 } 1804 c = lookup(ctab, s); 1805 if (c == (struct tab *)0) { 1806 reply(502, "Unknown command '%s'.", s); 1807 return; 1808 } 1809 if (CMD_IMPLEMENTED(c)) 1810 reply(214, "Syntax: %s%s %s", htype, c->name, c->help); 1811 else 1812 reply(504, "%s%-*s\t%s; not implemented.", htype, width, 1813 c->name, c->help); 1814} 1815 1816/* 1817 * Check that the structures used for a PORT, LPRT or EPRT command are 1818 * valid (data_dest, his_addr), and if necessary, detect ftp bounce attacks. 1819 * If family != -1 check that his_addr.su_family == family. 1820 */ 1821static void 1822port_check(const char *cmd, int family) 1823{ 1824 char h1[NI_MAXHOST], h2[NI_MAXHOST]; 1825 char s1[NI_MAXHOST], s2[NI_MAXHOST]; 1826#ifdef NI_WITHSCOPEID 1827 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID; 1828#else 1829 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV; 1830#endif 1831 1832 if (epsvall) { 1833 reply(501, "%s disallowed after EPSV ALL", cmd); 1834 return; 1835 } 1836 1837 if (family != -1 && his_addr.su_family != family) { 1838 port_check_fail: 1839 reply(500, "Illegal %s command rejected", cmd); 1840 return; 1841 } 1842 1843 if (data_dest.su_family != his_addr.su_family) 1844 goto port_check_fail; 1845 1846 /* be paranoid, if told so */ 1847 if (CURCLASS_FLAGS_ISSET(checkportcmd)) { 1848#ifdef INET6 1849 /* 1850 * be paranoid, there are getnameinfo implementation that does 1851 * not present scopeid portion 1852 */ 1853 if (data_dest.su_family == AF_INET6 && 1854 data_dest.su_scope_id != his_addr.su_scope_id) 1855 goto port_check_fail; 1856#endif 1857 1858 if (getnameinfo((struct sockaddr *)&data_dest, data_dest.su_len, 1859 h1, sizeof(h1), s1, sizeof(s1), niflags)) 1860 goto port_check_fail; 1861 if (getnameinfo((struct sockaddr *)&his_addr, his_addr.su_len, 1862 h2, sizeof(h2), s2, sizeof(s2), niflags)) 1863 goto port_check_fail; 1864 1865 if (atoi(s1) < IPPORT_RESERVED || strcmp(h1, h2) != 0) 1866 goto port_check_fail; 1867 } 1868 1869 usedefault = 0; 1870 if (pdata >= 0) { 1871 (void) close(pdata); 1872 pdata = -1; 1873 } 1874 reply(200, "%s command successful.", cmd); 1875} 1876