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