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