18a16b7a1SPedro F. Giffuni /*- 28a16b7a1SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 38a16b7a1SPedro F. Giffuni * 4ea022d16SRodney W. Grimes * Copyright (c) 1985, 1988, 1993, 1994 5ea022d16SRodney W. Grimes * The Regents of the University of California. All rights reserved. 6ea022d16SRodney W. Grimes * 7ea022d16SRodney W. Grimes * Redistribution and use in source and binary forms, with or without 8ea022d16SRodney W. Grimes * modification, are permitted provided that the following conditions 9ea022d16SRodney W. Grimes * are met: 10ea022d16SRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 11ea022d16SRodney W. Grimes * notice, this list of conditions and the following disclaimer. 12ea022d16SRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 13ea022d16SRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 14ea022d16SRodney W. Grimes * documentation and/or other materials provided with the distribution. 155efaea4cSChristian Brueffer * 3. Neither the name of the University nor the names of its contributors 16ea022d16SRodney W. Grimes * may be used to endorse or promote products derived from this software 17ea022d16SRodney W. Grimes * without specific prior written permission. 18ea022d16SRodney W. Grimes * 19ea022d16SRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20ea022d16SRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21ea022d16SRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22ea022d16SRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23ea022d16SRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24ea022d16SRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25ea022d16SRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26ea022d16SRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27ea022d16SRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28ea022d16SRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29ea022d16SRodney W. Grimes * SUCH DAMAGE. 30ea022d16SRodney W. Grimes */ 31ea022d16SRodney W. Grimes 32ea022d16SRodney W. Grimes /* 33ea022d16SRodney W. Grimes * Grammar for FTP commands. 34ea022d16SRodney W. Grimes * See RFC 959. 35ea022d16SRodney W. Grimes */ 36ea022d16SRodney W. Grimes 37ea022d16SRodney W. Grimes %{ 38ea022d16SRodney W. Grimes 39ea022d16SRodney W. Grimes #include <sys/param.h> 40ea022d16SRodney W. Grimes #include <sys/socket.h> 41ea022d16SRodney W. Grimes #include <sys/stat.h> 42ea022d16SRodney W. Grimes 43ea022d16SRodney W. Grimes #include <netinet/in.h> 44ea022d16SRodney W. Grimes #include <arpa/ftp.h> 45ea022d16SRodney W. Grimes 46ea022d16SRodney W. Grimes #include <ctype.h> 47ea022d16SRodney W. Grimes #include <errno.h> 48ea022d16SRodney W. Grimes #include <glob.h> 4939e99226SMaxim Konovalov #include <libutil.h> 507d0babdaSMaxim Konovalov #include <limits.h> 5139e99226SMaxim Konovalov #include <md5.h> 524dd8b5abSYoshinobu Inoue #include <netdb.h> 53ea022d16SRodney W. Grimes #include <pwd.h> 54ea022d16SRodney W. Grimes #include <signal.h> 55a57e1ef0SYaroslav Tykhiy #include <stdint.h> 56ea022d16SRodney W. Grimes #include <stdio.h> 57ea022d16SRodney W. Grimes #include <stdlib.h> 58ea022d16SRodney W. Grimes #include <string.h> 59ea022d16SRodney W. Grimes #include <syslog.h> 60ea022d16SRodney W. Grimes #include <time.h> 61ea022d16SRodney W. Grimes #include <unistd.h> 62ea022d16SRodney W. Grimes 63ea022d16SRodney W. Grimes #include "extern.h" 64dcb4f239SYaroslav Tykhiy #include "pathnames.h" 65ea022d16SRodney W. Grimes 66d836a9dbSJung-uk Kim #define yylex ftpcmd_yylex 67d836a9dbSJung-uk Kim 68ea022d16SRodney W. Grimes off_t restart_point; 69ea022d16SRodney W. Grimes 70ea022d16SRodney W. Grimes static int cmd_type; 71ea022d16SRodney W. Grimes static int cmd_form; 72ea022d16SRodney W. Grimes static int cmd_bytesz; 734b82fc95SYaroslav Tykhiy static int state; 74ea022d16SRodney W. Grimes char cbuf[512]; 75aa5a9d3fSYaroslav Tykhiy char *fromname = NULL; 76ea022d16SRodney W. Grimes 77ea022d16SRodney W. Grimes %} 78ea022d16SRodney W. Grimes 79ea022d16SRodney W. Grimes %union { 807d0babdaSMaxim Konovalov struct { 817d0babdaSMaxim Konovalov off_t o; 82ea022d16SRodney W. Grimes int i; 837d0babdaSMaxim Konovalov } u; 84ea022d16SRodney W. Grimes char *s; 85ea022d16SRodney W. Grimes } 86ea022d16SRodney W. Grimes 87ea022d16SRodney W. Grimes %token 88ea022d16SRodney W. Grimes A B C E F I 89ea022d16SRodney W. Grimes L N P R S T 904dd8b5abSYoshinobu Inoue ALL 91ea022d16SRodney W. Grimes 92ea022d16SRodney W. Grimes SP CRLF COMMA 93ea022d16SRodney W. Grimes 94ea022d16SRodney W. Grimes USER PASS ACCT REIN QUIT PORT 95ea022d16SRodney W. Grimes PASV TYPE STRU MODE RETR STOR 96ea022d16SRodney W. Grimes APPE MLFL MAIL MSND MSOM MSAM 97ea022d16SRodney W. Grimes MRSQ MRCP ALLO REST RNFR RNTO 98ea022d16SRodney W. Grimes ABOR DELE CWD LIST NLST SITE 99ea022d16SRodney W. Grimes STAT HELP NOOP MKD RMD PWD 100ea022d16SRodney W. Grimes CDUP STOU SMNT SYST SIZE MDTM 1012ea42282SYaroslav Tykhiy LPRT LPSV EPRT EPSV FEAT 102ea022d16SRodney W. Grimes 10353ba84a6SPoul-Henning Kamp UMASK IDLE CHMOD MDFIVE 104ea022d16SRodney W. Grimes 105371348aeSYaroslav Tykhiy LEXERR NOTIMPL 106ea022d16SRodney W. Grimes 107ea022d16SRodney W. Grimes %token <s> STRING 1087d0babdaSMaxim Konovalov %token <u> NUMBER 109ea022d16SRodney W. Grimes 1107d0babdaSMaxim Konovalov %type <u.i> check_login octal_number byte_size 1117d0babdaSMaxim Konovalov %type <u.i> check_login_ro check_login_epsv 1127d0babdaSMaxim Konovalov %type <u.i> struct_code mode_code type_code form_code 11370825609SPeter Wemm %type <s> pathstring pathname password username 114371348aeSYaroslav Tykhiy %type <s> ALL NOTIMPL 115ea022d16SRodney W. Grimes 116ea022d16SRodney W. Grimes %start cmd_list 117ea022d16SRodney W. Grimes 118ea022d16SRodney W. Grimes %% 119ea022d16SRodney W. Grimes 120ea022d16SRodney W. Grimes cmd_list 121ea022d16SRodney W. Grimes : /* empty */ 122ea022d16SRodney W. Grimes | cmd_list cmd 123ea022d16SRodney W. Grimes { 124c507cedeSDavid Malone if (fromname) 125c507cedeSDavid Malone free(fromname); 126aa5a9d3fSYaroslav Tykhiy fromname = NULL; 1270e519c96SYaroslav Tykhiy restart_point = 0; 128ea022d16SRodney W. Grimes } 129ea022d16SRodney W. Grimes | cmd_list rcmd 130ea022d16SRodney W. Grimes ; 131ea022d16SRodney W. Grimes 132ea022d16SRodney W. Grimes cmd 133ea022d16SRodney W. Grimes : USER SP username CRLF 134ea022d16SRodney W. Grimes { 135ea022d16SRodney W. Grimes user($3); 136ea022d16SRodney W. Grimes free($3); 137ea022d16SRodney W. Grimes } 138ea022d16SRodney W. Grimes | PASS SP password CRLF 139ea022d16SRodney W. Grimes { 140ea022d16SRodney W. Grimes pass($3); 141ea022d16SRodney W. Grimes free($3); 142ea022d16SRodney W. Grimes } 1437d6505e6SBrian Feldman | PASS CRLF 1447d6505e6SBrian Feldman { 1457d6505e6SBrian Feldman pass(""); 1467d6505e6SBrian Feldman } 14761f891a6SPaul Traina | PORT check_login SP host_port CRLF 14861f891a6SPaul Traina { 1494dd8b5abSYoshinobu Inoue if (epsvall) { 15002c97492SYaroslav Tykhiy reply(501, "No PORT allowed after EPSV ALL."); 1514dd8b5abSYoshinobu Inoue goto port_done; 1524dd8b5abSYoshinobu Inoue } 1534dd8b5abSYoshinobu Inoue if (!$2) 1544dd8b5abSYoshinobu Inoue goto port_done; 1554dd8b5abSYoshinobu Inoue if (port_check("PORT") == 1) 1564dd8b5abSYoshinobu Inoue goto port_done; 1574dd8b5abSYoshinobu Inoue #ifdef INET6 1584dd8b5abSYoshinobu Inoue if ((his_addr.su_family != AF_INET6 || 1594dd8b5abSYoshinobu Inoue !IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr))) { 1604dd8b5abSYoshinobu Inoue /* shoud never happen */ 16161f891a6SPaul Traina usedefault = 1; 1624dd8b5abSYoshinobu Inoue reply(500, "Invalid address rejected."); 1634dd8b5abSYoshinobu Inoue goto port_done; 1644dd8b5abSYoshinobu Inoue } 1654dd8b5abSYoshinobu Inoue port_check_v6("pcmd"); 1664dd8b5abSYoshinobu Inoue #endif 1674dd8b5abSYoshinobu Inoue port_done: 168012cdd2cSYaroslav Tykhiy ; 1694dd8b5abSYoshinobu Inoue } 1704dd8b5abSYoshinobu Inoue | LPRT check_login SP host_long_port CRLF 1714dd8b5abSYoshinobu Inoue { 1724dd8b5abSYoshinobu Inoue if (epsvall) { 17302c97492SYaroslav Tykhiy reply(501, "No LPRT allowed after EPSV ALL."); 1744dd8b5abSYoshinobu Inoue goto lprt_done; 1754dd8b5abSYoshinobu Inoue } 1764dd8b5abSYoshinobu Inoue if (!$2) 1774dd8b5abSYoshinobu Inoue goto lprt_done; 1784dd8b5abSYoshinobu Inoue if (port_check("LPRT") == 1) 1794dd8b5abSYoshinobu Inoue goto lprt_done; 1804dd8b5abSYoshinobu Inoue #ifdef INET6 1814dd8b5abSYoshinobu Inoue if (his_addr.su_family != AF_INET6) { 1824dd8b5abSYoshinobu Inoue usedefault = 1; 1834dd8b5abSYoshinobu Inoue reply(500, "Invalid address rejected."); 1844dd8b5abSYoshinobu Inoue goto lprt_done; 1854dd8b5abSYoshinobu Inoue } 1864dd8b5abSYoshinobu Inoue if (port_check_v6("LPRT") == 1) 1874dd8b5abSYoshinobu Inoue goto lprt_done; 1884dd8b5abSYoshinobu Inoue #endif 1894dd8b5abSYoshinobu Inoue lprt_done: 190012cdd2cSYaroslav Tykhiy ; 1914dd8b5abSYoshinobu Inoue } 1924dd8b5abSYoshinobu Inoue | EPRT check_login SP STRING CRLF 1934dd8b5abSYoshinobu Inoue { 1944dd8b5abSYoshinobu Inoue char delim; 1954dd8b5abSYoshinobu Inoue char *tmp = NULL; 1964dd8b5abSYoshinobu Inoue char *p, *q; 1974dd8b5abSYoshinobu Inoue char *result[3]; 1984dd8b5abSYoshinobu Inoue struct addrinfo hints; 1994dd8b5abSYoshinobu Inoue struct addrinfo *res; 2004dd8b5abSYoshinobu Inoue int i; 2014dd8b5abSYoshinobu Inoue 2024dd8b5abSYoshinobu Inoue if (epsvall) { 20302c97492SYaroslav Tykhiy reply(501, "No EPRT allowed after EPSV ALL."); 2044dd8b5abSYoshinobu Inoue goto eprt_done; 2054dd8b5abSYoshinobu Inoue } 2064dd8b5abSYoshinobu Inoue if (!$2) 2074dd8b5abSYoshinobu Inoue goto eprt_done; 2084dd8b5abSYoshinobu Inoue 2094dd8b5abSYoshinobu Inoue memset(&data_dest, 0, sizeof(data_dest)); 2104dd8b5abSYoshinobu Inoue tmp = strdup($4); 211618b0bbaSMark Murray if (ftpdebug) 2124dd8b5abSYoshinobu Inoue syslog(LOG_DEBUG, "%s", tmp); 2134dd8b5abSYoshinobu Inoue if (!tmp) { 214618b0bbaSMark Murray fatalerror("not enough core"); 2154dd8b5abSYoshinobu Inoue /*NOTREACHED*/ 2164dd8b5abSYoshinobu Inoue } 2174dd8b5abSYoshinobu Inoue p = tmp; 2184dd8b5abSYoshinobu Inoue delim = p[0]; 2194dd8b5abSYoshinobu Inoue p++; 2204dd8b5abSYoshinobu Inoue memset(result, 0, sizeof(result)); 2214dd8b5abSYoshinobu Inoue for (i = 0; i < 3; i++) { 2224dd8b5abSYoshinobu Inoue q = strchr(p, delim); 2234dd8b5abSYoshinobu Inoue if (!q || *q != delim) { 2244dd8b5abSYoshinobu Inoue parsefail: 22561f891a6SPaul Traina reply(500, 2264dd8b5abSYoshinobu Inoue "Invalid argument, rejected."); 2274dd8b5abSYoshinobu Inoue if (tmp) 2284dd8b5abSYoshinobu Inoue free(tmp); 2294dd8b5abSYoshinobu Inoue usedefault = 1; 2304dd8b5abSYoshinobu Inoue goto eprt_done; 231ea022d16SRodney W. Grimes } 2324dd8b5abSYoshinobu Inoue *q++ = '\0'; 2334dd8b5abSYoshinobu Inoue result[i] = p; 234618b0bbaSMark Murray if (ftpdebug) 2354dd8b5abSYoshinobu Inoue syslog(LOG_DEBUG, "%d: %s", i, p); 2364dd8b5abSYoshinobu Inoue p = q; 237ea022d16SRodney W. Grimes } 2384dd8b5abSYoshinobu Inoue 2394dd8b5abSYoshinobu Inoue /* some more sanity check */ 2404dd8b5abSYoshinobu Inoue p = result[0]; 2414dd8b5abSYoshinobu Inoue while (*p) { 2424dd8b5abSYoshinobu Inoue if (!isdigit(*p)) 2434dd8b5abSYoshinobu Inoue goto parsefail; 2444dd8b5abSYoshinobu Inoue p++; 24561f891a6SPaul Traina } 2464dd8b5abSYoshinobu Inoue p = result[2]; 2474dd8b5abSYoshinobu Inoue while (*p) { 2484dd8b5abSYoshinobu Inoue if (!isdigit(*p)) 2494dd8b5abSYoshinobu Inoue goto parsefail; 2504dd8b5abSYoshinobu Inoue p++; 2514dd8b5abSYoshinobu Inoue } 2524dd8b5abSYoshinobu Inoue 2534dd8b5abSYoshinobu Inoue /* grab address */ 2544dd8b5abSYoshinobu Inoue memset(&hints, 0, sizeof(hints)); 2554dd8b5abSYoshinobu Inoue if (atoi(result[0]) == 1) 2564dd8b5abSYoshinobu Inoue hints.ai_family = PF_INET; 2574dd8b5abSYoshinobu Inoue #ifdef INET6 2584dd8b5abSYoshinobu Inoue else if (atoi(result[0]) == 2) 2594dd8b5abSYoshinobu Inoue hints.ai_family = PF_INET6; 2604dd8b5abSYoshinobu Inoue #endif 2614dd8b5abSYoshinobu Inoue else 2624dd8b5abSYoshinobu Inoue hints.ai_family = PF_UNSPEC; /*XXX*/ 2634dd8b5abSYoshinobu Inoue hints.ai_socktype = SOCK_STREAM; 2644dd8b5abSYoshinobu Inoue i = getaddrinfo(result[1], result[2], &hints, &res); 2654dd8b5abSYoshinobu Inoue if (i) 2664dd8b5abSYoshinobu Inoue goto parsefail; 2674dd8b5abSYoshinobu Inoue memcpy(&data_dest, res->ai_addr, res->ai_addrlen); 2684dd8b5abSYoshinobu Inoue #ifdef INET6 2694dd8b5abSYoshinobu Inoue if (his_addr.su_family == AF_INET6 2704dd8b5abSYoshinobu Inoue && data_dest.su_family == AF_INET6) { 2714dd8b5abSYoshinobu Inoue /* XXX more sanity checks! */ 2724dd8b5abSYoshinobu Inoue data_dest.su_sin6.sin6_scope_id = 2734dd8b5abSYoshinobu Inoue his_addr.su_sin6.sin6_scope_id; 2744dd8b5abSYoshinobu Inoue } 2754dd8b5abSYoshinobu Inoue #endif 2764dd8b5abSYoshinobu Inoue free(tmp); 2774dd8b5abSYoshinobu Inoue tmp = NULL; 2784dd8b5abSYoshinobu Inoue 2794dd8b5abSYoshinobu Inoue if (port_check("EPRT") == 1) 2804dd8b5abSYoshinobu Inoue goto eprt_done; 2814dd8b5abSYoshinobu Inoue #ifdef INET6 2824dd8b5abSYoshinobu Inoue if (his_addr.su_family != AF_INET6) { 2834dd8b5abSYoshinobu Inoue usedefault = 1; 2844dd8b5abSYoshinobu Inoue reply(500, "Invalid address rejected."); 2854dd8b5abSYoshinobu Inoue goto eprt_done; 2864dd8b5abSYoshinobu Inoue } 2874dd8b5abSYoshinobu Inoue if (port_check_v6("EPRT") == 1) 2884dd8b5abSYoshinobu Inoue goto eprt_done; 2894dd8b5abSYoshinobu Inoue #endif 290c507cedeSDavid Malone eprt_done: 291c507cedeSDavid Malone free($4); 29261f891a6SPaul Traina } 29361f891a6SPaul Traina | PASV check_login CRLF 294ea022d16SRodney W. Grimes { 2954dd8b5abSYoshinobu Inoue if (epsvall) 29602c97492SYaroslav Tykhiy reply(501, "No PASV allowed after EPSV ALL."); 2974dd8b5abSYoshinobu Inoue else if ($2) 298ea022d16SRodney W. Grimes passive(); 299ea022d16SRodney W. Grimes } 3004dd8b5abSYoshinobu Inoue | LPSV check_login CRLF 3014dd8b5abSYoshinobu Inoue { 3024dd8b5abSYoshinobu Inoue if (epsvall) 30302c97492SYaroslav Tykhiy reply(501, "No LPSV allowed after EPSV ALL."); 3044dd8b5abSYoshinobu Inoue else if ($2) 3054dd8b5abSYoshinobu Inoue long_passive("LPSV", PF_UNSPEC); 3064dd8b5abSYoshinobu Inoue } 307a4b77a2aSPoul-Henning Kamp | EPSV check_login_epsv SP NUMBER CRLF 3084dd8b5abSYoshinobu Inoue { 3094dd8b5abSYoshinobu Inoue if ($2) { 3104dd8b5abSYoshinobu Inoue int pf; 3117d0babdaSMaxim Konovalov switch ($4.i) { 3124dd8b5abSYoshinobu Inoue case 1: 3134dd8b5abSYoshinobu Inoue pf = PF_INET; 3144dd8b5abSYoshinobu Inoue break; 3154dd8b5abSYoshinobu Inoue #ifdef INET6 3164dd8b5abSYoshinobu Inoue case 2: 3174dd8b5abSYoshinobu Inoue pf = PF_INET6; 3184dd8b5abSYoshinobu Inoue break; 3194dd8b5abSYoshinobu Inoue #endif 3204dd8b5abSYoshinobu Inoue default: 3214dd8b5abSYoshinobu Inoue pf = -1; /*junk value*/ 3224dd8b5abSYoshinobu Inoue break; 3234dd8b5abSYoshinobu Inoue } 3244dd8b5abSYoshinobu Inoue long_passive("EPSV", pf); 3254dd8b5abSYoshinobu Inoue } 3264dd8b5abSYoshinobu Inoue } 327a4b77a2aSPoul-Henning Kamp | EPSV check_login_epsv SP ALL CRLF 3284dd8b5abSYoshinobu Inoue { 3294dd8b5abSYoshinobu Inoue if ($2) { 33002c97492SYaroslav Tykhiy reply(200, "EPSV ALL command successful."); 3314dd8b5abSYoshinobu Inoue epsvall++; 3324dd8b5abSYoshinobu Inoue } 3334dd8b5abSYoshinobu Inoue } 334a4b77a2aSPoul-Henning Kamp | EPSV check_login_epsv CRLF 3354dd8b5abSYoshinobu Inoue { 3364dd8b5abSYoshinobu Inoue if ($2) 3374dd8b5abSYoshinobu Inoue long_passive("EPSV", PF_UNSPEC); 3384dd8b5abSYoshinobu Inoue } 3393fca54b6SChris D. Faulhaber | TYPE check_login SP type_code CRLF 340ea022d16SRodney W. Grimes { 3413fca54b6SChris D. Faulhaber if ($2) { 342ea022d16SRodney W. Grimes switch (cmd_type) { 343ea022d16SRodney W. Grimes 344ea022d16SRodney W. Grimes case TYPE_A: 345ea022d16SRodney W. Grimes if (cmd_form == FORM_N) { 346ea022d16SRodney W. Grimes reply(200, "Type set to A."); 347ea022d16SRodney W. Grimes type = cmd_type; 348ea022d16SRodney W. Grimes form = cmd_form; 349ea022d16SRodney W. Grimes } else 350ea022d16SRodney W. Grimes reply(504, "Form must be N."); 351ea022d16SRodney W. Grimes break; 352ea022d16SRodney W. Grimes 353ea022d16SRodney W. Grimes case TYPE_E: 354ea022d16SRodney W. Grimes reply(504, "Type E not implemented."); 355ea022d16SRodney W. Grimes break; 356ea022d16SRodney W. Grimes 357ea022d16SRodney W. Grimes case TYPE_I: 358ea022d16SRodney W. Grimes reply(200, "Type set to I."); 359ea022d16SRodney W. Grimes type = cmd_type; 360ea022d16SRodney W. Grimes break; 361ea022d16SRodney W. Grimes 362ea022d16SRodney W. Grimes case TYPE_L: 36389fdc4e1SMike Barcroft #if CHAR_BIT == 8 364ea022d16SRodney W. Grimes if (cmd_bytesz == 8) { 365ea022d16SRodney W. Grimes reply(200, 366ea022d16SRodney W. Grimes "Type set to L (byte size 8)."); 367ea022d16SRodney W. Grimes type = cmd_type; 368ea022d16SRodney W. Grimes } else 369ea022d16SRodney W. Grimes reply(504, "Byte size must be 8."); 37089fdc4e1SMike Barcroft #else /* CHAR_BIT == 8 */ 37189fdc4e1SMike Barcroft UNIMPLEMENTED for CHAR_BIT != 8 37289fdc4e1SMike Barcroft #endif /* CHAR_BIT == 8 */ 373ea022d16SRodney W. Grimes } 374ea022d16SRodney W. Grimes } 3753fca54b6SChris D. Faulhaber } 3763fca54b6SChris D. Faulhaber | STRU check_login SP struct_code CRLF 377ea022d16SRodney W. Grimes { 3783fca54b6SChris D. Faulhaber if ($2) { 3793fca54b6SChris D. Faulhaber switch ($4) { 380ea022d16SRodney W. Grimes 381ea022d16SRodney W. Grimes case STRU_F: 38202c97492SYaroslav Tykhiy reply(200, "STRU F accepted."); 383ea022d16SRodney W. Grimes break; 384ea022d16SRodney W. Grimes 385ea022d16SRodney W. Grimes default: 386ea022d16SRodney W. Grimes reply(504, "Unimplemented STRU type."); 387ea022d16SRodney W. Grimes } 388ea022d16SRodney W. Grimes } 3893fca54b6SChris D. Faulhaber } 3903fca54b6SChris D. Faulhaber | MODE check_login SP mode_code CRLF 391ea022d16SRodney W. Grimes { 3923fca54b6SChris D. Faulhaber if ($2) { 3933fca54b6SChris D. Faulhaber switch ($4) { 394ea022d16SRodney W. Grimes 395ea022d16SRodney W. Grimes case MODE_S: 39602c97492SYaroslav Tykhiy reply(200, "MODE S accepted."); 397ea022d16SRodney W. Grimes break; 398ea022d16SRodney W. Grimes 399ea022d16SRodney W. Grimes default: 400ea022d16SRodney W. Grimes reply(502, "Unimplemented MODE type."); 401ea022d16SRodney W. Grimes } 402ea022d16SRodney W. Grimes } 4033fca54b6SChris D. Faulhaber } 4043fca54b6SChris D. Faulhaber | ALLO check_login SP NUMBER CRLF 405ea022d16SRodney W. Grimes { 4063fca54b6SChris D. Faulhaber if ($2) { 407ea022d16SRodney W. Grimes reply(202, "ALLO command ignored."); 408ea022d16SRodney W. Grimes } 4093fca54b6SChris D. Faulhaber } 4103fca54b6SChris D. Faulhaber | ALLO check_login SP NUMBER SP R SP NUMBER CRLF 411ea022d16SRodney W. Grimes { 4123fca54b6SChris D. Faulhaber if ($2) { 413ea022d16SRodney W. Grimes reply(202, "ALLO command ignored."); 414ea022d16SRodney W. Grimes } 4153fca54b6SChris D. Faulhaber } 416ea022d16SRodney W. Grimes | RETR check_login SP pathname CRLF 417ea022d16SRodney W. Grimes { 4181cc9f0bbSSheldon Hearn if (noretr || (guest && noguestretr)) 41902c97492SYaroslav Tykhiy reply(500, "RETR command disabled."); 42062513e76SNik Clayton else if ($2 && $4 != NULL) 421aa5a9d3fSYaroslav Tykhiy retrieve(NULL, $4); 42262513e76SNik Clayton 423ea022d16SRodney W. Grimes if ($4 != NULL) 424ea022d16SRodney W. Grimes free($4); 425ea022d16SRodney W. Grimes } 426a4b77a2aSPoul-Henning Kamp | STOR check_login_ro SP pathname CRLF 427ea022d16SRodney W. Grimes { 428ea022d16SRodney W. Grimes if ($2 && $4 != NULL) 429ea022d16SRodney W. Grimes store($4, "w", 0); 430ea022d16SRodney W. Grimes if ($4 != NULL) 431ea022d16SRodney W. Grimes free($4); 432ea022d16SRodney W. Grimes } 433a4b77a2aSPoul-Henning Kamp | APPE check_login_ro SP pathname CRLF 434ea022d16SRodney W. Grimes { 435ea022d16SRodney W. Grimes if ($2 && $4 != NULL) 436ea022d16SRodney W. Grimes store($4, "a", 0); 437ea022d16SRodney W. Grimes if ($4 != NULL) 438ea022d16SRodney W. Grimes free($4); 439ea022d16SRodney W. Grimes } 440ea022d16SRodney W. Grimes | NLST check_login CRLF 441ea022d16SRodney W. Grimes { 442ea022d16SRodney W. Grimes if ($2) 443ea022d16SRodney W. Grimes send_file_list("."); 444ea022d16SRodney W. Grimes } 4451b9f1a4bSYaroslav Tykhiy | NLST check_login SP pathstring CRLF 446ea022d16SRodney W. Grimes { 4471b9f1a4bSYaroslav Tykhiy if ($2) 448ea022d16SRodney W. Grimes send_file_list($4); 449ea022d16SRodney W. Grimes free($4); 450ea022d16SRodney W. Grimes } 451ea022d16SRodney W. Grimes | LIST check_login CRLF 452ea022d16SRodney W. Grimes { 453ea022d16SRodney W. Grimes if ($2) 454*e2097150SAllan Jude retrieve(_PATH_LS " -lA", ""); 455ea022d16SRodney W. Grimes } 45670825609SPeter Wemm | LIST check_login SP pathstring CRLF 457ea022d16SRodney W. Grimes { 4581b9f1a4bSYaroslav Tykhiy if ($2) 459*e2097150SAllan Jude retrieve(_PATH_LS " -lA %s", $4); 460ea022d16SRodney W. Grimes free($4); 461ea022d16SRodney W. Grimes } 462ea022d16SRodney W. Grimes | STAT check_login SP pathname CRLF 463ea022d16SRodney W. Grimes { 464ea022d16SRodney W. Grimes if ($2 && $4 != NULL) 465ea022d16SRodney W. Grimes statfilecmd($4); 466ea022d16SRodney W. Grimes if ($4 != NULL) 467ea022d16SRodney W. Grimes free($4); 468ea022d16SRodney W. Grimes } 4693fca54b6SChris D. Faulhaber | STAT check_login CRLF 470ea022d16SRodney W. Grimes { 4713fca54b6SChris D. Faulhaber if ($2) { 472ea022d16SRodney W. Grimes statcmd(); 473ea022d16SRodney W. Grimes } 4743fca54b6SChris D. Faulhaber } 475a4b77a2aSPoul-Henning Kamp | DELE check_login_ro SP pathname CRLF 476ea022d16SRodney W. Grimes { 477ea022d16SRodney W. Grimes if ($2 && $4 != NULL) 478ea022d16SRodney W. Grimes delete($4); 479ea022d16SRodney W. Grimes if ($4 != NULL) 480ea022d16SRodney W. Grimes free($4); 481ea022d16SRodney W. Grimes } 482a4b77a2aSPoul-Henning Kamp | RNTO check_login_ro SP pathname CRLF 483ea022d16SRodney W. Grimes { 484effa0530SYaroslav Tykhiy if ($2 && $4 != NULL) { 485ea022d16SRodney W. Grimes if (fromname) { 48661f891a6SPaul Traina renamecmd(fromname, $4); 487ea022d16SRodney W. Grimes free(fromname); 488aa5a9d3fSYaroslav Tykhiy fromname = NULL; 489ea022d16SRodney W. Grimes } else { 490ea022d16SRodney W. Grimes reply(503, "Bad sequence of commands."); 491ea022d16SRodney W. Grimes } 49261f891a6SPaul Traina } 493effa0530SYaroslav Tykhiy if ($4 != NULL) 49461f891a6SPaul Traina free($4); 495ea022d16SRodney W. Grimes } 4963fca54b6SChris D. Faulhaber | ABOR check_login CRLF 497ea022d16SRodney W. Grimes { 4983fca54b6SChris D. Faulhaber if ($2) 499ea022d16SRodney W. Grimes reply(225, "ABOR command successful."); 500ea022d16SRodney W. Grimes } 501ea022d16SRodney W. Grimes | CWD check_login CRLF 502ea022d16SRodney W. Grimes { 5033fbaa839SDaniel O'Callaghan if ($2) { 504ce9287fcSYaroslav Tykhiy cwd(homedir); 505ea022d16SRodney W. Grimes } 5063fbaa839SDaniel O'Callaghan } 507ea022d16SRodney W. Grimes | CWD check_login SP pathname CRLF 508ea022d16SRodney W. Grimes { 509ea022d16SRodney W. Grimes if ($2 && $4 != NULL) 510ea022d16SRodney W. Grimes cwd($4); 511ea022d16SRodney W. Grimes if ($4 != NULL) 512ea022d16SRodney W. Grimes free($4); 513ea022d16SRodney W. Grimes } 514ea022d16SRodney W. Grimes | HELP CRLF 515ea022d16SRodney W. Grimes { 516aa5a9d3fSYaroslav Tykhiy help(cmdtab, NULL); 517ea022d16SRodney W. Grimes } 518ea022d16SRodney W. Grimes | HELP SP STRING CRLF 519ea022d16SRodney W. Grimes { 520ea022d16SRodney W. Grimes char *cp = $3; 521ea022d16SRodney W. Grimes 522ea022d16SRodney W. Grimes if (strncasecmp(cp, "SITE", 4) == 0) { 523ea022d16SRodney W. Grimes cp = $3 + 4; 524ea022d16SRodney W. Grimes if (*cp == ' ') 525ea022d16SRodney W. Grimes cp++; 526ea022d16SRodney W. Grimes if (*cp) 527ea022d16SRodney W. Grimes help(sitetab, cp); 528ea022d16SRodney W. Grimes else 529aa5a9d3fSYaroslav Tykhiy help(sitetab, NULL); 530ea022d16SRodney W. Grimes } else 531ea022d16SRodney W. Grimes help(cmdtab, $3); 532c507cedeSDavid Malone free($3); 533ea022d16SRodney W. Grimes } 534ea022d16SRodney W. Grimes | NOOP CRLF 535ea022d16SRodney W. Grimes { 536ea022d16SRodney W. Grimes reply(200, "NOOP command successful."); 537ea022d16SRodney W. Grimes } 538a4b77a2aSPoul-Henning Kamp | MKD check_login_ro SP pathname CRLF 539ea022d16SRodney W. Grimes { 540ea022d16SRodney W. Grimes if ($2 && $4 != NULL) 541ea022d16SRodney W. Grimes makedir($4); 542ea022d16SRodney W. Grimes if ($4 != NULL) 543ea022d16SRodney W. Grimes free($4); 544ea022d16SRodney W. Grimes } 545a4b77a2aSPoul-Henning Kamp | RMD check_login_ro SP pathname CRLF 546ea022d16SRodney W. Grimes { 547ea022d16SRodney W. Grimes if ($2 && $4 != NULL) 548ea022d16SRodney W. Grimes removedir($4); 549ea022d16SRodney W. Grimes if ($4 != NULL) 550ea022d16SRodney W. Grimes free($4); 551ea022d16SRodney W. Grimes } 552ea022d16SRodney W. Grimes | PWD check_login CRLF 553ea022d16SRodney W. Grimes { 554ea022d16SRodney W. Grimes if ($2) 555ea022d16SRodney W. Grimes pwd(); 556ea022d16SRodney W. Grimes } 557ea022d16SRodney W. Grimes | CDUP check_login CRLF 558ea022d16SRodney W. Grimes { 559ea022d16SRodney W. Grimes if ($2) 560ea022d16SRodney W. Grimes cwd(".."); 561ea022d16SRodney W. Grimes } 562ea022d16SRodney W. Grimes | SITE SP HELP CRLF 563ea022d16SRodney W. Grimes { 564aa5a9d3fSYaroslav Tykhiy help(sitetab, NULL); 565ea022d16SRodney W. Grimes } 566ea022d16SRodney W. Grimes | SITE SP HELP SP STRING CRLF 567ea022d16SRodney W. Grimes { 568ea022d16SRodney W. Grimes help(sitetab, $5); 569c507cedeSDavid Malone free($5); 570ea022d16SRodney W. Grimes } 57153ba84a6SPoul-Henning Kamp | SITE SP MDFIVE check_login SP pathname CRLF 57253ba84a6SPoul-Henning Kamp { 57353ba84a6SPoul-Henning Kamp char p[64], *q; 57453ba84a6SPoul-Henning Kamp 575effa0530SYaroslav Tykhiy if ($4 && $6) { 57653ba84a6SPoul-Henning Kamp q = MD5File($6, p); 57753ba84a6SPoul-Henning Kamp if (q != NULL) 57853ba84a6SPoul-Henning Kamp reply(200, "MD5(%s) = %s", $6, p); 57953ba84a6SPoul-Henning Kamp else 58053ba84a6SPoul-Henning Kamp perror_reply(550, $6); 58153ba84a6SPoul-Henning Kamp } 582c507cedeSDavid Malone if ($6) 583c507cedeSDavid Malone free($6); 58453ba84a6SPoul-Henning Kamp } 585ea022d16SRodney W. Grimes | SITE SP UMASK check_login CRLF 586ea022d16SRodney W. Grimes { 587ea022d16SRodney W. Grimes int oldmask; 588ea022d16SRodney W. Grimes 589ea022d16SRodney W. Grimes if ($4) { 590ea022d16SRodney W. Grimes oldmask = umask(0); 591ea022d16SRodney W. Grimes (void) umask(oldmask); 59202c97492SYaroslav Tykhiy reply(200, "Current UMASK is %03o.", oldmask); 593ea022d16SRodney W. Grimes } 594ea022d16SRodney W. Grimes } 595ea022d16SRodney W. Grimes | SITE SP UMASK check_login SP octal_number CRLF 596ea022d16SRodney W. Grimes { 597ea022d16SRodney W. Grimes int oldmask; 598ea022d16SRodney W. Grimes 599ea022d16SRodney W. Grimes if ($4) { 600ea022d16SRodney W. Grimes if (($6 == -1) || ($6 > 0777)) { 60102c97492SYaroslav Tykhiy reply(501, "Bad UMASK value."); 602ea022d16SRodney W. Grimes } else { 603ea022d16SRodney W. Grimes oldmask = umask($6); 604ea022d16SRodney W. Grimes reply(200, 60502c97492SYaroslav Tykhiy "UMASK set to %03o (was %03o).", 606ea022d16SRodney W. Grimes $6, oldmask); 607ea022d16SRodney W. Grimes } 608ea022d16SRodney W. Grimes } 609ea022d16SRodney W. Grimes } 610a4b77a2aSPoul-Henning Kamp | SITE SP CHMOD check_login_ro SP octal_number SP pathname CRLF 611ea022d16SRodney W. Grimes { 612ea022d16SRodney W. Grimes if ($4 && ($8 != NULL)) { 613c452fbe1SYaroslav Tykhiy if (($6 == -1 ) || ($6 > 0777)) 61402c97492SYaroslav Tykhiy reply(501, "Bad mode value."); 615ea022d16SRodney W. Grimes else if (chmod($8, $6) < 0) 616ea022d16SRodney W. Grimes perror_reply(550, $8); 617ea022d16SRodney W. Grimes else 618ea022d16SRodney W. Grimes reply(200, "CHMOD command successful."); 619ea022d16SRodney W. Grimes } 620ea022d16SRodney W. Grimes if ($8 != NULL) 621ea022d16SRodney W. Grimes free($8); 622ea022d16SRodney W. Grimes } 6233fca54b6SChris D. Faulhaber | SITE SP check_login IDLE CRLF 624ea022d16SRodney W. Grimes { 6253fca54b6SChris D. Faulhaber if ($3) 626ea022d16SRodney W. Grimes reply(200, 62702c97492SYaroslav Tykhiy "Current IDLE time limit is %d seconds; max %d.", 628ea022d16SRodney W. Grimes timeout, maxtimeout); 629ea022d16SRodney W. Grimes } 6303fca54b6SChris D. Faulhaber | SITE SP check_login IDLE SP NUMBER CRLF 631ea022d16SRodney W. Grimes { 6323fca54b6SChris D. Faulhaber if ($3) { 6337d0babdaSMaxim Konovalov if ($6.i < 30 || $6.i > maxtimeout) { 634ea022d16SRodney W. Grimes reply(501, 63502c97492SYaroslav Tykhiy "Maximum IDLE time must be between 30 and %d seconds.", 636ea022d16SRodney W. Grimes maxtimeout); 637ea022d16SRodney W. Grimes } else { 6387d0babdaSMaxim Konovalov timeout = $6.i; 639e3765043SYaroslav Tykhiy (void) alarm(timeout); 640ea022d16SRodney W. Grimes reply(200, 64102c97492SYaroslav Tykhiy "Maximum IDLE time set to %d seconds.", 642ea022d16SRodney W. Grimes timeout); 643ea022d16SRodney W. Grimes } 644ea022d16SRodney W. Grimes } 6453fca54b6SChris D. Faulhaber } 646a4b77a2aSPoul-Henning Kamp | STOU check_login_ro SP pathname CRLF 647ea022d16SRodney W. Grimes { 648ea022d16SRodney W. Grimes if ($2 && $4 != NULL) 649ea022d16SRodney W. Grimes store($4, "w", 1); 650ea022d16SRodney W. Grimes if ($4 != NULL) 651ea022d16SRodney W. Grimes free($4); 652ea022d16SRodney W. Grimes } 6532ea42282SYaroslav Tykhiy | FEAT CRLF 6542ea42282SYaroslav Tykhiy { 6552ea42282SYaroslav Tykhiy lreply(211, "Extensions supported:"); 6562ea42282SYaroslav Tykhiy #if 0 6572ea42282SYaroslav Tykhiy /* XXX these two keywords are non-standard */ 6582ea42282SYaroslav Tykhiy printf(" EPRT\r\n"); 6592ea42282SYaroslav Tykhiy if (!noepsv) 6602ea42282SYaroslav Tykhiy printf(" EPSV\r\n"); 6612ea42282SYaroslav Tykhiy #endif 6622ea42282SYaroslav Tykhiy printf(" MDTM\r\n"); 6632ea42282SYaroslav Tykhiy printf(" REST STREAM\r\n"); 6642ea42282SYaroslav Tykhiy printf(" SIZE\r\n"); 6652ea42282SYaroslav Tykhiy if (assumeutf8) { 6662ea42282SYaroslav Tykhiy /* TVFS requires UTF8, see RFC 3659 */ 6672ea42282SYaroslav Tykhiy printf(" TVFS\r\n"); 6682ea42282SYaroslav Tykhiy printf(" UTF8\r\n"); 6692ea42282SYaroslav Tykhiy } 6702ea42282SYaroslav Tykhiy reply(211, "End."); 6712ea42282SYaroslav Tykhiy } 6723fca54b6SChris D. Faulhaber | SYST check_login CRLF 673ea022d16SRodney W. Grimes { 674a278e092SYaroslav Tykhiy if ($2) { 675a278e092SYaroslav Tykhiy if (hostinfo) 676ea022d16SRodney W. Grimes #ifdef BSD 677ea022d16SRodney W. Grimes reply(215, "UNIX Type: L%d Version: BSD-%d", 67889fdc4e1SMike Barcroft CHAR_BIT, BSD); 679ea022d16SRodney W. Grimes #else /* BSD */ 68089fdc4e1SMike Barcroft reply(215, "UNIX Type: L%d", CHAR_BIT); 681ea022d16SRodney W. Grimes #endif /* BSD */ 682a278e092SYaroslav Tykhiy else 68389fdc4e1SMike Barcroft reply(215, "UNKNOWN Type: L%d", CHAR_BIT); 684a278e092SYaroslav Tykhiy } 685ea022d16SRodney W. Grimes } 686ea022d16SRodney W. Grimes 687ea022d16SRodney W. Grimes /* 688ea022d16SRodney W. Grimes * SIZE is not in RFC959, but Postel has blessed it and 689ea022d16SRodney W. Grimes * it will be in the updated RFC. 690ea022d16SRodney W. Grimes * 691ea022d16SRodney W. Grimes * Return size of file in a format suitable for 692ea022d16SRodney W. Grimes * using with RESTART (we just count bytes). 693ea022d16SRodney W. Grimes */ 694ea022d16SRodney W. Grimes | SIZE check_login SP pathname CRLF 695ea022d16SRodney W. Grimes { 696ea022d16SRodney W. Grimes if ($2 && $4 != NULL) 697ea022d16SRodney W. Grimes sizecmd($4); 698ea022d16SRodney W. Grimes if ($4 != NULL) 699ea022d16SRodney W. Grimes free($4); 700ea022d16SRodney W. Grimes } 701ea022d16SRodney W. Grimes 702ea022d16SRodney W. Grimes /* 703ea022d16SRodney W. Grimes * MDTM is not in RFC959, but Postel has blessed it and 704ea022d16SRodney W. Grimes * it will be in the updated RFC. 705ea022d16SRodney W. Grimes * 706ea022d16SRodney W. Grimes * Return modification time of file as an ISO 3307 707ea022d16SRodney W. Grimes * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx 708ea022d16SRodney W. Grimes * where xxx is the fractional second (of any precision, 709ea022d16SRodney W. Grimes * not necessarily 3 digits) 710ea022d16SRodney W. Grimes */ 711ea022d16SRodney W. Grimes | MDTM check_login SP pathname CRLF 712ea022d16SRodney W. Grimes { 713ea022d16SRodney W. Grimes if ($2 && $4 != NULL) { 714ea022d16SRodney W. Grimes struct stat stbuf; 715ea022d16SRodney W. Grimes if (stat($4, &stbuf) < 0) 71682c03024SYaroslav Tykhiy perror_reply(550, $4); 717ea022d16SRodney W. Grimes else if (!S_ISREG(stbuf.st_mode)) { 718ea022d16SRodney W. Grimes reply(550, "%s: not a plain file.", $4); 719ea022d16SRodney W. Grimes } else { 720ea022d16SRodney W. Grimes struct tm *t; 721ea022d16SRodney W. Grimes t = gmtime(&stbuf.st_mtime); 722ea022d16SRodney W. Grimes reply(213, 723a5a4544eSPaul Traina "%04d%02d%02d%02d%02d%02d", 724a5a4544eSPaul Traina 1900 + t->tm_year, 725a5a4544eSPaul Traina t->tm_mon+1, t->tm_mday, 726ea022d16SRodney W. Grimes t->tm_hour, t->tm_min, t->tm_sec); 727ea022d16SRodney W. Grimes } 728ea022d16SRodney W. Grimes } 729ea022d16SRodney W. Grimes if ($4 != NULL) 730ea022d16SRodney W. Grimes free($4); 731ea022d16SRodney W. Grimes } 732ea022d16SRodney W. Grimes | QUIT CRLF 733ea022d16SRodney W. Grimes { 734ea022d16SRodney W. Grimes reply(221, "Goodbye."); 735ea022d16SRodney W. Grimes dologout(0); 736ea022d16SRodney W. Grimes } 737371348aeSYaroslav Tykhiy | NOTIMPL 738371348aeSYaroslav Tykhiy { 739371348aeSYaroslav Tykhiy nack($1); 740371348aeSYaroslav Tykhiy } 7414b82fc95SYaroslav Tykhiy | error 742ea022d16SRodney W. Grimes { 7434b82fc95SYaroslav Tykhiy yyclearin; /* discard lookahead data */ 7444b82fc95SYaroslav Tykhiy yyerrok; /* clear error condition */ 745371348aeSYaroslav Tykhiy state = CMD; /* reset lexer state */ 746ea022d16SRodney W. Grimes } 747ea022d16SRodney W. Grimes ; 748ea022d16SRodney W. Grimes rcmd 749a4b77a2aSPoul-Henning Kamp : RNFR check_login_ro SP pathname CRLF 750ea022d16SRodney W. Grimes { 7510e519c96SYaroslav Tykhiy restart_point = 0; 752ea022d16SRodney W. Grimes if ($2 && $4) { 753c507cedeSDavid Malone if (fromname) 754c507cedeSDavid Malone free(fromname); 755aa5a9d3fSYaroslav Tykhiy fromname = NULL; 756c507cedeSDavid Malone if (renamefrom($4)) 757c507cedeSDavid Malone fromname = $4; 758c507cedeSDavid Malone else 759ea022d16SRodney W. Grimes free($4); 760c507cedeSDavid Malone } else if ($4) { 761c507cedeSDavid Malone free($4); 762ea022d16SRodney W. Grimes } 763ea022d16SRodney W. Grimes } 7647d0babdaSMaxim Konovalov | REST check_login SP NUMBER CRLF 765ea022d16SRodney W. Grimes { 7663fca54b6SChris D. Faulhaber if ($2) { 767c507cedeSDavid Malone if (fromname) 768c507cedeSDavid Malone free(fromname); 769aa5a9d3fSYaroslav Tykhiy fromname = NULL; 7707d0babdaSMaxim Konovalov restart_point = $4.o; 771a57e1ef0SYaroslav Tykhiy reply(350, "Restarting at %jd. %s", 772a57e1ef0SYaroslav Tykhiy (intmax_t)restart_point, 773ea022d16SRodney W. Grimes "Send STORE or RETRIEVE to initiate transfer."); 774ea022d16SRodney W. Grimes } 7753fca54b6SChris D. Faulhaber } 776ea022d16SRodney W. Grimes ; 777ea022d16SRodney W. Grimes 778ea022d16SRodney W. Grimes username 779ea022d16SRodney W. Grimes : STRING 780ea022d16SRodney W. Grimes ; 781ea022d16SRodney W. Grimes 782ea022d16SRodney W. Grimes password 783ea022d16SRodney W. Grimes : /* empty */ 784ea022d16SRodney W. Grimes { 785ea022d16SRodney W. Grimes $$ = (char *)calloc(1, sizeof(char)); 786ea022d16SRodney W. Grimes } 787ea022d16SRodney W. Grimes | STRING 788ea022d16SRodney W. Grimes ; 789ea022d16SRodney W. Grimes 790ea022d16SRodney W. Grimes byte_size 791ea022d16SRodney W. Grimes : NUMBER 7927d0babdaSMaxim Konovalov { 7937d0babdaSMaxim Konovalov $$ = $1.i; 7947d0babdaSMaxim Konovalov } 795ea022d16SRodney W. Grimes ; 796ea022d16SRodney W. Grimes 797ea022d16SRodney W. Grimes host_port 798ea022d16SRodney W. Grimes : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 799ea022d16SRodney W. Grimes NUMBER COMMA NUMBER 800ea022d16SRodney W. Grimes { 801ea022d16SRodney W. Grimes char *a, *p; 802ea022d16SRodney W. Grimes 8034dd8b5abSYoshinobu Inoue data_dest.su_len = sizeof(struct sockaddr_in); 8044dd8b5abSYoshinobu Inoue data_dest.su_family = AF_INET; 8054dd8b5abSYoshinobu Inoue p = (char *)&data_dest.su_sin.sin_port; 8067d0babdaSMaxim Konovalov p[0] = $9.i; p[1] = $11.i; 8074dd8b5abSYoshinobu Inoue a = (char *)&data_dest.su_sin.sin_addr; 8087d0babdaSMaxim Konovalov a[0] = $1.i; a[1] = $3.i; a[2] = $5.i; a[3] = $7.i; 809ea022d16SRodney W. Grimes } 810ea022d16SRodney W. Grimes ; 811ea022d16SRodney W. Grimes 8124dd8b5abSYoshinobu Inoue host_long_port 8134dd8b5abSYoshinobu Inoue : NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 8144dd8b5abSYoshinobu Inoue NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 8154dd8b5abSYoshinobu Inoue NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 8164dd8b5abSYoshinobu Inoue NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 8174dd8b5abSYoshinobu Inoue NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 8184dd8b5abSYoshinobu Inoue NUMBER 8194dd8b5abSYoshinobu Inoue { 8204dd8b5abSYoshinobu Inoue char *a, *p; 8214dd8b5abSYoshinobu Inoue 8224dd8b5abSYoshinobu Inoue memset(&data_dest, 0, sizeof(data_dest)); 8234dd8b5abSYoshinobu Inoue data_dest.su_len = sizeof(struct sockaddr_in6); 8244dd8b5abSYoshinobu Inoue data_dest.su_family = AF_INET6; 8254dd8b5abSYoshinobu Inoue p = (char *)&data_dest.su_port; 8267d0babdaSMaxim Konovalov p[0] = $39.i; p[1] = $41.i; 8274dd8b5abSYoshinobu Inoue a = (char *)&data_dest.su_sin6.sin6_addr; 8287d0babdaSMaxim Konovalov a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 8297d0babdaSMaxim Konovalov a[4] = $13.i; a[5] = $15.i; a[6] = $17.i; a[7] = $19.i; 8307d0babdaSMaxim Konovalov a[8] = $21.i; a[9] = $23.i; a[10] = $25.i; a[11] = $27.i; 8317d0babdaSMaxim Konovalov a[12] = $29.i; a[13] = $31.i; a[14] = $33.i; a[15] = $35.i; 8324dd8b5abSYoshinobu Inoue if (his_addr.su_family == AF_INET6) { 8334dd8b5abSYoshinobu Inoue /* XXX more sanity checks! */ 8344dd8b5abSYoshinobu Inoue data_dest.su_sin6.sin6_scope_id = 8354dd8b5abSYoshinobu Inoue his_addr.su_sin6.sin6_scope_id; 8364dd8b5abSYoshinobu Inoue } 8377d0babdaSMaxim Konovalov if ($1.i != 6 || $3.i != 16 || $37.i != 2) 8384dd8b5abSYoshinobu Inoue memset(&data_dest, 0, sizeof(data_dest)); 8394dd8b5abSYoshinobu Inoue } 8404dd8b5abSYoshinobu Inoue | NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 8414dd8b5abSYoshinobu Inoue NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA 8424dd8b5abSYoshinobu Inoue NUMBER 8434dd8b5abSYoshinobu Inoue { 8444dd8b5abSYoshinobu Inoue char *a, *p; 8454dd8b5abSYoshinobu Inoue 8464dd8b5abSYoshinobu Inoue memset(&data_dest, 0, sizeof(data_dest)); 8474dd8b5abSYoshinobu Inoue data_dest.su_sin.sin_len = sizeof(struct sockaddr_in); 8484dd8b5abSYoshinobu Inoue data_dest.su_family = AF_INET; 8494dd8b5abSYoshinobu Inoue p = (char *)&data_dest.su_port; 8507d0babdaSMaxim Konovalov p[0] = $15.i; p[1] = $17.i; 8514dd8b5abSYoshinobu Inoue a = (char *)&data_dest.su_sin.sin_addr; 8527d0babdaSMaxim Konovalov a[0] = $5.i; a[1] = $7.i; a[2] = $9.i; a[3] = $11.i; 8537d0babdaSMaxim Konovalov if ($1.i != 4 || $3.i != 4 || $13.i != 2) 8544dd8b5abSYoshinobu Inoue memset(&data_dest, 0, sizeof(data_dest)); 8554dd8b5abSYoshinobu Inoue } 8564dd8b5abSYoshinobu Inoue ; 8574dd8b5abSYoshinobu Inoue 858ea022d16SRodney W. Grimes form_code 859ea022d16SRodney W. Grimes : N 860ea022d16SRodney W. Grimes { 861ea022d16SRodney W. Grimes $$ = FORM_N; 862ea022d16SRodney W. Grimes } 863ea022d16SRodney W. Grimes | T 864ea022d16SRodney W. Grimes { 865ea022d16SRodney W. Grimes $$ = FORM_T; 866ea022d16SRodney W. Grimes } 867ea022d16SRodney W. Grimes | C 868ea022d16SRodney W. Grimes { 869ea022d16SRodney W. Grimes $$ = FORM_C; 870ea022d16SRodney W. Grimes } 871ea022d16SRodney W. Grimes ; 872ea022d16SRodney W. Grimes 873ea022d16SRodney W. Grimes type_code 874ea022d16SRodney W. Grimes : A 875ea022d16SRodney W. Grimes { 876ea022d16SRodney W. Grimes cmd_type = TYPE_A; 877ea022d16SRodney W. Grimes cmd_form = FORM_N; 878ea022d16SRodney W. Grimes } 879ea022d16SRodney W. Grimes | A SP form_code 880ea022d16SRodney W. Grimes { 881ea022d16SRodney W. Grimes cmd_type = TYPE_A; 882ea022d16SRodney W. Grimes cmd_form = $3; 883ea022d16SRodney W. Grimes } 884ea022d16SRodney W. Grimes | E 885ea022d16SRodney W. Grimes { 886ea022d16SRodney W. Grimes cmd_type = TYPE_E; 887ea022d16SRodney W. Grimes cmd_form = FORM_N; 888ea022d16SRodney W. Grimes } 889ea022d16SRodney W. Grimes | E SP form_code 890ea022d16SRodney W. Grimes { 891ea022d16SRodney W. Grimes cmd_type = TYPE_E; 892ea022d16SRodney W. Grimes cmd_form = $3; 893ea022d16SRodney W. Grimes } 894ea022d16SRodney W. Grimes | I 895ea022d16SRodney W. Grimes { 896ea022d16SRodney W. Grimes cmd_type = TYPE_I; 897ea022d16SRodney W. Grimes } 898ea022d16SRodney W. Grimes | L 899ea022d16SRodney W. Grimes { 900ea022d16SRodney W. Grimes cmd_type = TYPE_L; 90189fdc4e1SMike Barcroft cmd_bytesz = CHAR_BIT; 902ea022d16SRodney W. Grimes } 903ea022d16SRodney W. Grimes | L SP byte_size 904ea022d16SRodney W. Grimes { 905ea022d16SRodney W. Grimes cmd_type = TYPE_L; 906ea022d16SRodney W. Grimes cmd_bytesz = $3; 907ea022d16SRodney W. Grimes } 908ea022d16SRodney W. Grimes /* this is for a bug in the BBN ftp */ 909ea022d16SRodney W. Grimes | L byte_size 910ea022d16SRodney W. Grimes { 911ea022d16SRodney W. Grimes cmd_type = TYPE_L; 912ea022d16SRodney W. Grimes cmd_bytesz = $2; 913ea022d16SRodney W. Grimes } 914ea022d16SRodney W. Grimes ; 915ea022d16SRodney W. Grimes 916ea022d16SRodney W. Grimes struct_code 917ea022d16SRodney W. Grimes : F 918ea022d16SRodney W. Grimes { 919ea022d16SRodney W. Grimes $$ = STRU_F; 920ea022d16SRodney W. Grimes } 921ea022d16SRodney W. Grimes | R 922ea022d16SRodney W. Grimes { 923ea022d16SRodney W. Grimes $$ = STRU_R; 924ea022d16SRodney W. Grimes } 925ea022d16SRodney W. Grimes | P 926ea022d16SRodney W. Grimes { 927ea022d16SRodney W. Grimes $$ = STRU_P; 928ea022d16SRodney W. Grimes } 929ea022d16SRodney W. Grimes ; 930ea022d16SRodney W. Grimes 931ea022d16SRodney W. Grimes mode_code 932ea022d16SRodney W. Grimes : S 933ea022d16SRodney W. Grimes { 934ea022d16SRodney W. Grimes $$ = MODE_S; 935ea022d16SRodney W. Grimes } 936ea022d16SRodney W. Grimes | B 937ea022d16SRodney W. Grimes { 938ea022d16SRodney W. Grimes $$ = MODE_B; 939ea022d16SRodney W. Grimes } 940ea022d16SRodney W. Grimes | C 941ea022d16SRodney W. Grimes { 942ea022d16SRodney W. Grimes $$ = MODE_C; 943ea022d16SRodney W. Grimes } 944ea022d16SRodney W. Grimes ; 945ea022d16SRodney W. Grimes 946ea022d16SRodney W. Grimes pathname 947ea022d16SRodney W. Grimes : pathstring 948ea022d16SRodney W. Grimes { 94970825609SPeter Wemm if (logged_in && $1) { 9506cfbc841SYaroslav Tykhiy char *p; 951ea022d16SRodney W. Grimes 9526cfbc841SYaroslav Tykhiy /* 9536cfbc841SYaroslav Tykhiy * Expand ~user manually since glob(3) 9546cfbc841SYaroslav Tykhiy * will return the unexpanded pathname 9556cfbc841SYaroslav Tykhiy * if the corresponding file/directory 9566cfbc841SYaroslav Tykhiy * doesn't exist yet. Using sole glob(3) 9576cfbc841SYaroslav Tykhiy * would break natural commands like 9586cfbc841SYaroslav Tykhiy * MKD ~user/newdir 9596cfbc841SYaroslav Tykhiy * or 9606cfbc841SYaroslav Tykhiy * RNTO ~/newfile 9616cfbc841SYaroslav Tykhiy */ 9626cfbc841SYaroslav Tykhiy if ((p = exptilde($1)) != NULL) { 9636cfbc841SYaroslav Tykhiy $$ = expglob(p); 9646cfbc841SYaroslav Tykhiy free(p); 9656cfbc841SYaroslav Tykhiy } else 966ea022d16SRodney W. Grimes $$ = NULL; 967ea022d16SRodney W. Grimes free($1); 968ea022d16SRodney W. Grimes } else 969ea022d16SRodney W. Grimes $$ = $1; 970ea022d16SRodney W. Grimes } 971ea022d16SRodney W. Grimes ; 972ea022d16SRodney W. Grimes 973ea022d16SRodney W. Grimes pathstring 974ea022d16SRodney W. Grimes : STRING 975ea022d16SRodney W. Grimes ; 976ea022d16SRodney W. Grimes 977ea022d16SRodney W. Grimes octal_number 978ea022d16SRodney W. Grimes : NUMBER 979ea022d16SRodney W. Grimes { 980ea022d16SRodney W. Grimes int ret, dec, multby, digit; 981ea022d16SRodney W. Grimes 982ea022d16SRodney W. Grimes /* 983ea022d16SRodney W. Grimes * Convert a number that was read as decimal number 984ea022d16SRodney W. Grimes * to what it would be if it had been read as octal. 985ea022d16SRodney W. Grimes */ 9867d0babdaSMaxim Konovalov dec = $1.i; 987ea022d16SRodney W. Grimes multby = 1; 988ea022d16SRodney W. Grimes ret = 0; 989ea022d16SRodney W. Grimes while (dec) { 990ea022d16SRodney W. Grimes digit = dec%10; 991ea022d16SRodney W. Grimes if (digit > 7) { 992ea022d16SRodney W. Grimes ret = -1; 993ea022d16SRodney W. Grimes break; 994ea022d16SRodney W. Grimes } 995ea022d16SRodney W. Grimes ret += digit * multby; 996ea022d16SRodney W. Grimes multby *= 8; 997ea022d16SRodney W. Grimes dec /= 10; 998ea022d16SRodney W. Grimes } 999ea022d16SRodney W. Grimes $$ = ret; 1000ea022d16SRodney W. Grimes } 1001ea022d16SRodney W. Grimes ; 1002ea022d16SRodney W. Grimes 1003ea022d16SRodney W. Grimes 1004ea022d16SRodney W. Grimes check_login 1005ea022d16SRodney W. Grimes : /* empty */ 1006ea022d16SRodney W. Grimes { 1007a4b77a2aSPoul-Henning Kamp $$ = check_login1(); 1008a4b77a2aSPoul-Henning Kamp } 1009a4b77a2aSPoul-Henning Kamp ; 1010a4b77a2aSPoul-Henning Kamp 1011a4b77a2aSPoul-Henning Kamp check_login_epsv 1012a4b77a2aSPoul-Henning Kamp : /* empty */ 1013a4b77a2aSPoul-Henning Kamp { 1014a4b77a2aSPoul-Henning Kamp if (noepsv) { 101502c97492SYaroslav Tykhiy reply(500, "EPSV command disabled."); 1016ea022d16SRodney W. Grimes $$ = 0; 1017ea022d16SRodney W. Grimes } 1018a4b77a2aSPoul-Henning Kamp else 1019a4b77a2aSPoul-Henning Kamp $$ = check_login1(); 1020a4b77a2aSPoul-Henning Kamp } 1021a4b77a2aSPoul-Henning Kamp ; 1022a4b77a2aSPoul-Henning Kamp 1023a4b77a2aSPoul-Henning Kamp check_login_ro 1024a4b77a2aSPoul-Henning Kamp : /* empty */ 1025a4b77a2aSPoul-Henning Kamp { 1026a4b77a2aSPoul-Henning Kamp if (readonly) { 1027e22887cdSDag-Erling Smørgrav reply(550, "Permission denied."); 1028a4b77a2aSPoul-Henning Kamp $$ = 0; 1029a4b77a2aSPoul-Henning Kamp } 1030a4b77a2aSPoul-Henning Kamp else 1031a4b77a2aSPoul-Henning Kamp $$ = check_login1(); 1032ea022d16SRodney W. Grimes } 1033ea022d16SRodney W. Grimes ; 1034ea022d16SRodney W. Grimes 1035ea022d16SRodney W. Grimes %% 1036ea022d16SRodney W. Grimes 1037ea022d16SRodney W. Grimes #define CMD 0 /* beginning of command */ 1038ea022d16SRodney W. Grimes #define ARGS 1 /* expect miscellaneous arguments */ 1039ea022d16SRodney W. Grimes #define STR1 2 /* expect SP followed by STRING */ 1040ea022d16SRodney W. Grimes #define STR2 3 /* expect STRING */ 1041ea022d16SRodney W. Grimes #define OSTR 4 /* optional SP then STRING */ 10427d6505e6SBrian Feldman #define ZSTR1 5 /* optional SP then optional STRING */ 1043ea022d16SRodney W. Grimes #define ZSTR2 6 /* optional STRING after SP */ 1044ea022d16SRodney W. Grimes #define SITECMD 7 /* SITE command */ 1045ea022d16SRodney W. Grimes #define NSTR 8 /* Number followed by a string */ 1046ea022d16SRodney W. Grimes 10476d3fe674SChris D. Faulhaber #define MAXGLOBARGS 1000 10486d3fe674SChris D. Faulhaber 1049781cfb93SYaroslav Tykhiy #define MAXASIZE 10240 /* Deny ASCII SIZE on files larger than that */ 1050781cfb93SYaroslav Tykhiy 1051ea022d16SRodney W. Grimes struct tab { 1052ea022d16SRodney W. Grimes char *name; 1053ea022d16SRodney W. Grimes short token; 1054ea022d16SRodney W. Grimes short state; 1055ea022d16SRodney W. Grimes short implemented; /* 1 if command is implemented */ 1056ea022d16SRodney W. Grimes char *help; 1057ea022d16SRodney W. Grimes }; 1058ea022d16SRodney W. Grimes 1059ea022d16SRodney W. Grimes struct tab cmdtab[] = { /* In order defined in RFC 765 */ 1060ea022d16SRodney W. Grimes { "USER", USER, STR1, 1, "<sp> username" }, 10617d6505e6SBrian Feldman { "PASS", PASS, ZSTR1, 1, "[<sp> [password]]" }, 1062ea022d16SRodney W. Grimes { "ACCT", ACCT, STR1, 0, "(specify account)" }, 1063ea022d16SRodney W. Grimes { "SMNT", SMNT, ARGS, 0, "(structure mount)" }, 1064ea022d16SRodney W. Grimes { "REIN", REIN, ARGS, 0, "(reinitialize server state)" }, 1065ea022d16SRodney W. Grimes { "QUIT", QUIT, ARGS, 1, "(terminate service)", }, 1066e9b61cfeSYaroslav Tykhiy { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4, b5" }, 10674dd8b5abSYoshinobu Inoue { "LPRT", LPRT, ARGS, 1, "<sp> af, hal, h1, h2, h3,..., pal, p1, p2..." }, 10684dd8b5abSYoshinobu Inoue { "EPRT", EPRT, STR1, 1, "<sp> |af|addr|port|" }, 1069ea022d16SRodney W. Grimes { "PASV", PASV, ARGS, 1, "(set server in passive mode)" }, 10704dd8b5abSYoshinobu Inoue { "LPSV", LPSV, ARGS, 1, "(set server in passive mode)" }, 10714dd8b5abSYoshinobu Inoue { "EPSV", EPSV, ARGS, 1, "[<sp> af|ALL]" }, 1072e9b61cfeSYaroslav Tykhiy { "TYPE", TYPE, ARGS, 1, "<sp> { A | E | I | L }" }, 1073ea022d16SRodney W. Grimes { "STRU", STRU, ARGS, 1, "(specify file structure)" }, 1074ea022d16SRodney W. Grimes { "MODE", MODE, ARGS, 1, "(specify transfer mode)" }, 1075ea022d16SRodney W. Grimes { "RETR", RETR, STR1, 1, "<sp> file-name" }, 1076ea022d16SRodney W. Grimes { "STOR", STOR, STR1, 1, "<sp> file-name" }, 1077ea022d16SRodney W. Grimes { "APPE", APPE, STR1, 1, "<sp> file-name" }, 1078ea022d16SRodney W. Grimes { "MLFL", MLFL, OSTR, 0, "(mail file)" }, 1079ea022d16SRodney W. Grimes { "MAIL", MAIL, OSTR, 0, "(mail to user)" }, 1080ea022d16SRodney W. Grimes { "MSND", MSND, OSTR, 0, "(mail send to terminal)" }, 1081ea022d16SRodney W. Grimes { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" }, 1082ea022d16SRodney W. Grimes { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" }, 1083ea022d16SRodney W. Grimes { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" }, 1084ea022d16SRodney W. Grimes { "MRCP", MRCP, STR1, 0, "(mail recipient)" }, 1085ea022d16SRodney W. Grimes { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" }, 1086ea022d16SRodney W. Grimes { "REST", REST, ARGS, 1, "<sp> offset (restart command)" }, 1087ea022d16SRodney W. Grimes { "RNFR", RNFR, STR1, 1, "<sp> file-name" }, 1088ea022d16SRodney W. Grimes { "RNTO", RNTO, STR1, 1, "<sp> file-name" }, 1089ea022d16SRodney W. Grimes { "ABOR", ABOR, ARGS, 1, "(abort operation)" }, 1090ea022d16SRodney W. Grimes { "DELE", DELE, STR1, 1, "<sp> file-name" }, 1091ea022d16SRodney W. Grimes { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 1092ea022d16SRodney W. Grimes { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" }, 1093ea022d16SRodney W. Grimes { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" }, 1094ea022d16SRodney W. Grimes { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" }, 1095ea022d16SRodney W. Grimes { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" }, 1096ea022d16SRodney W. Grimes { "SYST", SYST, ARGS, 1, "(get type of operating system)" }, 10972ea42282SYaroslav Tykhiy { "FEAT", FEAT, ARGS, 1, "(get extended features)" }, 1098ea022d16SRodney W. Grimes { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" }, 1099ea022d16SRodney W. Grimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 1100ea022d16SRodney W. Grimes { "NOOP", NOOP, ARGS, 1, "" }, 1101ea022d16SRodney W. Grimes { "MKD", MKD, STR1, 1, "<sp> path-name" }, 1102ea022d16SRodney W. Grimes { "XMKD", MKD, STR1, 1, "<sp> path-name" }, 1103ea022d16SRodney W. Grimes { "RMD", RMD, STR1, 1, "<sp> path-name" }, 1104ea022d16SRodney W. Grimes { "XRMD", RMD, STR1, 1, "<sp> path-name" }, 1105ea022d16SRodney W. Grimes { "PWD", PWD, ARGS, 1, "(return current directory)" }, 1106ea022d16SRodney W. Grimes { "XPWD", PWD, ARGS, 1, "(return current directory)" }, 1107ea022d16SRodney W. Grimes { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" }, 1108ea022d16SRodney W. Grimes { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" }, 1109ea022d16SRodney W. Grimes { "STOU", STOU, STR1, 1, "<sp> file-name" }, 1110ea022d16SRodney W. Grimes { "SIZE", SIZE, OSTR, 1, "<sp> path-name" }, 1111ea022d16SRodney W. Grimes { "MDTM", MDTM, OSTR, 1, "<sp> path-name" }, 1112ea022d16SRodney W. Grimes { NULL, 0, 0, 0, 0 } 1113ea022d16SRodney W. Grimes }; 1114ea022d16SRodney W. Grimes 1115ea022d16SRodney W. Grimes struct tab sitetab[] = { 111653ba84a6SPoul-Henning Kamp { "MD5", MDFIVE, STR1, 1, "[ <sp> file-name ]" }, 1117ea022d16SRodney W. Grimes { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" }, 1118ea022d16SRodney W. Grimes { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" }, 1119ea022d16SRodney W. Grimes { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" }, 1120ea022d16SRodney W. Grimes { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" }, 1121ea022d16SRodney W. Grimes { NULL, 0, 0, 0, 0 } 1122ea022d16SRodney W. Grimes }; 1123ea022d16SRodney W. Grimes 1124e4bc453cSWarner Losh static char *copy(char *); 11256cfbc841SYaroslav Tykhiy static char *expglob(char *); 11266cfbc841SYaroslav Tykhiy static char *exptilde(char *); 1127e4bc453cSWarner Losh static void help(struct tab *, char *); 1128ea022d16SRodney W. Grimes static struct tab * 1129e4bc453cSWarner Losh lookup(struct tab *, char *); 1130e4bc453cSWarner Losh static int port_check(const char *); 113131ee80d8SYaroslav Tykhiy #ifdef INET6 1132e4bc453cSWarner Losh static int port_check_v6(const char *); 113331ee80d8SYaroslav Tykhiy #endif 1134e4bc453cSWarner Losh static void sizecmd(char *); 1135e4bc453cSWarner Losh static void toolong(int); 113631ee80d8SYaroslav Tykhiy #ifdef INET6 1137e4bc453cSWarner Losh static void v4map_data_dest(void); 113831ee80d8SYaroslav Tykhiy #endif 1139e4bc453cSWarner Losh static int yylex(void); 1140ea022d16SRodney W. Grimes 1141ea022d16SRodney W. Grimes static struct tab * 1142e4bc453cSWarner Losh lookup(struct tab *p, char *cmd) 1143ea022d16SRodney W. Grimes { 1144ea022d16SRodney W. Grimes 1145ea022d16SRodney W. Grimes for (; p->name != NULL; p++) 1146ea022d16SRodney W. Grimes if (strcmp(cmd, p->name) == 0) 1147ea022d16SRodney W. Grimes return (p); 1148ea022d16SRodney W. Grimes return (0); 1149ea022d16SRodney W. Grimes } 1150ea022d16SRodney W. Grimes 1151ea022d16SRodney W. Grimes #include <arpa/telnet.h> 1152ea022d16SRodney W. Grimes 1153ea022d16SRodney W. Grimes /* 1154f03ef840SBaptiste Daroussin * get_line - a hacked up version of fgets to ignore TELNET escape codes. 1155ea022d16SRodney W. Grimes */ 1156f0b40b1cSColin Percival int 1157f03ef840SBaptiste Daroussin get_line(char *s, int n, FILE *iop) 1158ea022d16SRodney W. Grimes { 1159ea022d16SRodney W. Grimes int c; 1160ea022d16SRodney W. Grimes register char *cs; 1161e25d3184SYaroslav Tykhiy sigset_t sset, osset; 1162ea022d16SRodney W. Grimes 1163ea022d16SRodney W. Grimes cs = s; 1164ea022d16SRodney W. Grimes /* tmpline may contain saved command from urgent mode interruption */ 1165ea022d16SRodney W. Grimes for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) { 1166ea022d16SRodney W. Grimes *cs++ = tmpline[c]; 1167ea022d16SRodney W. Grimes if (tmpline[c] == '\n') { 1168ea022d16SRodney W. Grimes *cs++ = '\0'; 1169618b0bbaSMark Murray if (ftpdebug) 1170ea022d16SRodney W. Grimes syslog(LOG_DEBUG, "command: %s", s); 1171ea022d16SRodney W. Grimes tmpline[0] = '\0'; 1172f0b40b1cSColin Percival return(0); 1173ea022d16SRodney W. Grimes } 1174ea022d16SRodney W. Grimes if (c == 0) 1175ea022d16SRodney W. Grimes tmpline[0] = '\0'; 1176ea022d16SRodney W. Grimes } 1177e25d3184SYaroslav Tykhiy /* SIGURG would interrupt stdio if not blocked during the read loop */ 1178e25d3184SYaroslav Tykhiy sigemptyset(&sset); 1179e25d3184SYaroslav Tykhiy sigaddset(&sset, SIGURG); 1180e25d3184SYaroslav Tykhiy sigprocmask(SIG_BLOCK, &sset, &osset); 1181ea022d16SRodney W. Grimes while ((c = getc(iop)) != EOF) { 1182ea022d16SRodney W. Grimes c &= 0377; 1183ea022d16SRodney W. Grimes if (c == IAC) { 118439b96ba7SYaroslav Tykhiy if ((c = getc(iop)) == EOF) 118539b96ba7SYaroslav Tykhiy goto got_eof; 1186ea022d16SRodney W. Grimes c &= 0377; 1187ea022d16SRodney W. Grimes switch (c) { 1188ea022d16SRodney W. Grimes case WILL: 1189ea022d16SRodney W. Grimes case WONT: 119039b96ba7SYaroslav Tykhiy if ((c = getc(iop)) == EOF) 119139b96ba7SYaroslav Tykhiy goto got_eof; 1192ea022d16SRodney W. Grimes printf("%c%c%c", IAC, DONT, 0377&c); 1193ea022d16SRodney W. Grimes (void) fflush(stdout); 1194ea022d16SRodney W. Grimes continue; 1195ea022d16SRodney W. Grimes case DO: 1196ea022d16SRodney W. Grimes case DONT: 119739b96ba7SYaroslav Tykhiy if ((c = getc(iop)) == EOF) 119839b96ba7SYaroslav Tykhiy goto got_eof; 1199ea022d16SRodney W. Grimes printf("%c%c%c", IAC, WONT, 0377&c); 1200ea022d16SRodney W. Grimes (void) fflush(stdout); 1201ea022d16SRodney W. Grimes continue; 1202ea022d16SRodney W. Grimes case IAC: 1203ea022d16SRodney W. Grimes break; 1204ea022d16SRodney W. Grimes default: 1205ea022d16SRodney W. Grimes continue; /* ignore command */ 1206ea022d16SRodney W. Grimes } 1207ea022d16SRodney W. Grimes } 1208ea022d16SRodney W. Grimes *cs++ = c; 1209f0b40b1cSColin Percival if (--n <= 0) { 1210f0b40b1cSColin Percival /* 1211f0b40b1cSColin Percival * If command doesn't fit into buffer, discard the 1212f0b40b1cSColin Percival * rest of the command and indicate truncation. 1213f0b40b1cSColin Percival * This prevents the command to be split up into 1214f0b40b1cSColin Percival * multiple commands. 1215f0b40b1cSColin Percival */ 1216f0b40b1cSColin Percival while (c != '\n' && (c = getc(iop)) != EOF) 1217f0b40b1cSColin Percival ; 1218f0b40b1cSColin Percival return (-2); 1219f0b40b1cSColin Percival } 1220f0b40b1cSColin Percival if (c == '\n') 1221ea022d16SRodney W. Grimes break; 1222ea022d16SRodney W. Grimes } 122339b96ba7SYaroslav Tykhiy got_eof: 1224e25d3184SYaroslav Tykhiy sigprocmask(SIG_SETMASK, &osset, NULL); 1225ea022d16SRodney W. Grimes if (c == EOF && cs == s) 1226f0b40b1cSColin Percival return (-1); 1227ea022d16SRodney W. Grimes *cs++ = '\0'; 1228618b0bbaSMark Murray if (ftpdebug) { 1229ea022d16SRodney W. Grimes if (!guest && strncasecmp("pass ", s, 5) == 0) { 1230ea022d16SRodney W. Grimes /* Don't syslog passwords */ 1231ea022d16SRodney W. Grimes syslog(LOG_DEBUG, "command: %.5s ???", s); 1232ea022d16SRodney W. Grimes } else { 1233ea022d16SRodney W. Grimes register char *cp; 1234ea022d16SRodney W. Grimes register int len; 1235ea022d16SRodney W. Grimes 1236ea022d16SRodney W. Grimes /* Don't syslog trailing CR-LF */ 1237ea022d16SRodney W. Grimes len = strlen(s); 1238ea022d16SRodney W. Grimes cp = s + len - 1; 1239ea022d16SRodney W. Grimes while (cp >= s && (*cp == '\n' || *cp == '\r')) { 1240ea022d16SRodney W. Grimes --cp; 1241ea022d16SRodney W. Grimes --len; 1242ea022d16SRodney W. Grimes } 1243ea022d16SRodney W. Grimes syslog(LOG_DEBUG, "command: %.*s", len, s); 1244ea022d16SRodney W. Grimes } 1245ea022d16SRodney W. Grimes } 1246f0b40b1cSColin Percival return (0); 1247ea022d16SRodney W. Grimes } 1248ea022d16SRodney W. Grimes 1249ea022d16SRodney W. Grimes static void 1250e4bc453cSWarner Losh toolong(int signo) 1251ea022d16SRodney W. Grimes { 1252ea022d16SRodney W. Grimes 1253ea022d16SRodney W. Grimes reply(421, 1254ea022d16SRodney W. Grimes "Timeout (%d seconds): closing control connection.", timeout); 1255ea022d16SRodney W. Grimes if (logging) 1256ea022d16SRodney W. Grimes syslog(LOG_INFO, "User %s timed out after %d seconds", 1257ea022d16SRodney W. Grimes (pw ? pw -> pw_name : "unknown"), timeout); 1258ea022d16SRodney W. Grimes dologout(1); 1259ea022d16SRodney W. Grimes } 1260ea022d16SRodney W. Grimes 1261ea022d16SRodney W. Grimes static int 1262e4bc453cSWarner Losh yylex(void) 1263ea022d16SRodney W. Grimes { 12644b82fc95SYaroslav Tykhiy static int cpos; 1265ea022d16SRodney W. Grimes char *cp, *cp2; 1266ea022d16SRodney W. Grimes struct tab *p; 1267ea022d16SRodney W. Grimes int n; 1268ea022d16SRodney W. Grimes char c; 1269ea022d16SRodney W. Grimes 1270ea022d16SRodney W. Grimes for (;;) { 1271ea022d16SRodney W. Grimes switch (state) { 1272ea022d16SRodney W. Grimes 1273ea022d16SRodney W. Grimes case CMD: 1274ea022d16SRodney W. Grimes (void) signal(SIGALRM, toolong); 1275e3765043SYaroslav Tykhiy (void) alarm(timeout); 1276f03ef840SBaptiste Daroussin n = get_line(cbuf, sizeof(cbuf)-1, stdin); 1277f0b40b1cSColin Percival if (n == -1) { 1278ea022d16SRodney W. Grimes reply(221, "You could at least say goodbye."); 1279ea022d16SRodney W. Grimes dologout(0); 1280f0b40b1cSColin Percival } else if (n == -2) { 1281f0b40b1cSColin Percival reply(500, "Command too long."); 1282f0b40b1cSColin Percival (void) alarm(0); 1283f0b40b1cSColin Percival continue; 1284ea022d16SRodney W. Grimes } 1285ea022d16SRodney W. Grimes (void) alarm(0); 1286ea022d16SRodney W. Grimes #ifdef SETPROCTITLE 1287d030d2d2SPoul-Henning Kamp if (strncasecmp(cbuf, "PASS", 4) != 0) 1288ea022d16SRodney W. Grimes setproctitle("%s: %s", proctitle, cbuf); 1289ea022d16SRodney W. Grimes #endif /* SETPROCTITLE */ 1290ea022d16SRodney W. Grimes if ((cp = strchr(cbuf, '\r'))) { 1291ea022d16SRodney W. Grimes *cp++ = '\n'; 1292ea022d16SRodney W. Grimes *cp = '\0'; 1293ea022d16SRodney W. Grimes } 1294ea022d16SRodney W. Grimes if ((cp = strpbrk(cbuf, " \n"))) 1295ea022d16SRodney W. Grimes cpos = cp - cbuf; 1296ea022d16SRodney W. Grimes if (cpos == 0) 1297ea022d16SRodney W. Grimes cpos = 4; 1298ea022d16SRodney W. Grimes c = cbuf[cpos]; 1299ea022d16SRodney W. Grimes cbuf[cpos] = '\0'; 1300ea022d16SRodney W. Grimes upper(cbuf); 1301ea022d16SRodney W. Grimes p = lookup(cmdtab, cbuf); 1302ea022d16SRodney W. Grimes cbuf[cpos] = c; 13030d78c1c0SPaul Traina if (p != 0) { 1304ea022d16SRodney W. Grimes yylval.s = p->name; 1305371348aeSYaroslav Tykhiy if (!p->implemented) 1306371348aeSYaroslav Tykhiy return (NOTIMPL); /* state remains CMD */ 1307371348aeSYaroslav Tykhiy state = p->state; 1308ea022d16SRodney W. Grimes return (p->token); 1309ea022d16SRodney W. Grimes } 1310ea022d16SRodney W. Grimes break; 1311ea022d16SRodney W. Grimes 1312ea022d16SRodney W. Grimes case SITECMD: 1313ea022d16SRodney W. Grimes if (cbuf[cpos] == ' ') { 1314ea022d16SRodney W. Grimes cpos++; 1315ea022d16SRodney W. Grimes return (SP); 1316ea022d16SRodney W. Grimes } 1317ea022d16SRodney W. Grimes cp = &cbuf[cpos]; 1318ea022d16SRodney W. Grimes if ((cp2 = strpbrk(cp, " \n"))) 1319ea022d16SRodney W. Grimes cpos = cp2 - cbuf; 1320ea022d16SRodney W. Grimes c = cbuf[cpos]; 1321ea022d16SRodney W. Grimes cbuf[cpos] = '\0'; 1322ea022d16SRodney W. Grimes upper(cp); 1323ea022d16SRodney W. Grimes p = lookup(sitetab, cp); 1324ea022d16SRodney W. Grimes cbuf[cpos] = c; 13259e53ab00SPaul Traina if (guest == 0 && p != 0) { 1326371348aeSYaroslav Tykhiy yylval.s = p->name; 1327371348aeSYaroslav Tykhiy if (!p->implemented) { 1328ea022d16SRodney W. Grimes state = CMD; 1329371348aeSYaroslav Tykhiy return (NOTIMPL); 1330ea022d16SRodney W. Grimes } 1331ea022d16SRodney W. Grimes state = p->state; 1332ea022d16SRodney W. Grimes return (p->token); 1333ea022d16SRodney W. Grimes } 1334ea022d16SRodney W. Grimes state = CMD; 1335ea022d16SRodney W. Grimes break; 1336ea022d16SRodney W. Grimes 13377d6505e6SBrian Feldman case ZSTR1: 1338ea022d16SRodney W. Grimes case OSTR: 1339ea022d16SRodney W. Grimes if (cbuf[cpos] == '\n') { 1340ea022d16SRodney W. Grimes state = CMD; 1341ea022d16SRodney W. Grimes return (CRLF); 1342ea022d16SRodney W. Grimes } 1343ea022d16SRodney W. Grimes /* FALLTHROUGH */ 1344ea022d16SRodney W. Grimes 1345ea022d16SRodney W. Grimes case STR1: 1346ea022d16SRodney W. Grimes dostr1: 1347ea022d16SRodney W. Grimes if (cbuf[cpos] == ' ') { 1348ea022d16SRodney W. Grimes cpos++; 134909ef98c6SAlfred Perlstein state = state == OSTR ? STR2 : state+1; 1350ea022d16SRodney W. Grimes return (SP); 1351ea022d16SRodney W. Grimes } 1352ea022d16SRodney W. Grimes break; 1353ea022d16SRodney W. Grimes 1354ea022d16SRodney W. Grimes case ZSTR2: 1355ea022d16SRodney W. Grimes if (cbuf[cpos] == '\n') { 1356ea022d16SRodney W. Grimes state = CMD; 1357ea022d16SRodney W. Grimes return (CRLF); 1358ea022d16SRodney W. Grimes } 1359ea022d16SRodney W. Grimes /* FALLTHROUGH */ 1360ea022d16SRodney W. Grimes 1361ea022d16SRodney W. Grimes case STR2: 1362ea022d16SRodney W. Grimes cp = &cbuf[cpos]; 1363ea022d16SRodney W. Grimes n = strlen(cp); 1364ea022d16SRodney W. Grimes cpos += n - 1; 1365ea022d16SRodney W. Grimes /* 1366ea022d16SRodney W. Grimes * Make sure the string is nonempty and \n terminated. 1367ea022d16SRodney W. Grimes */ 1368ea022d16SRodney W. Grimes if (n > 1 && cbuf[cpos] == '\n') { 1369ea022d16SRodney W. Grimes cbuf[cpos] = '\0'; 1370ea022d16SRodney W. Grimes yylval.s = copy(cp); 1371ea022d16SRodney W. Grimes cbuf[cpos] = '\n'; 1372ea022d16SRodney W. Grimes state = ARGS; 1373ea022d16SRodney W. Grimes return (STRING); 1374ea022d16SRodney W. Grimes } 1375ea022d16SRodney W. Grimes break; 1376ea022d16SRodney W. Grimes 1377ea022d16SRodney W. Grimes case NSTR: 1378ea022d16SRodney W. Grimes if (cbuf[cpos] == ' ') { 1379ea022d16SRodney W. Grimes cpos++; 1380ea022d16SRodney W. Grimes return (SP); 1381ea022d16SRodney W. Grimes } 1382ea022d16SRodney W. Grimes if (isdigit(cbuf[cpos])) { 1383ea022d16SRodney W. Grimes cp = &cbuf[cpos]; 1384ea022d16SRodney W. Grimes while (isdigit(cbuf[++cpos])) 1385ea022d16SRodney W. Grimes ; 1386ea022d16SRodney W. Grimes c = cbuf[cpos]; 1387ea022d16SRodney W. Grimes cbuf[cpos] = '\0'; 13887d0babdaSMaxim Konovalov yylval.u.i = atoi(cp); 1389ea022d16SRodney W. Grimes cbuf[cpos] = c; 1390ea022d16SRodney W. Grimes state = STR1; 1391ea022d16SRodney W. Grimes return (NUMBER); 1392ea022d16SRodney W. Grimes } 1393ea022d16SRodney W. Grimes state = STR1; 1394ea022d16SRodney W. Grimes goto dostr1; 1395ea022d16SRodney W. Grimes 1396ea022d16SRodney W. Grimes case ARGS: 1397ea022d16SRodney W. Grimes if (isdigit(cbuf[cpos])) { 1398ea022d16SRodney W. Grimes cp = &cbuf[cpos]; 1399ea022d16SRodney W. Grimes while (isdigit(cbuf[++cpos])) 1400ea022d16SRodney W. Grimes ; 1401ea022d16SRodney W. Grimes c = cbuf[cpos]; 1402ea022d16SRodney W. Grimes cbuf[cpos] = '\0'; 14037d0babdaSMaxim Konovalov yylval.u.i = atoi(cp); 14047e295315SYaroslav Tykhiy yylval.u.o = strtoull(cp, NULL, 10); 1405ea022d16SRodney W. Grimes cbuf[cpos] = c; 1406ea022d16SRodney W. Grimes return (NUMBER); 1407ea022d16SRodney W. Grimes } 14084dd8b5abSYoshinobu Inoue if (strncasecmp(&cbuf[cpos], "ALL", 3) == 0 14094dd8b5abSYoshinobu Inoue && !isalnum(cbuf[cpos + 3])) { 14104dd8b5abSYoshinobu Inoue cpos += 3; 14114dd8b5abSYoshinobu Inoue return ALL; 14124dd8b5abSYoshinobu Inoue } 1413ea022d16SRodney W. Grimes switch (cbuf[cpos++]) { 1414ea022d16SRodney W. Grimes 1415ea022d16SRodney W. Grimes case '\n': 1416ea022d16SRodney W. Grimes state = CMD; 1417ea022d16SRodney W. Grimes return (CRLF); 1418ea022d16SRodney W. Grimes 1419ea022d16SRodney W. Grimes case ' ': 1420ea022d16SRodney W. Grimes return (SP); 1421ea022d16SRodney W. Grimes 1422ea022d16SRodney W. Grimes case ',': 1423ea022d16SRodney W. Grimes return (COMMA); 1424ea022d16SRodney W. Grimes 1425ea022d16SRodney W. Grimes case 'A': 1426ea022d16SRodney W. Grimes case 'a': 1427ea022d16SRodney W. Grimes return (A); 1428ea022d16SRodney W. Grimes 1429ea022d16SRodney W. Grimes case 'B': 1430ea022d16SRodney W. Grimes case 'b': 1431ea022d16SRodney W. Grimes return (B); 1432ea022d16SRodney W. Grimes 1433ea022d16SRodney W. Grimes case 'C': 1434ea022d16SRodney W. Grimes case 'c': 1435ea022d16SRodney W. Grimes return (C); 1436ea022d16SRodney W. Grimes 1437ea022d16SRodney W. Grimes case 'E': 1438ea022d16SRodney W. Grimes case 'e': 1439ea022d16SRodney W. Grimes return (E); 1440ea022d16SRodney W. Grimes 1441ea022d16SRodney W. Grimes case 'F': 1442ea022d16SRodney W. Grimes case 'f': 1443ea022d16SRodney W. Grimes return (F); 1444ea022d16SRodney W. Grimes 1445ea022d16SRodney W. Grimes case 'I': 1446ea022d16SRodney W. Grimes case 'i': 1447ea022d16SRodney W. Grimes return (I); 1448ea022d16SRodney W. Grimes 1449ea022d16SRodney W. Grimes case 'L': 1450ea022d16SRodney W. Grimes case 'l': 1451ea022d16SRodney W. Grimes return (L); 1452ea022d16SRodney W. Grimes 1453ea022d16SRodney W. Grimes case 'N': 1454ea022d16SRodney W. Grimes case 'n': 1455ea022d16SRodney W. Grimes return (N); 1456ea022d16SRodney W. Grimes 1457ea022d16SRodney W. Grimes case 'P': 1458ea022d16SRodney W. Grimes case 'p': 1459ea022d16SRodney W. Grimes return (P); 1460ea022d16SRodney W. Grimes 1461ea022d16SRodney W. Grimes case 'R': 1462ea022d16SRodney W. Grimes case 'r': 1463ea022d16SRodney W. Grimes return (R); 1464ea022d16SRodney W. Grimes 1465ea022d16SRodney W. Grimes case 'S': 1466ea022d16SRodney W. Grimes case 's': 1467ea022d16SRodney W. Grimes return (S); 1468ea022d16SRodney W. Grimes 1469ea022d16SRodney W. Grimes case 'T': 1470ea022d16SRodney W. Grimes case 't': 1471ea022d16SRodney W. Grimes return (T); 1472ea022d16SRodney W. Grimes 1473ea022d16SRodney W. Grimes } 1474ea022d16SRodney W. Grimes break; 1475ea022d16SRodney W. Grimes 1476ea022d16SRodney W. Grimes default: 1477618b0bbaSMark Murray fatalerror("Unknown state in scanner."); 1478ea022d16SRodney W. Grimes } 1479ea022d16SRodney W. Grimes state = CMD; 14804b82fc95SYaroslav Tykhiy return (LEXERR); 1481ea022d16SRodney W. Grimes } 1482ea022d16SRodney W. Grimes } 1483ea022d16SRodney W. Grimes 1484ea022d16SRodney W. Grimes void 1485e4bc453cSWarner Losh upper(char *s) 1486ea022d16SRodney W. Grimes { 1487ea022d16SRodney W. Grimes while (*s != '\0') { 1488ea022d16SRodney W. Grimes if (islower(*s)) 1489ea022d16SRodney W. Grimes *s = toupper(*s); 1490ea022d16SRodney W. Grimes s++; 1491ea022d16SRodney W. Grimes } 1492ea022d16SRodney W. Grimes } 1493ea022d16SRodney W. Grimes 1494ea022d16SRodney W. Grimes static char * 1495e4bc453cSWarner Losh copy(char *s) 1496ea022d16SRodney W. Grimes { 1497ea022d16SRodney W. Grimes char *p; 1498ea022d16SRodney W. Grimes 1499e3765043SYaroslav Tykhiy p = malloc(strlen(s) + 1); 1500ea022d16SRodney W. Grimes if (p == NULL) 1501618b0bbaSMark Murray fatalerror("Ran out of memory."); 1502ea022d16SRodney W. Grimes (void) strcpy(p, s); 1503ea022d16SRodney W. Grimes return (p); 1504ea022d16SRodney W. Grimes } 1505ea022d16SRodney W. Grimes 1506ea022d16SRodney W. Grimes static void 1507e4bc453cSWarner Losh help(struct tab *ctab, char *s) 1508ea022d16SRodney W. Grimes { 1509ea022d16SRodney W. Grimes struct tab *c; 1510ea022d16SRodney W. Grimes int width, NCMDS; 1511ea022d16SRodney W. Grimes char *type; 1512ea022d16SRodney W. Grimes 1513ea022d16SRodney W. Grimes if (ctab == sitetab) 1514ea022d16SRodney W. Grimes type = "SITE "; 1515ea022d16SRodney W. Grimes else 1516ea022d16SRodney W. Grimes type = ""; 1517ea022d16SRodney W. Grimes width = 0, NCMDS = 0; 1518ea022d16SRodney W. Grimes for (c = ctab; c->name != NULL; c++) { 1519ea022d16SRodney W. Grimes int len = strlen(c->name); 1520ea022d16SRodney W. Grimes 1521ea022d16SRodney W. Grimes if (len > width) 1522ea022d16SRodney W. Grimes width = len; 1523ea022d16SRodney W. Grimes NCMDS++; 1524ea022d16SRodney W. Grimes } 1525ea022d16SRodney W. Grimes width = (width + 8) &~ 7; 1526ea022d16SRodney W. Grimes if (s == 0) { 1527ea022d16SRodney W. Grimes int i, j, w; 1528ea022d16SRodney W. Grimes int columns, lines; 1529ea022d16SRodney W. Grimes 1530ea022d16SRodney W. Grimes lreply(214, "The following %scommands are recognized %s.", 1531ea022d16SRodney W. Grimes type, "(* =>'s unimplemented)"); 1532ea022d16SRodney W. Grimes columns = 76 / width; 1533ea022d16SRodney W. Grimes if (columns == 0) 1534ea022d16SRodney W. Grimes columns = 1; 1535ea022d16SRodney W. Grimes lines = (NCMDS + columns - 1) / columns; 1536ea022d16SRodney W. Grimes for (i = 0; i < lines; i++) { 1537ea022d16SRodney W. Grimes printf(" "); 1538ea022d16SRodney W. Grimes for (j = 0; j < columns; j++) { 1539ea022d16SRodney W. Grimes c = ctab + j * lines + i; 1540ea022d16SRodney W. Grimes printf("%s%c", c->name, 1541ea022d16SRodney W. Grimes c->implemented ? ' ' : '*'); 1542ea022d16SRodney W. Grimes if (c + lines >= &ctab[NCMDS]) 1543ea022d16SRodney W. Grimes break; 1544ea022d16SRodney W. Grimes w = strlen(c->name) + 1; 1545ea022d16SRodney W. Grimes while (w < width) { 1546ea022d16SRodney W. Grimes putchar(' '); 1547ea022d16SRodney W. Grimes w++; 1548ea022d16SRodney W. Grimes } 1549ea022d16SRodney W. Grimes } 1550ea022d16SRodney W. Grimes printf("\r\n"); 1551ea022d16SRodney W. Grimes } 1552ea022d16SRodney W. Grimes (void) fflush(stdout); 1553c152df28SYaroslav Tykhiy if (hostinfo) 1554ea022d16SRodney W. Grimes reply(214, "Direct comments to ftp-bugs@%s.", hostname); 1555c152df28SYaroslav Tykhiy else 1556c152df28SYaroslav Tykhiy reply(214, "End."); 1557ea022d16SRodney W. Grimes return; 1558ea022d16SRodney W. Grimes } 1559ea022d16SRodney W. Grimes upper(s); 1560ea022d16SRodney W. Grimes c = lookup(ctab, s); 1561aa5a9d3fSYaroslav Tykhiy if (c == NULL) { 1562ea022d16SRodney W. Grimes reply(502, "Unknown command %s.", s); 1563ea022d16SRodney W. Grimes return; 1564ea022d16SRodney W. Grimes } 1565ea022d16SRodney W. Grimes if (c->implemented) 1566ea022d16SRodney W. Grimes reply(214, "Syntax: %s%s %s", type, c->name, c->help); 1567ea022d16SRodney W. Grimes else 1568ea022d16SRodney W. Grimes reply(214, "%s%-*s\t%s; unimplemented.", type, width, 1569ea022d16SRodney W. Grimes c->name, c->help); 1570ea022d16SRodney W. Grimes } 1571ea022d16SRodney W. Grimes 1572ea022d16SRodney W. Grimes static void 1573e4bc453cSWarner Losh sizecmd(char *filename) 1574ea022d16SRodney W. Grimes { 1575ea022d16SRodney W. Grimes switch (type) { 1576ea022d16SRodney W. Grimes case TYPE_L: 1577ea022d16SRodney W. Grimes case TYPE_I: { 1578ea022d16SRodney W. Grimes struct stat stbuf; 15796200918dSDag-Erling Smørgrav if (stat(filename, &stbuf) < 0) 15806200918dSDag-Erling Smørgrav perror_reply(550, filename); 15816200918dSDag-Erling Smørgrav else if (!S_ISREG(stbuf.st_mode)) 1582ea022d16SRodney W. Grimes reply(550, "%s: not a plain file.", filename); 1583ea022d16SRodney W. Grimes else 1584a57e1ef0SYaroslav Tykhiy reply(213, "%jd", (intmax_t)stbuf.st_size); 1585ea022d16SRodney W. Grimes break; } 1586ea022d16SRodney W. Grimes case TYPE_A: { 1587ea022d16SRodney W. Grimes FILE *fin; 1588ea022d16SRodney W. Grimes int c; 1589ea022d16SRodney W. Grimes off_t count; 1590ea022d16SRodney W. Grimes struct stat stbuf; 1591ea022d16SRodney W. Grimes fin = fopen(filename, "r"); 1592ea022d16SRodney W. Grimes if (fin == NULL) { 1593ea022d16SRodney W. Grimes perror_reply(550, filename); 1594ea022d16SRodney W. Grimes return; 1595ea022d16SRodney W. Grimes } 15966200918dSDag-Erling Smørgrav if (fstat(fileno(fin), &stbuf) < 0) { 15976200918dSDag-Erling Smørgrav perror_reply(550, filename); 15986200918dSDag-Erling Smørgrav (void) fclose(fin); 15996200918dSDag-Erling Smørgrav return; 16006200918dSDag-Erling Smørgrav } else if (!S_ISREG(stbuf.st_mode)) { 1601ea022d16SRodney W. Grimes reply(550, "%s: not a plain file.", filename); 1602ea022d16SRodney W. Grimes (void) fclose(fin); 1603ea022d16SRodney W. Grimes return; 1604781cfb93SYaroslav Tykhiy } else if (stbuf.st_size > MAXASIZE) { 1605781cfb93SYaroslav Tykhiy reply(550, "%s: too large for type A SIZE.", filename); 1606781cfb93SYaroslav Tykhiy (void) fclose(fin); 1607781cfb93SYaroslav Tykhiy return; 1608ea022d16SRodney W. Grimes } 1609ea022d16SRodney W. Grimes 1610ea022d16SRodney W. Grimes count = 0; 1611ea022d16SRodney W. Grimes while((c=getc(fin)) != EOF) { 1612ea022d16SRodney W. Grimes if (c == '\n') /* will get expanded to \r\n */ 1613ea022d16SRodney W. Grimes count++; 1614ea022d16SRodney W. Grimes count++; 1615ea022d16SRodney W. Grimes } 1616ea022d16SRodney W. Grimes (void) fclose(fin); 1617ea022d16SRodney W. Grimes 1618a57e1ef0SYaroslav Tykhiy reply(213, "%jd", (intmax_t)count); 1619ea022d16SRodney W. Grimes break; } 1620ea022d16SRodney W. Grimes default: 16214454edd6SYaroslav Tykhiy reply(504, "SIZE not implemented for type %s.", 16224454edd6SYaroslav Tykhiy typenames[type]); 1623ea022d16SRodney W. Grimes } 1624ea022d16SRodney W. Grimes } 16254dd8b5abSYoshinobu Inoue 16264dd8b5abSYoshinobu Inoue /* Return 1, if port check is done. Return 0, if not yet. */ 16274dd8b5abSYoshinobu Inoue static int 1628e4bc453cSWarner Losh port_check(const char *pcmd) 16294dd8b5abSYoshinobu Inoue { 16304dd8b5abSYoshinobu Inoue if (his_addr.su_family == AF_INET) { 16314dd8b5abSYoshinobu Inoue if (data_dest.su_family != AF_INET) { 16324dd8b5abSYoshinobu Inoue usedefault = 1; 16334dd8b5abSYoshinobu Inoue reply(500, "Invalid address rejected."); 16344dd8b5abSYoshinobu Inoue return 1; 16354dd8b5abSYoshinobu Inoue } 16364dd8b5abSYoshinobu Inoue if (paranoid && 16374dd8b5abSYoshinobu Inoue ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 16384dd8b5abSYoshinobu Inoue memcmp(&data_dest.su_sin.sin_addr, 16394dd8b5abSYoshinobu Inoue &his_addr.su_sin.sin_addr, 16404dd8b5abSYoshinobu Inoue sizeof(data_dest.su_sin.sin_addr)))) { 16414dd8b5abSYoshinobu Inoue usedefault = 1; 16424dd8b5abSYoshinobu Inoue reply(500, "Illegal PORT range rejected."); 16434dd8b5abSYoshinobu Inoue } else { 16444dd8b5abSYoshinobu Inoue usedefault = 0; 16454dd8b5abSYoshinobu Inoue if (pdata >= 0) { 16464dd8b5abSYoshinobu Inoue (void) close(pdata); 16474dd8b5abSYoshinobu Inoue pdata = -1; 16484dd8b5abSYoshinobu Inoue } 16494dd8b5abSYoshinobu Inoue reply(200, "%s command successful.", pcmd); 16504dd8b5abSYoshinobu Inoue } 16514dd8b5abSYoshinobu Inoue return 1; 16524dd8b5abSYoshinobu Inoue } 16534dd8b5abSYoshinobu Inoue return 0; 16544dd8b5abSYoshinobu Inoue } 16554dd8b5abSYoshinobu Inoue 1656a4b77a2aSPoul-Henning Kamp static int 1657e4bc453cSWarner Losh check_login1(void) 1658a4b77a2aSPoul-Henning Kamp { 1659a4b77a2aSPoul-Henning Kamp if (logged_in) 1660a4b77a2aSPoul-Henning Kamp return 1; 1661a4b77a2aSPoul-Henning Kamp else { 1662a4b77a2aSPoul-Henning Kamp reply(530, "Please login with USER and PASS."); 1663a4b77a2aSPoul-Henning Kamp return 0; 1664a4b77a2aSPoul-Henning Kamp } 1665a4b77a2aSPoul-Henning Kamp } 1666a4b77a2aSPoul-Henning Kamp 16676cfbc841SYaroslav Tykhiy /* 16686cfbc841SYaroslav Tykhiy * Replace leading "~user" in a pathname by the user's login directory. 16696cfbc841SYaroslav Tykhiy * Returned string will be in a freshly malloced buffer unless it's NULL. 16706cfbc841SYaroslav Tykhiy */ 16716cfbc841SYaroslav Tykhiy static char * 16726cfbc841SYaroslav Tykhiy exptilde(char *s) 16736cfbc841SYaroslav Tykhiy { 16746cfbc841SYaroslav Tykhiy char *p, *q; 16756cfbc841SYaroslav Tykhiy char *path, *user; 16766cfbc841SYaroslav Tykhiy struct passwd *ppw; 16776cfbc841SYaroslav Tykhiy 16786cfbc841SYaroslav Tykhiy if ((p = strdup(s)) == NULL) 16796cfbc841SYaroslav Tykhiy return (NULL); 16806cfbc841SYaroslav Tykhiy if (*p != '~') 16816cfbc841SYaroslav Tykhiy return (p); 16826cfbc841SYaroslav Tykhiy 16836cfbc841SYaroslav Tykhiy user = p + 1; /* skip tilde */ 16846cfbc841SYaroslav Tykhiy if ((path = strchr(p, '/')) != NULL) 16856cfbc841SYaroslav Tykhiy *(path++) = '\0'; /* separate ~user from the rest of path */ 168631f77a4bSYaroslav Tykhiy if (*user == '\0') /* no user specified, use the current user */ 168731f77a4bSYaroslav Tykhiy user = pw->pw_name; 168831f77a4bSYaroslav Tykhiy /* read passwd even for the current user since we may be chrooted */ 168931f77a4bSYaroslav Tykhiy if ((ppw = getpwnam(user)) != NULL) { 16906cfbc841SYaroslav Tykhiy /* user found, substitute login directory for ~user */ 16916cfbc841SYaroslav Tykhiy if (path) 16926cfbc841SYaroslav Tykhiy asprintf(&q, "%s/%s", ppw->pw_dir, path); 16936cfbc841SYaroslav Tykhiy else 16946cfbc841SYaroslav Tykhiy q = strdup(ppw->pw_dir); 16956cfbc841SYaroslav Tykhiy free(p); 16966cfbc841SYaroslav Tykhiy p = q; 16976cfbc841SYaroslav Tykhiy } else { 16986cfbc841SYaroslav Tykhiy /* user not found, undo the damage */ 16996cfbc841SYaroslav Tykhiy if (path) 17006cfbc841SYaroslav Tykhiy path[-1] = '/'; 17016cfbc841SYaroslav Tykhiy } 17026cfbc841SYaroslav Tykhiy return (p); 17036cfbc841SYaroslav Tykhiy } 17046cfbc841SYaroslav Tykhiy 17056cfbc841SYaroslav Tykhiy /* 17066cfbc841SYaroslav Tykhiy * Expand glob(3) patterns possibly present in a pathname. 17076cfbc841SYaroslav Tykhiy * Avoid expanding to a pathname including '\r' or '\n' in order to 17086cfbc841SYaroslav Tykhiy * not disrupt the FTP protocol. 17096cfbc841SYaroslav Tykhiy * The expansion found must be unique. 17101acf0dbaSUlrich Spörlein * Return the result as a malloced string, or NULL if an error occurred. 17116cfbc841SYaroslav Tykhiy * 17126cfbc841SYaroslav Tykhiy * Problem: this production is used for all pathname 17136cfbc841SYaroslav Tykhiy * processing, but only gives a 550 error reply. 17146cfbc841SYaroslav Tykhiy * This is a valid reply in some cases but not in others. 17156cfbc841SYaroslav Tykhiy */ 17166cfbc841SYaroslav Tykhiy static char * 17176cfbc841SYaroslav Tykhiy expglob(char *s) 17186cfbc841SYaroslav Tykhiy { 17196cfbc841SYaroslav Tykhiy char *p, **pp, *rval; 17206cfbc841SYaroslav Tykhiy int flags = GLOB_BRACE | GLOB_NOCHECK; 17216cfbc841SYaroslav Tykhiy int n; 17226cfbc841SYaroslav Tykhiy glob_t gl; 17236cfbc841SYaroslav Tykhiy 17246cfbc841SYaroslav Tykhiy memset(&gl, 0, sizeof(gl)); 17256cfbc841SYaroslav Tykhiy flags |= GLOB_LIMIT; 17266cfbc841SYaroslav Tykhiy gl.gl_matchc = MAXGLOBARGS; 17276cfbc841SYaroslav Tykhiy if (glob(s, flags, NULL, &gl) == 0 && gl.gl_pathc != 0) { 17286cfbc841SYaroslav Tykhiy for (pp = gl.gl_pathv, p = NULL, n = 0; *pp; pp++) 17296cfbc841SYaroslav Tykhiy if (*(*pp + strcspn(*pp, "\r\n")) == '\0') { 17306cfbc841SYaroslav Tykhiy p = *pp; 17316cfbc841SYaroslav Tykhiy n++; 17326cfbc841SYaroslav Tykhiy } 17336cfbc841SYaroslav Tykhiy if (n == 0) 17346cfbc841SYaroslav Tykhiy rval = strdup(s); 17356cfbc841SYaroslav Tykhiy else if (n == 1) 17366cfbc841SYaroslav Tykhiy rval = strdup(p); 17376cfbc841SYaroslav Tykhiy else { 173802c97492SYaroslav Tykhiy reply(550, "Wildcard is ambiguous."); 17396cfbc841SYaroslav Tykhiy rval = NULL; 17406cfbc841SYaroslav Tykhiy } 17416cfbc841SYaroslav Tykhiy } else { 174202c97492SYaroslav Tykhiy reply(550, "Wildcard expansion error."); 17436cfbc841SYaroslav Tykhiy rval = NULL; 17446cfbc841SYaroslav Tykhiy } 17456cfbc841SYaroslav Tykhiy globfree(&gl); 17466cfbc841SYaroslav Tykhiy return (rval); 17476cfbc841SYaroslav Tykhiy } 17486cfbc841SYaroslav Tykhiy 17494dd8b5abSYoshinobu Inoue #ifdef INET6 17504dd8b5abSYoshinobu Inoue /* Return 1, if port check is done. Return 0, if not yet. */ 17514dd8b5abSYoshinobu Inoue static int 1752e4bc453cSWarner Losh port_check_v6(const char *pcmd) 17534dd8b5abSYoshinobu Inoue { 17544dd8b5abSYoshinobu Inoue if (his_addr.su_family == AF_INET6) { 17554dd8b5abSYoshinobu Inoue if (IN6_IS_ADDR_V4MAPPED(&his_addr.su_sin6.sin6_addr)) 17564dd8b5abSYoshinobu Inoue /* Convert data_dest into v4 mapped sockaddr.*/ 17574dd8b5abSYoshinobu Inoue v4map_data_dest(); 17584dd8b5abSYoshinobu Inoue if (data_dest.su_family != AF_INET6) { 17594dd8b5abSYoshinobu Inoue usedefault = 1; 17604dd8b5abSYoshinobu Inoue reply(500, "Invalid address rejected."); 17614dd8b5abSYoshinobu Inoue return 1; 17624dd8b5abSYoshinobu Inoue } 17634dd8b5abSYoshinobu Inoue if (paranoid && 17644dd8b5abSYoshinobu Inoue ((ntohs(data_dest.su_port) < IPPORT_RESERVED) || 17654dd8b5abSYoshinobu Inoue memcmp(&data_dest.su_sin6.sin6_addr, 17664dd8b5abSYoshinobu Inoue &his_addr.su_sin6.sin6_addr, 17674dd8b5abSYoshinobu Inoue sizeof(data_dest.su_sin6.sin6_addr)))) { 17684dd8b5abSYoshinobu Inoue usedefault = 1; 17694dd8b5abSYoshinobu Inoue reply(500, "Illegal PORT range rejected."); 17704dd8b5abSYoshinobu Inoue } else { 17714dd8b5abSYoshinobu Inoue usedefault = 0; 17724dd8b5abSYoshinobu Inoue if (pdata >= 0) { 17734dd8b5abSYoshinobu Inoue (void) close(pdata); 17744dd8b5abSYoshinobu Inoue pdata = -1; 17754dd8b5abSYoshinobu Inoue } 17764dd8b5abSYoshinobu Inoue reply(200, "%s command successful.", pcmd); 17774dd8b5abSYoshinobu Inoue } 17784dd8b5abSYoshinobu Inoue return 1; 17794dd8b5abSYoshinobu Inoue } 17804dd8b5abSYoshinobu Inoue return 0; 17814dd8b5abSYoshinobu Inoue } 17824dd8b5abSYoshinobu Inoue 17834dd8b5abSYoshinobu Inoue static void 1784e4bc453cSWarner Losh v4map_data_dest(void) 17854dd8b5abSYoshinobu Inoue { 17864dd8b5abSYoshinobu Inoue struct in_addr savedaddr; 17874dd8b5abSYoshinobu Inoue int savedport; 17884dd8b5abSYoshinobu Inoue 17894dd8b5abSYoshinobu Inoue if (data_dest.su_family != AF_INET) { 17904dd8b5abSYoshinobu Inoue usedefault = 1; 17914dd8b5abSYoshinobu Inoue reply(500, "Invalid address rejected."); 17924dd8b5abSYoshinobu Inoue return; 17934dd8b5abSYoshinobu Inoue } 17944dd8b5abSYoshinobu Inoue 17954dd8b5abSYoshinobu Inoue savedaddr = data_dest.su_sin.sin_addr; 17964dd8b5abSYoshinobu Inoue savedport = data_dest.su_port; 17974dd8b5abSYoshinobu Inoue 17984dd8b5abSYoshinobu Inoue memset(&data_dest, 0, sizeof(data_dest)); 17994dd8b5abSYoshinobu Inoue data_dest.su_sin6.sin6_len = sizeof(struct sockaddr_in6); 18004dd8b5abSYoshinobu Inoue data_dest.su_sin6.sin6_family = AF_INET6; 18014dd8b5abSYoshinobu Inoue data_dest.su_sin6.sin6_port = savedport; 18024dd8b5abSYoshinobu Inoue memset((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[10], 0xff, 2); 18034dd8b5abSYoshinobu Inoue memcpy((caddr_t)&data_dest.su_sin6.sin6_addr.s6_addr[12], 18044dd8b5abSYoshinobu Inoue (caddr_t)&savedaddr, sizeof(savedaddr)); 18054dd8b5abSYoshinobu Inoue } 18064dd8b5abSYoshinobu Inoue #endif 1807