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