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