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