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