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