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