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