--- /dev/null
+Sun Jan 14 03:19:55 1996 Mark Eichin <eichin@cygnus.com>
+
+ * README.gssftp: new file, documents history of this implementation.
+
+Sat Sep 30 16:28:34 1995 Mark Eichin <eichin@cygnus.com>
+
+ * configure.in, Makefile.in: New files controlling the
+ construction of the GSSAPI implementation of ftp.
+
--- /dev/null
+CFLAGS = $(CCOPTS)
+LDFLAGS = -g
--- /dev/null
+Notes on "Secure FTP" Implementation
+===============================================
+Mark Eichin <eichin@cygnus.com>, Cygnus Support
+last modified: 1995 Jan 14
+===============================================
+
+This implementation is supplied by Cygnus Support for inclusion in the MIT
+Kerberos V5 Release.
+
+Copyrights:
+The original BSD ftp implementation is:
+ * Copyright (c) 1980, 1983, 1985, 1988, 1989, 1990, 1991 Regents of the
+ University of California.
+
+History and Credits (as of 1995 Jan 14)
+================================================
+
+Steve Lunt at Bellcore developed the original V4 kerberized ftp. He
+also started writing the IETF ftpsec draft at the time. This was
+available to the public, and Cygnus eventually incorporated it into
+CNS V4.
+
+Steve Lunt left Bellcore, and dropped out of the computer security
+field altogether, after handing the draft off to Marc Horowitz at
+OpenVision, who was working on a commercial GSSAPI implementation.
+
+Marc Horowitz left OpenVision and is back at MIT currently; in the
+mean time, Cygnus took the V4 ftp and upgraded it to use GSSAPI and
+draft-08, as well as integrating it into the Kerberos V5 autoconf
+based configuration scheme.
+
+Bill Schoofs <wjs@cray.com> supplied corrections to the implementation
+to more correctly match draft 8, as well as correcting some of the
+remaining KERBEROS_V4 code.
+
+Karri Balk - Contractor <kbalk@cup.hp.com> supplied additional
+corrections based on interoperation testing with non-free
+implementations.
+
+Marc Horowitz has indicated that a draft 9 is forthcoming, with some
+clarifications based on experience with this implementation.
+
+No other free implementation of draft-8 is known at this time.
+
+
--- /dev/null
+Sun Jan 14 01:51:18 1996 Bill Schoofs <wjs@cray.com>
+
+ * arpa/ftp.h: define PROT_E and add Confidential to levelnames,
+ so as to return more precise errors.
+
--- /dev/null
+/*
+ * Copyright (c) 1983, 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ftp.h 5.6 (Berkeley) 4/3/91
+ */
+
+#ifndef _FTP_H_
+#define _FTP_H_
+
+/* Definitions for FTP; see RFC-765. */
+
+/*
+ * Reply codes.
+ */
+#define PRELIM 1 /* positive preliminary */
+#define COMPLETE 2 /* positive completion */
+#define CONTINUE 3 /* positive intermediate */
+#define TRANSIENT 4 /* transient negative completion */
+#define ERROR 5 /* permanent negative completion */
+
+/*
+ * Type codes
+ */
+#define TYPE_A 1 /* ASCII */
+#define TYPE_E 2 /* EBCDIC */
+#define TYPE_I 3 /* image */
+#define TYPE_L 4 /* local byte size */
+
+#ifdef FTP_NAMES
+char *typenames[] = {"0", "ASCII", "EBCDIC", "Image", "Local" };
+#endif
+
+/*
+ * Form codes
+ */
+#define FORM_N 1 /* non-print */
+#define FORM_T 2 /* telnet format effectors */
+#define FORM_C 3 /* carriage control (ASA) */
+#ifdef FTP_NAMES
+char *formnames[] = {"0", "Nonprint", "Telnet", "Carriage-control" };
+#endif
+
+/*
+ * Structure codes
+ */
+#define STRU_F 1 /* file (no record structure) */
+#define STRU_R 2 /* record structure */
+#define STRU_P 3 /* page structure */
+#ifdef FTP_NAMES
+char *strunames[] = {"0", "File", "Record", "Page" };
+#endif
+
+/*
+ * Mode types
+ */
+#define MODE_S 1 /* stream */
+#define MODE_B 2 /* block */
+#define MODE_C 3 /* compressed */
+#ifdef FTP_NAMES
+char *modenames[] = {"0", "Stream", "Block", "Compressed" };
+#endif
+
+/*
+ * Protection levels
+ */
+#define PROT_C 1 /* clear */
+#define PROT_S 2 /* safe */
+#define PROT_P 3 /* private */
+#define PROT_E 4 /* confidential */
+
+#ifdef FTP_NAMES
+char *levelnames[] = {"0", "Clear", "Safe", "Private", "Confidential" };
+#endif
+
+#if defined(KERBEROS) && defined(NOENCRYPTION)
+/* define away krb_rd_priv and krb_mk_priv. Don't need them anyway. */
+/* This might not be the best place for this ... */
+#define krb_rd_priv(o,l,ses,s,h,c,m) krb_rd_safe(o,l,s,h,c,m)
+#define krb_mk_priv(i,o,l,ses,s,h,c) krb_mk_safe(i,o,l,s,h,c)
+#endif
+
+/*
+ * Record Tokens
+ */
+#define REC_ESC '\377' /* Record-mode Escape */
+#define REC_EOR '\001' /* Record-mode End-of-Record */
+#define REC_EOF '\002' /* Record-mode End-of-File */
+
+/*
+ * Block Header
+ */
+#define BLK_EOR 0x80 /* Block is End-of-Record */
+#define BLK_EOF 0x40 /* Block is End-of-File */
+#define BLK_ERRORS 0x20 /* Block is suspected of containing errors */
+#define BLK_RESTART 0x10 /* Block is Restart Marker */
+
+#define BLK_BYTECOUNT 2 /* Bytes in this block */
+
+#endif /* !_FTP_H_ */
--- /dev/null
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)telnet.h 5.14 (Berkeley) 4/3/91
+ */
+
+#ifndef _TELNET_H_
+#define _TELNET_H_
+
+/*
+ * Definitions for the TELNET protocol.
+ */
+#define IAC 255 /* interpret as command: */
+#define DONT 254 /* you are not to use option */
+#define DO 253 /* please, you use option */
+#define WONT 252 /* I won't use option */
+#define WILL 251 /* I will use option */
+#define SB 250 /* interpret as subnegotiation */
+#define GA 249 /* you may reverse the line */
+#define EL 248 /* erase the current line */
+#define EC 247 /* erase the current character */
+#define AYT 246 /* are you there */
+#define AO 245 /* abort output--but let prog finish */
+#define IP 244 /* interrupt process--permanently */
+#define BREAK 243 /* break */
+#define DM 242 /* data mark--for connect. cleaning */
+#define NOP 241 /* nop */
+#define SE 240 /* end sub negotiation */
+#define EOR 239 /* end of record (transparent mode) */
+#define ABORT 238 /* Abort process */
+#define SUSP 237 /* Suspend process */
+#define xEOF 236 /* End of file: EOF is already used... */
+
+#define SYNCH 242 /* for telfunc calls */
+
+#ifdef TELCMDS
+char *telcmds[] = {
+ "EOF", "SUSP", "ABORT", "EOR",
+ "SE", "NOP", "DMARK", "BRK", "IP", "AO", "AYT", "EC",
+ "EL", "GA", "SB", "WILL", "WONT", "DO", "DONT", "IAC", 0,
+};
+#else
+extern char *telcmds[];
+#endif
+
+#define TELCMD_FIRST xEOF
+#define TELCMD_LAST IAC
+#define TELCMD_OK(x) ((x) <= TELCMD_LAST && (x) >= TELCMD_FIRST)
+#define TELCMD(x) telcmds[(x)-TELCMD_FIRST]
+
+/* telnet options */
+#define TELOPT_BINARY 0 /* 8-bit data path */
+#define TELOPT_ECHO 1 /* echo */
+#define TELOPT_RCP 2 /* prepare to reconnect */
+#define TELOPT_SGA 3 /* suppress go ahead */
+#define TELOPT_NAMS 4 /* approximate message size */
+#define TELOPT_STATUS 5 /* give status */
+#define TELOPT_TM 6 /* timing mark */
+#define TELOPT_RCTE 7 /* remote controlled transmission and echo */
+#define TELOPT_NAOL 8 /* negotiate about output line width */
+#define TELOPT_NAOP 9 /* negotiate about output page size */
+#define TELOPT_NAOCRD 10 /* negotiate about CR disposition */
+#define TELOPT_NAOHTS 11 /* negotiate about horizontal tabstops */
+#define TELOPT_NAOHTD 12 /* negotiate about horizontal tab disposition */
+#define TELOPT_NAOFFD 13 /* negotiate about formfeed disposition */
+#define TELOPT_NAOVTS 14 /* negotiate about vertical tab stops */
+#define TELOPT_NAOVTD 15 /* negotiate about vertical tab disposition */
+#define TELOPT_NAOLFD 16 /* negotiate about output LF disposition */
+#define TELOPT_XASCII 17 /* extended ascic character set */
+#define TELOPT_LOGOUT 18 /* force logout */
+#define TELOPT_BM 19 /* byte macro */
+#define TELOPT_DET 20 /* data entry terminal */
+#define TELOPT_SUPDUP 21 /* supdup protocol */
+#define TELOPT_SUPDUPOUTPUT 22 /* supdup output */
+#define TELOPT_SNDLOC 23 /* send location */
+#define TELOPT_TTYPE 24 /* terminal type */
+#define TELOPT_EOR 25 /* end or record */
+#define TELOPT_TUID 26 /* TACACS user identification */
+#define TELOPT_OUTMRK 27 /* output marking */
+#define TELOPT_TTYLOC 28 /* terminal location number */
+#define TELOPT_3270REGIME 29 /* 3270 regime */
+#define TELOPT_X3PAD 30 /* X.3 PAD */
+#define TELOPT_NAWS 31 /* window size */
+#define TELOPT_TSPEED 32 /* terminal speed */
+#define TELOPT_LFLOW 33 /* remote flow control */
+#define TELOPT_LINEMODE 34 /* Linemode option */
+#define TELOPT_XDISPLOC 35 /* X Display Location */
+#define TELOPT_ENVIRON 36 /* Environment variables */
+#define TELOPT_AUTHENTICATION 37/* Authenticate */
+#define TELOPT_ENCRYPT 38 /* Encryption option */
+#define TELOPT_EXOPL 255 /* extended-options-list */
+
+
+#define NTELOPTS (1+TELOPT_ENCRYPT)
+#ifdef TELOPTS
+char *telopts[NTELOPTS+1] = {
+ "BINARY", "ECHO", "RCP", "SUPPRESS GO AHEAD", "NAME",
+ "STATUS", "TIMING MARK", "RCTE", "NAOL", "NAOP",
+ "NAOCRD", "NAOHTS", "NAOHTD", "NAOFFD", "NAOVTS",
+ "NAOVTD", "NAOLFD", "EXTEND ASCII", "LOGOUT", "BYTE MACRO",
+ "DATA ENTRY TERMINAL", "SUPDUP", "SUPDUP OUTPUT",
+ "SEND LOCATION", "TERMINAL TYPE", "END OF RECORD",
+ "TACACS UID", "OUTPUT MARKING", "TTYLOC",
+ "3270 REGIME", "X.3 PAD", "NAWS", "TSPEED", "LFLOW",
+ "LINEMODE", "XDISPLOC", "ENVIRON", "AUTHENTICATION",
+ "ENCRYPT",
+ 0,
+};
+#define TELOPT_FIRST TELOPT_BINARY
+#define TELOPT_LAST TELOPT_ENCRYPT
+#define TELOPT_OK(x) ((x) <= TELOPT_LAST && (x) >= TELOPT_FIRST)
+#define TELOPT(x) telopts[(x)-TELOPT_FIRST]
+#endif
+
+/* sub-option qualifiers */
+#define TELQUAL_IS 0 /* option is... */
+#define TELQUAL_SEND 1 /* send option */
+#define TELQUAL_INFO 2 /* ENVIRON: informational version of IS */
+#define TELQUAL_REPLY 2 /* AUTHENTICATION: client version of IS */
+#define TELQUAL_NAME 3 /* AUTHENTICATION: client version of IS */
+
+/*
+ * LINEMODE suboptions
+ */
+
+#define LM_MODE 1
+#define LM_FORWARDMASK 2
+#define LM_SLC 3
+
+#define MODE_EDIT 0x01
+#define MODE_TRAPSIG 0x02
+#define MODE_ACK 0x04
+#define MODE_SOFT_TAB 0x08
+#define MODE_LIT_ECHO 0x10
+
+#define MODE_MASK 0x1f
+
+/* Not part of protocol, but needed to simplify things... */
+#define MODE_FLOW 0x0100
+#define MODE_ECHO 0x0200
+#define MODE_INBIN 0x0400
+#define MODE_OUTBIN 0x0800
+#define MODE_FORCE 0x1000
+
+#define SLC_SYNCH 1
+#define SLC_BRK 2
+#define SLC_IP 3
+#define SLC_AO 4
+#define SLC_AYT 5
+#define SLC_EOR 6
+#define SLC_ABORT 7
+#define SLC_EOF 8
+#define SLC_SUSP 9
+#define SLC_EC 10
+#define SLC_EL 11
+#define SLC_EW 12
+#define SLC_RP 13
+#define SLC_LNEXT 14
+#define SLC_XON 15
+#define SLC_XOFF 16
+#define SLC_FORW1 17
+#define SLC_FORW2 18
+
+#define NSLC 18
+
+/*
+ * For backwards compatability, we define SLC_NAMES to be the
+ * list of names if SLC_NAMES is not defined.
+ */
+#define SLC_NAMELIST "0", "SYNCH", "BRK", "IP", "AO", "AYT", "EOR", \
+ "ABORT", "EOF", "SUSP", "EC", "EL", "EW", "RP", \
+ "LNEXT", "XON", "XOFF", "FORW1", "FORW2", 0,
+#ifdef SLC_NAMES
+char *slc_names[] = {
+ SLC_NAMELIST
+};
+#else
+extern char *slc_names[];
+#define SLC_NAMES SLC_NAMELIST
+#endif
+
+#define SLC_NAME_OK(x) ((x) >= 0 && (x) < NSLC)
+#define SLC_NAME(x) slc_names[x]
+
+#define SLC_NOSUPPORT 0
+#define SLC_CANTCHANGE 1
+#define SLC_VARIABLE 2
+#define SLC_DEFAULT 3
+#define SLC_LEVELBITS 0x03
+
+#define SLC_FUNC 0
+#define SLC_FLAGS 1
+#define SLC_VALUE 2
+
+#define SLC_ACK 0x80
+#define SLC_FLUSHIN 0x40
+#define SLC_FLUSHOUT 0x20
+
+#define ENV_VALUE 0
+#define ENV_VAR 1
+#define ENV_ESC 2
+
+/*
+ * AUTHENTICATION suboptions
+ */
+
+/*
+ * Who is authenticating who ...
+ */
+#define AUTH_WHO_CLIENT 0 /* Client authenticating server */
+#define AUTH_WHO_SERVER 1 /* Server authenticating client */
+#define AUTH_WHO_MASK 1
+
+/*
+ * amount of authentication done
+ */
+#define AUTH_HOW_ONE_WAY 0
+#define AUTH_HOW_MUTUAL 2
+#define AUTH_HOW_MASK 2
+
+#define AUTHTYPE_NULL 0
+#define AUTHTYPE_KERBEROS_V4 1
+#define AUTHTYPE_KERBEROS_V5 2
+#define AUTHTYPE_SPX 3
+#define AUTHTYPE_MINK 4
+#define AUTHTYPE_CNT 5
+
+#define AUTHTYPE_TEST 99
+
+#ifdef AUTH_NAMES
+char *authtype_names[] = {
+ "NULL", "KERBEROS_V4", "KERBEROS_V5", "SPX", "MINK", 0,
+};
+#else
+extern char *authtype_names[];
+#endif
+
+#define AUTHTYPE_NAME_OK(x) ((x) >= 0 && (x) < AUTHTYPE_CNT)
+#define AUTHTYPE_NAME(x) authtype_names[x]
+
+/*
+ * ENCRYPTion suboptions
+ */
+#define ENCRYPT_IS 0 /* I pick encryption type ... */
+#define ENCRYPT_SUPPORT 1 /* I support encryption types ... */
+#define ENCRYPT_REPLY 2 /* Initial setup response */
+#define ENCRYPT_START 3 /* Am starting to send encrypted */
+#define ENCRYPT_END 4 /* Am ending encrypted */
+#define ENCRYPT_REQSTART 5 /* Request you start encrypting */
+#define ENCRYPT_REQEND 6 /* Request you send encrypting */
+#define ENCRYPT_ENC_KEYID 7
+#define ENCRYPT_DEC_KEYID 8
+#define ENCRYPT_CNT 9
+
+#define ENCTYPE_ANY 0
+#define ENCTYPE_DES_CFB64 1
+#define ENCTYPE_DES_OFB64 2
+#define ENCTYPE_CNT 3
+
+#ifdef ENCRYPT_NAMES
+char *encrypt_names[] = {
+ "IS", "SUPPORT", "REPLY", "START", "END",
+ "REQUEST-START", "REQUEST-END", "ENC-KEYID", "DEC-KEYID",
+ 0,
+};
+char *enctype_names[] = {
+ "ANY", "DES_CFB64", "DES_OFB64", 0,
+};
+#else
+extern char *encrypt_names[];
+extern char *enctype_names[];
+#endif
+
+
+#define ENCRYPT_NAME_OK(x) ((x) >= 0 && (x) < ENCRYPT_CNT)
+#define ENCRYPT_NAME(x) encrypt_names[x]
+
+#define ENCTYPE_NAME_OK(x) ((x) >= 0 && (x) < ENCTYPE_CNT)
+#define ENCTYPE_NAME(x) enctype_names[x]
+
+#endif /* !_TELNET_H_ */
--- /dev/null
+AC_INIT(configure.in)
+CONFIG_RULES
+CONFIG_DIRS(ftp ftpd)
+DO_SUBDIRS
+V5_AC_OUTPUT_MAKEFILE
--- /dev/null
+Sun Jan 14 01:54:35 1996 Bill Schoofs <wjs@cray.com>
+
+ * Makefile.in (DEFINES): define NOCONFIDENTIAL for future use.
+ * ftp.c (command): recognize 533, not 402, for 'server unwilling
+ to accept'
+ (getreply): recognize 633 for confidential reply, and then don't
+ support it.
+
+Tue Jan 2 19:17:47 1996 Mark Eichin <eichin@cygnus.com>
+
+ * pclose.c: test HAVE_GETDTABLESIZE instead of hpux.
+ * configure.in: set HAVE_GETDTABLESIZE.
+
+Fri Oct 20 11:59:32 1995 Mark W. Eichin <eichin@cygnus.com>
+
+ * ftp.c (do_auth): synthesize channel bindings from myctladdr and
+ hisctladdr, based on changes pending for draft 9.
+
+Thu Oct 19 04:47:36 1995 Mark W. Eichin <eichin@cygnus.com>
+
+ * configure.in: check for POSIX_TERMIOS just like appl/bsd does.
+
+Wed Oct 4 19:24:39 1995 Mark Eichin <eichin@cygnus.com>
+
+ * ftp, pclose, ruserpass.c, secure.c: don't ever declare malloc.
+
+Sun Oct 1 03:30:30 1995 Mark Eichin <eichin@cygnus.com>
+
+ * ftp.c (do_auth): accept ADAT 3yz response. Clean up loops, add
+ lots of debugging messages.
+
+Sun Oct 1 00:56:55 1995 Mark Eichin <eichin@cygnus.com>
+
+ * Makefile.in: use FTP_BUFSIZ everywhere and make it large for
+ now.
+ * ftp.c: only look at "host" for now. Report error parsing
+ name. Handle gssapi error reporting better.
+
+
+Sat Sep 30 22:26:37 1995 Mark Eichin <eichin@cygnus.com>
+
+ * ftp.c, secure.c: correct gssapi includes. Fix some typos and
+ missing declarations.
+
+Sat Sep 30 21:31:09 1995 Mark Eichin <eichin@cygnus.com>
+
+ * Makefile.in (depend, install): change to double colon rules.
+ * cmds.c: no conf.h, check HAVE_GETCWD, use krb5_sigtype.
+ * configure.in: check KRB5_SIGTYPE, CHECK_SIGPROCMASK,
+ CHECK_WAIT_TYPE, and getcwd.
+ * ftp.c: no conf.h, use krb5_sigtype.
+ (secure_command): use user_gss_error.
+ (do_auth): cycle through gss_services.
+ (user_gss_error): new function, decomposes GSSAPI errors and sends
+ them to standard error.
+ (secure_gss_error): hook for secure.c common functions to get the
+ correct error routine.
+ * getpass.c, main.c: no conf.h, use krb5_sigtype.
+ * pclose.c: no conf.h, use krb5_sigtype.
+ (mypclose): obey USE_SIGPROCMASK.
+ * secure.c (secure_getbyte): use generic secure_gss_error.
+
+Sat Sep 30 16:43:28 1995 Mark Eichin <eichin@cygnus.com>
+
+ * configure.in, Makefile.in: new files for port to GSSAPI and
+ build within the Kerberos V5 build tree.
+ * ftp.c, secure.c: GSSAPI authentication changes based on the IETF
+ CAT working group ***DRAFT*** FTP Security specification, draft
+ number 8, appendix I.
+
+
+**** previous change logs from CNS V4 modifications of Steve Lunt's
+ draft-3 ftp client, which this is based on. ****
+
+Wed Jul 26 21:01:42 1995 Ken Raeburn <raeburn@cygnus.com>
+
+ * domacro.c: Include string.h.
+ * secure.c: Ditto.
+
+Mon Jul 10 14:54:41 1995 Michael Meissner <meissner@tiktok.cygnus.com>
+
+ * glob.c (matchdir): #if 0 code that uses a private member of the
+ DIR structure to check whether a filename passed to opendir is a
+ directory or not.
+
+Fri May 26 19:36:12 1995 Mark Eichin <eichin@cygnus.com>
+
+ * glob.c (matchdir): open "." explicitly if gpath is null.
+
+Fri May 19 16:11:07 1995 Mark Eichin <eichin@cygnus.com>
+
+ * ftp.c (ptransfer): be sure that printf(%g) gets a float, not an
+ implicit double, by doing the whole calculation in the variable.
+
+Thu Feb 2 13:40:04 1995 Ian Lance Taylor <ian@cygnus.com>
+
+ * ftp.c: Don't try to use IP_TOS if the IP_TOS argument
+ (IPTOS_LOWDELAY, etc.) is not defined.
+
+Wed Jan 18 14:07:33 1995 Ian Lance Taylor <ian@sanguine.cygnus.com>
+
+ * ftp.1: Include man1/tmac.doc.
+
+ * ftp.c (initconn): If the PASV command is rejected, turn off
+ passive mode and try again.
+
+Wed Jan 4 11:21:34 1995 Ian Lance Taylor <ian@tweedledumb.cygnus.com>
+
+ * cmds.c, ftp.c: Use mygetpass instead of getpass.
+
+ * ruserpass.c: Don't include <utmp.h>. Don't declare getlogin,
+ getpass, or getuttmp.
+
+Thu Dec 29 15:19:44 1994 Mark Eichin <eichin@cygnus.com>
+
+ * cmds.c (setpeer): add || defined(linux) to the NBBY == 8 check,
+ since this code is appropriate under linux.
+
+Thu Dec 29 14:11:37 1994 Mark Eichin <eichin@cygnus.com>
+
+ * cmds.c (siteidle): renamed idle() to avoid conflict with linux
+ idle(void).
+ * cmdtab.c: rename declaration and cmdtab entry.
+
+Tue Dec 27 13:29:08 1994 Ian Lance Taylor <ian@sanguine.cygnus.com>
+
+ * ftp.c: If STDARG is defined, or if __STDC__ is defined and
+ VARARGS is not defined, include <stdarg.h>, instead of
+ <varargs.h>.
+ (command): Use <stdarg.h> routines if STDARG || (__STDC__ && !
+ VARARGS).
+ (secure_error): Likewise.
+ * ftp_var.h (command): Declare if STDARG || (__STDC__ && !
+ VARARGS).
+ * secure.c (secure_error): Likewise.
+
+ * secure.h (hisaddr): Define as hisdataaddr.
+ * ftp.c (hisdataaddr): New global variable.
+ (initconn): Set hisdataaddr to data_addr.
+ (dataconn): Use hisdataaddr instead of local variable from.
+
+Fri Dec 23 15:18:12 1994 Ian Lance Taylor <ian@sanguine.cygnus.com>
+
+ * cmds.c (unix): Define if _AIX is defined (AIX compiler does not
+ predefine unix).
+
+ * ftp.c (login): When choosing the default login name, use the
+ values of the environment variables LOGNAME and then USER in
+ preference to calling getlogin.
+
+Thu Dec 22 14:59:34 1994 Ian Lance Taylor <ian@sanguine.cygnus.com>
+
+ * cmds.c (gettype): Sometimes type will be zero, which requires
+ special handling.
+
+ * main.c: Include <krb.h>.
+ (main): Support new option: -k.
+ * ftp.c (realm): New global variable.
+ (do_auth): Remove local variable realm; use new global instead.
+ Don't call krb_realmofhost if realm is set.
+ * ftp.1: Document -k.
+
+Fri Dec 16 10:53:08 1994 Ian Lance Taylor <ian@cygnus.com>
+
+ Fixes for Alpha OSF/1:
+ * cmds.c: Redefine sig_t to my_sig_t to avoid header file
+ conflict.
+ * ftp.c: Likewise.
+
+ Fixes for SCO:
+ * cmdtab.c: Include <stdio.h> before ftp_var.h.
+ * domacro.c: Move include of <stdio.h> before include of
+ ftp_var.h. Don't include <sys/ttychars.h>.
+ * main.c: Move include of <stdio.h> before include of ftp_var.h.
+
+ Fixes for AIX:
+ * cmds.c (mput): Use 0 instead of NULL when an integer is
+ expected.
+ (getit, mget): Likewise.
+ * ftp_var.h (strncpy, strncat, strcat, strcpy): Don't declare.
+ * ruserpass.c (strcpy): Don't declare.
+ * secure.c: Include <netinet/in.h>.
+
+ Fixes for Irix 4:
+ * ftp_var.h: Unless DEFINITIONS is defined, declare variables
+ rather than defining them.
+ * ftp.c: Define DEFINITIONS before including ftp_var.h.
+ (recvrequest): If NOSTBLKSIZE is defined, use BUFSIZ instead of
+ st_blksize.
+ * getpass.c: Put note after #endif in /* */
+ * pclose.c: Likewise.
+ * ruserpass.c (token): Move before ruserpass.
+ (ruserpass): Don't declare token.
+
+ General fixes to make it compile on Solaris: Use sigtype for
+ signal handler return values, including conf.h where needed. Add
+ a dummy argument to signal handler functions. Replace index,
+ rindex, bzero and bcopy with ANSI C functions. Cast Kerberos
+ routine arguments to avoid warnings. Also:
+ * cmds.c: Include <string.h>. If POSIX is defined, include
+ unistd.h, otherwise define getcwd to call getwd.
+ (lcd): Call getcwd instead of getwd.
+ (shell): If WAIT_USES_INT, use int instead of union wait.
+ * ftp.c: Include <string.h>.
+ (L_SET, L_INCR): Define if not defined.
+
+ * ftp_var.h (index, rindex): Don't declare.
+ * main.c: Inclue <string.h>.
+ * pclose.c (getdtablesize): New function on hpux or __svr4__.
+ * radix.c (radix_encode): Cast strcmp arguments to avoid warnings.
+ * ruserpass.c: Include <string.h>. If POSIX, include <stdlib.h>
+ and don't declare malloc.
+ (MAXHOSTNAMELEN): Define if not defined.
+ (index): Don't declare.
+
+Thu Dec 15 16:13:44 1994 Ian Lance Taylor <ian@sanguine.cygnus.com>
+
+ * Initial checkin. Based on Steve Lunt's ftp program, which was
+ based on BSD code.
--- /dev/null
+#
+# appl/gssftp/ftp/Makefile.in
+#
+CFLAGS = -DGSSAPI -DFTP_BUFSIZ=10240 $(CCOPTS) $(DEFS) $(LOCALINCLUDE)
+
+COMERRLIB=$(BUILDTOP)/util/et/libcom_err.a
+
+SRCS = cmds.c cmdtab.c domacro.c ftp.c getpass.c glob.c main.c pclose.c \
+ radix.c ruserpass.c secure.c
+
+
+OBJS = cmds.o cmdtab.o domacro.o ftp.o getpass.o glob.o main.o pclose.o \
+ radix.o ruserpass.o secure.o
+
+KLIB = -lgssapi_krb5 -lkrb5 -lcrypto $(COMERRLIB)
+DEPKLIB = $(TOPLIBD)/gssapi/libgssapi_krb5.a $(TOPLIBD)/libkrb5.a \
+ $(TOPLIBD)/libcrypto.a $(COMERRLIB)
+
+LOCALINCLUDE = -I$(srcdir)/..
+DEFINES = -DGSSAPI -DNOCONFIDENTIAL
+
+all:: ftp
+
+ftp: $(OBJS) $(DEPKLIB)
+ $(LD) $(LDFLAGS) $(LDARGS) -o ftp $(OBJS) $(KLIB) $(LIBS)
+
+clean::
+ $(RM) ftp
+
+depend::
+
+install::
+ $(INSTALL_PROGRAM) ftp $(DESTDIR)$(CLIENT_BINDIR)/ftp
+ $(INSTALL_DATA) $(srcdir)/ftp.M ${DESTDIR}$(CLIENT_MANDIR)/ftp.1
+
+ftp.o cmds.o main.o: $(srcdir)/../arpa/ftp.h
+ftp.o cmds.o cmdtab.o domacro.o main.o ruserpass.o: $(srcdir)/ftp_var.h
+secure.o: secure.h
+
+cmds.o: $(srcdir)/cmds.c
+cmdtab.o: $(srcdir)/cmdtab.c
+ftp.o: $(srcdir)/ftp.c
+getpass.o: $(srcdir)/getpass.c
+glob.o: $(srcdir)/glob.c
+main.o: $(srcdir)/main.c
+pclose.o: $(srcdir)/pclose.c
+ruserpass.o: $(srcdir)/ruserpass.c
+domacro.o: $(srcdir)/domacro.c
+radix.o: $(srcdir)/radix.c
+secure.o: $(srcdir)/secure.c
+
+# NOPOSTFIX
--- /dev/null
+/*
+ * Copyright (c) 1985, 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmds.c 5.26 (Berkeley) 3/5/91";
+#endif /* not lint */
+
+/*
+ * FTP User Program -- Command Routines.
+ */
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+
+#include <arpa/ftp.h>
+
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <ctype.h>
+#include <time.h>
+#include <netinet/in.h>
+
+#ifdef HAVE_GETCWD
+#define getwd(x) getcwd(x,MAXPATHLEN)
+#endif
+
+#include "ftp_var.h"
+#include "pathnames.h"
+
+#define sig_t my_sig_t
+#define sigtype krb5_sigtype
+typedef sigtype (*sig_t)();
+
+extern char *globerr;
+extern char **ftpglob();
+extern char *home;
+extern char *remglob();
+extern char *getenv();
+#define strerror(error) (sys_errlist[error])
+extern char *sys_errlist[];
+extern int errno;
+extern off_t restart_point;
+extern char reply_string[];
+
+char *mname;
+jmp_buf jabort;
+char *dotrans(), *domap();
+
+extern char *auth_type;
+extern int do_auth();
+
+/*
+ * `Another' gets another argument, and stores the new argc and argv.
+ * It reverts to the top level (via main.c's intr()) on EOF/error.
+ *
+ * Returns false if no new arguments have been added.
+ */
+another(pargc, pargv, prompt)
+ int *pargc;
+ char ***pargv;
+ char *prompt;
+{
+ int len = strlen(line), ret;
+ extern sig_t intr();
+
+ if (len >= sizeof(line) - 3) {
+ printf("sorry, arguments too long\n");
+ intr();
+ }
+ printf("(%s) ", prompt);
+ line[len++] = ' ';
+ if (fgets(&line[len], sizeof(line) - len, stdin) == NULL)
+ intr();
+ len += strlen(&line[len]);
+ if (len > 0 && line[len - 1] == '\n')
+ line[len - 1] = '\0';
+ makeargv();
+ ret = margc > *pargc;
+ *pargc = margc;
+ *pargv = margv;
+ return (ret);
+}
+
+/*
+ * Connect to peer server and
+ * auto-login, if possible.
+ */
+setpeer(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *host, *hookup();
+ short port;
+
+ if (connected) {
+ printf("Already connected to %s, use close first.\n",
+ hostname);
+ code = -1;
+ return;
+ }
+ if (argc < 2)
+ (void) another(&argc, &argv, "to");
+ if (argc < 2 || argc > 3) {
+ printf("usage: %s host-name [port]\n", argv[0]);
+ code = -1;
+ return;
+ }
+ port = sp->s_port;
+ if (argc > 2) {
+ port = atoi(argv[2]);
+ if (port <= 0) {
+ printf("%s: bad port number-- %s\n", argv[1], argv[2]);
+ printf ("usage: %s host-name [port]\n", argv[0]);
+ code = -1;
+ return;
+ }
+ port = htons(port);
+ }
+ host = hookup(argv[1], port);
+ if (host) {
+ int overbose;
+
+ connected = 1;
+ /*
+ * Set up defaults for FTP.
+ */
+ level = PROT_C;
+ type = TYPE_A;
+ curtype = TYPE_A;
+ form = FORM_N;
+ mode = MODE_S;
+ stru = STRU_F;
+ (void) strcpy(bytename, "8"), bytesize = 8;
+ if (autologin)
+ (void) login(argv[1]);
+
+ if (0) {
+ setpbsz(1<<20);
+ level = PROT_P;
+ if (command("PROT P") != COMPLETE)
+ fprintf(stderr, "auto PROT P setting failed\n");
+ }
+
+#ifndef unix
+#ifdef _AIX
+#define unix
+#endif
+#endif
+
+#if defined(unix) && (NBBY == 8 || defined(linux))
+/*
+ * this ifdef is to keep someone form "porting" this to an incompatible
+ * system and not checking this out. This way they have to think about it.
+ */
+ overbose = verbose;
+ if (debug == 0)
+ verbose = -1;
+ if (command("SYST") == COMPLETE && overbose) {
+ register char *cp, c;
+ cp = strchr(reply_string+4, ' ');
+ if (cp == NULL)
+ cp = strchr(reply_string+4, '\r');
+ if (cp) {
+ if (cp[-1] == '.')
+ cp--;
+ c = *cp;
+ *cp = '\0';
+ }
+
+ printf("Remote system type is %s.\n",
+ reply_string+4);
+ if (cp)
+ *cp = c;
+ }
+ if (!strncmp(reply_string, "215 UNIX Type: L8", 17)) {
+ if (proxy)
+ unix_proxy = 1;
+ else
+ unix_server = 1;
+ /*
+ * Set type to 0 (not specified by user),
+ * meaning binary by default, but don't bother
+ * telling server. We can use binary
+ * for text files unless changed by the user.
+ */
+ type = 0;
+ if (overbose)
+ printf("Using %s mode to transfer files.\n",
+ "binary");
+ } else {
+ if (proxy)
+ unix_proxy = 0;
+ else
+ unix_server = 0;
+ if (overbose &&
+ !strncmp(reply_string, "215 TOPS20", 10))
+ printf(
+"Remember to set tenex mode when transfering binary files from this machine.\n");
+ }
+ verbose = overbose;
+#endif /* unix */
+ }
+}
+
+struct levels {
+ char *p_name;
+ char *p_mode;
+ int p_level;
+} levels[] = {
+ { "clear", "C", PROT_C },
+ { "safe", "S", PROT_S },
+#ifndef NOENCRYPTION
+ { "private", "P", PROT_P },
+#endif
+ 0
+};
+
+char *
+getlevel()
+{
+ register struct levels *p;
+
+ for (p = levels; p->p_level != level; p++);
+ return(p->p_name);
+}
+
+/*
+ * Set protection level.
+ */
+setlevel(argc, argv)
+ char *argv[];
+{
+ register struct levels *p;
+ int comret;
+
+ if (argc > 2) {
+ char *sep;
+
+ printf("usage: %s [", argv[0]);
+ sep = " ";
+ for (p = levels; p->p_name; p++) {
+ printf("%s%s", sep, p->p_name);
+ if (*sep == ' ')
+ sep = " | ";
+ }
+ printf(" ]\n");
+ code = -1;
+ return;
+ }
+ if (argc < 2) {
+ printf("Using %s protection level to transfer files.\n",
+ getlevel());
+ code = 0;
+ return;
+ }
+ for (p = levels; p->p_name; p++)
+ if (strcmp(argv[1], p->p_name) == 0)
+ break;
+ if (p->p_name == 0) {
+ printf("%s: unknown protection level\n", argv[1]);
+ code = -1;
+ return;
+ }
+ if (!auth_type) {
+ if (strcmp(p->p_name, "clear"))
+ printf("Cannot set protection level to %s\n", argv[1]);
+ return;
+ }
+ /* Start with a PBSZ of 1 meg */
+ if (p->p_level != PROT_C) setpbsz(1<<20);
+ comret = command("PROT %s", p->p_mode);
+ if (comret == COMPLETE)
+ level = p->p_level;
+}
+
+char *plevel[] = {
+ "protect",
+ "",
+ 0
+};
+
+/*
+ * Set clear protection level.
+ */
+/*VARARGS*/
+setclear()
+{
+ plevel[1] = "clear";
+ setlevel(2, plevel);
+}
+
+/*
+ * Set safe protection level.
+ */
+/*VARARGS*/
+setsafe()
+{
+ plevel[1] = "safe";
+ setlevel(2, plevel);
+}
+
+#ifndef NOENCRYPTION
+/*
+ * Set private protection level.
+ */
+/*VARARGS*/
+setprivate()
+{
+ plevel[1] = "private";
+ setlevel(2, plevel);
+}
+#endif
+
+struct types {
+ char *t_name;
+ char *t_mode;
+ int t_type;
+ char *t_arg;
+} types[] = {
+ { "ascii", "A", TYPE_A, 0 },
+ { "binary", "I", TYPE_I, 0 },
+ { "image", "I", TYPE_I, 0 },
+ { "ebcdic", "E", TYPE_E, 0 },
+ { "tenex", "L", TYPE_L, bytename },
+ 0
+};
+
+char *
+gettype()
+{
+ register struct types *p;
+ int t;
+
+ t = type;
+ if (t == 0)
+ t = TYPE_I;
+ for (p = types; p->t_type != t; p++);
+ return(p->t_name);
+}
+
+/*
+ * Set transfer type.
+ */
+settype(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct types *p;
+ int comret;
+
+ if (argc > 2) {
+ char *sep;
+
+ printf("usage: %s [", argv[0]);
+ sep = " ";
+ for (p = types; p->t_name; p++) {
+ printf("%s%s", sep, p->t_name);
+ sep = " | ";
+ }
+ printf(" ]\n");
+ code = -1;
+ return;
+ }
+ if (argc < 2) {
+ printf("Using %s mode to transfer files.\n", gettype());
+ code = 0;
+ return;
+ }
+ for (p = types; p->t_name; p++)
+ if (strcmp(argv[1], p->t_name) == 0)
+ break;
+ if (p->t_name == 0) {
+ printf("%s: unknown mode\n", argv[1]);
+ code = -1;
+ return;
+ }
+ if ((p->t_arg != NULL) && (*(p->t_arg) != '\0'))
+ comret = command ("TYPE %s %s", p->t_mode, p->t_arg);
+ else
+ comret = command("TYPE %s", p->t_mode);
+ if (comret == COMPLETE)
+ curtype = type = p->t_type;
+}
+
+/*
+ * Internal form of settype; changes current type in use with server
+ * without changing our notion of the type for data transfers.
+ * Used to change to and from ascii for listings.
+ */
+changetype(newtype, show)
+ int newtype, show;
+{
+ register struct types *p;
+ int comret, oldverbose = verbose;
+
+ if (newtype == 0)
+ newtype = TYPE_I;
+ if (newtype == curtype)
+ return;
+ if (debug == 0 && show == 0)
+ verbose = 0;
+ for (p = types; p->t_name; p++)
+ if (newtype == p->t_type)
+ break;
+ if (p->t_name == 0) {
+ printf("ftp: internal error: unknown type %d\n", newtype);
+ return;
+ }
+ if (newtype == TYPE_L && bytename[0] != '\0')
+ comret = command("TYPE %s %s", p->t_mode, bytename);
+ else
+ comret = command("TYPE %s", p->t_mode);
+ if (comret == COMPLETE)
+ curtype = newtype;
+ verbose = oldverbose;
+}
+
+char *stype[] = {
+ "type",
+ "",
+ 0
+};
+
+/*
+ * Set binary transfer type.
+ */
+/*VARARGS*/
+setbinary()
+{
+ stype[1] = "binary";
+ settype(2, stype);
+}
+
+/*
+ * Set ascii transfer type.
+ */
+/*VARARGS*/
+setascii()
+{
+ stype[1] = "ascii";
+ settype(2, stype);
+}
+
+/*
+ * Set tenex transfer type.
+ */
+/*VARARGS*/
+settenex()
+{
+ stype[1] = "tenex";
+ settype(2, stype);
+}
+
+char *
+getmode()
+{
+ return("stream");
+}
+
+/*
+ * Set file transfer mode.
+ */
+/*ARGSUSED*/
+setmode(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ printf("We only support %s mode, sorry.\n", getmode());
+ code = -1;
+}
+
+char *
+getform()
+{
+ return("non-print");
+}
+
+/*
+ * Set file transfer format.
+ */
+/*ARGSUSED*/
+setform(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ printf("We only support %s format, sorry.\n", getform());
+ code = -1;
+}
+
+char *
+getstruct()
+{
+ return("file");
+}
+
+/*
+ * Set file transfer structure.
+ */
+/*ARGSUSED*/
+setstruct(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ printf("We only support %s structure, sorry.\n", getstruct());
+ code = -1;
+}
+
+/*
+ * Send a single file.
+ */
+put(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *cmd;
+ int loc = 0;
+ char *oldargv1, *oldargv2;
+
+ if (argc == 2) {
+ argc++;
+ argv[2] = argv[1];
+ loc++;
+ }
+ if (argc < 2 && !another(&argc, &argv, "local-file"))
+ goto usage;
+ if (argc < 3 && !another(&argc, &argv, "remote-file")) {
+usage:
+ printf("usage: %s local-file remote-file\n", argv[0]);
+ code = -1;
+ return;
+ }
+ oldargv1 = argv[1];
+ oldargv2 = argv[2];
+ if (!globulize(&argv[1])) {
+ code = -1;
+ return;
+ }
+ /*
+ * If "globulize" modifies argv[1], and argv[2] is a copy of
+ * the old argv[1], make it a copy of the new argv[1].
+ */
+ if (argv[1] != oldargv1 && argv[2] == oldargv1) {
+ argv[2] = argv[1];
+ }
+ cmd = (argv[0][0] == 'a') ? "APPE" : ((sunique) ? "STOU" : "STOR");
+ if (loc && ntflag) {
+ argv[2] = dotrans(argv[2]);
+ }
+ if (loc && mapflag) {
+ argv[2] = domap(argv[2]);
+ }
+ sendrequest(cmd, argv[1], argv[2],
+ argv[1] != oldargv1 || argv[2] != oldargv2);
+}
+
+/*
+ * Send multiple files.
+ */
+mput(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern jmp_buf jabort;
+ register int i;
+ sig_t oldintr;
+ int ointer;
+ char *tp;
+ sigtype mabort();
+
+ if (argc < 2 && !another(&argc, &argv, "local-files")) {
+ printf("usage: %s local-files\n", argv[0]);
+ code = -1;
+ return;
+ }
+ mname = argv[0];
+ mflag = 1;
+ oldintr = signal(SIGINT, mabort);
+ (void) setjmp(jabort);
+ if (proxy) {
+ char *cp, *tp2, tmpbuf[MAXPATHLEN];
+
+ while ((cp = remglob(argv,0)) != NULL) {
+ if (*cp == 0) {
+ mflag = 0;
+ continue;
+ }
+ if (mflag && confirm(argv[0], cp)) {
+ tp = cp;
+ if (mcase) {
+ while (*tp && !islower(*tp)) {
+ tp++;
+ }
+ if (!*tp) {
+ tp = cp;
+ tp2 = tmpbuf;
+ while ((*tp2 = *tp) != 0) {
+ if (isupper(*tp2)) {
+ *tp2 = 'a' + *tp2 - 'A';
+ }
+ tp++;
+ tp2++;
+ }
+ }
+ tp = tmpbuf;
+ }
+ if (ntflag) {
+ tp = dotrans(tp);
+ }
+ if (mapflag) {
+ tp = domap(tp);
+ }
+ sendrequest((sunique) ? "STOU" : "STOR",
+ cp, tp, cp != tp || !interactive);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with","mput")) {
+ mflag++;
+ }
+ interactive = ointer;
+ }
+ }
+ }
+ (void) signal(SIGINT, oldintr);
+ mflag = 0;
+ return;
+ }
+ for (i = 1; i < argc; i++) {
+ register char **cpp, **gargs;
+
+ if (!doglob) {
+ if (mflag && confirm(argv[0], argv[i])) {
+ tp = (ntflag) ? dotrans(argv[i]) : argv[i];
+ tp = (mapflag) ? domap(tp) : tp;
+ sendrequest((sunique) ? "STOU" : "STOR",
+ argv[i], tp, tp != argv[i] || !interactive);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with","mput")) {
+ mflag++;
+ }
+ interactive = ointer;
+ }
+ }
+ continue;
+ }
+ gargs = ftpglob(argv[i]);
+ if (globerr != NULL) {
+ printf("%s\n", globerr);
+ if (gargs) {
+ blkfree(gargs);
+ free((char *)gargs);
+ }
+ continue;
+ }
+ for (cpp = gargs; cpp && *cpp != NULL; cpp++) {
+ if (mflag && confirm(argv[0], *cpp)) {
+ tp = (ntflag) ? dotrans(*cpp) : *cpp;
+ tp = (mapflag) ? domap(tp) : tp;
+ sendrequest((sunique) ? "STOU" : "STOR",
+ *cpp, tp, *cpp != tp || !interactive);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with","mput")) {
+ mflag++;
+ }
+ interactive = ointer;
+ }
+ }
+ }
+ if (gargs != NULL) {
+ blkfree(gargs);
+ free((char *)gargs);
+ }
+ }
+ (void) signal(SIGINT, oldintr);
+ mflag = 0;
+}
+
+reget(argc, argv)
+ int argc;
+ char *argv[];
+{
+ (void) getit(argc, argv, 1, "r+w");
+}
+
+get(argc, argv)
+ int argc;
+ char *argv[];
+{
+ (void) getit(argc, argv, 0, restart_point ? "r+w" : "w" );
+}
+
+/*
+ * Receive one file.
+ */
+getit(argc, argv, restartit, mode)
+ int argc;
+ char *argv[];
+ char *mode;
+{
+ int loc = 0;
+ char *oldargv1, *oldargv2;
+
+ if (argc == 2) {
+ argc++;
+ argv[2] = argv[1];
+ loc++;
+ }
+ if (argc < 2 && !another(&argc, &argv, "remote-file"))
+ goto usage;
+ if (argc < 3 && !another(&argc, &argv, "local-file")) {
+usage:
+ printf("usage: %s remote-file [ local-file ]\n", argv[0]);
+ code = -1;
+ return (0);
+ }
+ oldargv1 = argv[1];
+ oldargv2 = argv[2];
+ if (!globulize(&argv[2])) {
+ code = -1;
+ return (0);
+ }
+ if (loc && mcase) {
+ char *tp = argv[1], *tp2, tmpbuf[MAXPATHLEN];
+
+ while (*tp && !islower(*tp)) {
+ tp++;
+ }
+ if (!*tp) {
+ tp = argv[2];
+ tp2 = tmpbuf;
+ while ((*tp2 = *tp) != 0) {
+ if (isupper(*tp2)) {
+ *tp2 = 'a' + *tp2 - 'A';
+ }
+ tp++;
+ tp2++;
+ }
+ argv[2] = tmpbuf;
+ }
+ }
+ if (loc && ntflag)
+ argv[2] = dotrans(argv[2]);
+ if (loc && mapflag)
+ argv[2] = domap(argv[2]);
+ if (restartit) {
+ struct stat stbuf;
+ int ret;
+
+ ret = stat(argv[2], &stbuf);
+ if (restartit == 1) {
+ if (ret < 0) {
+ fprintf(stderr, "local: %s: %s\n", argv[2],
+ strerror(errno));
+ return (0);
+ }
+ restart_point = stbuf.st_size;
+ } else {
+ if (ret == 0) {
+ int overbose;
+
+ overbose = verbose;
+ if (debug == 0)
+ verbose = -1;
+ if (command("MDTM %s", argv[1]) == COMPLETE) {
+ int yy, mo, day, hour, min, sec;
+ struct tm *tm;
+ verbose = overbose;
+ sscanf(reply_string,
+ "%*s %04d%02d%02d%02d%02d%02d",
+ &yy, &mo, &day, &hour, &min, &sec);
+ tm = gmtime(&stbuf.st_mtime);
+ tm->tm_mon++;
+ if (tm->tm_year > yy%100)
+ return (1);
+ else if (tm->tm_year == yy%100) {
+ if (tm->tm_mon > mo)
+ return (1);
+ } else if (tm->tm_mon == mo) {
+ if (tm->tm_mday > day)
+ return (1);
+ } else if (tm->tm_mday == day) {
+ if (tm->tm_hour > hour)
+ return (1);
+ } else if (tm->tm_hour == hour) {
+ if (tm->tm_min > min)
+ return (1);
+ } else if (tm->tm_min == min) {
+ if (tm->tm_sec > sec)
+ return (1);
+ }
+ } else {
+ printf("%s\n", reply_string);
+ verbose = overbose;
+ return (0);
+ }
+ }
+ }
+ }
+
+ recvrequest("RETR", argv[2], argv[1], mode,
+ argv[1] != oldargv1 || argv[2] != oldargv2);
+ restart_point = 0;
+ return (0);
+}
+
+sigtype
+mabort(sig)
+ int sig;
+{
+ int ointer;
+ extern jmp_buf jabort;
+
+ printf("\n");
+ (void) fflush(stdout);
+ if (mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with", mname)) {
+ interactive = ointer;
+ longjmp(jabort,0);
+ }
+ interactive = ointer;
+ }
+ mflag = 0;
+ longjmp(jabort,0);
+}
+
+/*
+ * Get multiple files.
+ */
+mget(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern jmp_buf jabort;
+ sig_t oldintr;
+ int ointer;
+ char *cp, *tp, *tp2, tmpbuf[MAXPATHLEN];
+ sigtype mabort();
+
+ if (argc < 2 && !another(&argc, &argv, "remote-files")) {
+ printf("usage: %s remote-files\n", argv[0]);
+ code = -1;
+ return;
+ }
+ mname = argv[0];
+ mflag = 1;
+ oldintr = signal(SIGINT,mabort);
+ (void) setjmp(jabort);
+ while ((cp = remglob(argv,proxy)) != NULL) {
+ if (*cp == '\0') {
+ mflag = 0;
+ continue;
+ }
+ if (mflag && confirm(argv[0], cp)) {
+ tp = cp;
+ if (mcase) {
+ while (*tp && !islower(*tp)) {
+ tp++;
+ }
+ if (!*tp) {
+ tp = cp;
+ tp2 = tmpbuf;
+ while ((*tp2 = *tp) != 0) {
+ if (isupper(*tp2)) {
+ *tp2 = 'a' + *tp2 - 'A';
+ }
+ tp++;
+ tp2++;
+ }
+ }
+ tp = tmpbuf;
+ }
+ if (ntflag) {
+ tp = dotrans(tp);
+ }
+ if (mapflag) {
+ tp = domap(tp);
+ }
+ recvrequest("RETR", tp, cp, "w",
+ tp != cp || !interactive);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with","mget")) {
+ mflag++;
+ }
+ interactive = ointer;
+ }
+ }
+ }
+ (void) signal(SIGINT,oldintr);
+ mflag = 0;
+}
+
+char *
+remglob(argv,doswitch)
+ char *argv[];
+ int doswitch;
+{
+ char temp[16];
+ static char buf[MAXPATHLEN];
+ static FILE *ftemp = NULL;
+ static char **args;
+ int oldverbose, oldhash;
+ char *cp, *mode;
+
+ if (!mflag) {
+ if (!doglob) {
+ args = NULL;
+ }
+ else {
+ if (ftemp) {
+ (void) fclose(ftemp);
+ ftemp = NULL;
+ }
+ }
+ return(NULL);
+ }
+ if (!doglob) {
+ if (args == NULL)
+ args = argv;
+ if ((cp = *++args) == NULL)
+ args = NULL;
+ return (cp);
+ }
+ if (ftemp == NULL) {
+ (void) strcpy(temp, _PATH_TMP);
+ (void) mktemp(temp);
+ oldverbose = verbose, verbose = 0;
+ oldhash = hash, hash = 0;
+ if (doswitch) {
+ pswitch(!proxy);
+ }
+ for (mode = "w"; *++argv != NULL; mode = "a")
+ recvrequest ("NLST", temp, *argv, mode, 0);
+ if (doswitch) {
+ pswitch(!proxy);
+ }
+ verbose = oldverbose; hash = oldhash;
+ ftemp = fopen(temp, "r");
+ (void) unlink(temp);
+ if (ftemp == NULL) {
+ printf("can't find list of remote files, oops\n");
+ return (NULL);
+ }
+ }
+ if (fgets(buf, sizeof (buf), ftemp) == NULL) {
+ (void) fclose(ftemp), ftemp = NULL;
+ return (NULL);
+ }
+ if ((cp = strchr(buf, '\n')) != NULL)
+ *cp = '\0';
+ return (buf);
+}
+
+char *
+onoff(bool)
+ int bool;
+{
+
+ return (bool ? "on" : "off");
+}
+
+cstatus()
+{
+ if (!connected) {
+ printf(proxy ? "No proxy connection.\n" : "Not connected.\n");
+ return;
+ }
+ printf("Connected %sto %s.\n",
+ proxy ? "for proxy commands " : "", hostname);
+ if (auth_type) printf("Authentication type: %s\n", auth_type);
+ printf("Protection Level: %s\n", getlevel());
+ printf("Passive mode %s\n", onoff(passivemode));
+ printf("Mode: %s; Type: %s; Form: %s; Structure: %s\n",
+ getmode(), gettype(), getform(), getstruct());
+ printf("Store unique: %s; Receive unique: %s\n", onoff(sunique),
+ onoff(runique));
+ printf("Case: %s; CR stripping: %s\n",onoff(mcase),onoff(crflag));
+ if (ntflag) {
+ printf("Ntrans: (in) %s (out) %s\n", ntin,ntout);
+ }
+ else {
+ printf("Ntrans: off\n");
+ }
+ if (mapflag) {
+ printf("Nmap: (in) %s (out) %s\n", mapin, mapout);
+ }
+ else {
+ printf("Nmap: off\n");
+ }
+}
+
+/*
+ * Show status.
+ */
+/*ARGSUSED*/
+status(argc, argv)
+ char *argv[];
+{
+ int i;
+
+ cstatus();
+ if (!proxy) {
+ pswitch(1);
+ if (connected) putchar('\n');
+ cstatus(argc,argv);
+ if (connected) putchar('\n');
+ pswitch(0);
+ }
+ printf("Hash mark printing: %s; Use of PORT cmds: %s\n",
+ onoff(hash), onoff(sendport));
+ printf("Verbose: %s; Bell: %s; Prompting: %s; Globbing: %s\n",
+ onoff(verbose), onoff(bell), onoff(interactive),
+ onoff(doglob));
+ if (macnum > 0) {
+ printf("Macros:\n");
+ for (i=0; i<macnum; i++) {
+ printf("\t%s\n",macros[i].mac_name);
+ }
+ }
+ code = 0;
+}
+
+/*
+ * Set beep on cmd completed mode.
+ */
+/*VARARGS*/
+setbell()
+{
+
+ bell = !bell;
+ printf("Bell mode %s.\n", onoff(bell));
+ code = bell;
+}
+
+/*
+ * Turn on packet tracing.
+ */
+/*VARARGS*/
+settrace()
+{
+
+ trace = !trace;
+ printf("Packet tracing %s.\n", onoff(trace));
+ code = trace;
+}
+
+/*
+ * Toggle hash mark printing during transfers.
+ */
+/*VARARGS*/
+sethash()
+{
+
+ hash = !hash;
+ printf("Hash mark printing %s", onoff(hash));
+ code = hash;
+ if (hash)
+ printf(" (%d bytes/hash mark)", 1024);
+ printf(".\n");
+}
+
+/*
+ * Turn on printing of server echo's.
+ */
+/*VARARGS*/
+setverbose()
+{
+
+ verbose = !verbose;
+ printf("Verbose mode %s.\n", onoff(verbose));
+ code = verbose;
+}
+
+/*
+ * Toggle PORT cmd use before each data connection.
+ */
+/*VARARGS*/
+setport()
+{
+
+ sendport = !sendport;
+ printf("Use of PORT cmds %s.\n", onoff(sendport));
+ code = sendport;
+}
+
+/*
+ * Turn on interactive prompting
+ * during mget, mput, and mdelete.
+ */
+/*VARARGS*/
+setprompt()
+{
+
+ interactive = !interactive;
+ printf("Interactive mode %s.\n", onoff(interactive));
+ code = interactive;
+}
+
+/*
+ * Toggle metacharacter interpretation
+ * on local file names.
+ */
+/*VARARGS*/
+setglob()
+{
+
+ doglob = !doglob;
+ printf("Globbing %s.\n", onoff(doglob));
+ code = doglob;
+}
+
+/*
+ * Set debugging mode on/off and/or
+ * set level of debugging.
+ */
+/*VARARGS*/
+setdebug(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int val;
+
+ if (argc > 1) {
+ val = atoi(argv[1]);
+ if (val < 0) {
+ printf("%s: bad debugging value.\n", argv[1]);
+ code = -1;
+ return;
+ }
+ } else
+ val = !debug;
+ debug = val;
+ if (debug)
+ options |= SO_DEBUG;
+ else
+ options &= ~SO_DEBUG;
+ printf("Debugging %s (debug=%d).\n", onoff(debug), debug);
+ code = debug > 0;
+}
+
+/*
+ * Set current working directory
+ * on remote machine.
+ */
+cd(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "remote-directory")) {
+ printf("usage: %s remote-directory\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (command("CWD %s", argv[1]) == ERROR && code == 500) {
+ if (verbose)
+ printf("CWD command not recognized, trying XCWD\n");
+ (void) command("XCWD %s", argv[1]);
+ }
+}
+
+/*
+ * Set current working directory
+ * on local machine.
+ */
+lcd(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char buf[MAXPATHLEN];
+
+ if (argc < 2)
+ argc++, argv[1] = home;
+ if (argc != 2) {
+ printf("usage: %s local-directory\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (!globulize(&argv[1])) {
+ code = -1;
+ return;
+ }
+ if (chdir(argv[1]) < 0) {
+ fprintf(stderr, "local: %s: %s\n", argv[1], strerror(errno));
+ code = -1;
+ return;
+ }
+ printf("Local directory now %s\n", getcwd(buf, sizeof buf));
+ code = 0;
+}
+
+/*
+ * Delete a single file.
+ */
+delete(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "remote-file")) {
+ printf("usage: %s remote-file\n", argv[0]);
+ code = -1;
+ return;
+ }
+ (void) command("DELE %s", argv[1]);
+}
+
+/*
+ * Delete multiple files.
+ */
+mdelete(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern jmp_buf jabort;
+ sig_t oldintr;
+ int ointer;
+ char *cp;
+ sigtype mabort();
+
+ if (argc < 2 && !another(&argc, &argv, "remote-files")) {
+ printf("usage: %s remote-files\n", argv[0]);
+ code = -1;
+ return;
+ }
+ mname = argv[0];
+ mflag = 1;
+ oldintr = signal(SIGINT, mabort);
+ (void) setjmp(jabort);
+ while ((cp = remglob(argv,0)) != NULL) {
+ if (*cp == '\0') {
+ mflag = 0;
+ continue;
+ }
+ if (mflag && confirm(argv[0], cp)) {
+ (void) command("DELE %s", cp);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with", "mdelete")) {
+ mflag++;
+ }
+ interactive = ointer;
+ }
+ }
+ }
+ (void) signal(SIGINT, oldintr);
+ mflag = 0;
+}
+
+/*
+ * Rename a remote file.
+ */
+renamefile(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "from-name"))
+ goto usage;
+ if (argc < 3 && !another(&argc, &argv, "to-name")) {
+usage:
+ printf("%s from-name to-name\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (command("RNFR %s", argv[1]) == CONTINUE)
+ (void) command("RNTO %s", argv[2]);
+}
+
+/*
+ * Get a directory listing
+ * of remote files.
+ */
+ls(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *cmd;
+
+ if (argc < 2)
+ argc++, argv[1] = NULL;
+ if (argc < 3)
+ argc++, argv[2] = "-";
+ if (argc > 3) {
+ printf("usage: %s remote-directory local-file\n", argv[0]);
+ code = -1;
+ return;
+ }
+ cmd = argv[0][0] == 'n' ? "NLST" : "LIST";
+ if (strcmp(argv[2], "-") && !globulize(&argv[2])) {
+ code = -1;
+ return;
+ }
+ if (strcmp(argv[2], "-") && *argv[2] != '|')
+ if (!globulize(&argv[2]) || !confirm("output to local-file:", argv[2])) {
+ code = -1;
+ return;
+ }
+ recvrequest(cmd, argv[2], argv[1], "w", 0);
+}
+
+/*
+ * Get a directory listing
+ * of multiple remote files.
+ */
+mls(argc, argv)
+ int argc;
+ char **argv;
+{
+ extern jmp_buf jabort;
+ sig_t oldintr;
+ int ointer, i;
+ char *cmd, mode[1], *dest;
+ sigtype mabort();
+
+ if (argc < 2 && !another(&argc, &argv, "remote-files"))
+ goto usage;
+ if (argc < 3 && !another(&argc, &argv, "local-file")) {
+usage:
+ printf("usage: %s remote-files local-file\n", argv[0]);
+ code = -1;
+ return;
+ }
+ dest = argv[argc - 1];
+ argv[argc - 1] = NULL;
+ if (strcmp(dest, "-") && *dest != '|')
+ if (!globulize(&dest) ||
+ !confirm("output to local-file:", dest)) {
+ code = -1;
+ return;
+ }
+ cmd = argv[0][1] == 'l' ? "NLST" : "LIST";
+ mname = argv[0];
+ mflag = 1;
+ oldintr = signal(SIGINT, mabort);
+ (void) setjmp(jabort);
+ for (i = 1; mflag && i < argc-1; ++i) {
+ *mode = (i == 1) ? 'w' : 'a';
+ recvrequest(cmd, dest, argv[i], mode, 0);
+ if (!mflag && fromatty) {
+ ointer = interactive;
+ interactive = 1;
+ if (confirm("Continue with", argv[0])) {
+ mflag ++;
+ }
+ interactive = ointer;
+ }
+ }
+ (void) signal(SIGINT, oldintr);
+ mflag = 0;
+}
+
+/*
+ * Do a shell escape
+ */
+/*ARGSUSED*/
+shell(argc, argv)
+ int argc;
+ char **argv;
+{
+ int pid;
+ sig_t old1, old2;
+ char shellnam[40], *shell, *namep;
+#ifdef WAIT_USES_INT
+ int status;
+#else
+ union wait status;
+#endif
+
+ old1 = signal (SIGINT, SIG_IGN);
+ old2 = signal (SIGQUIT, SIG_IGN);
+ if ((pid = fork()) == 0) {
+ for (pid = 3; pid < 20; pid++)
+ (void) close(pid);
+ (void) signal(SIGINT, SIG_DFL);
+ (void) signal(SIGQUIT, SIG_DFL);
+ shell = getenv("SHELL");
+ if (shell == NULL)
+ shell = "/bin/sh";
+ namep = strrchr(shell,'/');
+ if (namep == NULL)
+ namep = shell;
+ (void) strcpy(shellnam,"-");
+ (void) strcat(shellnam, ++namep);
+ if (strcmp(namep, "sh") != 0)
+ shellnam[0] = '+';
+ if (debug) {
+ printf ("%s\n", shell);
+ (void) fflush (stdout);
+ }
+ if (argc > 1) {
+ execl(shell,shellnam,"-c",altarg,(char *)0);
+ }
+ else {
+ execl(shell,shellnam,(char *)0);
+ }
+ perror(shell);
+ code = -1;
+ exit(1);
+ }
+ if (pid > 0)
+ while (wait(&status) != pid)
+ ;
+ (void) signal(SIGINT, old1);
+ (void) signal(SIGQUIT, old2);
+ if (pid == -1) {
+ perror("Try again later");
+ code = -1;
+ }
+ else {
+ code = 0;
+ }
+ return (0);
+}
+
+/*
+ * Send new user information (re-login)
+ */
+user(argc, argv)
+ int argc;
+ char **argv;
+{
+ char acct[80], *mygetpass();
+ int n, aflag = 0;
+
+ if (argc < 2)
+ (void) another(&argc, &argv, "username");
+ if (argc < 2 || argc > 4) {
+ printf("usage: %s username [password] [account]\n", argv[0]);
+ code = -1;
+ return (0);
+ }
+ n = command("USER %s", argv[1]);
+ if (n == COMPLETE)
+ n = command("PASS dummy");
+ else if (n == CONTINUE) {
+#ifndef NOENCRYPTION
+ int oldlevel;
+#endif
+ if (argc < 3 )
+ argv[2] = mygetpass("Password: "), argc++;
+#ifndef NOENCRYPTION
+ if ((oldlevel = level) == PROT_S) level = PROT_P;
+#endif
+ n = command("PASS %s", argv[2]);
+#ifndef NOENCRYPTION
+ /* level may have changed */
+ if (level == PROT_P) level = oldlevel;
+#endif
+ }
+ if (n == CONTINUE) {
+ if (argc < 4) {
+ printf("Account: "); (void) fflush(stdout);
+ (void) fgets(acct, sizeof(acct) - 1, stdin);
+ acct[strlen(acct) - 1] = '\0';
+ argv[3] = acct; argc++;
+ }
+ n = command("ACCT %s", argv[3]);
+ aflag++;
+ }
+ if (n != COMPLETE) {
+ fprintf(stdout, "Login failed.\n");
+ return (0);
+ }
+ if (!aflag && argc == 4) {
+ (void) command("ACCT %s", argv[3]);
+ }
+ return (1);
+}
+
+/*
+ * Print working directory.
+ */
+/*VARARGS*/
+pwd()
+{
+ int oldverbose = verbose;
+
+ /*
+ * If we aren't verbose, this doesn't do anything!
+ */
+ verbose = 1;
+ if (command("PWD") == ERROR && code == 500) {
+ printf("PWD command not recognized, trying XPWD\n");
+ (void) command("XPWD");
+ }
+ verbose = oldverbose;
+}
+
+/*
+ * Make a directory.
+ */
+makedir(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "directory-name")) {
+ printf("usage: %s directory-name\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (command("MKD %s", argv[1]) == ERROR && code == 500) {
+ if (verbose)
+ printf("MKD command not recognized, trying XMKD\n");
+ (void) command("XMKD %s", argv[1]);
+ }
+}
+
+/*
+ * Remove a directory.
+ */
+removedir(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "directory-name")) {
+ printf("usage: %s directory-name\n", argv[0]);
+ code = -1;
+ return;
+ }
+ if (command("RMD %s", argv[1]) == ERROR && code == 500) {
+ if (verbose)
+ printf("RMD command not recognized, trying XRMD\n");
+ (void) command("XRMD %s", argv[1]);
+ }
+}
+
+/*
+ * Send a line, verbatim, to the remote machine.
+ */
+quote(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "command line to send")) {
+ printf("usage: %s line-to-send\n", argv[0]);
+ code = -1;
+ return;
+ }
+ quote1("", argc, argv);
+}
+
+/*
+ * Send a SITE command to the remote machine. The line
+ * is sent verbatim to the remote machine, except that the
+ * word "SITE" is added at the front.
+ */
+site(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "arguments to SITE command")) {
+ printf("usage: %s line-to-send\n", argv[0]);
+ code = -1;
+ return;
+ }
+ quote1("SITE ", argc, argv);
+}
+
+/*
+ * Turn argv[1..argc) into a space-separated string, then prepend initial text.
+ * Send the result as a one-line command and get response.
+ */
+quote1(initial, argc, argv)
+ char *initial;
+ int argc;
+ char **argv;
+{
+ register int i, len;
+ char buf[FTP_BUFSIZ]; /* must be >= sizeof(line) */
+
+ (void) strcpy(buf, initial);
+ if (argc > 1) {
+ len = strlen(buf);
+ len += strlen(strcpy(&buf[len], argv[1]));
+ for (i = 2; i < argc; i++) {
+ buf[len++] = ' ';
+ len += strlen(strcpy(&buf[len], argv[i]));
+ }
+ }
+ if (command(buf) == PRELIM) {
+ while (getreply(0) == PRELIM);
+ }
+}
+
+do_chmod(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "mode"))
+ goto usage;
+ if (argc < 3 && !another(&argc, &argv, "file-name")) {
+usage:
+ printf("usage: %s mode file-name\n", argv[0]);
+ code = -1;
+ return;
+ }
+ (void) command("SITE CHMOD %s %s", argv[1], argv[2]);
+}
+
+do_umask(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int oldverbose = verbose;
+
+ verbose = 1;
+ (void) command(argc == 1 ? "SITE UMASK" : "SITE UMASK %s", argv[1]);
+ verbose = oldverbose;
+}
+
+siteidle(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int oldverbose = verbose;
+
+ verbose = 1;
+ (void) command(argc == 1 ? "SITE IDLE" : "SITE IDLE %s", argv[1]);
+ verbose = oldverbose;
+}
+
+/*
+ * Ask the other side for help.
+ */
+rmthelp(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int oldverbose = verbose;
+
+ verbose = 1;
+ (void) command(argc == 1 ? "HELP" : "HELP %s", argv[1]);
+ verbose = oldverbose;
+}
+
+/*
+ * Terminate session and exit.
+ */
+/*VARARGS*/
+quit()
+{
+
+ if (connected)
+ disconnect();
+ pswitch(1);
+ if (connected) {
+ disconnect();
+ }
+ exit(0);
+}
+
+/*
+ * Terminate session, but don't exit.
+ */
+disconnect()
+{
+ extern FILE *cout;
+ extern int data;
+
+ if (!connected)
+ return;
+ (void) command("QUIT");
+ if (cout) {
+ (void) fclose(cout);
+ }
+ cout = NULL;
+ connected = 0;
+ data = -1;
+ if (!proxy) {
+ macnum = 0;
+ }
+ auth_type = NULL;
+ level = PROT_C;
+}
+
+confirm(cmd, file)
+ char *cmd, *file;
+{
+ char line[FTP_BUFSIZ];
+
+ if (!interactive)
+ return (1);
+ printf("%s %s? ", cmd, file);
+ (void) fflush(stdout);
+ if (fgets(line, sizeof line, stdin) == NULL)
+ return (0);
+ return (*line != 'n' && *line != 'N');
+}
+
+fatal(msg)
+ char *msg;
+{
+
+ fprintf(stderr, "ftp: %s\n", msg);
+ exit(1);
+}
+
+/*
+ * Glob a local file name specification with
+ * the expectation of a single return value.
+ * Can't control multiple values being expanded
+ * from the expression, we return only the first.
+ */
+globulize(cpp)
+ char **cpp;
+{
+ char **globbed;
+
+ if (!doglob)
+ return (1);
+ globbed = ftpglob(*cpp);
+ if (globerr != NULL) {
+ printf("%s: %s\n", *cpp, globerr);
+ if (globbed) {
+ blkfree(globbed);
+ free((char *)globbed);
+ }
+ return (0);
+ }
+ if (globbed) {
+ *cpp = *globbed++;
+ /* don't waste too much memory */
+ if (*globbed) {
+ blkfree(globbed);
+ free((char *)globbed);
+ }
+ }
+ return (1);
+}
+
+account(argc,argv)
+ int argc;
+ char **argv;
+{
+ char acct[50], *mygetpass(), *ap;
+
+ if (argc > 1) {
+ ++argv;
+ --argc;
+ (void) strncpy(acct,*argv,49);
+ acct[49] = '\0';
+ while (argc > 1) {
+ --argc;
+ ++argv;
+ (void) strncat(acct,*argv, 49-strlen(acct));
+ }
+ ap = acct;
+ }
+ else {
+ ap = mygetpass("Account:");
+ }
+ (void) command("ACCT %s", ap);
+}
+
+jmp_buf abortprox;
+
+sigtype
+proxabort(sig)
+ int sig;
+{
+ extern int proxy;
+
+ if (!proxy) {
+ pswitch(1);
+ }
+ if (connected) {
+ proxflag = 1;
+ }
+ else {
+ proxflag = 0;
+ }
+ pswitch(0);
+ longjmp(abortprox,1);
+}
+
+doproxy(argc,argv)
+ int argc;
+ char *argv[];
+{
+ extern struct cmd cmdtab[];
+ extern jmp_buf abortprox;
+ register struct cmd *c;
+ struct cmd *getcmd();
+ sig_t oldintr;
+ sigtype proxabort();
+
+ if (argc < 2 && !another(&argc, &argv, "command")) {
+ printf("usage: %s command\n", argv[0]);
+ code = -1;
+ return;
+ }
+ c = getcmd(argv[1]);
+ if (c == (struct cmd *) -1) {
+ printf("?Ambiguous command\n");
+ (void) fflush(stdout);
+ code = -1;
+ return;
+ }
+ if (c == 0) {
+ printf("?Invalid command\n");
+ (void) fflush(stdout);
+ code = -1;
+ return;
+ }
+ if (!c->c_proxy) {
+ printf("?Invalid proxy command\n");
+ (void) fflush(stdout);
+ code = -1;
+ return;
+ }
+ if (setjmp(abortprox)) {
+ code = -1;
+ return;
+ }
+ oldintr = signal(SIGINT, proxabort);
+ pswitch(1);
+ if (c->c_conn && !connected) {
+ printf("Not connected\n");
+ (void) fflush(stdout);
+ pswitch(0);
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ (*c->c_handler)(argc-1, argv+1);
+ if (connected) {
+ proxflag = 1;
+ }
+ else {
+ proxflag = 0;
+ }
+ pswitch(0);
+ (void) signal(SIGINT, oldintr);
+}
+
+setcase()
+{
+ mcase = !mcase;
+ printf("Case mapping %s.\n", onoff(mcase));
+ code = mcase;
+}
+
+setcr()
+{
+ crflag = !crflag;
+ printf("Carriage Return stripping %s.\n", onoff(crflag));
+ code = crflag;
+}
+
+setntrans(argc,argv)
+ int argc;
+ char *argv[];
+{
+ if (argc == 1) {
+ ntflag = 0;
+ printf("Ntrans off.\n");
+ code = ntflag;
+ return;
+ }
+ ntflag++;
+ code = ntflag;
+ (void) strncpy(ntin, argv[1], 16);
+ ntin[16] = '\0';
+ if (argc == 2) {
+ ntout[0] = '\0';
+ return;
+ }
+ (void) strncpy(ntout, argv[2], 16);
+ ntout[16] = '\0';
+}
+
+char *
+dotrans(name)
+ char *name;
+{
+ static char new[MAXPATHLEN];
+ char *cp1, *cp2 = new;
+ register int i, ostop, found;
+
+ for (ostop = 0; *(ntout + ostop) && ostop < 16; ostop++);
+ for (cp1 = name; *cp1; cp1++) {
+ found = 0;
+ for (i = 0; *(ntin + i) && i < 16; i++) {
+ if (*cp1 == *(ntin + i)) {
+ found++;
+ if (i < ostop) {
+ *cp2++ = *(ntout + i);
+ }
+ break;
+ }
+ }
+ if (!found) {
+ *cp2++ = *cp1;
+ }
+ }
+ *cp2 = '\0';
+ return(new);
+}
+
+setnmap(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *cp;
+
+ if (argc == 1) {
+ mapflag = 0;
+ printf("Nmap off.\n");
+ code = mapflag;
+ return;
+ }
+ if (argc < 3 && !another(&argc, &argv, "mapout")) {
+ printf("Usage: %s [mapin mapout]\n",argv[0]);
+ code = -1;
+ return;
+ }
+ mapflag = 1;
+ code = 1;
+ cp = strchr(altarg, ' ');
+ if (proxy) {
+ while(*++cp == ' ');
+ altarg = cp;
+ cp = strchr(altarg, ' ');
+ }
+ *cp = '\0';
+ (void) strncpy(mapin, altarg, MAXPATHLEN - 1);
+ while (*++cp == ' ');
+ (void) strncpy(mapout, cp, MAXPATHLEN - 1);
+}
+
+char *
+domap(name)
+ char *name;
+{
+ static char new[MAXPATHLEN];
+ register char *cp1 = name, *cp2 = mapin;
+ char *tp[9], *te[9];
+ int i, toks[9], toknum = 0, match = 1;
+
+ for (i=0; i < 9; ++i) {
+ toks[i] = 0;
+ }
+ while (match && *cp1 && *cp2) {
+ switch (*cp2) {
+ case '\\':
+ if (*++cp2 != *cp1) {
+ match = 0;
+ }
+ break;
+ case '$':
+ if (*(cp2+1) >= '1' && (*cp2+1) <= '9') {
+ if (*cp1 != *(++cp2+1)) {
+ toks[toknum = *cp2 - '1']++;
+ tp[toknum] = cp1;
+ while (*++cp1 && *(cp2+1)
+ != *cp1);
+ te[toknum] = cp1;
+ }
+ cp2++;
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ if (*cp2 != *cp1) {
+ match = 0;
+ }
+ break;
+ }
+ if (match && *cp1) {
+ cp1++;
+ }
+ if (match && *cp2) {
+ cp2++;
+ }
+ }
+ if (!match && *cp1) /* last token mismatch */
+ {
+ toks[toknum] = 0;
+ }
+ cp1 = new;
+ *cp1 = '\0';
+ cp2 = mapout;
+ while (*cp2) {
+ match = 0;
+ switch (*cp2) {
+ case '\\':
+ if (*(cp2 + 1)) {
+ *cp1++ = *++cp2;
+ }
+ break;
+ case '[':
+LOOP:
+ if (*++cp2 == '$' && isdigit(*(cp2+1))) {
+ if (*++cp2 == '0') {
+ char *cp3 = name;
+
+ while (*cp3) {
+ *cp1++ = *cp3++;
+ }
+ match = 1;
+ }
+ else if (toks[toknum = *cp2 - '1']) {
+ char *cp3 = tp[toknum];
+
+ while (cp3 != te[toknum]) {
+ *cp1++ = *cp3++;
+ }
+ match = 1;
+ }
+ }
+ else {
+ while (*cp2 && *cp2 != ',' &&
+ *cp2 != ']') {
+ if (*cp2 == '\\') {
+ cp2++;
+ }
+ else if (*cp2 == '$' &&
+ isdigit(*(cp2+1))) {
+ if (*++cp2 == '0') {
+ char *cp3 = name;
+
+ while (*cp3) {
+ *cp1++ = *cp3++;
+ }
+ }
+ else if (toks[toknum =
+ *cp2 - '1']) {
+ char *cp3=tp[toknum];
+
+ while (cp3 !=
+ te[toknum]) {
+ *cp1++ = *cp3++;
+ }
+ }
+ }
+ else if (*cp2) {
+ *cp1++ = *cp2++;
+ }
+ }
+ if (!*cp2) {
+ printf("nmap: unbalanced brackets\n");
+ return(name);
+ }
+ match = 1;
+ cp2--;
+ }
+ if (match) {
+ while (*++cp2 && *cp2 != ']') {
+ if (*cp2 == '\\' && *(cp2 + 1)) {
+ cp2++;
+ }
+ }
+ if (!*cp2) {
+ printf("nmap: unbalanced brackets\n");
+ return(name);
+ }
+ break;
+ }
+ switch (*++cp2) {
+ case ',':
+ goto LOOP;
+ case ']':
+ break;
+ default:
+ cp2--;
+ goto LOOP;
+ }
+ break;
+ case '$':
+ if (isdigit(*(cp2 + 1))) {
+ if (*++cp2 == '0') {
+ char *cp3 = name;
+
+ while (*cp3) {
+ *cp1++ = *cp3++;
+ }
+ }
+ else if (toks[toknum = *cp2 - '1']) {
+ char *cp3 = tp[toknum];
+
+ while (cp3 != te[toknum]) {
+ *cp1++ = *cp3++;
+ }
+ }
+ break;
+ }
+ /* intentional drop through */
+ default:
+ *cp1++ = *cp2;
+ break;
+ }
+ cp2++;
+ }
+ *cp1 = '\0';
+ if (!*new) {
+ return(name);
+ }
+ return(new);
+}
+
+setsunique()
+{
+ sunique = !sunique;
+ printf("Store unique %s.\n", onoff(sunique));
+ code = sunique;
+}
+
+setrunique()
+{
+ runique = !runique;
+ printf("Receive unique %s.\n", onoff(runique));
+ code = runique;
+}
+
+/* change directory to perent directory */
+cdup()
+{
+ if (command("CDUP") == ERROR && code == 500) {
+ if (verbose)
+ printf("CDUP command not recognized, trying XCUP\n");
+ (void) command("XCUP");
+ }
+}
+
+/* restart transfer at specific point */
+restart(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern long atol();
+ if (argc != 2)
+ printf("restart: offset not specified\n");
+ else {
+ restart_point = atol(argv[1]);
+ printf("restarting at %ld. %s\n", restart_point,
+ "execute get, put or append to initiate transfer");
+ }
+}
+
+/* show remote system type */
+syst()
+{
+ (void) command("SYST");
+}
+
+macdef(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *tmp;
+ int c;
+
+ if (macnum == 16) {
+ printf("Limit of 16 macros have already been defined\n");
+ code = -1;
+ return;
+ }
+ if (argc < 2 && !another(&argc, &argv, "macro name")) {
+ printf("Usage: %s macro_name\n",argv[0]);
+ code = -1;
+ return;
+ }
+ if (interactive) {
+ printf("Enter macro line by line, terminating it with a null line\n");
+ }
+ (void) strncpy(macros[macnum].mac_name, argv[1], 8);
+ if (macnum == 0) {
+ macros[macnum].mac_start = macbuf;
+ }
+ else {
+ macros[macnum].mac_start = macros[macnum - 1].mac_end + 1;
+ }
+ tmp = macros[macnum].mac_start;
+ while (tmp != macbuf+4096) {
+ if ((c = getchar()) == EOF) {
+ printf("macdef:end of file encountered\n");
+ code = -1;
+ return;
+ }
+ if ((*tmp = c) == '\n') {
+ if (tmp == macros[macnum].mac_start) {
+ macros[macnum++].mac_end = tmp;
+ code = 0;
+ return;
+ }
+ if (*(tmp-1) == '\0') {
+ macros[macnum++].mac_end = tmp - 1;
+ code = 0;
+ return;
+ }
+ *tmp = '\0';
+ }
+ tmp++;
+ }
+ while (1) {
+ while ((c = getchar()) != '\n' && c != EOF)
+ /* LOOP */;
+ if (c == EOF || getchar() == '\n') {
+ printf("Macro not defined - 4k buffer exceeded\n");
+ code = -1;
+ return;
+ }
+ }
+}
+
+/*
+ * get size of file on remote machine
+ */
+sizecmd(argc, argv)
+ int argc;
+ char *argv[];
+{
+
+ if (argc < 2 && !another(&argc, &argv, "filename")) {
+ printf("usage: %s filename\n", argv[0]);
+ code = -1;
+ return;
+ }
+ (void) command("SIZE %s", argv[1]);
+}
+
+/*
+ * get last modification time of file on remote machine
+ */
+modtime(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int overbose;
+
+ if (argc < 2 && !another(&argc, &argv, "filename")) {
+ printf("usage: %s filename\n", argv[0]);
+ code = -1;
+ return;
+ }
+ overbose = verbose;
+ if (debug == 0)
+ verbose = -1;
+ if (command("MDTM %s", argv[1]) == COMPLETE) {
+ int yy, mo, day, hour, min, sec;
+ sscanf(reply_string, "%*s %04d%02d%02d%02d%02d%02d", &yy, &mo,
+ &day, &hour, &min, &sec);
+ /* might want to print this in local time */
+ printf("%s\t%02d/%02d/%04d %02d:%02d:%02d GMT\n", argv[1],
+ mo, day, yy, hour, min, sec);
+ } else
+ printf("%s\n", reply_string);
+ verbose = overbose;
+}
+
+/*
+ * show status on reomte machine
+ */
+rmtstatus(argc, argv)
+ int argc;
+ char *argv[];
+{
+ (void) command(argc > 1 ? "STAT %s" : "STAT" , argv[1]);
+}
+
+/*
+ * get file if modtime is more recent than current file
+ */
+newer(argc, argv)
+ int argc;
+ char *argv[];
+{
+ if (getit(argc, argv, -1, "w"))
+ printf("Local file \"%s\" is newer than remote file \"%s\"\n",
+ argv[1], argv[2]);
+}
+
+#ifndef NO_PASSIVE_MODE
+/*
+ * Start up passive mode interaction
+ */
+
+/*VARARGS*/
+setpassive()
+{
+
+ passivemode = !passivemode;
+ printf("Passive mode %s.\n", onoff(passivemode));
+ code = passivemode;
+}
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1985, 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmdtab.c 5.10 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+#include <stdio.h>
+#include "ftp_var.h"
+
+/*
+ * User FTP -- Command Tables.
+ */
+int setascii(), setbell(), setbinary(), setdebug(), setform();
+int setglob(), sethash(), setmode(), setpeer(), setport();
+int setprompt(), setstruct();
+int settenex(), settrace(), settype(), setverbose();
+int setlevel(), setclear(), setsafe();
+#ifndef NOENCRYPTION
+int setprivate();
+#endif
+int disconnect(), restart(), reget(), syst();
+int cd(), lcd(), delete(), mdelete(), user();
+int ls(), mls(), get(), mget(), help(), append(), put(), mput();
+int quit(), renamefile(), status();
+int quote(), rmthelp(), shell(), site();
+int pwd(), makedir(), removedir(), setcr();
+int account(), doproxy(), reset(), setcase(), setntrans(), setnmap();
+int setsunique(), setrunique(), cdup(), macdef(), domacro();
+int sizecmd(), modtime(), newer(), rmtstatus();
+int do_chmod(), do_umask(), siteidle();
+#ifndef NO_PASSIVE_MODE
+int setpassive();
+#endif
+
+char accounthelp[] = "send account command to remote server";
+char appendhelp[] = "append to a file";
+char asciihelp[] = "set ascii transfer type";
+char beephelp[] = "beep when command completed";
+char binaryhelp[] = "set binary transfer type";
+char casehelp[] = "toggle mget upper/lower case id mapping";
+char cdhelp[] = "change remote working directory";
+char cduphelp[] = "change remote working directory to parent directory";
+char chmodhelp[] = "change file permissions of remote file";
+char clearhelp[] = "set clear protection level for file transfer";
+char connecthelp[] = "connect to remote ftp";
+char crhelp[] = "toggle carriage return stripping on ascii gets";
+char deletehelp[] = "delete remote file";
+char debughelp[] = "toggle/set debugging mode";
+char dirhelp[] = "list contents of remote directory";
+char disconhelp[] = "terminate ftp session";
+char domachelp[] = "execute macro";
+char formhelp[] = "set file transfer format";
+char globhelp[] = "toggle metacharacter expansion of local file names";
+char hashhelp[] = "toggle printing `#' for each buffer transferred";
+char helphelp[] = "print local help information";
+char idlehelp[] = "get (set) idle timer on remote side";
+char lcdhelp[] = "change local working directory";
+char levelhelp[] = "set protection level for file transfer";
+char lshelp[] = "list contents of remote directory";
+char macdefhelp[] = "define a macro";
+char mdeletehelp[] = "delete multiple files";
+char mdirhelp[] = "list contents of multiple remote directories";
+char mgethelp[] = "get multiple files";
+char mkdirhelp[] = "make directory on the remote machine";
+char mlshelp[] = "list contents of multiple remote directories";
+char modtimehelp[] = "show last modification time of remote file";
+char modehelp[] = "set file transfer mode";
+char mputhelp[] = "send multiple files";
+char newerhelp[] = "get file if remote file is newer than local file ";
+char nlisthelp[] = "nlist contents of remote directory";
+char nmaphelp[] = "set templates for default file name mapping";
+char ntranshelp[] = "set translation table for default file name mapping";
+char porthelp[] = "toggle use of PORT cmd for each data connection";
+#ifndef NOENCRYPTION
+char privatehelp[] = "set private protection level for file transfer";
+#endif
+char prompthelp[] = "force interactive prompting on multiple commands";
+char proxyhelp[] = "issue command on alternate connection";
+char pwdhelp[] = "print working directory on remote machine";
+char quithelp[] = "terminate ftp session and exit";
+char quotehelp[] = "send arbitrary ftp command";
+char receivehelp[] = "receive file";
+char regethelp[] = "get file restarting at end of local file";
+char remotehelp[] = "get help from remote server";
+char renamehelp[] = "rename file";
+char restarthelp[]= "restart file transfer at bytecount";
+char rmdirhelp[] = "remove directory on the remote machine";
+char rmtstatushelp[]="show status of remote machine";
+char runiquehelp[] = "toggle store unique for local files";
+char resethelp[] = "clear queued command replies";
+char safehelp[] = "set safe protection level for file transfer";
+char sendhelp[] = "send one file";
+char sitehelp[] = "send site specific command to remote server\n\t\tTry \"rhelp site\" or \"site help\" for more information";
+char shellhelp[] = "escape to the shell";
+char sizecmdhelp[] = "show size of remote file";
+char statushelp[] = "show current status";
+char structhelp[] = "set file transfer structure";
+char suniquehelp[] = "toggle store unique on remote machine";
+char systemhelp[] = "show remote system type";
+char tenexhelp[] = "set tenex file transfer type";
+char tracehelp[] = "toggle packet tracing";
+char typehelp[] = "set file transfer type";
+char umaskhelp[] = "get (set) umask on remote side";
+char userhelp[] = "send new user information";
+char verbosehelp[] = "toggle verbose mode";
+#ifndef NO_PASSIVE_MODE
+char setpassivehelp[] = "enter passive transfer mode";
+#endif
+
+struct cmd cmdtab[] = {
+ { "!", shellhelp, 0, 0, 0, shell },
+ { "$", domachelp, 1, 0, 0, domacro },
+ { "account", accounthelp, 0, 1, 1, account},
+ { "append", appendhelp, 1, 1, 1, put },
+ { "ascii", asciihelp, 0, 1, 1, setascii },
+ { "bell", beephelp, 0, 0, 0, setbell },
+ { "binary", binaryhelp, 0, 1, 1, setbinary },
+ { "bye", quithelp, 0, 0, 0, quit },
+ { "case", casehelp, 0, 0, 1, setcase },
+ { "cd", cdhelp, 0, 1, 1, cd },
+ { "cdup", cduphelp, 0, 1, 1, cdup },
+ { "chmod", chmodhelp, 0, 1, 1, do_chmod },
+ { "clear", clearhelp, 0, 1, 1, setclear },
+ { "close", disconhelp, 0, 1, 1, disconnect },
+ { "cr", crhelp, 0, 0, 0, setcr },
+ { "delete", deletehelp, 0, 1, 1, delete },
+ { "debug", debughelp, 0, 0, 0, setdebug },
+ { "dir", dirhelp, 1, 1, 1, ls },
+ { "disconnect", disconhelp, 0, 1, 1, disconnect },
+ { "form", formhelp, 0, 1, 1, setform },
+ { "get", receivehelp, 1, 1, 1, get },
+ { "glob", globhelp, 0, 0, 0, setglob },
+ { "hash", hashhelp, 0, 0, 0, sethash },
+ { "help", helphelp, 0, 0, 1, help },
+ { "idle", idlehelp, 0, 1, 1, siteidle },
+ { "image", binaryhelp, 0, 1, 1, setbinary },
+ { "lcd", lcdhelp, 0, 0, 0, lcd },
+ { "ls", lshelp, 1, 1, 1, ls },
+ { "macdef", macdefhelp, 0, 0, 0, macdef },
+ { "mdelete", mdeletehelp, 1, 1, 1, mdelete },
+ { "mdir", mdirhelp, 1, 1, 1, mls },
+ { "mget", mgethelp, 1, 1, 1, mget },
+ { "mkdir", mkdirhelp, 0, 1, 1, makedir },
+ { "mls", mlshelp, 1, 1, 1, mls },
+ { "mode", modehelp, 0, 1, 1, setmode },
+ { "modtime", modtimehelp, 0, 1, 1, modtime },
+ { "mput", mputhelp, 1, 1, 1, mput },
+ { "newer", newerhelp, 1, 1, 1, newer },
+ { "nmap", nmaphelp, 0, 0, 1, setnmap },
+ { "nlist", nlisthelp, 1, 1, 1, ls },
+ { "ntrans", ntranshelp, 0, 0, 1, setntrans },
+ { "open", connecthelp, 0, 0, 1, setpeer },
+#ifndef NO_PASSIVE_MODE
+ { "passive", setpassivehelp, 0, 0, 0, setpassive },
+#endif
+#ifndef NOENCRYPTION
+ { "private", privatehelp, 0, 1, 1, setprivate },
+#endif
+ { "prompt", prompthelp, 0, 0, 0, setprompt },
+ { "protect", levelhelp, 0, 1, 1, setlevel },
+ { "proxy", proxyhelp, 0, 0, 1, doproxy },
+ { "sendport", porthelp, 0, 0, 0, setport },
+ { "put", sendhelp, 1, 1, 1, put },
+ { "pwd", pwdhelp, 0, 1, 1, pwd },
+ { "quit", quithelp, 0, 0, 0, quit },
+ { "quote", quotehelp, 1, 1, 1, quote },
+ { "recv", receivehelp, 1, 1, 1, get },
+ { "reget", regethelp, 1, 1, 1, reget },
+ { "rstatus", rmtstatushelp, 0, 1, 1, rmtstatus },
+ { "rhelp", remotehelp, 0, 1, 1, rmthelp },
+ { "rename", renamehelp, 0, 1, 1, renamefile },
+ { "reset", resethelp, 0, 1, 1, reset },
+ { "restart", restarthelp, 1, 1, 1, restart },
+ { "rmdir", rmdirhelp, 0, 1, 1, removedir },
+ { "runique", runiquehelp, 0, 0, 1, setrunique },
+ { "safe", safehelp, 0, 1, 1, setsafe },
+ { "send", sendhelp, 1, 1, 1, put },
+ { "site", sitehelp, 0, 1, 1, site },
+ { "size", sizecmdhelp, 1, 1, 1, sizecmd },
+ { "status", statushelp, 0, 0, 1, status },
+ { "struct", structhelp, 0, 1, 1, setstruct },
+ { "system", systemhelp, 0, 1, 1, syst },
+ { "sunique", suniquehelp, 0, 0, 1, setsunique },
+ { "tenex", tenexhelp, 0, 1, 1, settenex },
+ { "trace", tracehelp, 0, 0, 0, settrace },
+ { "type", typehelp, 0, 1, 1, settype },
+ { "user", userhelp, 0, 1, 1, user },
+ { "umask", umaskhelp, 0, 1, 1, do_umask },
+ { "verbose", verbosehelp, 0, 0, 0, setverbose },
+ { "?", helphelp, 0, 0, 1, help },
+ { 0 },
+};
+
+int NCMDS = (sizeof (cmdtab) / sizeof (cmdtab[0])) - 1;
--- /dev/null
+AC_INIT(ftp.c)
+CONFIG_RULES
+AC_CONST
+AC_PROG_INSTALL
+KRB5_SIGTYPE
+CHECK_SIGPROCMASK
+CHECK_WAIT_TYPE
+AC_FUNC_VFORK
+AC_HAVE_FUNCS(getcwd getdtablesize)
+AC_HEADER_CHECK(termios.h,AC_FUNC_CHECK(cfsetispeed,AC_DEFINE(POSIX_TERMIOS)))
+V5_USE_SHARED_LIB
+V5_AC_OUTPUT_MAKEFILE
--- /dev/null
+/*
+ * Copyright (c) 1985 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)domacro.c 1.8 (Berkeley) 9/28/90";
+#endif /* not lint */
+
+#include <stdio.h>
+
+#include "ftp_var.h"
+
+#include <signal.h>
+#include <errno.h>
+#include <ctype.h>
+#include <string.h>
+
+domacro(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int i, j;
+ register char *cp1, *cp2;
+ int count = 2, loopflg = 0;
+ char line2[200];
+ extern char **glob(), *globerr;
+ struct cmd *getcmd(), *c;
+ extern struct cmd cmdtab[];
+
+ if (argc < 2 && !another(&argc, &argv, "macro name")) {
+ printf("Usage: %s macro_name.\n", argv[0]);
+ code = -1;
+ return;
+ }
+ for (i = 0; i < macnum; ++i) {
+ if (!strncmp(argv[1], macros[i].mac_name, 9)) {
+ break;
+ }
+ }
+ if (i == macnum) {
+ printf("'%s' macro not found.\n", argv[1]);
+ code = -1;
+ return;
+ }
+ (void) strcpy(line2, line);
+TOP:
+ cp1 = macros[i].mac_start;
+ while (cp1 != macros[i].mac_end) {
+ while (isspace(*cp1)) {
+ cp1++;
+ }
+ cp2 = line;
+ while (*cp1 != '\0') {
+ switch(*cp1) {
+ case '\\':
+ *cp2++ = *++cp1;
+ break;
+ case '$':
+ if (isdigit(*(cp1+1))) {
+ j = 0;
+ while (isdigit(*++cp1)) {
+ j = 10*j + *cp1 - '0';
+ }
+ cp1--;
+ if (argc - 2 >= j) {
+ (void) strcpy(cp2, argv[j+1]);
+ cp2 += strlen(argv[j+1]);
+ }
+ break;
+ }
+ if (*(cp1+1) == 'i') {
+ loopflg = 1;
+ cp1++;
+ if (count < argc) {
+ (void) strcpy(cp2, argv[count]);
+ cp2 += strlen(argv[count]);
+ }
+ break;
+ }
+ /* intentional drop through */
+ default:
+ *cp2++ = *cp1;
+ break;
+ }
+ if (*cp1 != '\0') {
+ cp1++;
+ }
+ }
+ *cp2 = '\0';
+ makeargv();
+ c = getcmd(margv[0]);
+ if (c == (struct cmd *)-1) {
+ printf("?Ambiguous command\n");
+ code = -1;
+ }
+ else if (c == 0) {
+ printf("?Invalid command\n");
+ code = -1;
+ }
+ else if (c->c_conn && !connected) {
+ printf("Not connected.\n");
+ code = -1;
+ }
+ else {
+ if (verbose) {
+ printf("%s\n",line);
+ }
+ (*c->c_handler)(margc, margv);
+ if (bell && c->c_bell) {
+ (void) putchar('\007');
+ }
+ (void) strcpy(line, line2);
+ makeargv();
+ argc = margc;
+ argv = margv;
+ }
+ if (cp1 != macros[i].mac_end) {
+ cp1++;
+ }
+ }
+ if (loopflg && ++count < argc) {
+ goto TOP;
+ }
+}
--- /dev/null
+.\" Copyright (c) 1985, 1989, 1990 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ftp.1 6.18 (Berkeley) 7/30/91
+.\"
+.so man1/tmac.doc
+.Dd July 30, 1991
+.Dt FTP 1
+.Os BSD 4.2
+.Sh NAME
+.Nm ftp
+.Nd
+.Tn ARPANET
+file transfer program
+.Sh SYNOPSIS
+.Nm ftp
+.Op Fl v
+.Op Fl d
+.Op Fl i
+.Op Fl n
+.Op Fl g
+.Op Fl k Ar realm
+.Op Ar host
+.Sh DESCRIPTION
+.Nm Ftp
+is the user interface to the
+.Tn ARPANET
+standard File Transfer Protocol.
+The program allows a user to transfer files to and from a
+remote network site.
+.Pp
+Options may be specified at the command line, or to the
+command interpreter.
+.Bl -tag -width flag
+.It Fl v
+Verbose option forces
+.Nm ftp
+to show all responses from the remote server, as well
+as report on data transfer statistics.
+.It Fl n
+Restrains
+.Nm ftp
+from attempting \*(Lqauto-login\*(Rq upon initial connection.
+If auto-login is enabled,
+.Nm ftp
+attempts to authenticate to the
+.Tn FTP
+server by sending the
+.Dv AUTH
+command, using whichever authentication types are locally supported.
+Once an authentication type is accepted, an authentication protocol
+will proceed by issuing
+.Dv ADAT
+commands.
+.Nm ftp
+then
+will check the
+.Pa .netrc
+(see below) file in the user's home directory for an entry describing
+an account on the remote machine.
+If no entry exists,
+.Nm ftp
+will prompt for the remote machine login name (default is the user
+identity on the local machine), and, if necessary, prompt for a password
+and an account with which to login.
+.It Fl i
+Turns off interactive prompting during
+multiple file transfers.
+.It Fl d
+Enables debugging.
+.It Fl g
+Disables file name globbing.
+.It Fl k Ar realm
+When using Kerberos V4 authentication, get tickets in
+.Ar realm.
+.El
+.Pp
+The client host with which
+.Nm ftp
+is to communicate may be specified on the command line.
+If this is done,
+.Nm ftp
+will immediately attempt to establish a connection to an
+.Tn FTP
+server on that host; otherwise,
+.Nm ftp
+will enter its command interpreter and await instructions
+from the user.
+When
+.Nm ftp
+is awaiting commands from the user the prompt
+.Ql ftp>
+is provided to the user.
+The following commands are recognized
+by
+.Nm ftp :
+.Bl -tag -width Fl
+.It Ic \&! Op Ar command Op Ar args
+Invoke an interactive shell on the local machine.
+If there are arguments, the first is taken to be a command to execute
+directly, with the rest of the arguments as its arguments.
+.It Ic \&$ Ar macro-name Op Ar args
+Execute the macro
+.Ar macro-name
+that was defined with the
+.Ic macdef
+command.
+Arguments are passed to the macro unglobbed.
+.It Ic account Op Ar passwd
+Supply a supplemental password required by a remote system for access
+to resources once a login has been successfully completed.
+If no argument is included, the user will be prompted for an account
+password in a non-echoing input mode.
+.It Ic append Ar local-file Op Ar remote-file
+Append a local file to a file on the remote machine.
+If
+.Ar remote-file
+is left unspecified, the local file name is used in naming the
+remote file after being altered by any
+.Ic ntrans
+or
+.Ic nmap
+setting.
+File transfer uses the current settings for
+.Ic type ,
+.Ic format ,
+.Ic mode ,
+and
+.Ic structure .
+.It Ic ascii
+Set the file transfer
+.Ic type
+to network
+.Tn ASCII .
+This is the default type.
+.It Ic bell
+Arrange that a bell be sounded after each file transfer
+command is completed.
+.It Ic binary
+Set the file transfer
+.Ic type
+to support binary image transfer.
+.It Ic bye
+Terminate the
+.Tn FTP
+session with the remote server
+and exit
+.Nm ftp .
+An end of file will also terminate the session and exit.
+.It Ic case
+Toggle remote computer file name case mapping during
+.Ic mget
+commands.
+When
+.Ic case
+is on (default is off), remote computer file names with all letters in
+upper case are written in the local directory with the letters mapped
+to lower case.
+.It Ic \&cd Ar remote-directory
+Change the working directory on the remote machine
+to
+.Ar remote-directory .
+.It Ic cdup
+Change the remote machine working directory to the parent of the
+current remote machine working directory.
+.It Ic chmod Ar mode file-name
+Change the permission modes of the file
+.Ar file-name
+on the remote
+system to
+.Ar mode .
+.It Ic clear
+Set the protection level on data transfers to \*(Lqclear\*(Rq.
+If no
+.Dv ADAT
+command succeeded, then this is the default protection level.
+.It Ic close
+Terminate the
+.Tn FTP
+session with the remote server, and
+return to the command interpreter.
+Any defined macros are erased.
+.It Ic \&cr
+Toggle carriage return stripping during
+ascii type file retrieval.
+Records are denoted by a carriage return/linefeed sequence
+during ascii type file transfer.
+When
+.Ic \&cr
+is on (the default), carriage returns are stripped from this
+sequence to conform with the
+.Ux
+single linefeed record
+delimiter.
+Records on
+.Pf non\- Ns Ux
+remote systems may contain single linefeeds;
+when an ascii type transfer is made, these linefeeds may be
+distinguished from a record delimiter only when
+.Ic \&cr
+is off.
+.It Ic delete Ar remote-file
+Delete the file
+.Ar remote-file
+on the remote machine.
+.It Ic debug Op Ar debug-value
+Toggle debugging mode.
+If an optional
+.Ar debug-value
+is specified it is used to set the debugging level.
+When debugging is on,
+.Nm ftp
+prints each command sent to the remote machine, preceded
+by the string
+.Ql \-\->
+.It Xo
+.Ic dir
+.Op Ar remote-directory
+.Op Ar local-file
+.Xc
+Print a listing of the directory contents in the
+directory,
+.Ar remote-directory ,
+and, optionally, placing the output in
+.Ar local-file .
+If interactive prompting is on,
+.Nm ftp
+will prompt the user to verify that the last argument is indeed the
+target local file for receiving
+.Ic dir
+output.
+If no directory is specified, the current working
+directory on the remote machine is used.
+If no local
+file is specified, or
+.Ar local-file
+is
+.Fl ,
+output comes to the terminal.
+.It Ic disconnect
+A synonym for
+.Ar close .
+.It Ic form Ar format
+Set the file transfer
+.Ic form
+to
+.Ar format .
+The default format is \*(Lqfile\*(Rq.
+.It Ic get Ar remote-file Op Ar local-file
+Retrieve the
+.Ar remote-file
+and store it on the local machine.
+If the local
+file name is not specified, it is given the same
+name it has on the remote machine, subject to
+alteration by the current
+.Ic case ,
+.Ic ntrans ,
+and
+.Ic nmap
+settings.
+The current settings for
+.Ic type ,
+.Ic form ,
+.Ic mode ,
+and
+.Ic structure
+are used while transferring the file.
+.It Ic glob
+Toggle filename expansion for
+.Ic mdelete ,
+.Ic mget
+and
+.Ic mput .
+If globbing is turned off with
+.Ic glob ,
+the file name arguments
+are taken literally and not expanded.
+Globbing for
+.Ic mput
+is done as in
+.Xr csh 1 .
+For
+.Ic mdelete
+and
+.Ic mget ,
+each remote file name is expanded
+separately on the remote machine and the lists are not merged.
+Expansion of a directory name is likely to be
+different from expansion of the name of an ordinary file:
+the exact result depends on the foreign operating system and ftp server,
+and can be previewed by doing
+.Ql mls remote-files \-
+Note:
+.Ic mget
+and
+.Ic mput
+are not meant to transfer
+entire directory subtrees of files.
+That can be done by
+transferring a
+.Xr tar 1
+archive of the subtree (in binary mode).
+.It Ic hash
+Toggle hash-sign (``#'') printing for each data block
+transferred.
+The size of a data block is 1024 bytes.
+.It Ic help Op Ar command
+Print an informative message about the meaning of
+.Ar command .
+If no argument is given,
+.Nm ftp
+prints a list of the known commands.
+.It Ic idle Op Ar seconds
+Set the inactivity timer on the remote server to
+.Ar seconds
+seconds.
+If
+.Ar seconds
+is omitted, the current inactivity timer is printed.
+.It Ic lcd Op Ar directory
+Change the working directory on the local machine.
+If
+no
+.Ar directory
+is specified, the user's home directory is used.
+.It Xo
+.Ic \&ls
+.Op Ar remote-directory
+.Op Ar local-file
+.Xc
+Print a listing of the contents of a
+directory on the remote machine.
+The listing includes any system-dependent information that the server
+chooses to include; for example, most
+.Ux
+systems will produce
+output from the command
+.Ql ls \-l .
+(See also
+.Ic nlist . )
+If
+.Ar remote-directory
+is left unspecified, the current working directory is used.
+If interactive prompting is on,
+.Nm ftp
+will prompt the user to verify that the last argument is indeed the
+target local file for receiving
+.Ic \&ls
+output.
+If no local file is specified, or if
+.Ar local-file
+is
+.Sq Fl ,
+the output is sent to the terminal.
+.It Ic macdef Ns Ar macro-name
+Define a macro.
+Subsequent lines are stored as the macro
+.Ar macro-name ;
+a null line (consecutive newline characters
+in a file or
+carriage returns from the terminal) terminates macro input mode.
+There is a limit of 16 macros and 4096 total characters in all
+defined macros.
+Macros remain defined until a
+.Ic close
+command is executed.
+The macro processor interprets `$' and `\e' as special characters.
+A `$' followed by a number (or numbers) is replaced by the
+corresponding argument on the macro invocation command line.
+A `$' followed by an `i' signals that macro processor that the
+executing macro is to be looped.
+On the first pass `$i' is
+replaced by the first argument on the macro invocation command line,
+on the second pass it is replaced by the second argument, and so on.
+A `\e' followed by any character is replaced by that character.
+Use the `\e' to prevent special treatment of the `$'.
+.It Ic mdelete Op Ar remote-files
+Delete the
+.Ar remote-files
+on the remote machine.
+.It Ic mdir Ar remote-files local-file
+Like
+.Ic dir ,
+except multiple remote files may be specified.
+If interactive prompting is on,
+.Nm ftp
+will prompt the user to verify that the last argument is indeed the
+target local file for receiving
+.Ic mdir
+output.
+.It Ic mget Ar remote-files
+Expand the
+.Ar remote-files
+on the remote machine
+and do a
+.Ic get
+for each file name thus produced.
+See
+.Ic glob
+for details on the filename expansion.
+Resulting file names will then be processed according to
+.Ic case ,
+.Ic ntrans ,
+and
+.Ic nmap
+settings.
+Files are transferred into the local working directory,
+which can be changed with
+.Ql lcd directory ;
+new local directories can be created with
+.Ql "\&! mkdir directory" .
+.It Ic mkdir Ar directory-name
+Make a directory on the remote machine.
+.It Ic mls Ar remote-files local-file
+Like
+.Ic nlist ,
+except multiple remote files may be specified,
+and the
+.Ar local-file
+must be specified.
+If interactive prompting is on,
+.Nm ftp
+will prompt the user to verify that the last argument is indeed the
+target local file for receiving
+.Ic mls
+output.
+.It Ic mode Op Ar mode-name
+Set the file transfer
+.Ic mode
+to
+.Ar mode-name .
+The default mode is \*(Lqstream\*(Rq mode.
+.It Ic modtime Ar file-name
+Show the last modification time of the file on the remote machine.
+.It Ic mput Ar local-files
+Expand wild cards in the list of local files given as arguments
+and do a
+.Ic put
+for each file in the resulting list.
+See
+.Ic glob
+for details of filename expansion.
+Resulting file names will then be processed according to
+.Ic ntrans
+and
+.Ic nmap
+settings.
+.It Ic newer Ar file-name
+Get the file only if the modification time of the remote file is more
+recent that the file on the current system.
+If the file does not
+exist on the current system, the remote file is considered
+.Ic newer .
+Otherwise, this command is identical to
+.Ar get .
+.It Xo
+.Ic nlist
+.Op Ar remote-directory
+.Op Ar local-file
+.Xc
+Print a list of the files in a
+directory on the remote machine.
+If
+.Ar remote-directory
+is left unspecified, the current working directory is used.
+If interactive prompting is on,
+.Nm ftp
+will prompt the user to verify that the last argument is indeed the
+target local file for receiving
+.Ic nlist
+output.
+If no local file is specified, or if
+.Ar local-file
+is
+.Fl ,
+the output is sent to the terminal.
+.It Ic nmap Op Ar inpattern outpattern
+Set or unset the filename mapping mechanism.
+If no arguments are specified, the filename mapping mechanism is unset.
+If arguments are specified, remote filenames are mapped during
+.Ic mput
+commands and
+.Ic put
+commands issued without a specified remote target filename.
+If arguments are specified, local filenames are mapped during
+.Ic mget
+commands and
+.Ic get
+commands issued without a specified local target filename.
+This command is useful when connecting to a
+.No non\- Ns Ux
+remote computer
+with different file naming conventions or practices.
+The mapping follows the pattern set by
+.Ar inpattern
+and
+.Ar outpattern .
+.Op Ar Inpattern
+is a template for incoming filenames (which may have already been
+processed according to the
+.Ic ntrans
+and
+.Ic case
+settings).
+Variable templating is accomplished by including the
+sequences `$1', `$2', ..., `$9' in
+.Ar inpattern .
+Use `\\' to prevent this special treatment of the `$' character.
+All other characters are treated literally, and are used to determine the
+.Ic nmap
+.Op Ar inpattern
+variable values.
+For example, given
+.Ar inpattern
+$1.$2 and the remote file name "mydata.data", $1 would have the value
+"mydata", and $2 would have the value "data".
+The
+.Ar outpattern
+determines the resulting mapped filename.
+The sequences `$1', `$2', ...., `$9' are replaced by any value resulting
+from the
+.Ar inpattern
+template.
+The sequence `$0' is replace by the original filename.
+Additionally, the sequence
+.Ql Op Ar seq1 , Ar seq2
+is replaced by
+.Op Ar seq1
+if
+.Ar seq1
+is not a null string; otherwise it is replaced by
+.Ar seq2 .
+For example, the command
+.Pp
+.Bd -literal -offset indent -compact
+nmap $1.$2.$3 [$1,$2].[$2,file]
+.Ed
+.Pp
+would yield
+the output filename "myfile.data" for input filenames "myfile.data" and
+"myfile.data.old", "myfile.file" for the input filename "myfile", and
+"myfile.myfile" for the input filename ".myfile".
+Spaces may be included in
+.Ar outpattern ,
+as in the example: `nmap $1 sed "s/ *$//" > $1' .
+Use the `\e' character to prevent special treatment
+of the `$','[','[', and `,' characters.
+.It Ic ntrans Op Ar inchars Op Ar outchars
+Set or unset the filename character translation mechanism.
+If no arguments are specified, the filename character
+translation mechanism is unset.
+If arguments are specified, characters in
+remote filenames are translated during
+.Ic mput
+commands and
+.Ic put
+commands issued without a specified remote target filename.
+If arguments are specified, characters in
+local filenames are translated during
+.Ic mget
+commands and
+.Ic get
+commands issued without a specified local target filename.
+This command is useful when connecting to a
+.No non\- Ns Ux
+remote computer
+with different file naming conventions or practices.
+Characters in a filename matching a character in
+.Ar inchars
+are replaced with the corresponding character in
+.Ar outchars .
+If the character's position in
+.Ar inchars
+is longer than the length of
+.Ar outchars ,
+the character is deleted from the file name.
+.It Ic open Ar host Op Ar port
+Establish a connection to the specified
+.Ar host
+.Tn FTP
+server.
+An optional port number may be supplied,
+in which case,
+.Nm ftp
+will attempt to contact an
+.Tn FTP
+server at that port.
+If the
+.Ic auto-login
+option is on (default),
+.Nm ftp
+will attempt to authenticate to the
+.Tn FTP
+server by sending the
+.Dv AUTH
+command, using whichever authentication types which are locally supported.
+Once an authentication type is accepted, an authentication protocol
+will proceed by issuing
+.Dv ADAT
+commands.
+.Nm ftp
+will also attempt to automatically log the user in to
+the
+.Tn FTP
+server (see below).
+.It Ic private
+Set the protection level on data transfers to \*(Lqprivate\*(Rq.
+Data transmissions will now be
+confidentiality and integrity protected by encryption.
+If no
+.Dv ADAT
+command succeeded, then the only possible level is \*(Lqclear\*(Rq.
+.It Ic prompt
+Toggle interactive prompting.
+Interactive prompting
+occurs during multiple file transfers to allow the
+user to selectively retrieve or store files.
+If prompting is turned off (default is on), any
+.Ic mget
+or
+.Ic mput
+will transfer all files, and any
+.Ic mdelete
+will delete all files.
+.It Ic protect Op Ar protection-level
+Set the protection level on data transfers to
+.Ar protection-level .
+The valid protection levels are \*(Lqclear\*(Rq
+for unprotected data transmissions, \*(Lqsafe\*(Rq
+for data transmissions integrity
+protected by cryptographic checksum, and \*(Lqprivate\*(Rq
+for data transmissions confidentiality and integrity
+protected by encryption.
+If no
+.Dv ADAT
+command succeeded, then the only possible level is \*(Lqclear\*(Rq.
+If no level is specified, the current level is printed.
+The default protection level is \*(Lqclear\*(Rq.
+.It Ic proxy Ar ftp-command
+Execute an ftp command on a secondary control connection.
+This command allows simultaneous connection to two remote ftp
+servers for transferring files between the two servers.
+The first
+.Ic proxy
+command should be an
+.Ic open ,
+to establish the secondary control connection.
+Enter the command "proxy ?" to see other ftp commands executable on the
+secondary connection.
+The following commands behave differently when prefaced by
+.Ic proxy :
+.Ic open
+will not define new macros during the auto-login process,
+.Ic close
+will not erase existing macro definitions,
+.Ic get
+and
+.Ic mget
+transfer files from the host on the primary control connection
+to the host on the secondary control connection, and
+.Ic put ,
+.Ic mput ,
+and
+.Ic append
+transfer files from the host on the secondary control connection
+to the host on the primary control connection.
+Third party file transfers depend upon support of the ftp protocol
+.Dv PASV
+command by the server on the secondary control connection.
+.It Ic put Ar local-file Op Ar remote-file
+Store a local file on the remote machine.
+If
+.Ar remote-file
+is left unspecified, the local file name is used
+after processing according to any
+.Ic ntrans
+or
+.Ic nmap
+settings
+in naming the remote file.
+File transfer uses the
+current settings for
+.Ic type ,
+.Ic format ,
+.Ic mode ,
+and
+.Ic structure .
+.It Ic pwd
+Print the name of the current working directory on the remote
+machine.
+.It Ic quit
+A synonym for
+.Ic bye .
+.It Ic quote Ar arg1 arg2 ...
+The arguments specified are sent, verbatim, to the remote
+.Tn FTP
+server.
+.It Ic recv Ar remote-file Op Ar local-file
+A synonym for get.
+.It Ic reget Ar remote-file Op Ar local-file
+Reget acts like get, except that if
+.Ar local-file
+exists and is
+smaller than
+.Ar remote-file ,
+.Ar local-file
+is presumed to be
+a partially transferred copy of
+.Ar remote-file
+and the transfer
+is continued from the apparent point of failure.
+This command
+is useful when transferring very large files over networks that
+are prone to dropping connections.
+.It Ic remotehelp Op Ar command-name
+Request help from the remote
+.Tn FTP
+server.
+If a
+.Ar command-name
+is specified it is supplied to the server as well.
+.It Ic remotestatus Op Ar file-name
+With no arguments, show status of remote machine.
+If
+.Ar file-name
+is specified, show status of
+.Ar file-name
+on remote machine.
+.It Xo
+.Ic rename
+.Op Ar from
+.Op Ar to
+.Xc
+Rename the file
+.Ar from
+on the remote machine, to the file
+.Ar to .
+.It Ic reset
+Clear reply queue.
+This command re-synchronizes command/reply sequencing with the remote
+ftp server.
+Resynchronization may be necessary following a violation of the ftp protocol
+by the remote server.
+.It Ic restart Ar marker
+Restart the immediately following
+.Ic get
+or
+.Ic put
+at the
+indicated
+.Ar marker .
+On
+.Ux
+systems, marker is usually a byte
+offset into the file.
+.It Ic rmdir Ar directory-name
+Delete a directory on the remote machine.
+.It Ic runique
+Toggle storing of files on the local system with unique filenames.
+If a file already exists with a name equal to the target
+local filename for a
+.Ic get
+or
+.Ic mget
+command, a ".1" is appended to the name.
+If the resulting name matches another existing file,
+a ".2" is appended to the original name.
+If this process continues up to ".99", an error
+message is printed, and the transfer does not take place.
+The generated unique filename will be reported.
+Note that
+.Ic runique
+will not affect local files generated from a shell command
+(see below).
+The default value is off.
+.It Ic safe
+Set the protection level on data transfers to \*(Lqsafe\*(Rq.
+Data transmissions will now be
+integrity protected by cryptographic checksum.
+If no
+.Dv ADAT
+command succeeded, then the only possible level is \*(Lqclear\*(Rq.
+.It Ic send Ar local-file Op Ar remote-file
+A synonym for put.
+.It Ic sendport
+Toggle the use of
+.Dv PORT
+commands.
+By default,
+.Nm ftp
+will attempt to use a
+.Dv PORT
+command when establishing
+a connection for each data transfer.
+The use of
+.Dv PORT
+commands can prevent delays
+when performing multiple file transfers.
+If the
+.Dv PORT
+command fails,
+.Nm ftp
+will use the default data port.
+When the use of
+.Dv PORT
+commands is disabled, no attempt will be made to use
+.Dv PORT
+commands for each data transfer.
+This is useful
+for certain
+.Tn FTP
+implementations which do ignore
+.Dv PORT
+commands but, incorrectly, indicate they've been accepted.
+.It Ic site Ar arg1 arg2 ...
+The arguments specified are sent, verbatim, to the remote
+.Tn FTP
+server as a
+.Dv SITE
+command.
+.It Ic size Ar file-name
+Return size of
+.Ar file-name
+on remote machine.
+.It Ic status
+Show the current status of
+.Nm ftp .
+.It Ic struct Op Ar struct-name
+Set the file transfer
+.Ar structure
+to
+.Ar struct-name .
+By default \*(Lqstream\*(Rq structure is used.
+.It Ic sunique
+Toggle storing of files on remote machine under unique file names.
+Remote ftp server must support ftp protocol
+.Dv STOU
+command for
+successful completion.
+The remote server will report unique name.
+Default value is off.
+.It Ic system
+Show the type of operating system running on the remote machine.
+.It Ic tenex
+Set the file transfer type to that needed to
+talk to
+.Tn TENEX
+machines.
+.It Ic trace
+Toggle packet tracing.
+.It Ic type Op Ar type-name
+Set the file transfer
+.Ic type
+to
+.Ar type-name .
+If no type is specified, the current type
+is printed.
+The default type is network
+.Tn ASCII .
+.It Ic umask Op Ar newmask
+Set the default umask on the remote server to
+.Ar newmask .
+If
+.Ar newmask
+is omitted, the current umask is printed.
+.It Xo
+.Ic user Ar user-name
+.Op Ar password
+.Op Ar account
+.Xc
+Identify yourself to the remote
+.Tn FTP
+server.
+If the
+.Ar password
+is not specified and the server requires it,
+.Nm ftp
+will prompt the user for it (after disabling local echo).
+If an
+.Ar account
+field is not specified, and the
+.Tn FTP
+server
+requires it, the user will be prompted for it.
+If an
+.Ar account
+field is specified, an account command will
+be relayed to the remote server after the login sequence
+is completed if the remote server did not require it
+for logging in.
+Unless
+.Nm ftp
+is invoked with \*(Lqauto-login\*(Rq disabled, this
+process is done automatically on initial connection to
+the
+.Tn FTP
+server.
+.It Ic verbose
+Toggle verbose mode.
+In verbose mode, all responses from
+the
+.Tn FTP
+server are displayed to the user.
+In addition,
+if verbose is on, when a file transfer completes, statistics
+regarding the efficiency of the transfer are reported.
+By default,
+verbose is on.
+.It Ic ? Op Ar command
+A synonym for help.
+.El
+.Pp
+Command arguments which have embedded spaces may be quoted with
+quote `"' marks.
+.Sh ABORTING A FILE TRANSFER
+To abort a file transfer, use the terminal interrupt key
+(usually Ctrl-C).
+Sending transfers will be immediately halted.
+Receiving transfers will be halted by sending a ftp protocol
+.Dv ABOR
+command to the remote server, and discarding any further data received.
+The speed at which this is accomplished depends upon the remote
+server's support for
+.Dv ABOR
+processing.
+If the remote server does not support the
+.Dv ABOR
+command, an
+.Ql ftp>
+prompt will not appear until the remote server has completed
+sending the requested file.
+.Pp
+The terminal interrupt key sequence will be ignored when
+.Nm ftp
+has completed any local processing and is awaiting a reply
+from the remote server.
+A long delay in this mode may result from the ABOR processing described
+above, or from unexpected behavior by the remote server, including
+violations of the ftp protocol.
+If the delay results from unexpected remote server behavior, the local
+.Nm ftp
+program must be killed by hand.
+.Sh FILE NAMING CONVENTIONS
+Files specified as arguments to
+.Nm ftp
+commands are processed according to the following rules.
+.Bl -enum
+.It
+If the file name
+.Sq Fl
+is specified, the
+.Ar stdin
+(for reading) or
+.Ar stdout
+(for writing) is used.
+.It
+If the first character of the file name is
+.Sq \&| ,
+the
+remainder of the argument is interpreted as a shell command.
+.Nm Ftp
+then forks a shell, using
+.Xr popen 3
+with the argument supplied, and reads (writes) from the stdout
+(stdin).
+If the shell command includes spaces, the argument
+must be quoted; e.g.
+\*(Lq" ls -lt"\*(Rq.
+A particularly
+useful example of this mechanism is: \*(Lqdir more\*(Rq.
+.It
+Failing the above checks, if ``globbing'' is enabled,
+local file names are expanded
+according to the rules used in the
+.Xr csh 1 ;
+c.f. the
+.Ic glob
+command.
+If the
+.Nm ftp
+command expects a single local file (.e.g.
+.Ic put ) ,
+only the first filename generated by the "globbing" operation is used.
+.It
+For
+.Ic mget
+commands and
+.Ic get
+commands with unspecified local file names, the local filename is
+the remote filename, which may be altered by a
+.Ic case ,
+.Ic ntrans ,
+or
+.Ic nmap
+setting.
+The resulting filename may then be altered if
+.Ic runique
+is on.
+.It
+For
+.Ic mput
+commands and
+.Ic put
+commands with unspecified remote file names, the remote filename is
+the local filename, which may be altered by a
+.Ic ntrans
+or
+.Ic nmap
+setting.
+The resulting filename may then be altered by the remote server if
+.Ic sunique
+is on.
+.El
+.Sh FILE TRANSFER PARAMETERS
+The FTP specification specifies many parameters which may
+affect a file transfer.
+The
+.Ic type
+may be one of \*(Lqascii\*(Rq, \*(Lqimage\*(Rq (binary),
+\*(Lqebcdic\*(Rq, and \*(Lqlocal byte size\*(Rq (for
+.Tn PDP Ns -10's
+and
+.Tn PDP Ns -20's
+mostly).
+.Nm Ftp
+supports the ascii and image types of file transfer,
+plus local byte size 8 for
+.Ic tenex
+mode transfers.
+.Pp
+.Nm Ftp
+supports only the default values for the remaining
+file transfer parameters:
+.Ic mode ,
+.Ic form ,
+and
+.Ic struct .
+.Sh THE .netrc FILE
+The
+.Pa .netrc
+file contains login and initialization information
+used by the auto-login process.
+It resides in the user's home directory.
+The following tokens are recognized; they may be separated by spaces,
+tabs, or new-lines:
+.Bl -tag -width password
+.It Ic machine Ar name
+Identify a remote machine
+.Ar name .
+The auto-login process searches the
+.Pa .netrc
+file for a
+.Ic machine
+token that matches the remote machine specified on the
+.Nm ftp
+command line or as an
+.Ic open
+command argument.
+Once a match is made, the subsequent
+.Pa .netrc
+tokens are processed,
+stopping when the end of file is reached or another
+.Ic machine
+or a
+.Ic default
+token is encountered.
+.It Ic default
+This is the same as
+.Ic machine
+.Ar name
+except that
+.Ic default
+matches any name.
+There can be only one
+.Ic default
+token, and it must be after all
+.Ic machine
+tokens.
+This is normally used as:
+.Pp
+.Dl default login anonymous password user@site
+.Pp
+thereby giving the user
+.Ar automatic
+anonymous ftp login to
+machines not specified in
+.Pa .netrc .
+This can be overridden
+by using the
+.Fl n
+flag to disable auto-login.
+.It Ic login Ar name
+Identify a user on the remote machine.
+If this token is present, the auto-login process will initiate
+a login using the specified
+.Ar name .
+.It Ic password Ar string
+Supply a password.
+If this token is present, the auto-login process will supply the
+specified string if the remote server requires a password as part
+of the login process.
+Note that if this token is present in the
+.Pa .netrc
+file for any user other
+than
+.Ar anonymous ,
+.Nm ftp
+will abort the auto-login process if the
+.Pa .netrc
+is readable by
+anyone besides the user.
+.It Ic account Ar string
+Supply an additional account password.
+If this token is present, the auto-login process will supply the
+specified string if the remote server requires an additional
+account password, or the auto-login process will initiate an
+.Dv ACCT
+command if it does not.
+.It Ic macdef Ar name
+Define a macro.
+This token functions like the
+.Nm ftp
+.Ic macdef
+command functions.
+A macro is defined with the specified name; its contents begin with the
+next
+.Pa .netrc
+line and continue until a null line (consecutive new-line
+characters) is encountered.
+If a macro named
+.Ic init
+is defined, it is automatically executed as the last step in the
+auto-login process.
+.El
+.Sh ENVIRONMENT
+.Nm Ftp
+utilizes the following environment variables.
+.Bl -tag -width Fl
+.It Ev HOME
+For default location of a
+.Pa .netrc
+file, if one exists.
+.It Ev SHELL
+For default shell.
+.El
+.Sh SEE ALSO
+.Xr ftpd 8
+.Pp
+Lunt, S. J.,
+FTP Security Extensions,
+Internet Draft,
+November 1993.
+.Sh HISTORY
+The
+.Nm ftp
+command appeared in
+.Bx 4.2 .
+.Sh BUGS
+Correct execution of many commands depends upon proper behavior
+by the remote server.
+.Pp
+An error in the treatment of carriage returns
+in the
+.Bx 4.2
+ascii-mode transfer code
+has been corrected.
+This correction may result in incorrect transfers of binary files
+to and from
+.Bx 4.2
+servers using the ascii type.
+Avoid this problem by using the binary image type.
--- /dev/null
+/*
+ * Copyright (c) 1985, 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ftp.c 5.38 (Berkeley) 4/22/91";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/file.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/ftp.h>
+#include <arpa/telnet.h>
+
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <pwd.h>
+#ifndef STDARG
+#if defined(__STDC__) && ! defined(VARARGS)
+#define STDARG
+#endif
+#endif
+#ifdef STDARG
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#ifdef POSIX
+#include <stdlib.h>
+#endif
+
+#ifndef L_SET
+#define L_SET 0
+#endif
+#ifndef L_INCR
+#define L_INCR 1
+#endif
+
+#ifdef KERBEROS
+#include <krb.h>
+
+KTEXT_ST ticket;
+CREDENTIALS cred;
+Key_schedule schedule;
+MSG_DAT msg_data;
+#endif /* KERBEROS */
+#ifdef GSSAPI
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+gss_ctx_id_t gcontext;
+#endif /* GSSAPI */
+
+
+static int kerror; /* XXX needed for all auth types */
+
+char *auth_type; /* Authentication succeeded? If so, what type? */
+
+unsigned int maxbuf, actualbuf;
+unsigned char *ucbuf;
+
+#define DEFINITIONS
+#include "ftp_var.h"
+
+#define sig_t my_sig_t
+#define sigtype krb5_sigtype
+typedef sigtype (*sig_t)();
+
+struct sockaddr_in hisctladdr;
+struct sockaddr_in hisdataaddr;
+struct sockaddr_in data_addr;
+int data = -1;
+int abrtflag = 0;
+int ptflag = 0;
+struct sockaddr_in myctladdr;
+uid_t getuid();
+sig_t lostpeer();
+off_t restart_point = 0;
+
+#define strerror(error) (sys_errlist[error])
+extern char *sys_errlist[];
+extern int connected, errno;
+
+#define herror() printf("unknown host\n")
+
+FILE *cin, *cout;
+FILE *dataconn();
+
+char *
+hookup(host, port)
+ char *host;
+ int port;
+{
+ register struct hostent *hp = 0;
+ int s, len, tos;
+ static char hostnamebuf[80];
+
+ memset((char *)&hisctladdr, 0, sizeof (hisctladdr));
+ hisctladdr.sin_addr.s_addr = inet_addr(host);
+ if (hisctladdr.sin_addr.s_addr != -1) {
+ hisctladdr.sin_family = AF_INET;
+ (void) strncpy(hostnamebuf, host, sizeof(hostnamebuf));
+ } else {
+ hp = gethostbyname(host);
+ if (hp == NULL) {
+ fprintf(stderr, "ftp: %s: ", host);
+ herror();
+ code = -1;
+ return((char *) 0);
+ }
+ hisctladdr.sin_family = hp->h_addrtype;
+ memcpy((caddr_t)&hisctladdr.sin_addr, hp->h_addr_list[0],
+ hp->h_length);
+ (void) strncpy(hostnamebuf, hp->h_name, sizeof(hostnamebuf));
+ }
+ hostname = hostnamebuf;
+ s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
+ if (s < 0) {
+ perror("ftp: socket");
+ code = -1;
+ return (0);
+ }
+ hisctladdr.sin_port = port;
+ while (connect(s, (struct sockaddr *)&hisctladdr, sizeof (hisctladdr)) < 0) {
+ if (hp && hp->h_addr_list[1]) {
+ int oerrno = errno;
+ extern char *inet_ntoa();
+
+ fprintf(stderr, "ftp: connect to address %s: ",
+ inet_ntoa(hisctladdr.sin_addr));
+ errno = oerrno;
+ perror((char *) 0);
+ hp->h_addr_list++;
+ memcpy((caddr_t)&hisctladdr.sin_addr,
+ hp->h_addr_list[0], hp->h_length);
+ fprintf(stdout, "Trying %s...\n",
+ inet_ntoa(hisctladdr.sin_addr));
+ (void) close(s);
+ s = socket(hisctladdr.sin_family, SOCK_STREAM, 0);
+ if (s < 0) {
+ perror("ftp: socket");
+ code = -1;
+ return (0);
+ }
+ continue;
+ }
+ perror("ftp: connect");
+ code = -1;
+ goto bad;
+ }
+ len = sizeof (myctladdr);
+ if (getsockname(s, (struct sockaddr *)&myctladdr, &len) < 0) {
+ perror("ftp: getsockname");
+ code = -1;
+ goto bad;
+ }
+#ifdef IP_TOS
+#ifdef IPTOS_LOWDELAY
+ tos = IPTOS_LOWDELAY;
+ if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
+ perror("ftp: setsockopt TOS (ignored)");
+#endif
+#endif
+ cin = fdopen(s, "r");
+ cout = fdopen(s, "w");
+ if (cin == NULL || cout == NULL) {
+ fprintf(stderr, "ftp: fdopen failed.\n");
+ if (cin)
+ (void) fclose(cin);
+ if (cout)
+ (void) fclose(cout);
+ code = -1;
+ goto bad;
+ }
+ if (verbose)
+ printf("Connected to %s.\n", hostname);
+ if (getreply(0) > 2) { /* read startup message from server */
+ if (cin)
+ (void) fclose(cin);
+ if (cout)
+ (void) fclose(cout);
+ code = -1;
+ goto bad;
+ }
+#ifdef SO_OOBINLINE
+ {
+ int on = 1;
+
+ if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on))
+ < 0 && debug) {
+ perror("ftp: setsockopt");
+ }
+ }
+#endif /* SO_OOBINLINE */
+
+ return (hostname);
+bad:
+ (void) close(s);
+ return ((char *)0);
+}
+
+login(host)
+ char *host;
+{
+ char tmp[80];
+ char *user, *pass, *acct, *getenv(), *getlogin(), *mygetpass();
+ int n, aflag = 0;
+
+ do_auth();
+ user = pass = acct = 0;
+ if (ruserpass(host, &user, &pass, &acct) < 0) {
+ code = -1;
+ return(0);
+ }
+ while (user == NULL) {
+ char *myname;
+
+ myname = getenv("LOGNAME");
+ if (myname == NULL)
+ myname = getenv("USER");
+ if (myname == NULL)
+ myname = getlogin();
+ if (myname == NULL) {
+ struct passwd *pp = getpwuid(getuid());
+
+ if (pp != NULL)
+ myname = pp->pw_name;
+ }
+ if (myname)
+ printf("Name (%s:%s): ", host, myname);
+ else
+ printf("Name (%s): ", host);
+ (void) fgets(tmp, sizeof(tmp) - 1, stdin);
+ tmp[strlen(tmp) - 1] = '\0';
+ if (*tmp == '\0')
+ user = myname;
+ else
+ user = tmp;
+ }
+ n = command("USER %s", user);
+ if (n == COMPLETE)
+ n = command("PASS dummy");
+ else if (n == CONTINUE) {
+#ifndef NOENCRYPTION
+ int oldlevel;
+#endif
+ if (pass == NULL)
+ pass = mygetpass("Password:");
+#ifndef NOENCRYPTION
+ if ((oldlevel = level) == PROT_S) level = PROT_P;
+#endif
+ n = command("PASS %s", pass);
+#ifndef NOENCRYPTION
+ /* level may have changed */
+ if (level == PROT_P) level = oldlevel;
+#endif
+ }
+ if (n == CONTINUE) {
+ aflag++;
+ acct = mygetpass("Account:");
+ n = command("ACCT %s", acct);
+ }
+ if (n != COMPLETE) {
+ fprintf(stderr, "Login failed.\n");
+ return (0);
+ }
+ if (!aflag && acct != NULL)
+ (void) command("ACCT %s", acct);
+ if (proxy)
+ return(1);
+ for (n = 0; n < macnum; ++n) {
+ if (!strcmp("init", macros[n].mac_name)) {
+ (void) strcpy(line, "$init");
+ makeargv();
+ domacro(margc, margv);
+ break;
+ }
+ }
+ return (1);
+}
+
+sigtype
+cmdabort(sig)
+ int sig;
+{
+ extern jmp_buf ptabort;
+
+ printf("\n");
+ (void) fflush(stdout);
+ abrtflag++;
+ if (ptflag)
+ longjmp(ptabort,1);
+}
+
+secure_command(cmd)
+ char *cmd;
+{
+ char in[FTP_BUFSIZ], out[FTP_BUFSIZ];
+ int length;
+
+ if (auth_type) {
+ /*
+ * File protection level also determines whether
+ * commands are MIC or ENC. Should be independent ...
+ */
+#ifdef KERBEROS
+ if (strcmp(auth_type, "KERBEROS_V4") == 0)
+ if ((length = level == PROT_P ?
+ krb_mk_priv((unsigned char *)cmd, (unsigned char *)out,
+ strlen(cmd), schedule,
+ &cred.session, &myctladdr, &hisctladdr)
+ : krb_mk_safe((unsigned char *)cmd, (unsigned char *)out,
+ strlen(cmd), &cred.session,
+ &myctladdr, &hisctladdr)) == -1) {
+ fprintf(stderr, "krb_mk_%s failed for KERBEROS_V4\n",
+ level == PROT_P ? "priv" : "safe");
+ return(0);
+ }
+#endif /* KERBEROS */
+#ifdef GSSAPI
+ /* secure_command (based on level) */
+ if (strcmp(auth_type, "GSSAPI") == 0) {
+ gss_buffer_desc in_buf, out_buf;
+ OM_uint32 maj_stat, min_stat;
+ int conf_state;
+/* level = PROT_P; */
+ in_buf.value = cmd;
+ in_buf.length = strlen(cmd) + 1;
+ maj_stat = gss_seal(&min_stat, gcontext,
+ (level==PROT_P), /* confidential */
+ GSS_C_QOP_DEFAULT,
+ &in_buf, &conf_state,
+ &out_buf);
+ if (maj_stat != GSS_S_COMPLETE) {
+ /* generally need to deal */
+ user_gss_error(maj_stat, min_stat,
+ (level==PROT_P)?
+ "gss_seal ENC didn't complete":
+ "gss_seal MIC didn't complete");
+ } else if ((level == PROT_P) && !conf_state) {
+ fprintf(stderr,
+ "GSSAPI didn't encrypt message");
+ } else {
+ if (debug)
+ fprintf(stderr, "sealed (%s) %d bytes\n",
+ level==PROT_P?"ENC":"MIC",
+ out_buf.length);
+ memcpy(out, out_buf.value,
+ length=out_buf.length);
+ gss_release_buffer(&min_stat, &out_buf);
+ }
+ }
+#endif /* GSSAPI */
+ /* Other auth types go here ... */
+ if (kerror = radix_encode(out, in, &length, 0)) {
+ fprintf(stderr,"Couldn't base 64 encode command (%s)\n",
+ radix_error(kerror));
+ return(0);
+ }
+ fprintf(cout, "%s %s", level == PROT_P ? "ENC" : "MIC", in);
+ if(debug)
+ fprintf(stderr, "secure_command(%s)\nencoding %d bytes %s %s\n",
+ cmd, length, level==PROT_P ? "ENC" : "MIC", in);
+ } else fputs(cmd, cout);
+ fprintf(cout, "\r\n");
+ (void) fflush(cout);
+ return(1);
+}
+
+#ifdef STDARG
+command(char *fmt, ...)
+#else
+/*VARARGS*/
+command(va_alist)
+va_dcl
+#endif
+{
+ char in[FTP_BUFSIZ];
+ va_list ap;
+#ifndef STDARG
+ char *fmt;
+#endif
+ int r;
+ sig_t oldintr;
+ sigtype cmdabort();
+
+ abrtflag = 0;
+ if (debug) {
+ if (proxflag) printf("%s ", hostname);
+ printf("---> ");
+#ifdef STDARG
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+ fmt = va_arg(ap, char *);
+#endif
+ if (strncmp("PASS ", fmt, 5) == 0)
+ printf("PASS XXXX");
+ else
+ vfprintf(stdout, fmt, ap);
+ va_end(ap);
+ printf("\n");
+ (void) fflush(stdout);
+ }
+ if (cout == NULL) {
+ perror ("No control connection for command");
+ code = -1;
+ return (0);
+ }
+ oldintr = signal(SIGINT, cmdabort);
+#ifdef STDARG
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+ fmt = va_arg(ap, char *);
+#endif
+ vsprintf(in, fmt, ap);
+ va_end(ap);
+again: if (secure_command(in) == 0)
+ return(0);
+ cpend = 1;
+ r = getreply(!strcmp(fmt, "QUIT"));
+#ifndef NOENCRYPTION
+ if (r == 533 && level == PROT_P) {
+ fprintf(stderr,
+ "ENC command not supported at server; retrying under MIC...\n");
+ level = PROT_S;
+ goto again;
+ }
+#endif
+ if (abrtflag && oldintr != SIG_IGN)
+ (*oldintr)(SIGINT);
+ (void) signal(SIGINT, oldintr);
+ return(r);
+}
+
+char reply_string[FTP_BUFSIZ]; /* last line of previous reply */
+
+/* for parsing replies to the ADAT command */
+char *reply_parse, reply_buf[FTP_BUFSIZ], *reply_ptr;
+
+#include <ctype.h>
+
+getreply(expecteof)
+ int expecteof;
+{
+ register int i, c, n;
+ register int dig;
+ register char *cp;
+ int originalcode = 0, continuation = 0;
+ sig_t oldintr;
+ int pflag = 0;
+ char *pt = pasv;
+ sigtype cmdabort();
+ char ibuf[FTP_BUFSIZ], obuf[FTP_BUFSIZ];
+ int safe = 0;
+ extern char *strpbrk(), *strstr();
+
+ ibuf[0] = '\0';
+ if (reply_parse) reply_ptr = reply_buf;
+ oldintr = signal(SIGINT, cmdabort);
+ for (;;) {
+ obuf[0] = '\0';
+ dig = n = code = i = 0;
+ cp = reply_string;
+ while ((c = ibuf[0] ? ibuf[i++] : getc(cin)) != '\n') {
+ if (c == IAC) { /* handle telnet commands */
+ switch (c = getc(cin)) {
+ case WILL:
+ case WONT:
+ c = getc(cin);
+ fprintf(cout, "%c%c%c", IAC, DONT, c);
+ (void) fflush(cout);
+ break;
+ case DO:
+ case DONT:
+ c = getc(cin);
+ fprintf(cout, "%c%c%c", IAC, WONT, c);
+ (void) fflush(cout);
+ break;
+ default:
+ break;
+ }
+ continue;
+ }
+ dig++;
+ if (c == EOF) {
+ if (expecteof) {
+ (void) signal(SIGINT,oldintr);
+ code = 221;
+ return (0);
+ }
+ lostpeer();
+ if (verbose) {
+ printf("421 Service not available, remote server has closed connection\n");
+ (void) fflush(stdout);
+ }
+ code = 421;
+ return(4);
+ }
+ if (n == 0)
+ n = c;
+ if (auth_type && !ibuf[0] &&
+ (n == '6' || continuation)) {
+ if (c != '\r' && dig > 4)
+ obuf[i++] = c;
+ } else {
+ if (auth_type && !ibuf[0] && dig == 1 && verbose)
+ printf("Unauthenticated reply received from server:\n");
+ if (reply_parse) *reply_ptr++ = c;
+ if (c != '\r' && (verbose > 0 ||
+ (verbose > -1 && n == '5' && dig > 4))) {
+ if (proxflag &&
+ (dig == 1 || dig == 5 && verbose == 0))
+ printf("%s:",hostname);
+ (void) putchar(c);
+ }
+ }
+ if (auth_type && !ibuf[0] && n != '6') continue;
+ if (dig < 4 && isdigit(c))
+ code = code * 10 + (c - '0');
+ if (!pflag && code == 227)
+ pflag = 1;
+ if (dig > 4 && pflag == 1 && isdigit(c))
+ pflag = 2;
+ if (pflag == 2) {
+ if (c != '\r' && c != ')')
+ *pt++ = c;
+ else {
+ *pt = '\0';
+ pflag = 3;
+ }
+ }
+ if (dig == 4 && c == '-' && n != '6') {
+ if (continuation)
+ code = 0;
+ continuation++;
+ }
+ if (cp < &reply_string[sizeof(reply_string) - 1])
+ *cp++ = c;
+ }
+ if (auth_type && !ibuf[0] && n != '6')
+ return(getreply(expecteof));
+ ibuf[0] = obuf[i] = '\0';
+ if (code && n == '6')
+ if (code != 631 && code != 632 && code != 633)
+ printf("Unknown reply: %d %s\n", code, obuf);
+ else safe = code == 631;
+ if (obuf[0]) /* if there is a string to decode */
+ if (!auth_type)
+ printf("Cannot decode reply:\n%d %s\n", code, obuf);
+#ifdef NOENCRYPTION
+ else if (code == 632)
+ printf("Cannot decrypt %d reply: %s\n", code, obuf);
+#endif
+#ifdef NOCONFIDENTIAL
+ else if (code == 633)
+ printf("Cannot decrypt %d reply: %s\n", code, obuf);
+#endif
+ else {
+ int len;
+ if (kerror = radix_encode(obuf, ibuf, &len, 1))
+ printf("Can't base 64 decode reply %d (%s)\n\"%s\"\n",
+ code, radix_error(kerror), obuf);
+#ifdef KERBEROS
+ else if (strcmp(auth_type, "KERBEROS_V4") == 0)
+ if ((kerror = safe ?
+ krb_rd_safe((unsigned char *)ibuf, len,
+ &cred.session,
+ &hisctladdr, &myctladdr, &msg_data)
+ : krb_rd_priv((unsigned char *)ibuf, len,
+ schedule, &cred.session,
+ &hisctladdr, &myctladdr, &msg_data))
+ != KSUCCESS)
+ printf("%d reply %s! (krb_rd_%s: %s)\n", code,
+ safe ? "modified" : "garbled",
+ safe ? "safe" : "priv",
+ krb_get_err_text(kerror));
+ else {
+ if (verbose) printf("%c:", safe ? 'S' : 'P');
+ memcpy(ibuf, msg_data.app_data,
+ msg_data.app_length);
+ strcpy(&ibuf[msg_data.app_length], "\r\n");
+ continue;
+ }
+#endif
+#ifdef GSSAPI
+ else if (strcmp(auth_type, "GSSAPI") == 0) {
+ gss_buffer_desc xmit_buf, msg_buf;
+ OM_uint32 maj_stat, min_stat;
+ int conf_state;
+ xmit_buf.value = ibuf;
+ xmit_buf.length = len;
+ /* decrypt/verify the message */
+ conf_state = safe;
+ maj_stat = gss_unseal(&min_stat, gcontext,
+ &xmit_buf, &msg_buf,
+ &conf_state, NULL);
+ if (maj_stat != GSS_S_COMPLETE) {
+ user_gss_error(maj_stat, min_stat,
+ "failed unsealing reply");
+ } else {
+ memcpy(ibuf, msg_buf.value,
+ msg_buf.length);
+ strcpy(&ibuf[msg_buf.length], "\r\n");
+ gss_release_buffer(&min_stat,&msg_buf);
+ continue;
+ }
+ }
+#endif
+ /* Other auth types go here... */
+ }
+ else
+ if (verbose > 0 || verbose > -1 && n == '5') {
+ (void) putchar(c);
+ (void) fflush (stdout);
+ }
+ if (continuation && code != originalcode) {
+ if (originalcode == 0)
+ originalcode = code;
+ continue;
+ }
+ *cp = '\0';
+ if (n != '1')
+ cpend = 0;
+ (void) signal(SIGINT,oldintr);
+ if (code == 421 || originalcode == 421)
+ lostpeer();
+ if (abrtflag && oldintr != cmdabort && oldintr != SIG_IGN)
+ (*oldintr)(SIGINT);
+ if (reply_parse) {
+ *reply_ptr = '\0';
+ if (reply_ptr = strstr(reply_buf, reply_parse)) {
+ reply_parse = reply_ptr + strlen(reply_parse);
+ if (reply_ptr = strpbrk(reply_parse, " \r"))
+ *reply_ptr = '\0';
+ } else reply_parse = reply_ptr;
+ }
+ return (n - '0');
+ }
+}
+
+empty(mask, sec)
+ struct fd_set *mask;
+ int sec;
+{
+ struct timeval t;
+
+ t.tv_sec = (long) sec;
+ t.tv_usec = 0;
+ return(select(32, mask, (struct fd_set *) 0, (struct fd_set *) 0, &t));
+}
+
+jmp_buf sendabort;
+
+sigtype
+abortsend(sig)
+ int sig;
+{
+
+ mflag = 0;
+ abrtflag = 0;
+ printf("\nsend aborted\nwaiting for remote to finish abort\n");
+ (void) fflush(stdout);
+ longjmp(sendabort, 1);
+}
+
+#ifdef STDARG
+secure_error(char *fmt, ...)
+#else
+/* VARARGS1 */
+secure_error(fmt, p1, p2, p3, p4, p5)
+ char *fmt;
+#endif
+{
+#ifdef STDARG
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+#else
+ fprintf(stderr, fmt, p1, p2, p3, p4, p5);
+#endif
+ putc('\n', stderr);
+}
+
+#define HASHBYTES 1024
+
+sendrequest(cmd, local, remote, printnames)
+ char *cmd, *local, *remote;
+ int printnames;
+{
+ struct stat st;
+ struct timeval start, stop;
+ register int c, d;
+ FILE *fin, *dout = 0, *popen();
+ int (*closefunc)(), pclose(), fclose();
+ sig_t oldintr, oldintp;
+ long bytes = 0, hashbytes = HASHBYTES;
+ char *lmode, buf[FTP_BUFSIZ], *bufp;
+ sigtype abortsend();
+
+ if (verbose && printnames) {
+ if (local && *local != '-')
+ printf("local: %s ", local);
+ if (remote)
+ printf("remote: %s\n", remote);
+ }
+ if (proxy) {
+ proxtrans(cmd, local, remote);
+ return;
+ }
+ if (curtype != type)
+ changetype(type, 0);
+ closefunc = NULL;
+ oldintr = NULL;
+ oldintp = NULL;
+ lmode = "w";
+ if (setjmp(sendabort)) {
+ while (cpend) {
+ (void) getreply(0);
+ }
+ if (data >= 0) {
+ (void) close(data);
+ data = -1;
+ }
+ if (oldintr)
+ (void) signal(SIGINT,oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE,oldintp);
+ code = -1;
+ return;
+ }
+ oldintr = signal(SIGINT, abortsend);
+ if (strcmp(local, "-") == 0)
+ fin = stdin;
+ else if (*local == '|') {
+ oldintp = signal(SIGPIPE,SIG_IGN);
+ fin = popen(local + 1, "r");
+ if (fin == NULL) {
+ perror(local + 1);
+ (void) signal(SIGINT, oldintr);
+ (void) signal(SIGPIPE, oldintp);
+ code = -1;
+ return;
+ }
+ closefunc = pclose;
+ } else {
+ fin = fopen(local, "r");
+ if (fin == NULL) {
+ fprintf(stderr, "local: %s: %s\n", local,
+ strerror(errno));
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ closefunc = fclose;
+ if (fstat(fileno(fin), &st) < 0 ||
+ (st.st_mode&S_IFMT) != S_IFREG) {
+ fprintf(stdout, "%s: not a plain file.\n", local);
+ (void) signal(SIGINT, oldintr);
+ fclose(fin);
+ code = -1;
+ return;
+ }
+ }
+ if (initconn()) {
+ (void) signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+ code = -1;
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ return;
+ }
+ if (setjmp(sendabort))
+ goto abort;
+
+ if (restart_point &&
+ (strcmp(cmd, "STOR") == 0 || strcmp(cmd, "APPE") == 0)) {
+ if (fseek(fin, (long) restart_point, 0) < 0) {
+ fprintf(stderr, "local: %s: %s\n", local,
+ strerror(errno));
+ restart_point = 0;
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ return;
+ }
+ if (command("REST %ld", (long) restart_point)
+ != CONTINUE) {
+ restart_point = 0;
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ return;
+ }
+ restart_point = 0;
+ lmode = "r+w";
+ }
+ if (remote) {
+ if (command("%s %s", cmd, remote) != PRELIM) {
+ (void) signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ return;
+ }
+ } else
+ if (command("%s", cmd) != PRELIM) {
+ (void) signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ return;
+ }
+ dout = dataconn(lmode);
+ if (dout == NULL)
+ goto abort;
+ (void) gettimeofday(&start, (struct timezone *)0);
+ oldintp = signal(SIGPIPE, SIG_IGN);
+ switch (curtype) {
+
+ case TYPE_I:
+ case TYPE_L:
+ errno = d = 0;
+ while ((c = read(fileno(fin), buf, sizeof (buf))) > 0) {
+ bytes += c;
+ for (bufp = buf; c > 0; c -= d, bufp += d)
+ if ((d = secure_write(fileno(dout), bufp, c)) <= 0)
+ break;
+ if (hash) {
+ while (bytes >= hashbytes) {
+ (void) putchar('#');
+ hashbytes += HASHBYTES;
+ }
+ (void) fflush(stdout);
+ }
+ }
+ if (hash && bytes > 0) {
+ if (bytes < HASHBYTES)
+ (void) putchar('#');
+ (void) putchar('\n');
+ (void) fflush(stdout);
+ }
+ if (c < 0)
+ fprintf(stderr, "local: %s: %s\n", local,
+ strerror(errno));
+ if (d < 0 || (d = secure_flush(fileno(dout))) < 0) {
+ if (d == -1 && errno != EPIPE)
+ perror("netout");
+ bytes = -1;
+ }
+ break;
+
+ case TYPE_A:
+ while ((c = getc(fin)) != EOF) {
+ if (c == '\n') {
+ while (hash && (bytes >= hashbytes)) {
+ (void) putchar('#');
+ (void) fflush(stdout);
+ hashbytes += HASHBYTES;
+ }
+ if (ferror(dout) ||
+ secure_putc('\r', dout) < 0)
+ break;
+ bytes++;
+ }
+ if (secure_putc(c, dout) < 0)
+ break;
+ bytes++;
+ /* if (c == '\r') { */
+ /* (void) putc('\0', dout); /* this violates rfc */
+ /* bytes++; */
+ /* } */
+ }
+ if (hash) {
+ if (bytes < hashbytes)
+ (void) putchar('#');
+ (void) putchar('\n');
+ (void) fflush(stdout);
+ }
+ if (ferror(fin))
+ fprintf(stderr, "local: %s: %s\n", local,
+ strerror(errno));
+ d = 0;
+ if (ferror(dout) || (d = secure_flush(fileno(dout))) < 0) {
+ if ((ferror(dout) || d == -1) && errno != EPIPE)
+ perror("netout");
+ bytes = -1;
+ }
+ break;
+ }
+ (void) gettimeofday(&stop, (struct timezone *)0);
+ if (closefunc != NULL)
+ (*closefunc)(fin);
+ (void) fclose(dout);
+ (void) getreply(0);
+ (void) signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+ if (bytes > 0)
+ ptransfer("sent", bytes, &start, &stop);
+ return;
+abort:
+ (void) gettimeofday(&stop, (struct timezone *)0);
+ (void) signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+ if (!cpend) {
+ code = -1;
+ return;
+ }
+ if (data >= 0) {
+ (void) close(data);
+ data = -1;
+ }
+ if (dout)
+ (void) fclose(dout);
+ (void) getreply(0);
+ code = -1;
+ if (closefunc != NULL && fin != NULL)
+ (*closefunc)(fin);
+ if (bytes > 0)
+ ptransfer("sent", bytes, &start, &stop);
+}
+
+jmp_buf recvabort;
+
+sigtype
+abortrecv(sig)
+ int sig;
+{
+
+ mflag = 0;
+ abrtflag = 0;
+ printf("\nreceive aborted\nwaiting for remote to finish abort\n");
+ (void) fflush(stdout);
+ longjmp(recvabort, 1);
+}
+
+recvrequest(cmd, local, remote, lmode, printnames)
+ char *cmd, *local, *remote, *lmode;
+{
+ FILE *fout, *din = 0, *popen();
+ int (*closefunc)(), pclose(), fclose();
+ sig_t oldintr, oldintp;
+ int is_retr, tcrflag, bare_lfs = 0;
+ char *gunique();
+ static int bufsize;
+ static char *buf;
+ int blksize;
+ long bytes = 0, hashbytes = HASHBYTES;
+ register int c, d;
+ struct timeval start, stop;
+ struct stat st;
+ off_t lseek();
+ sigtype abortrecv();
+
+ is_retr = strcmp(cmd, "RETR") == 0;
+ if (is_retr && verbose && printnames) {
+ if (local && *local != '-')
+ printf("local: %s ", local);
+ if (remote)
+ printf("remote: %s\n", remote);
+ }
+ if (proxy && is_retr) {
+ proxtrans(cmd, local, remote);
+ return;
+ }
+ closefunc = NULL;
+ oldintr = NULL;
+ oldintp = NULL;
+ tcrflag = !crflag && is_retr;
+ if (setjmp(recvabort)) {
+ while (cpend) {
+ (void) getreply(0);
+ }
+ if (data >= 0) {
+ (void) close(data);
+ data = -1;
+ }
+ if (oldintr)
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ oldintr = signal(SIGINT, abortrecv);
+ if (strcmp(local, "-") && *local != '|') {
+ if (access(local, 2) < 0) {
+ char *dir = strrchr(local, '/');
+
+ if (errno != ENOENT && errno != EACCES) {
+ fprintf(stderr, "local: %s: %s\n", local,
+ strerror(errno));
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ if (dir != NULL)
+ *dir = 0;
+ d = access(dir ? local : ".", 2);
+ if (dir != NULL)
+ *dir = '/';
+ if (d < 0) {
+ fprintf(stderr, "local: %s: %s\n", local,
+ strerror(errno));
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ if (!runique && errno == EACCES &&
+ chmod(local, 0600) < 0) {
+ fprintf(stderr, "local: %s: %s\n", local,
+ strerror(errno));
+ (void) signal(SIGINT, oldintr);
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ if (runique && errno == EACCES &&
+ (local = gunique(local)) == NULL) {
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ }
+ else if (runique && (local = gunique(local)) == NULL) {
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ }
+ if (!is_retr) {
+ if (curtype != TYPE_A)
+ changetype(TYPE_A, 0);
+ } else if (curtype != type)
+ changetype(type, 0);
+ if (initconn()) {
+ (void) signal(SIGINT, oldintr);
+ code = -1;
+ return;
+ }
+ if (setjmp(recvabort))
+ goto abort;
+ if (is_retr && restart_point &&
+ command("REST %ld", (long) restart_point) != CONTINUE)
+ return;
+ if (remote) {
+ if (command("%s %s", cmd, remote) != PRELIM) {
+ (void) signal(SIGINT, oldintr);
+ return;
+ }
+ } else {
+ if (command("%s", cmd) != PRELIM) {
+ (void) signal(SIGINT, oldintr);
+ return;
+ }
+ }
+ din = dataconn("r");
+ if (din == NULL)
+ goto abort;
+ if (strcmp(local, "-") == 0)
+ fout = stdout;
+ else if (*local == '|') {
+ oldintp = signal(SIGPIPE, SIG_IGN);
+ fout = popen(local + 1, "w");
+ if (fout == NULL) {
+ perror(local+1);
+ goto abort;
+ }
+ closefunc = pclose;
+ } else {
+ fout = fopen(local, lmode);
+ if (fout == NULL) {
+ fprintf(stderr, "local: %s: %s\n", local,
+ strerror(errno));
+ goto abort;
+ }
+ closefunc = fclose;
+ }
+ blksize = FTP_BUFSIZ;
+#ifndef NOSTBLKSIZE
+ if (fstat(fileno(fout), &st) == 0 && st.st_blksize != 0)
+ blksize = st.st_blksize;
+#endif
+ if (blksize > bufsize) {
+ if (buf)
+ (void) free(buf);
+ buf = (char *)malloc((unsigned)blksize);
+ if (buf == NULL) {
+ perror("malloc");
+ bufsize = 0;
+ goto abort;
+ }
+ bufsize = blksize;
+ }
+ (void) gettimeofday(&start, (struct timezone *)0);
+ switch (curtype) {
+
+ case TYPE_I:
+ case TYPE_L:
+ if (restart_point &&
+ lseek(fileno(fout), (long) restart_point, L_SET) < 0) {
+ fprintf(stderr, "local: %s: %s\n", local,
+ strerror(errno));
+ if (closefunc != NULL)
+ (*closefunc)(fout);
+ return;
+ }
+ errno = d = 0;
+ while ((c = secure_read(fileno(din), buf, bufsize)) > 0) {
+ if ((d = write(fileno(fout), buf, c)) != c)
+ break;
+ bytes += c;
+ if (hash) {
+ while (bytes >= hashbytes) {
+ (void) putchar('#');
+ hashbytes += HASHBYTES;
+ }
+ (void) fflush(stdout);
+ }
+ }
+ if (hash && bytes > 0) {
+ if (bytes < HASHBYTES)
+ (void) putchar('#');
+ (void) putchar('\n');
+ (void) fflush(stdout);
+ }
+ if (c < 0) {
+ if (c == -1 && errno != EPIPE)
+ perror("netin");
+ bytes = -1;
+ }
+ if (d < c) {
+ if (d < 0)
+ fprintf(stderr, "local: %s: %s\n", local,
+ strerror(errno));
+ else
+ fprintf(stderr, "%s: short write\n", local);
+ }
+ break;
+
+ case TYPE_A:
+ if (restart_point) {
+ register int i, n, ch;
+
+ if (fseek(fout, 0L, L_SET) < 0)
+ goto done;
+ n = restart_point;
+ for (i = 0; i++ < n;) {
+ if ((ch = getc(fout)) == EOF)
+ goto done;
+ if (ch == '\n')
+ i++;
+ }
+ if (fseek(fout, 0L, L_INCR) < 0) {
+done:
+ fprintf(stderr, "local: %s: %s\n", local,
+ strerror(errno));
+ if (closefunc != NULL)
+ (*closefunc)(fout);
+ return;
+ }
+ }
+ while ((c = secure_getc(din)) >= 0) {
+ if (c == '\n')
+ bare_lfs++;
+ while (c == '\r') {
+ while (hash && (bytes >= hashbytes)) {
+ (void) putchar('#');
+ (void) fflush(stdout);
+ hashbytes += HASHBYTES;
+ }
+ bytes++;
+ if ((c = secure_getc(din)) != '\n' || tcrflag) {
+ if (ferror(fout))
+ goto break2;
+ (void) putc('\r', fout);
+ if (c == '\0') {
+ bytes++;
+ goto contin2;
+ }
+ }
+ }
+ if (c < 0) break;
+ (void) putc(c, fout);
+ bytes++;
+ contin2: ;
+ }
+break2:
+ if (bare_lfs) {
+ printf("WARNING! %d bare linefeeds received in ASCII mode\n", bare_lfs);
+ printf("File may not have transferred correctly.\n");
+ }
+ if (hash) {
+ if (bytes < hashbytes)
+ (void) putchar('#');
+ (void) putchar('\n');
+ (void) fflush(stdout);
+ }
+ if (ferror(din)) {
+ if (errno != EPIPE)
+ perror("netin");
+ bytes = -1;
+ }
+ if (ferror(fout) || c == -2) {
+ if (c != -2)
+ fprintf(stderr, "local: %s: %s\n", local,
+ strerror(errno));
+ bytes = -1;
+ }
+ break;
+ }
+ if (closefunc != NULL)
+ (*closefunc)(fout);
+ (void) signal(SIGINT, oldintr);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintp);
+ (void) gettimeofday(&stop, (struct timezone *)0);
+ (void) fclose(din);
+ (void) getreply(0);
+ if (bytes > 0 && is_retr)
+ ptransfer("received", bytes, &start, &stop);
+ return;
+abort:
+
+/* abort using RFC959 recommended IP,SYNC sequence */
+
+ (void) gettimeofday(&stop, (struct timezone *)0);
+ if (oldintp)
+ (void) signal(SIGPIPE, oldintr);
+ (void) signal(SIGINT, SIG_IGN);
+ if (!cpend) {
+ code = -1;
+ (void) signal(SIGINT, oldintr);
+ return;
+ }
+
+ abort_remote(din);
+ code = -1;
+ if (data >= 0) {
+ (void) close(data);
+ data = -1;
+ }
+ if (closefunc != NULL && fout != NULL)
+ (*closefunc)(fout);
+ if (din)
+ (void) fclose(din);
+ if (bytes > 0)
+ ptransfer("received", bytes, &start, &stop);
+ (void) signal(SIGINT, oldintr);
+}
+
+/*
+ * Need to start a listen on the data channel before we send the command,
+ * otherwise the server's connect may fail.
+ */
+initconn()
+{
+ register char *p, *a;
+ int result, len, tmpno = 0;
+ int on = 1;
+#ifndef NO_PASSIVE_MODE
+ int a1,a2,a3,a4,p1,p2;
+
+ if (passivemode) {
+ data = socket(AF_INET, SOCK_STREAM, 0);
+ if (data < 0) {
+ perror("ftp: socket");
+ return(1);
+ }
+ if (options & SO_DEBUG &&
+ setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof (on)) < 0)
+ perror("ftp: setsockopt (ignored)");
+ if (command("PASV") != COMPLETE) {
+ printf("Passive mode refused. Turning off passive mode.\n");
+ passivemode = 0;
+ return initconn();
+ }
+
+/*
+ * What we've got at this point is a string of comma separated
+ * one-byte unsigned integer values, separated by commas.
+ * The first four are the an IP address. The fifth is the MSB
+ * of the port number, the sixth is the LSB. From that we'll
+ * prepare a sockaddr_in.
+ */
+
+ if (sscanf(pasv,"%d,%d,%d,%d,%d,%d",&a1,&a2,&a3,&a4,&p1,&p2) != 6) {
+ printf("Passive mode address scan failure. Shouldn't happen!\n");
+ return(1);
+ };
+
+ data_addr.sin_family = AF_INET;
+ data_addr.sin_addr.s_addr = htonl((a1<<24)|(a2<<16)|(a3<<8)|a4);
+ data_addr.sin_port = htons((p1<<8)|p2);
+
+ if (connect(data, (struct sockaddr *) &data_addr, sizeof(data_addr))<0) {
+ perror("ftp: connect");
+ return(1);
+ }
+#ifdef IP_TOS
+#ifdef IPTOS_THROUGHPUT
+ on = IPTOS_THROUGHPUT;
+ if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
+ perror("ftp: setsockopt TOS (ignored)");
+#endif
+#endif
+ hisdataaddr = data_addr;
+ return(0);
+ }
+#endif
+
+noport:
+ data_addr = myctladdr;
+ if (sendport)
+ data_addr.sin_port = 0; /* let system pick one */
+ if (data != -1)
+ (void) close(data);
+ data = socket(AF_INET, SOCK_STREAM, 0);
+ if (data < 0) {
+ perror("ftp: socket");
+ if (tmpno)
+ sendport = 1;
+ return (1);
+ }
+ if (!sendport)
+ if (setsockopt(data, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof (on)) < 0) {
+ perror("ftp: setsockopt (reuse address)");
+ goto bad;
+ }
+ if (bind(data, (struct sockaddr *)&data_addr, sizeof (data_addr)) < 0) {
+ perror("ftp: bind");
+ goto bad;
+ }
+ if (options & SO_DEBUG &&
+ setsockopt(data, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof (on)) < 0)
+ perror("ftp: setsockopt (ignored)");
+ len = sizeof (data_addr);
+ if (getsockname(data, (struct sockaddr *)&data_addr, &len) < 0) {
+ perror("ftp: getsockname");
+ goto bad;
+ }
+ if (listen(data, 1) < 0)
+ perror("ftp: listen");
+ if (sendport) {
+ a = (char *)&data_addr.sin_addr;
+ p = (char *)&data_addr.sin_port;
+#define UC(b) (((int)b)&0xff)
+ result =
+ command("PORT %d,%d,%d,%d,%d,%d",
+ UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ UC(p[0]), UC(p[1]));
+ if (result == ERROR && sendport == -1) {
+ sendport = 0;
+ tmpno = 1;
+ goto noport;
+ }
+ return (result != COMPLETE);
+ }
+ if (tmpno)
+ sendport = 1;
+#ifdef IP_TOS
+#ifdef IPTOS_THROUGHPUT
+ on = IPTOS_THROUGHPUT;
+ if (setsockopt(data, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
+ perror("ftp: setsockopt TOS (ignored)");
+#endif
+#endif
+ return (0);
+bad:
+ (void) close(data), data = -1;
+ if (tmpno)
+ sendport = 1;
+ return (1);
+}
+
+FILE *
+dataconn(lmode)
+ char *lmode;
+{
+ int s, fromlen = sizeof (hisdataaddr), tos;
+
+#ifndef NO_PASSIVE_MODE
+if (passivemode)
+ return (fdopen(data, lmode));
+#endif
+ s = accept(data, (struct sockaddr *) &hisdataaddr, &fromlen);
+ if (s < 0) {
+ perror("ftp: accept");
+ (void) close(data), data = -1;
+ return (NULL);
+ }
+ (void) close(data);
+ data = s;
+#ifdef IP_TOS
+#ifdef IPTOS_THROUGHPUT
+ tos = IPTOS_THROUGHPUT;
+ if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
+ perror("ftp: setsockopt TOS (ignored)");
+#endif
+#endif
+ return (fdopen(data, lmode));
+}
+
+ptransfer(direction, bytes, t0, t1)
+ char *direction;
+ long bytes;
+ struct timeval *t0, *t1;
+{
+ struct timeval td;
+ float s, kbs;
+
+ if (verbose) {
+ tvsub(&td, t1, t0);
+ s = td.tv_sec + (td.tv_usec / 1000000.);
+#define nz(x) ((x) == 0 ? 1 : (x))
+ kbs = (bytes / nz(s))/1024.0;
+ printf("%ld bytes %s in %.2g seconds (%.2g Kbytes/s)\n",
+ bytes, direction, s, kbs);
+ }
+}
+
+/*tvadd(tsum, t0)
+ struct timeval *tsum, *t0;
+{
+
+ tsum->tv_sec += t0->tv_sec;
+ tsum->tv_usec += t0->tv_usec;
+ if (tsum->tv_usec > 1000000)
+ tsum->tv_sec++, tsum->tv_usec -= 1000000;
+} */
+
+tvsub(tdiff, t1, t0)
+ struct timeval *tdiff, *t1, *t0;
+{
+
+ tdiff->tv_sec = t1->tv_sec - t0->tv_sec;
+ tdiff->tv_usec = t1->tv_usec - t0->tv_usec;
+ if (tdiff->tv_usec < 0)
+ tdiff->tv_sec--, tdiff->tv_usec += 1000000;
+}
+
+sigtype
+psabort(sig)
+ int sig;
+{
+ extern int abrtflag;
+
+ abrtflag++;
+}
+
+pswitch(flag)
+ int flag;
+{
+ extern int proxy, abrtflag;
+ sig_t oldintr;
+ static struct comvars {
+ int connect;
+ char name[MAXHOSTNAMELEN];
+ struct sockaddr_in mctl;
+ struct sockaddr_in hctl;
+ FILE *in;
+ FILE *out;
+ int tpe;
+ int curtpe;
+ int cpnd;
+ int sunqe;
+ int runqe;
+ int mcse;
+ int ntflg;
+ char nti[17];
+ char nto[17];
+ int mapflg;
+ char mi[MAXPATHLEN];
+ char mo[MAXPATHLEN];
+ char *authtype;
+ int lvl;
+#ifdef KERBEROS
+ C_Block session;
+ Key_schedule schedule;
+#endif /* KERBEROS */
+ } proxstruct, tmpstruct;
+ struct comvars *ip, *op;
+
+ abrtflag = 0;
+ oldintr = signal(SIGINT, psabort);
+ if (flag) {
+ if (proxy)
+ return;
+ ip = &tmpstruct;
+ op = &proxstruct;
+ proxy++;
+ } else {
+ if (!proxy)
+ return;
+ ip = &proxstruct;
+ op = &tmpstruct;
+ proxy = 0;
+ }
+ ip->connect = connected;
+ connected = op->connect;
+ if (hostname) {
+ (void) strncpy(ip->name, hostname, sizeof(ip->name) - 1);
+ ip->name[strlen(ip->name)] = '\0';
+ } else
+ ip->name[0] = 0;
+ hostname = op->name;
+ ip->hctl = hisctladdr;
+ hisctladdr = op->hctl;
+ ip->mctl = myctladdr;
+ myctladdr = op->mctl;
+ ip->in = cin;
+ cin = op->in;
+ ip->out = cout;
+ cout = op->out;
+ ip->tpe = type;
+ type = op->tpe;
+ ip->curtpe = curtype;
+ curtype = op->curtpe;
+ ip->cpnd = cpend;
+ cpend = op->cpnd;
+ ip->sunqe = sunique;
+ sunique = op->sunqe;
+ ip->runqe = runique;
+ runique = op->runqe;
+ ip->mcse = mcase;
+ mcase = op->mcse;
+ ip->ntflg = ntflag;
+ ntflag = op->ntflg;
+ (void) strncpy(ip->nti, ntin, 16);
+ (ip->nti)[strlen(ip->nti)] = '\0';
+ (void) strcpy(ntin, op->nti);
+ (void) strncpy(ip->nto, ntout, 16);
+ (ip->nto)[strlen(ip->nto)] = '\0';
+ (void) strcpy(ntout, op->nto);
+ ip->mapflg = mapflag;
+ mapflag = op->mapflg;
+ (void) strncpy(ip->mi, mapin, MAXPATHLEN - 1);
+ (ip->mi)[strlen(ip->mi)] = '\0';
+ (void) strcpy(mapin, op->mi);
+ (void) strncpy(ip->mo, mapout, MAXPATHLEN - 1);
+ (ip->mo)[strlen(ip->mo)] = '\0';
+ (void) strcpy(mapout, op->mo);
+ ip->authtype = auth_type;
+ auth_type = op->authtype;
+ ip->lvl = level;
+ level = op->lvl;
+ if (!level)
+ level = 1;
+#ifdef KERBEROS
+ memcpy(ip->session, cred.session, sizeof(cred.session));
+ memcpy(cred.session, op->session, sizeof(cred.session));
+ memcpy(ip->schedule, schedule, sizeof(schedule));
+ memcpy(schedule, op->schedule, sizeof(schedule));
+#endif /* KERBEROS */
+ (void) signal(SIGINT, oldintr);
+ if (abrtflag) {
+ abrtflag = 0;
+ (*oldintr)(SIGINT);
+ }
+}
+
+jmp_buf ptabort;
+int ptabflg;
+
+sigtype
+abortpt(sig)
+ int sig;
+{
+ printf("\n");
+ (void) fflush(stdout);
+ ptabflg++;
+ mflag = 0;
+ abrtflag = 0;
+ longjmp(ptabort, 1);
+}
+
+proxtrans(cmd, local, remote)
+ char *cmd, *local, *remote;
+{
+ sig_t oldintr;
+ int secndflag = 0, prox_type, nfnd;
+ extern jmp_buf ptabort;
+ char *cmd2;
+ struct fd_set mask;
+ sigtype abortpt();
+
+ if (strcmp(cmd, "RETR"))
+ cmd2 = "RETR";
+ else
+ cmd2 = runique ? "STOU" : "STOR";
+ if ((prox_type = type) == 0) {
+ if (unix_server && unix_proxy)
+ prox_type = TYPE_I;
+ else
+ prox_type = TYPE_A;
+ }
+ if (curtype != prox_type)
+ changetype(prox_type, 1);
+ if (command("PASV") != COMPLETE) {
+ printf("proxy server does not support third party transfers.\n");
+ return;
+ }
+ pswitch(0);
+ if (!connected) {
+ printf("No primary connection\n");
+ pswitch(1);
+ code = -1;
+ return;
+ }
+ if (curtype != prox_type)
+ changetype(prox_type, 1);
+ if (command("PORT %s", pasv) != COMPLETE) {
+ pswitch(1);
+ return;
+ }
+ if (setjmp(ptabort))
+ goto abort;
+ oldintr = signal(SIGINT, abortpt);
+ if (command("%s %s", cmd, remote) != PRELIM) {
+ (void) signal(SIGINT, oldintr);
+ pswitch(1);
+ return;
+ }
+ sleep(2);
+ pswitch(1);
+ secndflag++;
+ if (command("%s %s", cmd2, local) != PRELIM)
+ goto abort;
+ ptflag++;
+ (void) getreply(0);
+ pswitch(0);
+ (void) getreply(0);
+ (void) signal(SIGINT, oldintr);
+ pswitch(1);
+ ptflag = 0;
+ printf("local: %s remote: %s\n", local, remote);
+ return;
+abort:
+ (void) signal(SIGINT, SIG_IGN);
+ ptflag = 0;
+ if (strcmp(cmd, "RETR") && !proxy)
+ pswitch(1);
+ else if (!strcmp(cmd, "RETR") && proxy)
+ pswitch(0);
+ if (!cpend && !secndflag) { /* only here if cmd = "STOR" (proxy=1) */
+ if (command("%s %s", cmd2, local) != PRELIM) {
+ pswitch(0);
+ if (cpend)
+ abort_remote((FILE *) NULL);
+ }
+ pswitch(1);
+ if (ptabflg)
+ code = -1;
+ (void) signal(SIGINT, oldintr);
+ return;
+ }
+ if (cpend)
+ abort_remote((FILE *) NULL);
+ pswitch(!proxy);
+ if (!cpend && !secndflag) { /* only if cmd = "RETR" (proxy=1) */
+ if (command("%s %s", cmd2, local) != PRELIM) {
+ pswitch(0);
+ if (cpend)
+ abort_remote((FILE *) NULL);
+ pswitch(1);
+ if (ptabflg)
+ code = -1;
+ (void) signal(SIGINT, oldintr);
+ return;
+ }
+ }
+ if (cpend)
+ abort_remote((FILE *) NULL);
+ pswitch(!proxy);
+ if (cpend) {
+ FD_ZERO(&mask);
+ FD_SET(fileno(cin), &mask);
+ if ((nfnd = empty(&mask, 10)) <= 0) {
+ if (nfnd < 0) {
+ perror("abort");
+ }
+ if (ptabflg)
+ code = -1;
+ lostpeer();
+ }
+ (void) getreply(0);
+ (void) getreply(0);
+ }
+ if (proxy)
+ pswitch(0);
+ pswitch(1);
+ if (ptabflg)
+ code = -1;
+ (void) signal(SIGINT, oldintr);
+}
+
+reset()
+{
+ struct fd_set mask;
+ int nfnd = 1;
+
+ FD_ZERO(&mask);
+ while (nfnd > 0) {
+ FD_SET(fileno(cin), &mask);
+ if ((nfnd = empty(&mask,0)) < 0) {
+ perror("reset");
+ code = -1;
+ lostpeer();
+ }
+ else if (nfnd) {
+ (void) getreply(0);
+ }
+ }
+}
+
+char *
+gunique(local)
+ char *local;
+{
+ static char new[MAXPATHLEN];
+ char *cp = strrchr(local, '/');
+ int d, count=0;
+ char ext = '1';
+
+ if (cp)
+ *cp = '\0';
+ d = access(cp ? local : ".", 2);
+ if (cp)
+ *cp = '/';
+ if (d < 0) {
+ fprintf(stderr, "local: %s: %s\n", local, strerror(errno));
+ return((char *) 0);
+ }
+ (void) strcpy(new, local);
+ cp = new + strlen(new);
+ *cp++ = '.';
+ while (!d) {
+ if (++count == 100) {
+ printf("runique: can't find unique file name.\n");
+ return((char *) 0);
+ }
+ *cp++ = ext;
+ *cp = '\0';
+ if (ext == '9')
+ ext = '0';
+ else
+ ext++;
+ if ((d = access(new, 0)) < 0)
+ break;
+ if (ext != '0')
+ cp--;
+ else if (*(cp - 2) == '.')
+ *(cp - 1) = '1';
+ else {
+ *(cp - 2) = *(cp - 2) + 1;
+ cp--;
+ }
+ }
+ return(new);
+}
+
+#ifdef KERBEROS
+char realm[REALM_SZ + 1];
+#endif /* KERBEROS */
+
+#ifdef GSSAPI
+/* for testing, we don't have an ftp key yet */
+char* gss_services[] = { /* "ftp",*/ "host", 0 };
+#endif /* GSSAPI */
+
+do_auth()
+{
+ extern int setsafe();
+ int oldverbose;
+#ifdef KERBEROS
+ char *service, inst[INST_SZ];
+ u_long cksum, checksum = (u_long) getpid();
+#endif /* KERBEROS */
+#if defined(KERBEROS) || defined(GSSAPI)
+ u_char out_buf[FTP_BUFSIZ];
+ int i;
+#endif /* KERBEROS */
+
+ if (auth_type) return(1); /* auth already succeeded */
+
+ /* Other auth types go here ... */
+
+#ifdef KERBEROS
+ if (command("AUTH %s", "KERBEROS_V4") == CONTINUE) {
+ if (verbose)
+ printf("%s accepted as authentication type\n", "KERBEROS_V4");
+
+ strcpy(inst, (char *) krb_get_phost(hostname));
+ if (realm[0] == '\0')
+ strcpy(realm, (char *) krb_realmofhost(hostname));
+ if ((kerror = krb_mk_req(&ticket, service = "ftp",
+ inst, realm, checksum))
+ && (kerror != KDC_PR_UNKNOWN ||
+ (kerror = krb_mk_req(&ticket, service = "rcmd",
+ inst, realm, checksum))))
+ fprintf(stderr, "Kerberos V4 krb_mk_req failed: %s\n",
+ krb_get_err_text(kerror));
+ else if (kerror = krb_get_cred(service, inst, realm, &cred))
+ fprintf(stderr, "Kerberos V4 krb_get_cred failed: %s\n",
+ krb_get_err_text(kerror));
+ else {
+ key_sched(cred.session, schedule);
+ reply_parse = "ADAT=";
+ oldverbose = verbose;
+ verbose = 0;
+ i = ticket.length;
+ if (kerror = radix_encode(ticket.dat, out_buf, &i, 0))
+ fprintf(stderr, "Base 64 encoding failed: %s\n",
+ radix_error(kerror));
+ else if (command("ADAT %s", out_buf) != COMPLETE)
+ fprintf(stderr, "Kerberos V4 authentication failed\n");
+ else if (!reply_parse)
+ fprintf(stderr,
+ "No authentication data received from server\n");
+ else if (kerror = radix_encode(reply_parse, out_buf, &i, 1))
+ fprintf(stderr, "Base 64 decoding failed: %s\n",
+ radix_error(kerror));
+ else if (kerror = krb_rd_safe(out_buf, i, &cred.session,
+ &hisctladdr, &myctladdr, &msg_data))
+ fprintf(stderr, "Kerberos V4 krb_rd_safe failed: %s\n",
+ krb_get_err_text(kerror));
+ else {
+ /* fetch the (modified) checksum */
+ (void) memcpy(&cksum, msg_data.app_data, sizeof(cksum));
+ if (ntohl(cksum) == checksum + 1) {
+ verbose = oldverbose;
+ if (verbose)
+ printf("Kerberos V4 authentication succeeded\n");
+ reply_parse = NULL;
+ auth_type = "KERBEROS_V4";
+ return(1);
+ } else fprintf(stderr,
+ "Kerberos V4 mutual authentication failed\n");
+ }
+ verbose = oldverbose;
+ reply_parse = NULL;
+ }
+ } else fprintf(stderr, "%s rejected as an authentication type\n",
+ "KERBEROS_V4");
+#endif /* KERBEROS */
+#ifdef GSSAPI
+ if (command("AUTH %s", "GSSAPI") == CONTINUE) {
+ OM_uint32 maj_stat, min_stat;
+ gss_name_t target_name;
+ gss_buffer_desc send_tok, recv_tok, *token_ptr;
+ char stbuf[FTP_BUFSIZ];
+ char **service_name, **end_service_name;
+ int comcode;
+ struct gss_channel_bindings_struct chan;
+ chan.initiator_addrtype = GSS_C_AF_INET; /* OM_uint32 */
+ chan.initiator_address.length = 4;
+ chan.initiator_address.value = &myctladdr.sin_addr.s_addr;
+ chan.acceptor_addrtype = GSS_C_AF_INET; /* OM_uint32 */
+ chan.acceptor_address.length = 4;
+ chan.acceptor_address.value = &hisctladdr.sin_addr.s_addr;
+ chan.application_data.length = 0;
+ chan.application_data.value = 0;
+
+ for (end_service_name = gss_services; *end_service_name; )
+ end_service_name++;
+ end_service_name--;
+
+ if (verbose)
+ printf("%s accepted as authentication type\n", "GSSAPI");
+
+ /* blob from gss-client */
+
+
+ for (service_name = gss_services; *service_name; service_name++) {
+
+ /* ftp@hostname first, the host@hostname */
+ /* the V5 GSSAPI binding canonicalizes this for us... */
+ sprintf(stbuf, "%s@%s", *service_name, hostname);
+ if (debug)
+ fprintf(stderr, "Trying to authenticate to <%s>\n", stbuf);
+
+ send_tok.value = stbuf;
+ send_tok.length = strlen(stbuf) + 1;
+ maj_stat = gss_import_name(&min_stat, &send_tok,
+ gss_nt_service_name, &target_name);
+
+ if (maj_stat != GSS_S_COMPLETE) {
+ user_gss_error(maj_stat, min_stat, "parsing name");
+ secure_error("name parsed <%s>\n", stbuf);
+ continue;
+ }
+
+ token_ptr = GSS_C_NO_BUFFER;
+ gcontext = GSS_C_NO_CONTEXT; /* structure copy */
+
+ do {
+ if (debug)
+ fprintf(stderr, "calling gss_init_sec_context\n");
+ maj_stat =
+ gss_init_sec_context(&min_stat,
+ GSS_C_NO_CREDENTIAL,
+ &gcontext,
+ target_name,
+ GSS_C_NULL_OID,
+ GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG,
+ 0,
+ &chan, /* channel bindings */
+ token_ptr,
+ NULL, /* ignore mech type */
+ &send_tok,
+ NULL, /* ignore ret_flags */
+ NULL); /* ignore time_rec */
+
+
+ if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED){
+ user_gss_error(maj_stat, min_stat, "initializing context");
+ (void) gss_release_name(&min_stat, &target_name);
+ /* could just be that we missed on the service name */
+ goto outer_loop;
+ }
+
+ if (send_tok.length != 0) {
+ int len = send_tok.length;
+ reply_parse = "ADAT="; /* for command() later */
+ oldverbose = verbose;
+ verbose = 0;
+ kerror = radix_encode(send_tok.value, out_buf, &len, 0);
+ if (kerror) {
+ fprintf(stderr, "Base 64 encoding failed: %s\n",
+ radix_error(kerror));
+ } else if ((comcode = command("ADAT %s", out_buf))!=COMPLETE
+ /* && comcode != 3 (335)*/) {
+ fprintf(stderr, "GSSAPI ADAT failed\n");
+ /* force out of loop */
+ maj_stat = GSS_S_FAILURE;
+ } else if (!reply_parse) {
+ fprintf(stderr,
+ "No authentication data received from server\n");
+ if (maj_stat == GSS_S_COMPLETE) {
+ fprintf(stderr, "...but no more was needed\n");
+ goto gss_complete_loop;
+ } else {
+ user_gss_error(maj_stat, min_stat, "no reply, huh?");
+ goto gss_complete_loop;
+ }
+ } else if (kerror = radix_encode(reply_parse,out_buf,&i,1)) {
+ fprintf(stderr, "Base 64 decoding failed: %s\n",
+ radix_error(kerror));
+ } else {
+ /* everything worked */
+ token_ptr = &recv_tok;
+ recv_tok.value = out_buf;
+ recv_tok.length = i;
+ continue;
+ }
+
+ /* get out of loop clean */
+ gss_complete_loop:
+ service_name = end_service_name;
+ gss_release_buffer(&min_stat, &send_tok);
+ gss_release_name(&min_stat, &target_name);
+ goto outer_loop;
+ }
+ } while (maj_stat == GSS_S_CONTINUE_NEEDED);
+ outer_loop:
+ ;
+ }
+ verbose = oldverbose;
+ if (maj_stat == GSS_S_COMPLETE) {
+ if (verbose)
+ printf("GSSAPI authentication succeeded\n");
+ reply_parse = NULL;
+ auth_type = "GSSAPI";
+ return(1);
+ } else {
+ fprintf(stderr, "GSSAPI authentication failed\n");
+ verbose = oldverbose;
+ reply_parse = NULL;
+ }
+ }
+#endif /* GSSAPI */
+
+ /* Other auth types go here ... */
+
+ return(0);
+}
+
+setpbsz(size)
+unsigned int size;
+{
+ int oldverbose;
+
+ if (ucbuf) (void) free(ucbuf);
+ actualbuf = size;
+ while ((ucbuf = (unsigned char *)malloc(actualbuf)) == NULL)
+ if (actualbuf)
+ actualbuf >>= 2;
+ else {
+ perror("Error while trying to malloc PROT buffer:");
+ exit(1);
+ }
+ oldverbose = verbose;
+ verbose = 0;
+ reply_parse = "PBSZ=";
+ if (command("PBSZ %u", actualbuf) != COMPLETE)
+ fatal("Cannot set PROT buffer size");
+ if (reply_parse) {
+ if ((maxbuf = (unsigned int) atol(reply_parse)) > actualbuf)
+ maxbuf = actualbuf;
+ } else maxbuf = actualbuf;
+ reply_parse = NULL;
+ verbose = oldverbose;
+}
+
+abort_remote(din)
+FILE *din;
+{
+ char buf[FTP_BUFSIZ];
+ int nfnd;
+ struct fd_set mask;
+
+ /*
+ * send IAC in urgent mode instead of DM because 4.3BSD places oob mark
+ * after urgent byte rather than before as is protocol now
+ */
+ sprintf(buf, "%c%c%c", IAC, IP, IAC);
+ if (send(fileno(cout), buf, 3, MSG_OOB) != 3)
+ perror("abort");
+ putc(DM, cout);
+ (void) secure_command("ABOR");
+ FD_ZERO(&mask);
+ FD_SET(fileno(cin), &mask);
+ if (din) {
+ FD_SET(fileno(din), &mask);
+ }
+ if ((nfnd = empty(&mask, 10)) <= 0) {
+ if (nfnd < 0) {
+ perror("abort");
+ }
+ if (ptabflg)
+ code = -1;
+ lostpeer();
+ }
+ if (din && FD_ISSET(fileno(din), &mask)) {
+ /* Security: No threat associated with this read. */
+ while (read(fileno(din), buf, FTP_BUFSIZ) > 0)
+ /* LOOP */;
+ }
+ if (getreply(0) == ERROR && code == 552) {
+ /* 552 needed for nic style abort */
+ (void) getreply(0);
+ }
+ (void) getreply(0);
+}
+#ifdef GSSAPI
+user_gss_error(maj_stat, min_stat, s)
+OM_uint32 maj_stat, min_stat;
+char *s;
+{
+ /* a lot of work just to report the error */
+ OM_uint32 gmaj_stat, gmin_stat;
+ gss_buffer_desc msg;
+ int msg_ctx;
+ msg_ctx = 0;
+ while (!msg_ctx) {
+ gmaj_stat = gss_display_status(&gmin_stat, maj_stat,
+ GSS_C_GSS_CODE,
+ GSS_C_NULL_OID,
+ &msg_ctx, &msg);
+ if ((gmaj_stat == GSS_S_COMPLETE)||
+ (gmaj_stat == GSS_S_CONTINUE_NEEDED)) {
+ fprintf(stderr, "GSSAPI error major: %s\n",
+ (char*)msg.value);
+ (void) gss_release_buffer(&gmin_stat, &msg);
+ }
+ if (gmaj_stat != GSS_S_CONTINUE_NEEDED)
+ break;
+ }
+ msg_ctx = 0;
+ while (!msg_ctx) {
+ gmaj_stat = gss_display_status(&gmin_stat, min_stat,
+ GSS_C_MECH_CODE,
+ GSS_C_NULL_OID,
+ &msg_ctx, &msg);
+ if ((gmaj_stat == GSS_S_COMPLETE)||
+ (gmaj_stat == GSS_S_CONTINUE_NEEDED)) {
+ fprintf(stderr, "GSSAPI error minor: %s\n",
+ (char*)msg.value);
+ (void) gss_release_buffer(&gmin_stat, &msg);
+ }
+ if (gmaj_stat != GSS_S_CONTINUE_NEEDED)
+ break;
+ }
+ fprintf(stderr, "GSSAPI error: %s\n", s);
+}
+
+secure_gss_error(maj_stat, min_stat, s)
+ OM_uint32 maj_stat, min_stat;
+ char *s;
+{
+ return user_gss_error(maj_stat, min_stat, s);
+}
+#endif /* GSSAPI */
--- /dev/null
+/*
+ * Copyright (c) 1985, 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ftp_var.h 5.9 (Berkeley) 6/1/90
+ */
+
+/*
+ * FTP global variables.
+ */
+
+#ifdef DEFINITIONS
+#define extern
+#endif
+
+/*
+ * Options and other state info.
+ */
+extern int trace; /* trace packets exchanged */
+extern int hash; /* print # for each buffer transferred */
+extern int sendport; /* use PORT cmd for each data connection */
+extern int verbose; /* print messages coming back from server */
+extern int connected; /* connected to server */
+extern int fromatty; /* input is from a terminal */
+extern int interactive; /* interactively prompt on m* cmds */
+extern int debug; /* debugging level */
+extern int bell; /* ring bell on cmd completion */
+extern int doglob; /* glob local file names */
+extern int autologin; /* establish user account on connection */
+extern int proxy; /* proxy server connection active */
+extern int proxflag; /* proxy connection exists */
+extern int sunique; /* store files on server with unique name */
+extern int runique; /* store local files with unique name */
+extern int mcase; /* map upper to lower case for mget names */
+extern int ntflag; /* use ntin ntout tables for name translation */
+extern int mapflag; /* use mapin mapout templates on file names */
+extern int code; /* return/reply code for ftp command */
+extern int crflag; /* if 1, strip car. rets. on ascii gets */
+extern char pasv[64]; /* passive port for proxy data connection */
+#ifndef NO_PASSIVE_MODE
+extern int passivemode; /* passive mode enabled */
+#endif
+extern char *altarg; /* argv[1] with no shell-like preprocessing */
+extern char ntin[17]; /* input translation table */
+extern char ntout[17]; /* output translation table */
+#include <sys/param.h>
+extern char mapin[MAXPATHLEN]; /* input map template */
+extern char mapout[MAXPATHLEN]; /* output map template */
+extern int level; /* protection level */
+extern int type; /* requested file transfer type */
+extern int curtype; /* current file transfer type */
+extern int stru; /* file transfer structure */
+extern int form; /* file transfer format */
+extern int mode; /* file transfer mode */
+extern char bytename[32]; /* local byte size in ascii */
+extern int bytesize; /* local byte size in binary */
+
+extern char *hostname; /* name of host connected to */
+extern int unix_server; /* server is unix, can use binary for ascii */
+extern int unix_proxy; /* proxy is unix, can use binary for ascii */
+
+extern struct servent *sp; /* service spec for tcp/ftp */
+
+#include <setjmp.h>
+extern jmp_buf toplevel; /* non-local goto stuff for cmd scanner */
+
+extern char line[200]; /* input line buffer */
+extern char *stringbase; /* current scan point in line buffer */
+extern char argbuf[200]; /* argument storage buffer */
+extern char *argbase; /* current storage point in arg buffer */
+extern int margc; /* count of arguments on input line */
+extern char *margv[20]; /* args parsed from input line */
+extern int cpend; /* flag: if != 0, then pending server reply */
+extern int mflag; /* flag: if != 0, then active multi command */
+
+extern int options; /* used during socket creation */
+
+/*
+ * Format of command table.
+ */
+struct cmd {
+ char *c_name; /* name of command */
+ char *c_help; /* help string */
+ char c_bell; /* give bell when command completes */
+ char c_conn; /* must be connected to use command */
+ char c_proxy; /* proxy server may execute */
+ int (*c_handler)(); /* function to call */
+};
+
+struct macel {
+ char mac_name[9]; /* macro name */
+ char *mac_start; /* start of macro in macbuf */
+ char *mac_end; /* end of macro in macbuf */
+};
+
+extern int macnum; /* number of defined macros */
+extern struct macel macros[16];
+extern char macbuf[4096];
+
+#ifdef DEFINITIONS
+#undef extern
+#endif
+
+extern char *tail();
+extern char *remglob();
+extern int errno;
+extern char *mktemp();
+
+#if defined(STDARG) || (defined(__STDC__) && ! defined(VARARGS))
+extern command(char *, ...);
+#endif
--- /dev/null
+/*
+ * Copyright (c) 1985 Regents of the University of California.
+ * All rights reserved. The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)getpass.c 1.1 90/04/28 SMI"; /* from UCB 5.4 3/7/86 */
+#endif /* not lint */
+
+#include <stdio.h>
+#include <signal.h>
+
+#if defined (POSIX) || defined (POSIX_TERMIOS)
+#include <termios.h>
+static struct termios ttyo, ttyb;
+#define stty(f, t) tcsetattr(f, TCSANOW, t)
+#define gtty(f, t) tcgetattr(f, t)
+#else
+#include <sgtty.h>
+static struct sgttyb ttyo, ttyb;
+#endif
+
+static FILE *fi;
+
+#define sig_t my_sig_t
+#define sigtype krb5_sigtype
+typedef sigtype (*sig_t)();
+
+static sigtype
+intfix(sig)
+ int sig;
+{
+ if (fi != NULL)
+ (void) stty(fileno(fi), &ttyo);
+ exit(SIGINT);
+}
+
+char *
+mygetpass(prompt)
+char *prompt;
+{
+ register char *p;
+ register c;
+ static char pbuf[50+1];
+ sigtype (*sig)();
+
+ if ((fi = fopen("/dev/tty", "r")) == NULL)
+ fi = stdin;
+ else
+ setbuf(fi, (char *)NULL);
+ sig = signal(SIGINT, intfix);
+ (void) gtty(fileno(fi), &ttyb);
+ ttyo = ttyb;
+#if defined (POSIX) || defined (POSIX_TERMIOS)
+ ttyb.c_lflag &= ~ECHO;
+#else
+ ttyb.sg_flags &= ~ECHO;
+#endif
+ (void) stty(fileno(fi), &ttyb);
+ fprintf(stderr, "%s", prompt); (void) fflush(stderr);
+ for (p=pbuf; (c = getc(fi))!='\n' && c!=EOF;) {
+ if (p < &pbuf[sizeof(pbuf)-1])
+ *p++ = c;
+ }
+ *p = '\0';
+ fprintf(stderr, "\n"); (void) fflush(stderr);
+ (void) stty(fileno(fi), &ttyo);
+ (void) signal(SIGINT, sig);
+ if (fi != stdin)
+ (void) fclose(fi);
+ return(pbuf);
+}
--- /dev/null
+/*
+ * Copyright (c) 1980 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)glob.c 5.9 (Berkeley) 2/25/91";
+#endif /* not lint */
+
+/*
+ * C-shell glob for random programs.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+#include <pwd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef POSIX
+#include <limits.h>
+#endif
+
+#ifdef ARG_MAX
+#undef NCARGS
+#define NCARGS ARG_MAX
+#endif
+
+#ifndef NCARGS
+#define NCARGS 4096
+#endif
+
+#define QUOTE 0200
+#define TRIM 0177
+#define eq(a,b) (strcmp(a, b)==0)
+#define GAVSIZ (NCARGS/6)
+#define isdir(d) ((d.st_mode & S_IFMT) == S_IFDIR)
+
+static char **gargv; /* Pointer to the (stack) arglist */
+static int gargc; /* Number args in gargv */
+static int gnleft;
+static short gflag;
+static int tglob();
+char **ftpglob();
+char *globerr;
+char *home;
+extern int errno;
+static char *strspl(), *strend();
+char **copyblk();
+
+static void acollect(), addpath(), collect(), expand(), Gcat();
+static void ginit(), matchdir(), rscan(), sort();
+static int amatch(), execbrc(), match();
+
+static int globcnt;
+
+char *globchars = "`{[*?";
+
+static char *gpath, *gpathp, *lastgpathp;
+static int globbed;
+static char *entp;
+static char **sortbas;
+
+char **
+ftpglob(v)
+ register char *v;
+{
+ char agpath[FTP_BUFSIZ];
+ char *agargv[GAVSIZ];
+ char *vv[2];
+ vv[0] = v;
+ vv[1] = 0;
+ gflag = 0;
+ rscan(vv, tglob);
+ if (gflag == 0)
+ return (copyblk(vv));
+
+ globerr = 0;
+ gpath = agpath; gpathp = gpath; *gpathp = 0;
+ lastgpathp = &gpath[sizeof agpath - 2];
+ ginit(agargv); globcnt = 0;
+ collect(v);
+ if (globcnt == 0 && (gflag&1)) {
+ blkfree(gargv), gargv = 0;
+ return (0);
+ } else
+ return (gargv = copyblk(gargv));
+}
+
+static void
+ginit(agargv)
+ char **agargv;
+{
+
+ agargv[0] = 0; gargv = agargv; sortbas = agargv; gargc = 0;
+ gnleft = NCARGS - 4;
+}
+
+static void
+collect(as)
+ register char *as;
+{
+ if (eq(as, "{") || eq(as, "{}")) {
+ Gcat(as, "");
+ sort();
+ } else
+ acollect(as);
+}
+
+static void
+acollect(as)
+ register char *as;
+{
+ register int ogargc = gargc;
+
+ gpathp = gpath; *gpathp = 0; globbed = 0;
+ expand(as);
+ if (gargc != ogargc)
+ sort();
+}
+
+static void
+sort()
+{
+ register char **p1, **p2, *c;
+ char **Gvp = &gargv[gargc];
+
+ p1 = sortbas;
+ while (p1 < Gvp-1) {
+ p2 = p1;
+ while (++p2 < Gvp)
+ if (strcmp(*p1, *p2) > 0)
+ c = *p1, *p1 = *p2, *p2 = c;
+ p1++;
+ }
+ sortbas = Gvp;
+}
+
+static void
+expand(as)
+ char *as;
+{
+ register char *cs;
+ register char *sgpathp, *oldcs;
+ struct stat stb;
+
+ sgpathp = gpathp;
+ cs = as;
+ if (*cs == '~' && gpathp == gpath) {
+ addpath('~');
+ for (cs++; letter(*cs) || digit(*cs) || *cs == '-';)
+ addpath(*cs++);
+ if (!*cs || *cs == '/') {
+ if (gpathp != gpath + 1) {
+ *gpathp = 0;
+ if (gethdir(gpath + 1))
+ globerr = "Unknown user name after ~";
+ (void) strcpy(gpath, gpath + 1);
+ } else
+ (void) strcpy(gpath, home);
+ gpathp = strend(gpath);
+ }
+ }
+ while (!any(*cs, globchars)) {
+ if (*cs == 0) {
+ if (!globbed)
+ Gcat(gpath, "");
+ else if (stat(gpath, &stb) >= 0) {
+ Gcat(gpath, "");
+ globcnt++;
+ }
+ goto endit;
+ }
+ addpath(*cs++);
+ }
+ oldcs = cs;
+ while (cs > as && *cs != '/')
+ cs--, gpathp--;
+ if (*cs == '/')
+ cs++, gpathp++;
+ *gpathp = 0;
+ if (*oldcs == '{') {
+ (void) execbrc(cs, ((char *)0));
+ return;
+ }
+ matchdir(cs);
+endit:
+ gpathp = sgpathp;
+ *gpathp = 0;
+}
+
+static void
+matchdir(pattern)
+ char *pattern;
+{
+ struct stat stb;
+ register struct dirent *dp;
+ DIR *dirp;
+
+ dirp = opendir(*gpath?gpath:".");
+ if (dirp == NULL) {
+ if (globbed)
+ return;
+ goto patherr2;
+ }
+ /* This fails on systems where you can't inspect the contents of
+ the DIR structure. If there are systems whose opendir does
+ not check for a directory, then use stat, not fstat. */
+#if 0
+ if (fstat(dirp->dd_fd, &stb) < 0)
+ goto patherr1;
+ if (!isdir(stb)) {
+ errno = ENOTDIR;
+ goto patherr1;
+ }
+#endif
+ while ((dp = readdir(dirp)) != NULL) {
+ if (dp->d_ino == 0)
+ continue;
+ if (match(dp->d_name, pattern)) {
+ Gcat(gpath, dp->d_name);
+ globcnt++;
+ }
+ }
+ closedir(dirp);
+ return;
+
+patherr1:
+ closedir(dirp);
+patherr2:
+ globerr = "Bad directory components";
+}
+
+static int
+execbrc(p, s)
+ char *p, *s;
+{
+ char restbuf[FTP_BUFSIZ + 2];
+ register char *pe, *pm, *pl;
+ int brclev = 0;
+ char *lm, savec, *sgpathp;
+
+ for (lm = restbuf; *p != '{'; *lm++ = *p++)
+ continue;
+ for (pe = ++p; *pe; pe++)
+ switch (*pe) {
+
+ case '{':
+ brclev++;
+ continue;
+
+ case '}':
+ if (brclev == 0)
+ goto pend;
+ brclev--;
+ continue;
+
+ case '[':
+ for (pe++; *pe && *pe != ']'; pe++)
+ continue;
+ continue;
+ }
+pend:
+ brclev = 0;
+ for (pl = pm = p; pm <= pe; pm++)
+ switch (*pm & (QUOTE|TRIM)) {
+
+ case '{':
+ brclev++;
+ continue;
+
+ case '}':
+ if (brclev) {
+ brclev--;
+ continue;
+ }
+ goto doit;
+
+ case ','|QUOTE:
+ case ',':
+ if (brclev)
+ continue;
+doit:
+ savec = *pm;
+ *pm = 0;
+ (void) strcpy(lm, pl);
+ (void) strcat(restbuf, pe + 1);
+ *pm = savec;
+ if (s == 0) {
+ sgpathp = gpathp;
+ expand(restbuf);
+ gpathp = sgpathp;
+ *gpathp = 0;
+ } else if (amatch(s, restbuf))
+ return (1);
+ sort();
+ pl = pm + 1;
+ if (brclev)
+ return (0);
+ continue;
+
+ case '[':
+ for (pm++; *pm && *pm != ']'; pm++)
+ continue;
+ if (!*pm)
+ pm--;
+ continue;
+ }
+ if (brclev)
+ goto doit;
+ return (0);
+}
+
+static int
+match(s, p)
+ char *s, *p;
+{
+ register int c;
+ register char *sentp;
+ char sglobbed = globbed;
+
+ if (*s == '.' && *p != '.')
+ return (0);
+ sentp = entp;
+ entp = s;
+ c = amatch(s, p);
+ entp = sentp;
+ globbed = sglobbed;
+ return (c);
+}
+
+static int
+amatch(s, p)
+ register char *s, *p;
+{
+ register int scc;
+ int ok, lc;
+ char *sgpathp;
+ struct stat stb;
+ int c, cc;
+
+ globbed = 1;
+ for (;;) {
+ scc = *s++ & TRIM;
+ switch (c = *p++) {
+
+ case '{':
+ return (execbrc(p - 1, s - 1));
+
+ case '[':
+ ok = 0;
+ lc = 077777;
+ while (cc = *p++) {
+ if (cc == ']') {
+ if (ok)
+ break;
+ return (0);
+ }
+ if (cc == '-') {
+ if (lc <= scc && scc <= *p++)
+ ok++;
+ } else
+ if (scc == (lc = cc))
+ ok++;
+ }
+ if (cc == 0)
+ if (ok)
+ p--;
+ else
+ return 0;
+ continue;
+
+ case '*':
+ if (!*p)
+ return (1);
+ if (*p == '/') {
+ p++;
+ goto slash;
+ }
+ s--;
+ do {
+ if (amatch(s, p))
+ return (1);
+ } while (*s++);
+ return (0);
+
+ case 0:
+ return (scc == 0);
+
+ default:
+ if (c != scc)
+ return (0);
+ continue;
+
+ case '?':
+ if (scc == 0)
+ return (0);
+ continue;
+
+ case '/':
+ if (scc)
+ return (0);
+slash:
+ s = entp;
+ sgpathp = gpathp;
+ while (*s)
+ addpath(*s++);
+ addpath('/');
+ if (stat(gpath, &stb) == 0 && isdir(stb))
+ if (*p == 0) {
+ Gcat(gpath, "");
+ globcnt++;
+ } else
+ expand(p);
+ gpathp = sgpathp;
+ *gpathp = 0;
+ return (0);
+ }
+ }
+}
+
+static
+Gmatch(s, p)
+ register char *s, *p;
+{
+ register int scc;
+ int ok, lc;
+ int c, cc;
+
+ for (;;) {
+ scc = *s++ & TRIM;
+ switch (c = *p++) {
+
+ case '[':
+ ok = 0;
+ lc = 077777;
+ while (cc = *p++) {
+ if (cc == ']') {
+ if (ok)
+ break;
+ return (0);
+ }
+ if (cc == '-') {
+ if (lc <= scc && scc <= *p++)
+ ok++;
+ } else
+ if (scc == (lc = cc))
+ ok++;
+ }
+ if (cc == 0)
+ if (ok)
+ p--;
+ else
+ return 0;
+ continue;
+
+ case '*':
+ if (!*p)
+ return (1);
+ for (s--; *s; s++)
+ if (Gmatch(s, p))
+ return (1);
+ return (0);
+
+ case 0:
+ return (scc == 0);
+
+ default:
+ if ((c & TRIM) != scc)
+ return (0);
+ continue;
+
+ case '?':
+ if (scc == 0)
+ return (0);
+ continue;
+
+ }
+ }
+}
+
+static void
+Gcat(s1, s2)
+ register char *s1, *s2;
+{
+ register int len = strlen(s1) + strlen(s2) + 1;
+
+ if (len >= gnleft || gargc >= GAVSIZ - 1)
+ globerr = "Arguments too long";
+ else {
+ gargc++;
+ gnleft -= len;
+ gargv[gargc] = 0;
+ gargv[gargc - 1] = strspl(s1, s2);
+ }
+}
+
+static void
+addpath(c)
+ char c;
+{
+
+ if (gpathp >= lastgpathp)
+ globerr = "Pathname too long";
+ else {
+ *gpathp++ = c;
+ *gpathp = 0;
+ }
+}
+
+static void
+rscan(t, f)
+ register char **t;
+ int (*f)();
+{
+ register char *p, c;
+
+ while (p = *t++) {
+ if (f == tglob)
+ if (*p == '~')
+ gflag |= 2;
+ else if (eq(p, "{") || eq(p, "{}"))
+ continue;
+ while (c = *p++)
+ (*f)(c);
+ }
+}
+/*
+static
+scan(t, f)
+ register char **t;
+ int (*f)();
+{
+ register char *p, c;
+
+ while (p = *t++)
+ while (c = *p)
+ *p++ = (*f)(c);
+} */
+
+static
+tglob(c)
+ register char c;
+{
+
+ if (any(c, globchars))
+ gflag |= c == '{' ? 2 : 1;
+ return (c);
+}
+/*
+static
+trim(c)
+ char c;
+{
+
+ return (c & TRIM);
+} */
+
+
+letter(c)
+ register char c;
+{
+
+ return (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_');
+}
+
+digit(c)
+ register char c;
+{
+
+ return (c >= '0' && c <= '9');
+}
+
+any(c, s)
+ register int c;
+ register char *s;
+{
+
+ while (*s)
+ if (*s++ == c)
+ return(1);
+ return(0);
+}
+blklen(av)
+ register char **av;
+{
+ register int i = 0;
+
+ while (*av++)
+ i++;
+ return (i);
+}
+
+char **
+blkcpy(oav, bv)
+ char **oav;
+ register char **bv;
+{
+ register char **av = oav;
+
+ while (*av++ = *bv++)
+ continue;
+ return (oav);
+}
+
+blkfree(av0)
+ char **av0;
+{
+ register char **av = av0;
+
+ while (*av)
+ free(*av++);
+}
+
+static
+char *
+strspl(cp, dp)
+ register char *cp, *dp;
+{
+ register char *ep = malloc((unsigned)(strlen(cp) + strlen(dp) + 1));
+
+ if (ep == (char *)0)
+ fatal("Out of memory");
+ (void) strcpy(ep, cp);
+ (void) strcat(ep, dp);
+ return (ep);
+}
+
+char **
+copyblk(v)
+ register char **v;
+{
+ register char **nv = (char **)malloc((unsigned)((blklen(v) + 1) *
+ sizeof(char **)));
+ if (nv == (char **)0)
+ fatal("Out of memory");
+
+ return (blkcpy(nv, v));
+}
+
+static
+char *
+strend(cp)
+ register char *cp;
+{
+
+ while (*cp)
+ cp++;
+ return (cp);
+}
+/*
+ * Extract a home directory from the password file
+ * The argument points to a buffer where the name of the
+ * user whose home directory is sought is currently.
+ * We write the home directory of the user back there.
+ */
+gethdir(home)
+ char *home;
+{
+ register struct passwd *pp = getpwnam(home);
+
+ if (!pp || home + strlen(pp->pw_dir) >= lastgpathp)
+ return (1);
+ (void) strcpy(home, pp->pw_dir);
+ return (0);
+}
--- /dev/null
+/*
+ * Copyright (c) 1985, 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1985, 1989 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 5.18 (Berkeley) 3/1/91";
+#endif /* not lint */
+
+/*
+ * FTP User Program -- Command Interface.
+ */
+#include <stdio.h>
+#include "ftp_var.h"
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#include <arpa/ftp.h>
+
+#include <signal.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <netdb.h>
+#include <pwd.h>
+
+#define sig_t my_sig_t
+#define sigtype krb5_sigtype
+typedef sigtype (*sig_t)();
+
+uid_t getuid();
+sigtype intr(), lostpeer();
+extern char *home;
+char *getlogin();
+#ifdef KERBEROS
+#include <krb.h>
+struct servent staticsp;
+extern char realm[];
+#endif /* KERBEROS */
+
+main(argc, argv)
+ char *argv[];
+{
+ register char *cp;
+ int top;
+ struct passwd *pw = NULL;
+ char homedir[MAXPATHLEN];
+
+ sp = getservbyname("ftp", "tcp");
+ if (sp == 0) {
+ fprintf(stderr, "ftp: ftp/tcp: unknown service\n");
+ exit(1);
+ }
+#ifdef KERBEROS
+/* GDM need to static sp so that the information is not lost
+ when kerberos calls getservbyname */
+ memcpy(&staticsp,sp,sizeof(struct servent));
+ sp = &staticsp;
+#endif /* KERBEROS */
+ doglob = 1;
+ interactive = 1;
+ autologin = 1;
+ argc--, argv++;
+ while (argc > 0 && **argv == '-') {
+ for (cp = *argv + 1; *cp; cp++)
+ switch (*cp) {
+
+ case 'd':
+ options |= SO_DEBUG;
+ debug++;
+ break;
+
+#ifdef KERBEROS
+ case 'k':
+ if (*++cp != '\0')
+ strncpy(realm, ++cp, REALM_SZ);
+ else if (argc > 1) {
+ argc--, argv++;
+ strncpy(realm, *argv, REALM_SZ);
+ }
+ else
+ fprintf(stderr, "ftp: -k expects arguments\n");
+ goto nextopt;
+#endif
+
+ case 'v':
+ verbose++;
+ break;
+
+ case 't':
+ trace++;
+ break;
+
+ case 'i':
+ interactive = 0;
+ break;
+
+ case 'n':
+ autologin = 0;
+ break;
+
+ case 'g':
+ doglob = 0;
+ break;
+
+ default:
+ fprintf(stdout,
+ "ftp: %c: unknown option\n", *cp);
+ exit(1);
+ }
+ nextopt:
+ argc--, argv++;
+ }
+ fromatty = isatty(fileno(stdin));
+ if (fromatty)
+ verbose++;
+ cpend = 0; /* no pending replies */
+ proxy = 0; /* proxy not active */
+#ifndef NO_PASSIVE_MODE
+ passivemode = 1; /* passive mode active */
+#endif
+ crflag = 1; /* strip c.r. on ascii gets */
+ sendport = -1; /* not using ports */
+ /*
+ * Set up the home directory in case we're globbing.
+ */
+ cp = getlogin();
+ if (cp != NULL) {
+ pw = getpwnam(cp);
+ }
+ if (pw == NULL)
+ pw = getpwuid(getuid());
+ if (pw != NULL) {
+ home = homedir;
+ (void) strcpy(home, pw->pw_dir);
+ }
+ if (argc > 0) {
+ if (setjmp(toplevel))
+ exit(0);
+ (void) signal(SIGINT, intr);
+ (void) signal(SIGPIPE, lostpeer);
+ setpeer(argc + 1, argv - 1);
+ }
+ top = setjmp(toplevel) == 0;
+ if (top) {
+ (void) signal(SIGINT, intr);
+ (void) signal(SIGPIPE, lostpeer);
+ }
+ for (;;) {
+ cmdscanner(top);
+ top = 1;
+ }
+}
+
+sigtype
+intr(sig)
+ int sig;
+{
+
+ longjmp(toplevel, 1);
+}
+
+sigtype
+lostpeer(sig)
+ int sig;
+{
+ extern FILE *cout;
+ extern int data;
+ extern char *auth_type;
+ extern int level;
+
+ if (connected) {
+ if (cout != NULL) {
+ (void) shutdown(fileno(cout), 1+1);
+ (void) fclose(cout);
+ cout = NULL;
+ }
+ if (data >= 0) {
+ (void) shutdown(data, 1+1);
+ (void) close(data);
+ data = -1;
+ }
+ connected = 0;
+ auth_type = NULL;
+ level = PROT_C;
+ }
+ pswitch(1);
+ if (connected) {
+ if (cout != NULL) {
+ (void) shutdown(fileno(cout), 1+1);
+ (void) fclose(cout);
+ cout = NULL;
+ }
+ connected = 0;
+ auth_type = NULL;
+ level = PROT_C;
+ }
+ proxflag = 0;
+ pswitch(0);
+}
+
+/*char *
+tail(filename)
+ char *filename;
+{
+ register char *s;
+
+ while (*filename) {
+ s = strrchr(filename, '/');
+ if (s == NULL)
+ break;
+ if (s[1])
+ return (s + 1);
+ *s = '\0';
+ }
+ return (filename);
+}
+*/
+/*
+ * Command parser.
+ */
+cmdscanner(top)
+ int top;
+{
+ register struct cmd *c;
+ register int l;
+ struct cmd *getcmd();
+ extern int help();
+
+ if (!top)
+ (void) putchar('\n');
+ for (;;) {
+ if (fromatty) {
+ printf("ftp> ");
+ (void) fflush(stdout);
+ }
+ if (fgets(line, sizeof line, stdin) == NULL)
+ quit();
+ l = strlen(line);
+ if (l == 0)
+ break;
+ if (line[--l] == '\n') {
+ if (l == 0)
+ break;
+ line[l] = '\0';
+ } else if (l == sizeof(line) - 2) {
+ printf("sorry, input line too long\n");
+ while ((l = getchar()) != '\n' && l != EOF)
+ /* void */;
+ break;
+ } /* else it was a line without a newline */
+ makeargv();
+ if (margc == 0) {
+ continue;
+ }
+ c = getcmd(margv[0]);
+ if (c == (struct cmd *)-1) {
+ printf("?Ambiguous command\n");
+ continue;
+ }
+ if (c == 0) {
+ printf("?Invalid command\n");
+ continue;
+ }
+ if (c->c_conn && !connected) {
+ printf("Not connected.\n");
+ continue;
+ }
+ (*c->c_handler)(margc, margv);
+ if (bell && c->c_bell)
+ (void) putchar('\007');
+ if (c->c_handler != help)
+ break;
+ }
+ (void) signal(SIGINT, intr);
+ (void) signal(SIGPIPE, lostpeer);
+}
+
+struct cmd *
+getcmd(name)
+ register char *name;
+{
+ extern struct cmd cmdtab[];
+ register char *p, *q;
+ register struct cmd *c, *found;
+ register int nmatches, longest;
+
+ longest = 0;
+ nmatches = 0;
+ found = 0;
+ for (c = cmdtab; p = c->c_name; c++) {
+ for (q = name; *q == *p++; q++)
+ if (*q == 0) /* exact match? */
+ return (c);
+ if (!*q) { /* the name was a prefix */
+ if (q - name > longest) {
+ longest = q - name;
+ nmatches = 1;
+ found = c;
+ } else if (q - name == longest)
+ nmatches++;
+ }
+ }
+ if (nmatches > 1)
+ return ((struct cmd *)-1);
+ return (found);
+}
+
+/*
+ * Slice a string up into argc/argv.
+ */
+
+int slrflag;
+
+makeargv()
+{
+ char **argp;
+ char *slurpstring();
+
+ margc = 0;
+ argp = margv;
+ stringbase = line; /* scan from first of buffer */
+ argbase = argbuf; /* store from first of buffer */
+ slrflag = 0;
+ while (*argp++ = slurpstring())
+ margc++;
+}
+
+/*
+ * Parse string into argbuf;
+ * implemented with FSM to
+ * handle quoting and strings
+ */
+char *
+slurpstring()
+{
+ int got_one = 0;
+ register char *sb = stringbase;
+ register char *ap = argbase;
+ char *tmp = argbase; /* will return this if token found */
+
+ if (*sb == '!' || *sb == '$') { /* recognize ! as a token for shell */
+ switch (slrflag) { /* and $ as token for macro invoke */
+ case 0:
+ slrflag++;
+ stringbase++;
+ return ((*sb == '!') ? "!" : "$");
+ /* NOTREACHED */
+ case 1:
+ slrflag++;
+ altarg = stringbase;
+ break;
+ default:
+ break;
+ }
+ }
+
+S0:
+ switch (*sb) {
+
+ case '\0':
+ goto OUT;
+
+ case ' ':
+ case '\t':
+ sb++; goto S0;
+
+ default:
+ switch (slrflag) {
+ case 0:
+ slrflag++;
+ break;
+ case 1:
+ slrflag++;
+ altarg = sb;
+ break;
+ default:
+ break;
+ }
+ goto S1;
+ }
+
+S1:
+ switch (*sb) {
+
+ case ' ':
+ case '\t':
+ case '\0':
+ goto OUT; /* end of token */
+
+ case '\\':
+ sb++; goto S2; /* slurp next character */
+
+ case '"':
+ sb++; goto S3; /* slurp quoted string */
+
+ default:
+ *ap++ = *sb++; /* add character to token */
+ got_one = 1;
+ goto S1;
+ }
+
+S2:
+ switch (*sb) {
+
+ case '\0':
+ goto OUT;
+
+ default:
+ *ap++ = *sb++;
+ got_one = 1;
+ goto S1;
+ }
+
+S3:
+ switch (*sb) {
+
+ case '\0':
+ goto OUT;
+
+ case '"':
+ sb++; goto S1;
+
+ default:
+ *ap++ = *sb++;
+ got_one = 1;
+ goto S3;
+ }
+
+OUT:
+ if (got_one)
+ *ap++ = '\0';
+ argbase = ap; /* update storage pointer */
+ stringbase = sb; /* update scan pointer */
+ if (got_one) {
+ return(tmp);
+ }
+ switch (slrflag) {
+ case 0:
+ slrflag++;
+ break;
+ case 1:
+ slrflag++;
+ altarg = (char *) 0;
+ break;
+ default:
+ break;
+ }
+ return((char *)0);
+}
+
+#define HELPINDENT (sizeof ("directory"))
+
+/*
+ * Help command.
+ * Call each command handler with argc == 0 and argv[0] == name.
+ */
+help(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern struct cmd cmdtab[];
+ register struct cmd *c;
+
+ if (argc == 1) {
+ register int i, j, w, k;
+ int columns, width = 0, lines;
+ extern int NCMDS;
+
+ printf("Commands may be abbreviated. Commands are:\n\n");
+ for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
+ int len = strlen(c->c_name);
+
+ if (len > width)
+ width = len;
+ }
+ width = (width + 8) &~ 7;
+ columns = 80 / width;
+ if (columns == 0)
+ columns = 1;
+ lines = (NCMDS + columns - 1) / columns;
+ for (i = 0; i < lines; i++) {
+ for (j = 0; j < columns; j++) {
+ c = cmdtab + j * lines + i;
+ if (c->c_name && (!proxy || c->c_proxy)) {
+ printf("%s", c->c_name);
+ }
+ else if (c->c_name) {
+ for (k=0; k < strlen(c->c_name); k++) {
+ (void) putchar(' ');
+ }
+ }
+ if (c + lines >= &cmdtab[NCMDS]) {
+ printf("\n");
+ break;
+ }
+ w = strlen(c->c_name);
+ while (w < width) {
+ w = (w + 8) &~ 7;
+ (void) putchar('\t');
+ }
+ }
+ }
+ return;
+ }
+ while (--argc > 0) {
+ register char *arg;
+ arg = *++argv;
+ c = getcmd(arg);
+ if (c == (struct cmd *)-1)
+ printf("?Ambiguous help command %s\n", arg);
+ else if (c == (struct cmd *)0)
+ printf("?Invalid help command %s\n", arg);
+ else
+ printf("%-*s\t%s\n", HELPINDENT,
+ c->c_name, c->c_help);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 5.2 (Berkeley) 6/1/90
+ */
+
+#undef _PATH_TMP
+#define _PATH_TMP "/tmp/ftpXXXXXX"
--- /dev/null
+/*
+ * Copyright (c) 1980 Regents of the University of California.
+ * All rights reserved. The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)pclose.c 1.1 90/04/28 SMI"; /* from UCB 1.2 3/7/86 */
+#endif /* not lint */
+
+#include <stdio.h>
+#include <signal.h>
+#include <sys/param.h>
+#include <sys/wait.h>
+#ifdef HAVE_VFORK_H
+#include <vfork.h>
+#endif
+#define sig_t my_sig_t
+#define sigtype krb5_sigtype
+typedef sigtype (*sig_t)();
+
+#define tst(a,b) (*mode == 'r'? (b) : (a))
+#define RDR 0
+#define WTR 1
+
+static int *popen_pid;
+static int nfiles;
+
+#ifndef HAVE_GETDTABLESIZE
+#include <sys/resource.h>
+int getdtablesize() {
+ struct rlimit rl;
+ getrlimit(RLIMIT_NOFILE, &rl);
+ return rl.rlim_cur;
+}
+#endif
+
+FILE *
+mypopen(cmd,mode)
+ char *cmd;
+ char *mode;
+{
+ int p[2];
+ int myside, hisside, pid;
+
+ if (nfiles <= 0)
+ nfiles = getdtablesize();
+ if (popen_pid == NULL) {
+ popen_pid = (int *)malloc((unsigned) nfiles * sizeof *popen_pid);
+ if (popen_pid == NULL)
+ return (NULL);
+ for (pid = 0; pid < nfiles; pid++)
+ popen_pid[pid] = -1;
+ }
+ if (pipe(p) < 0)
+ return (NULL);
+ myside = tst(p[WTR], p[RDR]);
+ hisside = tst(p[RDR], p[WTR]);
+ if ((pid = vfork()) == 0) {
+ /* myside and hisside reverse roles in child */
+ (void) close(myside);
+ if (hisside != tst(0, 1)) {
+ (void) dup2(hisside, tst(0, 1));
+ (void) close(hisside);
+ }
+ execl("/bin/sh", "sh", "-c", cmd, (char *)NULL);
+ _exit(127);
+ }
+ if (pid == -1) {
+ (void) close(myside);
+ (void) close(hisside);
+ return (NULL);
+ }
+ popen_pid[myside] = pid;
+ (void) close(hisside);
+ return (fdopen(myside, mode));
+}
+
+sigtype
+pabort(sig)
+ int sig;
+{
+ extern int mflag;
+
+ mflag = 0;
+}
+
+mypclose(ptr)
+ FILE *ptr;
+{
+ int child, pid;
+#ifdef USE_SIGPROCMASK
+ sigset_t old, new;
+#else
+ int omask;
+#endif
+ sigtype pabort(), (*istat)();
+#ifdef WAIT_USES_INT
+ int status;
+#else
+ union wait status;
+#endif
+
+ child = popen_pid[fileno(ptr)];
+ popen_pid[fileno(ptr)] = -1;
+ (void) fclose(ptr);
+ if (child == -1)
+ return (-1);
+ istat = signal(SIGINT, pabort);
+#ifdef USE_SIGPROCMASK
+ sigemptyset(&old);
+ sigemptyset(&new);
+ sigaddset(&new,SIGQUIT);
+ sigaddset(&new,SIGHUP);
+ sigprocmask(SIG_BLOCK, &new, &old);
+ while ((pid = wait(&status)) != child && pid != -1)
+ ;
+ sigprocmask(SIG_SETMASK, &old, NULL);
+#else
+ omask = sigblock(sigmask(SIGQUIT)|sigmask(SIGHUP));
+ while ((pid = wait(&status)) != child && pid != -1)
+ ;
+ sigsetmask(omask);
+#endif
+ (void) signal(SIGINT, istat);
+ return (pid == -1 ? -1 : 0);
+}
--- /dev/null
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+static char *radixN =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static char pad = '=';
+
+radix_encode(inbuf, outbuf, len, decode)
+unsigned char inbuf[], outbuf[];
+int *len, decode;
+{
+ int i,j,D;
+ char *p;
+ unsigned char c;
+
+ if (decode) {
+ for (i=0,j=0; inbuf[i] && inbuf[i] != pad; i++) {
+ if ((p = strchr(radixN, inbuf[i])) == NULL) return(1);
+ D = p - radixN;
+ switch (i&3) {
+ case 0:
+ outbuf[j] = D<<2;
+ break;
+ case 1:
+ outbuf[j++] |= D>>4;
+ outbuf[j] = (D&15)<<4;
+ break;
+ case 2:
+ outbuf[j++] |= D>>2;
+ outbuf[j] = (D&3)<<6;
+ break;
+ case 3:
+ outbuf[j++] |= D;
+ }
+ }
+ switch (i&3) {
+ case 1: return(3);
+ case 2: if (D&15) return(3);
+ if (strcmp((char *)&inbuf[i], "==")) return(2);
+ break;
+ case 3: if (D&3) return(3);
+ if (strcmp((char *)&inbuf[i], "=")) return(2);
+ }
+ *len = j;
+ } else {
+ for (i=0,j=0; i < *len; i++)
+ switch (i%3) {
+ case 0:
+ outbuf[j++] = radixN[inbuf[i]>>2];
+ c = (inbuf[i]&3)<<4;
+ break;
+ case 1:
+ outbuf[j++] = radixN[c|inbuf[i]>>4];
+ c = (inbuf[i]&15)<<2;
+ break;
+ case 2:
+ outbuf[j++] = radixN[c|inbuf[i]>>6];
+ outbuf[j++] = radixN[inbuf[i]&63];
+ c = 0;
+ }
+ if (i%3) outbuf[j++] = radixN[c];
+ switch (i%3) {
+ case 1: outbuf[j++] = pad;
+ case 2: outbuf[j++] = pad;
+ }
+ outbuf[*len = j] = '\0';
+ }
+ return(0);
+}
+
+char *
+radix_error(e)
+{
+ switch (e) {
+ case 0: return("Success");
+ case 1: return("Bad character in encoding");
+ case 2: return("Encoding not properly padded");
+ case 3: return("Decoded # of bits not a multiple of 8");
+ default: return("Unknown error");
+ }
+}
+
+#ifdef STANDALONE
+usage(s)
+char *s;
+{
+ fprintf(stderr, "Usage: %s [ -d ] [ string ]\n", s);
+ exit(2);
+}
+
+static int n;
+
+putbuf(inbuf, outbuf, len, decode)
+unsigned char inbuf[], outbuf[];
+int len, decode;
+{
+ int c;
+
+ if (c = radix_encode(inbuf, outbuf, &len, decode)) {
+ fprintf(stderr, "Couldn't %scode input: %s\n",
+ decode ? "de" : "en", radix_error(c));
+ exit(1);
+ }
+ if (decode)
+ write(1, outbuf, len);
+ else
+ for (c = 0; c < len;) {
+ putchar(outbuf[c++]);
+ if (++n%76 == 0) putchar('\n');
+ }
+}
+
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ unsigned char *inbuf, *outbuf;
+ int c, len = 0, decode = 0;
+ extern int optind;
+
+ while ((c = getopt(argc, argv, "d")) != EOF)
+ switch(c) {
+ default:
+ usage(argv[0]);
+ case 'd':
+ decode++;
+ }
+
+ switch (argc - optind) {
+ case 0:
+ inbuf = (unsigned char *) malloc(5);
+ outbuf = (unsigned char *) malloc(5);
+ while ((c = getchar()) != EOF)
+ if (c != '\n') {
+ inbuf[len++] = c;
+ if (len == (decode ? 4 : 3)) {
+ inbuf[len] = '\0';
+ putbuf(inbuf, outbuf, len, decode);
+ len=0;
+ }
+ }
+ if (len) {
+ inbuf[len] = '\0';
+ putbuf(inbuf, outbuf, len, decode);
+ }
+ break;
+ case 1:
+ inbuf = (unsigned char *)argv[optind];
+ len = strlen(inbuf);
+ outbuf = (unsigned char *)
+ malloc((len * (decode?3:4)) / (decode?4:3) + 1);
+ putbuf(inbuf, outbuf, len, decode);
+ break;
+ default:
+ fprintf(stderr, "Only one argument allowed\n");
+ usage(argv[0]);
+ }
+ if (n%76) putchar('\n');
+ exit(0);
+}
+#endif /* STANDALONE */
--- /dev/null
+/*
+ * Copyright (c) 1985 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)ruserpass.c 5.3 (Berkeley) 3/1/91";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef POSIX
+#include <stdlib.h>
+#endif
+#include <ctype.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include "ftp_var.h"
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+char *renvlook(), *getenv();
+static FILE *cfile;
+
+#define DEFAULT 1
+#define LOGIN 2
+#define PASSWD 3
+#define ACCOUNT 4
+#define MACDEF 5
+#define ID 10
+#define MACH 11
+
+static char tokval[100];
+
+static struct toktab {
+ char *tokstr;
+ int tval;
+} toktab[]= {
+ "default", DEFAULT,
+ "login", LOGIN,
+ "password", PASSWD,
+ "passwd", PASSWD,
+ "account", ACCOUNT,
+ "machine", MACH,
+ "macdef", MACDEF,
+ 0, 0
+};
+
+
+static
+token()
+{
+ char *cp;
+ int c;
+ struct toktab *t;
+
+ if (feof(cfile))
+ return (0);
+ while ((c = getc(cfile)) != EOF &&
+ (c == '\n' || c == '\t' || c == ' ' || c == ','))
+ continue;
+ if (c == EOF)
+ return (0);
+ cp = tokval;
+ if (c == '"') {
+ while ((c = getc(cfile)) != EOF && c != '"') {
+ if (c == '\\')
+ c = getc(cfile);
+ *cp++ = c;
+ }
+ } else {
+ *cp++ = c;
+ while ((c = getc(cfile)) != EOF
+ && c != '\n' && c != '\t' && c != ' ' && c != ',') {
+ if (c == '\\')
+ c = getc(cfile);
+ *cp++ = c;
+ }
+ }
+ *cp = 0;
+ if (tokval[0] == 0)
+ return (0);
+ for (t = toktab; t->tokstr; t++)
+ if (!strcmp(t->tokstr, tokval))
+ return (t->tval);
+ return (ID);
+}
+
+ruserpass(host, aname, apass, aacct)
+ char *host, **aname, **apass, **aacct;
+{
+ char *hdir, buf[FTP_BUFSIZ], *tmp;
+ char myname[MAXHOSTNAMELEN], *mydomain;
+ int t, i, c, usedefault = 0;
+ struct stat stb;
+
+ hdir = getenv("HOME");
+ if (hdir == NULL)
+ hdir = ".";
+ (void) sprintf(buf, "%s/.netrc", hdir);
+ cfile = fopen(buf, "r");
+ if (cfile == NULL) {
+ if (errno != ENOENT)
+ perror(buf);
+ return(0);
+ }
+ if (gethostname(myname, sizeof(myname)) < 0)
+ myname[0] = '\0';
+ if ((mydomain = strchr(myname, '.')) == NULL)
+ mydomain = "";
+next:
+ while ((t = token())) switch(t) {
+
+ case DEFAULT:
+ usedefault = 1;
+ /* FALL THROUGH */
+
+ case MACH:
+ if (!usedefault) {
+ if (token() != ID)
+ continue;
+ /*
+ * Allow match either for user's input host name
+ * or official hostname. Also allow match of
+ * incompletely-specified host in local domain.
+ */
+ if (strcasecmp(host, tokval) == 0)
+ goto match;
+ if (strcasecmp(hostname, tokval) == 0)
+ goto match;
+ if ((tmp = strchr(hostname, '.')) != NULL &&
+ strcasecmp(tmp, mydomain) == 0 &&
+ strncasecmp(hostname, tokval, tmp-hostname) == 0 &&
+ tokval[tmp - hostname] == '\0')
+ goto match;
+ if ((tmp = strchr(host, '.')) != NULL &&
+ strcasecmp(tmp, mydomain) == 0 &&
+ strncasecmp(host, tokval, tmp - host) == 0 &&
+ tokval[tmp - host] == '\0')
+ goto match;
+ continue;
+ }
+ match:
+ while ((t = token()) && t != MACH && t != DEFAULT) switch(t) {
+
+ case LOGIN:
+ if (token())
+ if (*aname == 0) {
+ *aname = malloc((unsigned) strlen(tokval) + 1);
+ (void) strcpy(*aname, tokval);
+ } else {
+ if (strcmp(*aname, tokval))
+ goto next;
+ }
+ break;
+ case PASSWD:
+ if (strcmp(*aname, "anonymous") &&
+ fstat(fileno(cfile), &stb) >= 0 &&
+ (stb.st_mode & 077) != 0) {
+ fprintf(stderr, "Error - .netrc file not correct mode.\n");
+ fprintf(stderr, "Remove password or correct mode.\n");
+ goto bad;
+ }
+ if (token() && *apass == 0) {
+ *apass = malloc((unsigned) strlen(tokval) + 1);
+ (void) strcpy(*apass, tokval);
+ }
+ break;
+ case ACCOUNT:
+ if (fstat(fileno(cfile), &stb) >= 0
+ && (stb.st_mode & 077) != 0) {
+ fprintf(stderr, "Error - .netrc file not correct mode.\n");
+ fprintf(stderr, "Remove account or correct mode.\n");
+ goto bad;
+ }
+ if (token() && *aacct == 0) {
+ *aacct = malloc((unsigned) strlen(tokval) + 1);
+ (void) strcpy(*aacct, tokval);
+ }
+ break;
+ case MACDEF:
+ if (proxy) {
+ (void) fclose(cfile);
+ return(0);
+ }
+ while ((c=getc(cfile)) != EOF && c == ' ' || c == '\t');
+ if (c == EOF || c == '\n') {
+ printf("Missing macdef name argument.\n");
+ goto bad;
+ }
+ if (macnum == 16) {
+ printf("Limit of 16 macros have already been defined\n");
+ goto bad;
+ }
+ tmp = macros[macnum].mac_name;
+ *tmp++ = c;
+ for (i=0; i < 8 && (c=getc(cfile)) != EOF &&
+ !isspace(c); ++i) {
+ *tmp++ = c;
+ }
+ if (c == EOF) {
+ printf("Macro definition missing null line terminator.\n");
+ goto bad;
+ }
+ *tmp = '\0';
+ if (c != '\n') {
+ while ((c=getc(cfile)) != EOF && c != '\n');
+ }
+ if (c == EOF) {
+ printf("Macro definition missing null line terminator.\n");
+ goto bad;
+ }
+ if (macnum == 0) {
+ macros[macnum].mac_start = macbuf;
+ }
+ else {
+ macros[macnum].mac_start = macros[macnum-1].mac_end + 1;
+ }
+ tmp = macros[macnum].mac_start;
+ while (tmp != macbuf + 4096) {
+ if ((c=getc(cfile)) == EOF) {
+ printf("Macro definition missing null line terminator.\n");
+ goto bad;
+ }
+ *tmp = c;
+ if (*tmp == '\n') {
+ if (*(tmp-1) == '\0') {
+ macros[macnum++].mac_end = tmp - 1;
+ break;
+ }
+ *tmp = '\0';
+ }
+ tmp++;
+ }
+ if (tmp == macbuf + 4096) {
+ printf("4K macro buffer exceeded\n");
+ goto bad;
+ }
+ break;
+ default:
+ fprintf(stderr, "Unknown .netrc keyword %s\n", tokval);
+ break;
+ }
+ goto done;
+ }
+done:
+ (void) fclose(cfile);
+ return(0);
+bad:
+ (void) fclose(cfile);
+ return(-1);
+}
--- /dev/null
+/*
+ * Shared routines for client and server for
+ * secure read(), write(), getc(), and putc().
+ * Only one security context, thus only work on one fd at a time!
+ */
+
+#include "secure.h" /* stuff which is specific to client or server */
+
+#ifdef KERBEROS
+#include <krb.h>
+
+CRED_DECL
+extern KTEXT_ST ticket;
+extern MSG_DAT msg_data;
+extern Key_schedule schedule;
+#endif /* KERBEROS */
+#ifdef GSSAPI
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+extern gss_ctx_id_t gcontext;
+#endif /* GSSAPI */
+
+#include <arpa/ftp.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <errno.h>
+extern int errno;
+extern char *sys_errlist[];
+
+extern struct sockaddr_in hisaddr;
+extern struct sockaddr_in myaddr;
+extern int level;
+extern char *auth_type;
+
+#define MAX maxbuf
+extern unsigned int maxbuf; /* maximum output buffer size */
+extern unsigned char *ucbuf; /* cleartext buffer */
+static unsigned int nout, bufp; /* number of chars in ucbuf,
+ * pointer into ucbuf */
+
+#ifdef KERBEROS
+#define FUDGE_FACTOR 32 /* Amount of growth
+ * from cleartext to ciphertext.
+ * krb_mk_priv adds this # bytes.
+ * Must be defined for each auth type.
+ */
+#endif /* KERBEROS */
+
+#ifndef FUDGE_FACTOR /* In case no auth types define it. */
+#define FUDGE_FACTOR 0
+#endif
+
+#ifdef KERBEROS
+/* XXX - The following must be redefined if KERBEROS_V4 is not used
+ * but some other auth type is. They must have the same properties. */
+#define looping_write krb_net_write
+#define looping_read krb_net_read
+#endif
+
+/* perhaps use these in general, certainly use them for GSSAPI */
+
+#ifndef looping_write
+static int
+looping_write(fd, buf, len)
+ int fd;
+ register const char *buf;
+ int len;
+{
+ int cc;
+ register int wrlen = len;
+ do {
+ cc = write(fd, buf, wrlen);
+ if (cc < 0) {
+ if (errno == EINTR)
+ continue;
+ return(cc);
+ }
+ else {
+ buf += cc;
+ wrlen -= cc;
+ }
+ } while (wrlen > 0);
+ return(len);
+}
+#endif
+#ifndef looping_read
+static int
+looping_read(fd, buf, len)
+ int fd;
+ register char *buf;
+ register int len;
+{
+ int cc, len2 = 0;
+
+ do {
+ cc = read(fd, buf, len);
+ if (cc < 0) {
+ if (errno == EINTR)
+ continue;
+ return(cc); /* errno is already set */
+ }
+ else if (cc == 0) {
+ return(len2);
+ } else {
+ buf += cc;
+ len2 += cc;
+ len -= cc;
+ }
+ } while (len > 0);
+ return(len2);
+}
+#endif
+
+
+#if defined(STDARG) || (defined(__STDC__) && ! defined(VARARGS))
+extern secure_error(char *, ...);
+#else
+extern secure_error();
+#endif
+
+#define ERR -2
+
+static
+secure_putbyte(fd, c)
+int fd;
+unsigned char c;
+{
+ int ret;
+
+ ucbuf[nout++] = c;
+ if (nout == MAX - FUDGE_FACTOR)
+ if (ret = secure_putbuf(fd, ucbuf, nout))
+ return(ret);
+ else nout = 0;
+ return(c);
+}
+
+/* returns:
+ * 0 on success
+ * -1 on error (errno set)
+ * -2 on security error
+ */
+secure_flush(fd)
+int fd;
+{
+ int ret;
+
+ if (level == PROT_C)
+ return(0);
+ if (nout)
+ if (ret = secure_putbuf(fd, ucbuf, nout))
+ return(ret);
+ return(secure_putbuf(fd, "", nout = 0));
+}
+
+/* returns:
+ * c>=0 on success
+ * -1 on error
+ * -2 on security error
+ */
+secure_putc(c, stream)
+char c;
+FILE *stream;
+{
+ if (level == PROT_C)
+ return(putc(c,stream));
+ return(secure_putbyte(fileno(stream), (unsigned char) c));
+}
+
+/* returns:
+ * nbyte on success
+ * -1 on error (errno set)
+ * -2 on security error
+ */
+secure_write(fd, buf, nbyte)
+int fd;
+unsigned char *buf;
+unsigned int nbyte;
+{
+ unsigned int i;
+ int c;
+
+ if (level == PROT_C)
+ return(write(fd,buf,nbyte));
+ for (i=0; nbyte>0; nbyte--)
+ if ((c = secure_putbyte(fd, buf[i++])) < 0)
+ return(c);
+ return(i);
+}
+
+/* returns:
+ * 0 on success
+ * -1 on error (errno set)
+ * -2 on security error
+ */
+secure_putbuf(fd, buf, nbyte)
+int fd;
+unsigned char *buf;
+unsigned int nbyte;
+{
+ static char *outbuf; /* output ciphertext */
+ static unsigned int bufsize; /* size of outbuf */
+ long length;
+ u_long net_len;
+
+ if (bufsize < nbyte + FUDGE_FACTOR) {
+ if (outbuf) (void) free(outbuf);
+ if (outbuf = malloc((unsigned) (nbyte + FUDGE_FACTOR)))
+ bufsize = nbyte + FUDGE_FACTOR;
+ else {
+ bufsize = 0;
+ secure_error("%s (in malloc of PROT buffer)",
+ sys_errlist[errno]);
+ return(ERR);
+ }
+ }
+ /* Other auth types go here ... */
+#ifdef KERBEROS
+ if (strcmp(auth_type, "KERBEROS_V4") == 0)
+ if ((length = level == PROT_P ?
+ krb_mk_priv(buf, (unsigned char *) outbuf, nbyte, schedule,
+ SESSION, &myaddr, &hisaddr)
+ : krb_mk_safe(buf, (unsigned char *) outbuf, nbyte, SESSION,
+ &myaddr, &hisaddr)) == -1) {
+ secure_error("krb_mk_%s failed for KERBEROS_V4",
+ level == PROT_P ? "priv" : "safe");
+ return(ERR);
+ }
+#endif /* KERBEROS */
+#ifdef GSSAPI
+ if (strcmp(auth_type, "GSSAPI") == 0) {
+ gss_buffer_desc in_buf, out_buf;
+ OM_uint32 maj_stat, min_stat;
+ int conf_state;
+
+ in_buf.value = buf;
+ in_buf.length = nbyte;
+ maj_stat = gss_seal(&min_stat, gcontext,
+ (level == PROT_P), /* confidential */
+ GSS_C_QOP_DEFAULT,
+ &in_buf, &conf_state,
+ &out_buf);
+ if (maj_stat != GSS_S_COMPLETE) {
+ /* generally need to deal */
+ /* ie. should loop, but for now just fail */
+ secure_gss_error(maj_stat, min_stat,
+ level == PROT_P?
+ "GSSAPI seal failed":
+ "GSSAPI sign failed");
+ return(ERR);
+ }
+ memcpy(outbuf, out_buf.value, length=out_buf.length);
+ gss_release_buffer(&min_stat, &out_buf);
+
+ }
+#endif /* GSSAPI */
+ net_len = htonl((u_long) length);
+ if (looping_write(fd, &net_len, sizeof(net_len)) == -1) return(-1);
+ if (looping_write(fd, outbuf, length) != length) return(-1);
+ return(0);
+}
+
+static
+secure_getbyte(fd)
+int fd;
+{
+ /* number of chars in ucbuf, pointer into ucbuf */
+ static unsigned int nin, bufp;
+ int kerror;
+ u_long length;
+
+ if (nin == 0) {
+ if ((kerror = looping_read(fd, &length, sizeof(length)))
+ != sizeof(length)) {
+ secure_error("Couldn't read PROT buffer length: %d/%s",
+ kerror,
+ kerror == -1 ? sys_errlist[errno]
+ : "premature EOF");
+ return(ERR);
+ }
+ if ((length = (u_long) ntohl(length)) > MAX) {
+ secure_error("Length (%d) of PROT buffer > PBSZ=%u",
+ length, MAX);
+ return(ERR);
+ }
+ if ((kerror = looping_read(fd, ucbuf, length)) != length) {
+ secure_error("Couldn't read %u byte PROT buffer: %s",
+ length, kerror == -1 ?
+ sys_errlist[errno] : "premature EOF");
+ return(ERR);
+ }
+ /* Other auth types go here ... */
+#ifdef KERBEROS
+ if (strcmp(auth_type, "KERBEROS_V4") == 0) {
+ if (kerror = level == PROT_P ?
+ krb_rd_priv(ucbuf, length, schedule, SESSION,
+ &hisaddr, &myaddr, &msg_data)
+ : krb_rd_safe(ucbuf, length, SESSION,
+ &hisaddr, &myaddr, &msg_data)) {
+ secure_error("krb_rd_%s failed for KERBEROS_V4 (%s)",
+ level == PROT_P ? "priv" : "safe",
+ krb_get_err_text(kerror));
+ return(ERR);
+ }
+ memcpy(ucbuf, msg_data.app_data, msg_data.app_length);
+ nin = bufp = msg_data.app_length;
+ }
+#endif /* KERBEROS */
+#ifdef GSSAPI
+ if (strcmp(auth_type, "GSSAPI") == 0) {
+ gss_buffer_desc xmit_buf, msg_buf;
+ OM_uint32 maj_stat, min_stat;
+ int conf_state;
+
+ xmit_buf.value = ucbuf;
+ xmit_buf.length = length;
+ conf_state = (level == PROT_P);
+ /* decrypt/verify the message */
+ maj_stat = gss_unseal(&min_stat, gcontext, &xmit_buf,
+ &msg_buf, &conf_state, NULL);
+ if (maj_stat != GSS_S_COMPLETE) {
+ secure_gss_error(maj_stat, min_stat,
+ (level == PROT_P)?
+ "failed unsealing ENC message":
+ "failed unsealing MIC message");
+ return ERR;
+ }
+
+ memcpy(ucbuf, msg_buf.value, nin = bufp = msg_buf.length);
+ gss_release_buffer(&min_stat, &msg_buf);
+ }
+#endif /* GSSAPI */
+ /* Other auth types go here ... */
+ }
+ if (nin == 0)
+ return(EOF);
+ else return(ucbuf[bufp - nin--]);
+}
+
+/* returns:
+ * c>=0 on success
+ * -1 on EOF
+ * -2 on security error
+ */
+secure_getc(stream)
+FILE *stream;
+{
+ if (level == PROT_C)
+ return(getc(stream));
+ return(secure_getbyte(fileno(stream)));
+}
+
+/* returns:
+ * n>0 on success (n == # of bytes read)
+ * 0 on EOF
+ * -1 on error (errno set), only for PROT_C
+ * -2 on security error
+ */
+secure_read(fd, buf, nbyte)
+int fd;
+char *buf;
+int nbyte;
+{
+ static int c;
+ int i;
+
+ if (level == PROT_C)
+ return(read(fd,buf,nbyte));
+ if (c == EOF)
+ return(c = 0);
+ for (i=0; nbyte>0; nbyte--)
+ switch (c = secure_getbyte(fd)) {
+ case ERR: return(c);
+ case EOF: if (!i) c = 0;
+ return(i);
+ default: buf[i++] = c;
+ }
+ return(i);
+}
--- /dev/null
+#define CRED_DECL extern CREDENTIALS cred;
+#define SESSION &cred.session
+#define myaddr myctladdr
+#define hisaddr hisdataaddr
--- /dev/null
+This version of ftpd has been fixed to conform to RFC959.
+
+Unfortunately, this conformance introduces a user visible change. While
+technically, this is the fault of the client (ftp) instead of the server
+(ftpd), the change will be seen whenever an old ftp client calls a new ftpd
+server.
+
+The problem is that the old ftpd implemented the NLST command by execing
+/bin/ls. This produced non-conformant output in some cases. The new
+ftpd no longer executes /bin/ls for the NLST command as it has it's own
+built-in code.
+
+The user visible change in the ftp behavior is caused by the ftp client
+"knowing" that the daemon will exec /bin/ls. This assumption should not
+have been made.
+
+When the old ftp client is used, one of the options is the "ls" command
+which sends the command NLST to the ftpd server. The client should really
+be sending the LIST command. The new ftp client has been corrected to do
+this.
+
+NLST should not normally be used directly by humans. It is intended to
+interface with commands like mget or mput.
+
+Users who are not able to upgrade their ftp client may obtain the previous
+behavior, by using the command "dir" instead of "ls".
+
+These changes only apply to those sites using code derived from the Berkeley
+software releases (which means almost every UNIX based implementation will
+see this problem).
+
--- /dev/null
+Sun Jan 14 02:58:42 1996 Mark Eichin <eichin@cygnus.com>
+
+ * ftpd.c (auth_data): call gss_release_cred on the server_creds we
+ acquired, before they leave scope.
+
+Sun Jan 14 02:47:19 1996 Karri Balk - Contractor <kbalk@cup.hp.com>
+
+ * ftpd.c (gss_services): list of gssapi service names to try.
+ (auth_data): loop over gss_services.
+
+Sun Jan 14 01:54:35 1996 Bill Schoofs <wjs@cray.com>
+
+ * Makefile.in (DEFINES): define NOCONFIDENTIAL for future use.
+ * ftpcmd.y (CCC): ditch broken CCC code, return proper 534 code.
+ (PBSZ): return proper code 501 for bad PBSZ argument
+ (PROT): recognize PROT E.
+ (cmdtab): add CCC so as to cleanly reject it.
+ (getline): reject CONF as unsupported (but put in code to
+ potentially recognize it in the future.) Reject protected commands
+ of auth_type isn't yet set.
+ * ftpd.c (setlevel): use 536, not 504, for invalid level, and
+ use levelnames to find the proper name.
+ (user): if kuserok succeeds, respond 232, not 231; if it fails,
+ respond 336 (though 331 might be more appropriate.)
+ (auth): fix spelling error.
+
+Tue Jan 2 19:19:16 1996 Mark Eichin <eichin@cygnus.com>
+
+ * ftpd.c: use HAVE_SETEUID and HAVE_SETRESUID to figure out how
+ to emulate seteuid instead of assuming hpux.
+ * configure.in: test for seteuid, setreuid and setresuid.
+
+Fri Oct 20 17:17:19 1995 Mark Eichin <eichin@cygnus.com>
+
+ * ftpd.c (auth_data): supply correct channel bindings to accept,
+ matching the client changes.
+
+Thu Oct 19 12:22:28 1995 Mark W. Eichin <eichin@cygnus.com>
+
+ * configure.in: check WITH_DBM_LNAME since we use an_to_ln.
+
+Wed Oct 4 19:26:50 1995 Mark Eichin <eichin@cygnus.com>
+
+ * ftpd.c (user): use HAVE_GETUSERSHELL.
+ * configure.in: check for getusershell.
+
+Mon Oct 2 16:43:54 1995 Mark Eichin <eichin@cygnus.com>
+
+ * popen.c (ftpd_popen): malloc all strings, not just globbed ones.
+
+Sun Oct 1 03:31:24 1995 Mark Eichin <eichin@cygnus.com>
+
+ * ftpd.c (auth_data): acquire credentials (currently fixed for
+ service "host".) Fix loop reply logic. Add debugging syslogs. Set
+ auth_type *after* 235 success reply, so it doesn't get encrypted.
+
+Sun Oct 1 00:58:39 1995 Mark Eichin <eichin@cygnus.com>
+
+ * Makefile.in: use FTP_BUFSIZ everywhere and make it large for
+ now.
+ * configure.in: check for headers need to build getdtablesize.
+ * ftpd.c (secure_reply): add GSSAPI hooks.
+ (reply_gss_error): better gssapi error reporting.
+
+
+Sat Sep 30 22:26:25 1995 Mark Eichin <eichin@cygnus.com>
+
+ * ftpd.c: correct gssapi includes. Fix type of client_name. Use
+ gss_ok instead of kerb_ok for GSSAPI case (to simplify future
+ combined code.) Fix some declarations. Fix arguments to
+ gss_accept_sec_context for type.
+ * ftpcmd.y: correct gssapi includes.
+
+Sat Sep 30 21:40:30 1995 Mark Eichin <eichin@cygnus.com>
+
+ * Makefile.in: hook setenv.c and getdtablesize.c from appl/bsd.
+ Use double-colon rules for clean, depend, install.
+ configure.in: check for yacc, SIGTYPE, UTMP, SIGPROCMASK,
+ WAIT_TYPE, getdtablesize, getcwd, setenv (using the test from
+ appl/bsd.)
+ * ftpcmd.y: no conf.h. declare level. Use krb5_sigtype directly.
+ * ftpd.c: use getcwd directly, make -s srvtab KERBEROS specific.
+ (user): return 331 as per draft-8, but suggest 53z.
+ (auth_data): return 535 and 335 and quote draft-8 as to why.
+ (secure_gss_error): generic interface for secure.c functions to
+ call reply_gss_error instead.
+ * logwtmp.c: no conf.h, check NO_UT_HOST.
+ * popen.c: no conf.h, no getdtablesize.
+ (ftpd_pclose): Obey USE_SIGPROCMASK.
+
+Sat Sep 30 16:43:28 1995 Mark Eichin <eichin@cygnus.com>
+
+ * configure.in, Makefile.in: new files for port to GSSAPI and
+ build within the Kerberos V5 build tree.
+ * ftpcmd.y, ftpd.c, secure.c: GSSAPI authentication changes based
+ on the IETF CAT working group ***DRAFT*** FTP Security
+ specification, draft number 8, appendix I.
+
+
+**** previous change logs from CNS V4 modifications of Steve Lunt's
+ draft-3 ftp daemon, which this is based on. ****
+
+Wed Jul 26 21:03:13 1995 Ken Raeburn <raeburn@cygnus.com>
+
+ * secure.c: Include string.h.
+
+ * ftpd.c (main): Cast signal() return value to long instead of
+ int; it's more likely to fit.
+
+Thu Feb 2 13:41:24 1995 Ian Lance Taylor <ian@cygnus.com>
+
+ * ftpcmd.y (NBBY): Explicitly define if __pyrsoft and MIPSEB.
+ (cmd_list): In handling of SYST, undefine BSD if __svr4__ is
+ defined.
+
+ * ftpd.c: Don't try to use IP_TOS if the IP_TOS argument
+ (IPTOS_LOWDELAY, etc.) is not defined.
+
+Wed Jan 18 17:12:22 1995 Ian Lance Taylor <ian@sanguine.cygnus.com>
+
+ * ftpd.8: Include man1/tmac.doc.
+
+Wed Jan 11 15:29:10 1995 Ian Lance Taylor <ian@sanguine.cygnus.com>
+
+ * ftpd.c (authenticate): New variable.
+ (main): Handle -a (require authentication) option.
+ (user): If authenticate is set, reply with an error if kuserok
+ fails or if no Kerberos authentication was used.
+ * ftpcmd.y: Use check_login when parsing the PASV command.
+ * ftpd.8: Document new -a option.
+
+Tue Jan 3 01:25:57 1995 Mark Eichin <eichin@cygnus.com>
+
+ * Makefile.in (clean): explicitly delete ftpcmd.c on clean.
+
+Thu Dec 29 15:17:12 1994 Mark Eichin <eichin@cygnus.com>
+
+ * ftpcmd.y (rcmd): don't declare atol, since it isn't used here
+ anyhow, and it's a macro under linux.
+ (top level): #define NBBY 8 for linux.
+
+Thu Dec 29 14:51:41 1994 Mark Eichin <eichin@cygnus.com>
+
+ * ftpd.c (statcmd): don't use NBBY check -- linux doesn't have it,
+ it is in no spec, and if it isn't 8, it won't work anyway. (Use
+ strcat instead of sprintf, while we're at it...)
+
+Tue Dec 27 16:29:24 1994 Ian Lance Taylor <ian@sanguine.cygnus.com>
+
+ * ftpcmd.y (reply, lreply): Declare if STDARG || (__STDC__ && !
+ VARARGS).
+ * ftpd.c: If STDARG is defined, or if __STDC__ is defined and
+ VARARGS is not defined, include <stdarg.h>.
+ (secure_error): Use <stdarg.h> routines if STDARG || (__STDC__ &&
+ ! VARARGS).
+ (reply, lreply): Likewise.
+ (setproctitle): Just use one argument.
+ * secure.c (secure_error): Declare if STDARG || (__STDC__ && !
+ VARARGS).
+
+Fri Dec 23 16:25:44 1994 Ian Lance Taylor <ian@sanguine.cygnus.com>
+
+ * ftpcmd.y (unix): Define if _AIX is defined (AIX compiler does
+ not predefine unix).
+
+Thu Dec 22 15:05:14 1994 Ian Lance Taylor <ian@sanguine.cygnus.com>
+
+ * ftpd.c (keyfile): New global variable.
+ (main): Move option processing before check of remote socket. Add
+ new options -p, -r, and -s. Handle -p by accepting a remote
+ connection.
+ (kpass): Use keyfile variable rather than KEYFILE. Pass keyfile
+ explicitly to krb_rd_req.
+ (auth_data): Likewise.
+ * ftpd.8: Document new -p, -r, and -s options.
+
+Fri Dec 16 11:06:16 1994 Ian Lance Taylor <ian@cygnus.com>
+
+ Fixes for HP/UX:
+ * ftpd.c: On HP/UX, define seteuid and setegid as macros which
+ call setresuid and setresgid.
+
+ Fixes for UnixWare:
+ * ftpd.c (main): Use a temporary variable rather than calling
+ htons(ntohs(X)).
+ * ftpcmd.y: Include conf.h.
+ (getline): Cast arguments to Kerberos routines to avoid warnings.
+ (toolong): Declare as type sigtype, and add dummy argument.
+
+ Fixes for SCO:
+ * cmdtab.y: Include <sys/types.h>.
+ * ftpd.c (initgroups): Define on SCO.
+ (main): Don't handle SIGURG if it is not defined.
+ (pass): Don't try to use crypt on SCO; instead, require Kerberos
+ password or anonymous login.
+
+ Fixes for AIX:
+ * ftpcmd.y (index): Don't define.
+ (strpbrk, strcpy): Don't declare.
+ * ftpd.c (index, rindex): Don't define.
+ * logwtmp.c (strncpy): Don't declare.
+ * secure.c: Include <netinet/in.h>.
+
+ Fixes for Ultrix:
+ * ftpd.c (main): Define LOG_NDELAY and LOG_DAEMON as zero if they
+ are not already defined by <syslog.h>.
+
+ Fixes for Irix 4:
+ * ftpd.c (retrieve): Don't refer to st_blksize if NOSTBLKSIZE is
+ defined.
+
+ * ftpcmd.y: Fix yacc code to use %union and %type.
+ (yylex): Assign to fields of yylval, rather than to yylval
+ directly.
+
+ General fixes to make it compile on Solaris: Use sigtype for
+ signal handler return values, including conf.h where needed. Add
+ a dummy argument to signal handler functions. Replace index,
+ rindex, bzero and bcopy with ANSI C functions. Cast Kerberos
+ routine arguments to avoid warnings. Also:
+ * ftpd.c: Don't include <varargs.h>. If POSIX is defined, include
+ unistd.h, otherwise define getcwd to call getwd.
+ (L_SET, L_INCR): Define if not defined.
+ (pwd): Use getcwd instead of getwd. If POSIX, change the error
+ handling accordingly.
+ * popen.c (getdtablesize): New function on hpux or __svr4__.
+ (ftpd_pclose): If WAIT_USES_INT, use int instead of union wait.
+
+Thu Dec 15 16:13:44 1994 Ian Lance Taylor <ian@sanguine.cygnus.com>
+
+ * Initial checkin. Based on Steve Lunt's ftp program, which was
+ based on BSD code.
--- /dev/null
+#
+# appl/gssftp/ftpd/Makefile.in
+#
+CFLAGS = -DGSSAPI -DFTP_BUFSIZ=10240 $(CCOPTS) $(DEFS) $(LOCALINCLUDE)
+
+SETENVSRC=@SETENVSRC@
+SETENVOBJ=@SETENVOBJ@
+COMERRLIB=$(BUILDTOP)/util/et/libcom_err.a
+
+SRCS = ftpd.c ftpcmd.y logwtmp.c popen.c vers.c \
+ $(srcdir)../ftp/glob.c \
+ $(srcdir)../ftp/radix.c \
+ $(srcdir)../ftp/secure.c \
+ $(srcdir)../../bsd/getdtablesize.c $(SETENVSRC)
+
+OBJS = ftpd.o ftpcmd.o glob.o popen.o logwtmp.o vers.o radix.o \
+ secure.o getdtablesize.o $(SETENVOBJ)
+
+KLIB = -lgssapi_krb5 -lkrb5 -lcrypto $(COMERRLIB)
+DEPKLIB = $(TOPLIBD)/gssapi/libgssapi_krb5.a $(TOPLIBD)/libkrb5.a \
+ $(TOPLIBD)/libcrypto.a $(COMERRLIB)
+
+LOCALINCLUDE = -I$(srcdir)/..
+DEFINES = -DGSSAPI -DNOCONFIDENTIAL
+
+all:: ftpd
+
+ftpd: $(OBJS) $(DEPKLIB)
+ $(LD) $(LDFLAGS) $(LDARGS) -o $@ $(OBJS) $(KLIB) $(LIBS)
+
+clean::
+ $(RM) ftp ftpcmd.c
+
+depend::
+
+install::
+ $(INSTALL_PROGRAM) ftpd $(DESTDIR)$(SERVER_BINDIR)/ftpd
+ $(INSTALL_DATA) $(srcdir)/ftpd.M ${DESTDIR}$(SERVER_MANDIR)/ftp.8
+
+
+ftpcmd.c: $(srcdir)/ftpcmd.y
+ $(RM) ftpcmd.c y.tab.c
+ $(YACC) $(srcdir)/ftpcmd.y
+ $(MV) y.tab.c ftpcmd.c
+
+glob.o: $(srcdir)/../ftp/glob.c
+ $(CC) -c $(CFLAGS) $(srcdir)/../ftp/glob.c
+radix.o: $(srcdir)/../ftp/radix.c
+ $(CC) -c $(CFLAGS) $(srcdir)/../ftp/radix.c
+secure.o: $(srcdir)/../ftp/secure.c
+ $(CC) -c $(CFLAGS) $(srcdir)/../ftp/secure.c
+
+getdtablesize.o: $(srcdir)/../../bsd/getdtablesize.c
+ $(CC) -c $(CFLAGS) $(srcdir)/../../bsd/getdtablesize.c
+
+setenv.o: $(srcdir)/../../bsd/setenv.c
+ $(CC) -c $(CFLAGS) $(srcdir)/../../bsd/setenv.c
+
+
+ftpd.o: $(srcdir)/pathnames.h
+secure.o: $(srcdir)/secure.h
+
+ftpd.o: $(srcdir)/ftpd.c
+ftpcmd.o: ftpcmd.c
+popen.o: $(srcdir)/popen.c
+logwtmp.o: $(srcdir)/logwtmp.c
+vers.o: $(srcdir)/vers.c
+
+# NOPOSTFIX
--- /dev/null
+AC_INIT(ftpcmd.y)
+CONFIG_RULES
+AC_CONST
+AC_PROG_INSTALL
+AC_PROG_YACC
+KRB5_SIGTYPE
+CHECK_UTMP
+CHECK_SIGPROCMASK
+CHECK_WAIT_TYPE
+AC_FUNC_VFORK
+AC_CHECK_HEADERS(unistd.h stdlib.h string.h)
+AC_REPLACE_FUNCS(getdtablesize)
+AC_HAVE_FUNCS(getcwd getusershell seteuid setreuid setresuid)
+dnl
+dnl copied from appl/bsd/configure.in
+AC_MSG_CHECKING([setenv])
+AC_CACHE_VAL(krb5_cv_setenv,
+[AC_TRY_LINK(
+[],[setenv("PATH","/bin",0);],
+krb5_cv_setenv=yes,krb5_cv_setenv=no)])
+AC_MSG_RESULT($krb5_cv_setenv)
+if test $krb5_cv_setenv = no; then
+SETENVSRC='$(srcdir)/../bsd/setenv.c'
+SETENVOBJ=setenv.o
+AC_SUBST([SETENVSRC])
+AC_SUBST([SETENVOBJ])
+fi
+dnl
+WITH_DBM_LNAME
+V5_USE_SHARED_LIB
+V5_AC_OUTPUT_MAKEFILE
--- /dev/null
+/* -*- fundamental -*-
+ * Copyright (c) 1985, 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ftpcmd.y 5.24 (Berkeley) 2/25/91
+ */
+
+/*
+ * Grammar for FTP commands.
+ * See RFC 959.
+ * See Also draft-ietf-cat-ftpsec-08.txt.
+ */
+
+%{
+
+#ifndef lint
+static char sccsid[] = "@(#)ftpcmd.y 5.24 (Berkeley) 2/25/91";
+#endif /* not lint */
+
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/ftp.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <syslog.h>
+#include <time.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
+extern char *auth_type;
+
+unsigned int maxbuf, actualbuf;
+unsigned char *ucbuf;
+
+#if defined(STDARG) || (defined(__STDC__) && ! defined(VARARGS))
+extern reply(int, char *, ...);
+extern lreply(int, char *, ...);
+#endif
+
+static int kerror; /* XXX needed for all auth types */
+#ifdef KERBEROS
+extern struct sockaddr_in his_addr, ctrl_addr;
+#include <krb.h>
+extern AUTH_DAT kdata;
+extern Key_schedule schedule;
+extern MSG_DAT msg_data;
+#endif /* KERBEROS */
+#ifdef GSSAPI
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+extern gss_ctx_id_t gcontext;
+#endif
+
+#ifndef unix
+#ifdef _AIX
+#define unix
+#endif
+#endif
+
+#ifndef NBBY
+#ifdef linux
+#define NBBY 8
+#endif
+#ifdef __pyrsoft
+#ifdef MIPSEB
+#define NBBY 8
+#endif
+#endif
+#endif
+
+extern struct sockaddr_in data_dest;
+extern int logged_in;
+extern struct passwd *pw;
+extern int guest;
+extern int logging;
+extern int type;
+extern int form;
+extern int debug;
+extern int timeout;
+extern int maxtimeout;
+extern int pdata;
+extern char hostname[], remotehost[];
+extern char proctitle[];
+extern char *globerr;
+extern int usedefault;
+extern int transflag;
+extern char tmpline[];
+char **ftpglob();
+
+off_t restart_point;
+
+static int cmd_type;
+static int cmd_form;
+static int cmd_bytesz;
+char cbuf[FTP_BUFSIZ]; /* was 512 */
+char *fromname;
+
+/* bison needs these decls up front */
+extern jmp_buf errcatch;
+
+#define CMD 0 /* beginning of command */
+#define ARGS 1 /* expect miscellaneous arguments */
+#define STR1 2 /* expect SP followed by STRING */
+#define STR2 3 /* expect STRING */
+#define OSTR 4 /* optional SP then STRING */
+#define ZSTR1 5 /* SP then optional STRING */
+#define ZSTR2 6 /* optional STRING after SP */
+#define SITECMD 7 /* SITE command */
+#define NSTR 8 /* Number followed by a string */
+
+struct tab {
+ char *name;
+ short token;
+ short state;
+ short implemented; /* 1 if command is implemented */
+ char *help;
+};
+struct tab cmdtab[];
+struct tab sitetab[];
+%}
+
+%union { int num; char *str; }
+
+%token
+ A B C E F I
+ L N P R S T
+
+ SP CRLF COMMA STRING NUMBER
+
+ USER PASS ACCT REIN QUIT PORT
+ PASV TYPE STRU MODE RETR STOR
+ APPE MLFL MAIL MSND MSOM MSAM
+ MRSQ MRCP ALLO REST RNFR RNTO
+ ABOR DELE CWD LIST NLST SITE
+ STAT HELP NOOP MKD RMD PWD
+ CDUP STOU SMNT SYST SIZE MDTM
+ AUTH ADAT PROT PBSZ
+ CCC
+
+ UMASK IDLE CHMOD
+
+ LEXERR
+
+%type <num> NUMBER
+%type <num> form_code prot_code struct_code mode_code octal_number
+%type <num> check_login byte_size
+
+%type <str> STRING
+%type <str> password pathname username pathstring
+
+%start cmd_list
+
+%%
+
+cmd_list: /* empty */
+ | cmd_list cmd
+ = {
+ fromname = (char *) 0;
+ restart_point = (off_t) 0;
+ }
+ | cmd_list rcmd
+ ;
+
+cmd: USER SP username CRLF
+ = {
+ user((char *) $3);
+ free((char *) $3);
+ }
+ | PASS SP password CRLF
+ = {
+ pass((char *) $3);
+ free((char *) $3);
+ }
+ | PORT SP host_port CRLF
+ = {
+ usedefault = 0;
+ if (pdata >= 0) {
+ (void) close(pdata);
+ pdata = -1;
+ }
+ reply(200, "PORT command successful.");
+ }
+ | PASV check_login CRLF
+ = {
+ if ($2)
+ passive();
+ }
+ | PROT SP prot_code CRLF
+ = {
+ if (maxbuf)
+ setlevel ($3);
+ else
+ reply(503, "Must first set PBSZ");
+ }
+ | CCC CRLF
+ = {
+ reply(534, "CCC not supported");
+ }
+ | PBSZ SP STRING CRLF
+ = {
+ /* Others may want to do something more fancy here */
+ if (!auth_type)
+ reply(503, "Must first perform authentication");
+ else if (strlen($3) > 10 ||
+ strlen($3) == 10 && strcmp($3,"4294967296") >= 0)
+ reply(501, "Bad value for PBSZ: %s", $3);
+ else if (actualbuf >= (maxbuf =(unsigned int) atol($3)))
+ reply(200, "PBSZ=%u", actualbuf);
+ else {
+ if (ucbuf) (void) free(ucbuf);
+ actualbuf = (unsigned int) atol($3);
+ /* I attempt what is asked for first, and if that
+ fails, I try dividing by 4 */
+ while ((ucbuf = (unsigned char *)malloc(actualbuf)) == NULL)
+ if (actualbuf)
+ lreply(200, "Trying %u", actualbuf >>= 2);
+ else {
+ perror_reply(421,
+ "Local resource failure: malloc");
+ dologout(1);
+ }
+ reply(200, "PBSZ=%u", maxbuf = actualbuf);
+ }
+ }
+ | TYPE SP type_code CRLF
+ = {
+ switch (cmd_type) {
+
+ case TYPE_A:
+ if (cmd_form == FORM_N) {
+ reply(200, "Type set to A.");
+ type = cmd_type;
+ form = cmd_form;
+ } else
+ reply(504, "Form must be N.");
+ break;
+
+ case TYPE_E:
+ reply(504, "Type E not implemented.");
+ break;
+
+ case TYPE_I:
+ reply(200, "Type set to I.");
+ type = cmd_type;
+ break;
+
+ case TYPE_L:
+#if NBBY == 8
+ if (cmd_bytesz == 8) {
+ reply(200,
+ "Type set to L (byte size 8).");
+ type = cmd_type;
+ } else
+ reply(504, "Byte size must be 8.");
+#else /* NBBY == 8 */
+ UNIMPLEMENTED for NBBY != 8
+#endif /* NBBY == 8 */
+ }
+ }
+ | STRU SP struct_code CRLF
+ = {
+ switch ($3) {
+
+ case STRU_F:
+ reply(200, "STRU F ok.");
+ break;
+
+ default:
+ reply(504, "Unimplemented STRU type.");
+ }
+ }
+ | MODE SP mode_code CRLF
+ = {
+ switch ($3) {
+
+ case MODE_S:
+ reply(200, "MODE S ok.");
+ break;
+
+ default:
+ reply(502, "Unimplemented MODE type.");
+ }
+ }
+ | ALLO SP NUMBER CRLF
+ = {
+ reply(202, "ALLO command ignored.");
+ }
+ | ALLO SP NUMBER SP R SP NUMBER CRLF
+ = {
+ reply(202, "ALLO command ignored.");
+ }
+ | RETR check_login SP pathname CRLF
+ = {
+ if ($2 && $4 != NULL)
+ retrieve((char *) 0, (char *) $4);
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+ | STOR check_login SP pathname CRLF
+ = {
+ if ($2 && $4 != NULL)
+ store((char *) $4, "w", 0);
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+ | APPE check_login SP pathname CRLF
+ = {
+ if ($2 && $4 != NULL)
+ store((char *) $4, "a", 0);
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+ | NLST check_login CRLF
+ = {
+ if ($2)
+ send_file_list(".");
+ }
+ | NLST check_login SP STRING CRLF
+ = {
+ if ($2 && $4 != NULL)
+ send_file_list((char *) $4);
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+ | LIST check_login CRLF
+ = {
+ if ($2)
+ retrieve("/bin/ls -lgA", "");
+ }
+ | LIST check_login SP pathname CRLF
+ = {
+ if ($2 && $4 != NULL)
+ retrieve("/bin/ls -lgA %s", (char *) $4);
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+ | STAT check_login SP pathname CRLF
+ = {
+ if ($2 && $4 != NULL)
+ statfilecmd((char *) $4);
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+ | STAT CRLF
+ = {
+ statcmd();
+ }
+ | DELE check_login SP pathname CRLF
+ = {
+ if ($2 && $4 != NULL)
+ delete((char *) $4);
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+ | RNTO SP pathname CRLF
+ = {
+ if (fromname) {
+ renamecmd(fromname, (char *) $3);
+ free(fromname);
+ fromname = (char *) 0;
+ } else {
+ reply(503, "Bad sequence of commands.");
+ }
+ free((char *) $3);
+ }
+ | ABOR CRLF
+ = {
+ reply(225, "ABOR command successful.");
+ }
+ | CWD check_login CRLF
+ = {
+ if ($2)
+ cwd(pw->pw_dir);
+ }
+ | CWD check_login SP pathname CRLF
+ = {
+ if ($2 && $4 != NULL)
+ cwd((char *) $4);
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+ | HELP CRLF
+ = {
+ help(cmdtab, (char *) 0);
+ }
+ | HELP SP STRING CRLF
+ = {
+ register char *cp = (char *)$3;
+
+ if (strncasecmp(cp, "SITE", 4) == 0) {
+ cp = (char *)$3 + 4;
+ if (*cp == ' ')
+ cp++;
+ if (*cp)
+ help(sitetab, cp);
+ else
+ help(sitetab, (char *) 0);
+ } else
+ help(cmdtab, (char *) $3);
+ }
+ | NOOP CRLF
+ = {
+ reply(200, "NOOP command successful.");
+ }
+ | MKD check_login SP pathname CRLF
+ = {
+ if ($2 && $4 != NULL)
+ makedir((char *) $4);
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+ | RMD check_login SP pathname CRLF
+ = {
+ if ($2 && $4 != NULL)
+ removedir((char *) $4);
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+ | PWD check_login CRLF
+ = {
+ if ($2)
+ pwd();
+ }
+ | CDUP check_login CRLF
+ = {
+ if ($2)
+ cwd("..");
+ }
+ | SITE SP HELP CRLF
+ = {
+ help(sitetab, (char *) 0);
+ }
+ | SITE SP HELP SP STRING CRLF
+ = {
+ help(sitetab, (char *) $5);
+ }
+ | SITE SP UMASK check_login CRLF
+ = {
+ int oldmask;
+
+ if ($4) {
+ oldmask = umask(0);
+ (void) umask(oldmask);
+ reply(200, "Current UMASK is %03o", oldmask);
+ }
+ }
+ | SITE SP UMASK check_login SP octal_number CRLF
+ = {
+ int oldmask;
+
+ if ($4) {
+ if (($6 == -1) || ($6 > 0777)) {
+ reply(501, "Bad UMASK value");
+ } else {
+ oldmask = umask($6);
+ reply(200,
+ "UMASK set to %03o (was %03o)",
+ $6, oldmask);
+ }
+ }
+ }
+ | SITE SP CHMOD check_login SP octal_number SP pathname CRLF
+ = {
+ if ($4 && ($8 != NULL)) {
+ if ($6 > 0777)
+ reply(501,
+ "CHMOD: Mode value must be between 0 and 0777");
+ else if (chmod((char *) $8, $6) < 0)
+ perror_reply(550, (char *) $8);
+ else
+ reply(200, "CHMOD command successful.");
+ }
+ if ($8 != NULL)
+ free((char *) $8);
+ }
+ | SITE SP IDLE CRLF
+ = {
+ reply(200,
+ "Current IDLE time limit is %d seconds; max %d",
+ timeout, maxtimeout);
+ }
+ | SITE SP IDLE SP NUMBER CRLF
+ = {
+ if ($5 < 30 || $5 > maxtimeout) {
+ reply(501,
+ "Maximum IDLE time must be between 30 and %d seconds",
+ maxtimeout);
+ } else {
+ timeout = $5;
+ (void) alarm((unsigned) timeout);
+ reply(200,
+ "Maximum IDLE time set to %d seconds",
+ timeout);
+ }
+ }
+ | STOU check_login SP pathname CRLF
+ = {
+ if ($2 && $4 != NULL)
+ store((char *) $4, "w", 1);
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+ | SYST CRLF
+ = {
+#ifdef unix
+#ifdef __svr4__
+#undef BSD
+#endif
+#ifdef BSD
+ reply(215, "UNIX Type: L%d Version: BSD-%d",
+ NBBY, BSD);
+#else /* BSD */
+ reply(215, "UNIX Type: L%d", NBBY);
+#endif /* BSD */
+#else /* unix */
+ reply(215, "UNKNOWN Type: L%d", NBBY);
+#endif /* unix */
+ }
+
+ /*
+ * SIZE is not in RFC959, but Postel has blessed it and
+ * it will be in the updated RFC.
+ *
+ * Return size of file in a format suitable for
+ * using with RESTART (we just count bytes).
+ */
+ | SIZE check_login SP pathname CRLF
+ = {
+ if ($2 && $4 != NULL)
+ sizecmd((char *) $4);
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+
+ /*
+ * MDTM is not in RFC959, but Postel has blessed it and
+ * it will be in the updated RFC.
+ *
+ * Return modification time of file as an ISO 3307
+ * style time. E.g. YYYYMMDDHHMMSS or YYYYMMDDHHMMSS.xxx
+ * where xxx is the fractional second (of any precision,
+ * not necessarily 3 digits)
+ */
+ | MDTM check_login SP pathname CRLF
+ = {
+ if ($2 && $4 != NULL) {
+ struct stat stbuf;
+ if (stat((char *) $4, &stbuf) < 0)
+ perror_reply(550, "%s", (char *) $4);
+ else if ((stbuf.st_mode&S_IFMT) != S_IFREG) {
+ reply(550, "%s: not a plain file.",
+ (char *) $4);
+ } else {
+ register struct tm *t;
+ struct tm *gmtime();
+ t = gmtime(&stbuf.st_mtime);
+ reply(213,
+ "19%02d%02d%02d%02d%02d%02d",
+ t->tm_year, t->tm_mon+1, t->tm_mday,
+ t->tm_hour, t->tm_min, t->tm_sec);
+ }
+ }
+ if ($4 != NULL)
+ free((char *) $4);
+ }
+ | AUTH SP STRING CRLF
+ = {
+ auth((char *) $3);
+ }
+ | ADAT SP STRING CRLF
+ = {
+ auth_data((char *) $3);
+ free((char *) $3);
+ }
+ | QUIT CRLF
+ = {
+ reply(221, "Goodbye.");
+ dologout(0);
+ }
+ | error CRLF
+ = {
+ yyerrok;
+ }
+ ;
+rcmd: RNFR check_login SP pathname CRLF
+ = {
+ char *renamefrom();
+
+ restart_point = (off_t) 0;
+ if ($2 && $4) {
+ fromname = renamefrom((char *) $4);
+ if (fromname == (char *) 0 && $4) {
+ free((char *) $4);
+ }
+ }
+ }
+ | REST SP byte_size CRLF
+ = {
+ fromname = (char *) 0;
+ restart_point = $3;
+ reply(350, "Restarting at %ld. %s", restart_point,
+ "Send STORE or RETRIEVE to initiate transfer.");
+ }
+ ;
+
+username: STRING
+ ;
+
+password: /* empty */
+ = {
+ *(char **)&($$) = (char *)calloc(1, sizeof(char));
+ }
+ | STRING
+ ;
+
+byte_size: NUMBER
+ ;
+
+host_port: NUMBER COMMA NUMBER COMMA NUMBER COMMA NUMBER COMMA
+ NUMBER COMMA NUMBER
+ = {
+ register char *a, *p;
+
+ a = (char *)&data_dest.sin_addr;
+ a[0] = $1; a[1] = $3; a[2] = $5; a[3] = $7;
+ p = (char *)&data_dest.sin_port;
+ p[0] = $9; p[1] = $11;
+ data_dest.sin_family = AF_INET;
+ }
+ ;
+
+form_code: N
+ = {
+ $$ = FORM_N;
+ }
+ | T
+ = {
+ $$ = FORM_T;
+ }
+ | C
+ = {
+ $$ = FORM_C;
+ }
+ ;
+
+prot_code: C
+ = {
+ $$ = PROT_C;
+ }
+ | S
+ = {
+ $$ = PROT_S;
+ }
+ | P
+ = {
+ $$ = PROT_P;
+ }
+ | E
+ = {
+ $$ = PROT_E;
+ }
+ ;
+
+type_code: A
+ = {
+ cmd_type = TYPE_A;
+ cmd_form = FORM_N;
+ }
+ | A SP form_code
+ = {
+ cmd_type = TYPE_A;
+ cmd_form = $3;
+ }
+ | E
+ = {
+ cmd_type = TYPE_E;
+ cmd_form = FORM_N;
+ }
+ | E SP form_code
+ = {
+ cmd_type = TYPE_E;
+ cmd_form = $3;
+ }
+ | I
+ = {
+ cmd_type = TYPE_I;
+ }
+ | L
+ = {
+ cmd_type = TYPE_L;
+ cmd_bytesz = NBBY;
+ }
+ | L SP byte_size
+ = {
+ cmd_type = TYPE_L;
+ cmd_bytesz = $3;
+ }
+ /* this is for a bug in the BBN ftp */
+ | L byte_size
+ = {
+ cmd_type = TYPE_L;
+ cmd_bytesz = $2;
+ }
+ ;
+
+struct_code: F
+ = {
+ $$ = STRU_F;
+ }
+ | R
+ = {
+ $$ = STRU_R;
+ }
+ | P
+ = {
+ $$ = STRU_P;
+ }
+ ;
+
+mode_code: S
+ = {
+ $$ = MODE_S;
+ }
+ | B
+ = {
+ $$ = MODE_B;
+ }
+ | C
+ = {
+ $$ = MODE_C;
+ }
+ ;
+
+pathname: pathstring
+ = {
+ /*
+ * Problem: this production is used for all pathname
+ * processing, but only gives a 550 error reply.
+ * This is a valid reply in some cases but not in others.
+ */
+ if (logged_in && $1 && strncmp((char *) $1, "~", 1) == 0) {
+ *(char **)&($$) = *ftpglob((char *) $1);
+ if (globerr != NULL) {
+ reply(550, globerr);
+ $$ = NULL;
+ }
+ free((char *) $1);
+ } else
+ $$ = $1;
+ }
+ ;
+
+pathstring: STRING
+ ;
+
+octal_number: NUMBER
+ = {
+ register int ret, dec, multby, digit;
+
+ /*
+ * Convert a number that was read as decimal number
+ * to what it would be if it had been read as octal.
+ */
+ dec = $1;
+ multby = 1;
+ ret = 0;
+ while (dec) {
+ digit = dec%10;
+ if (digit > 7) {
+ ret = -1;
+ break;
+ }
+ ret += digit * multby;
+ multby *= 8;
+ dec /= 10;
+ }
+ $$ = ret;
+ }
+ ;
+
+check_login: /* empty */
+ = {
+ if (logged_in)
+ $$ = 1;
+ else {
+ reply(530, "Please login with USER and PASS.");
+ $$ = 0;
+ }
+ }
+ ;
+
+%%
+
+struct tab cmdtab[] = { /* In order defined in RFC 765 */
+ { "USER", USER, STR1, 1, "<sp> username" },
+ { "PASS", PASS, ZSTR1, 1, "<sp> password" },
+ { "ACCT", ACCT, STR1, 0, "(specify account)" },
+ { "SMNT", SMNT, ARGS, 0, "(structure mount)" },
+ { "REIN", REIN, ARGS, 0, "(reinitialize server state)" },
+ { "QUIT", QUIT, ARGS, 1, "(terminate service)", },
+ { "PORT", PORT, ARGS, 1, "<sp> b0, b1, b2, b3, b4" },
+ { "PASV", PASV, ARGS, 1, "(set server in passive mode)" },
+ { "TYPE", TYPE, ARGS, 1, "<sp> [ A | E | I | L ]" },
+ { "STRU", STRU, ARGS, 1, "(specify file structure)" },
+ { "MODE", MODE, ARGS, 1, "(specify transfer mode)" },
+ { "RETR", RETR, STR1, 1, "<sp> file-name" },
+ { "STOR", STOR, STR1, 1, "<sp> file-name" },
+ { "APPE", APPE, STR1, 1, "<sp> file-name" },
+ { "MLFL", MLFL, OSTR, 0, "(mail file)" },
+ { "MAIL", MAIL, OSTR, 0, "(mail to user)" },
+ { "MSND", MSND, OSTR, 0, "(mail send to terminal)" },
+ { "MSOM", MSOM, OSTR, 0, "(mail send to terminal or mailbox)" },
+ { "MSAM", MSAM, OSTR, 0, "(mail send to terminal and mailbox)" },
+ { "MRSQ", MRSQ, OSTR, 0, "(mail recipient scheme question)" },
+ { "MRCP", MRCP, STR1, 0, "(mail recipient)" },
+ { "ALLO", ALLO, ARGS, 1, "allocate storage (vacuously)" },
+ { "REST", REST, ARGS, 1, "(restart command)" },
+ { "RNFR", RNFR, STR1, 1, "<sp> file-name" },
+ { "RNTO", RNTO, STR1, 1, "<sp> file-name" },
+ { "ABOR", ABOR, ARGS, 1, "(abort operation)" },
+ { "DELE", DELE, STR1, 1, "<sp> file-name" },
+ { "CWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
+ { "XCWD", CWD, OSTR, 1, "[ <sp> directory-name ]" },
+ { "LIST", LIST, OSTR, 1, "[ <sp> path-name ]" },
+ { "NLST", NLST, OSTR, 1, "[ <sp> path-name ]" },
+ { "SITE", SITE, SITECMD, 1, "site-cmd [ <sp> arguments ]" },
+ { "SYST", SYST, ARGS, 1, "(get type of operating system)" },
+ { "STAT", STAT, OSTR, 1, "[ <sp> path-name ]" },
+ { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
+ { "NOOP", NOOP, ARGS, 1, "" },
+ { "MKD", MKD, STR1, 1, "<sp> path-name" },
+ { "XMKD", MKD, STR1, 1, "<sp> path-name" },
+ { "RMD", RMD, STR1, 1, "<sp> path-name" },
+ { "XRMD", RMD, STR1, 1, "<sp> path-name" },
+ { "PWD", PWD, ARGS, 1, "(return current directory)" },
+ { "XPWD", PWD, ARGS, 1, "(return current directory)" },
+ { "CDUP", CDUP, ARGS, 1, "(change to parent directory)" },
+ { "XCUP", CDUP, ARGS, 1, "(change to parent directory)" },
+ { "STOU", STOU, STR1, 1, "<sp> file-name" },
+ { "AUTH", AUTH, STR1, 1, "<sp> auth-type" },
+ { "ADAT", ADAT, STR1, 1, "<sp> auth-data" },
+ { "PROT", PROT, ARGS, 1, "<sp> protection-level" },
+ { "PBSZ", PBSZ, STR1, 1, "<sp> buffer-size" },
+ { "CCC", CCC, ARGS, 1, "(clear command channel)" },
+ { "SIZE", SIZE, OSTR, 1, "<sp> path-name" },
+ { "MDTM", MDTM, OSTR, 1, "<sp> path-name" },
+ { NULL, 0, 0, 0, 0 }
+};
+
+struct tab sitetab[] = {
+ { "UMASK", UMASK, ARGS, 1, "[ <sp> umask ]" },
+ { "IDLE", IDLE, ARGS, 1, "[ <sp> maximum-idle-time ]" },
+ { "CHMOD", CHMOD, NSTR, 1, "<sp> mode <sp> file-name" },
+ { "HELP", HELP, OSTR, 1, "[ <sp> <string> ]" },
+ { NULL, 0, 0, 0, 0 }
+};
+
+struct tab *
+lookup(p, cmd)
+ register struct tab *p;
+ char *cmd;
+{
+
+ for (; p->name != NULL; p++)
+ if (strcmp(cmd, p->name) == 0)
+ return (p);
+ return (0);
+}
+
+#include <arpa/telnet.h>
+
+/*
+ * getline - a hacked up version of fgets to ignore TELNET escape codes.
+ */
+char *
+getline(s, n, iop)
+ char *s;
+ register FILE *iop;
+{
+ register c;
+ register char *cs;
+
+ cs = s;
+/* tmpline may contain saved command from urgent mode interruption */
+ for (c = 0; tmpline[c] != '\0' && --n > 0; ++c) {
+ *cs++ = tmpline[c];
+ if (tmpline[c] == '\n') {
+ *cs++ = '\0';
+ if (debug)
+ syslog(LOG_DEBUG, "command: %s", s);
+ tmpline[0] = '\0';
+ return(s);
+ }
+ if (c == 0)
+ tmpline[0] = '\0';
+ }
+ while ((c = getc(iop)) != EOF) {
+ c &= 0377;
+ if (c == IAC) {
+ if ((c = getc(iop)) != EOF) {
+ c &= 0377;
+ switch (c) {
+ case WILL:
+ case WONT:
+ c = getc(iop);
+ printf("%c%c%c", IAC, DONT, 0377&c);
+ (void) fflush(stdout);
+ continue;
+ case DO:
+ case DONT:
+ c = getc(iop);
+ printf("%c%c%c", IAC, WONT, 0377&c);
+ (void) fflush(stdout);
+ continue;
+ case IAC:
+ break;
+ default:
+ continue; /* ignore command */
+ }
+ }
+ }
+ *cs++ = c;
+ if (--n <= 0 || c == '\n')
+ break;
+ }
+ if (c == EOF && cs == s)
+ return (NULL);
+ *cs++ = '\0';
+ if (auth_type) {
+ char out[sizeof(cbuf)], *cp;
+ int len, mic;
+
+ if ((cs = strpbrk(s, " \r\n")))
+ *cs++ = '\0';
+ upper(s);
+#ifdef NOCONFIDENTIAL
+ if (!strcmp(s, "CONF")) {
+ reply(537, "CONF protected commands not supported.");
+ *s = '\0';
+ return(s);
+ }
+#endif
+ if ((mic = strcmp(s, "ENC")) && strcmp(s, "MIC")
+#ifndef NOCONFIDENTIAL
+ && strcmp(s, "CONF")
+#endif
+ ) {
+ reply(533, "All commands must be protected.");
+ syslog(LOG_ERR, "Unprotected command received");
+ *s = '\0';
+ return(s);
+ } else if (debug)
+ syslog(LOG_INFO, "command %s received (mic=%d)", s, mic);
+/* Some paranoid sites may want to require that commands be encrypted. */
+#ifdef PARANOID
+ if (mic) {
+ reply(533, "All commands must be ENC protected. Retry command under ENC.");
+ *s = '\0';
+ return(s);
+ }
+#endif /* PARANOID */
+#ifdef NOENCRYPTION
+ if (!mic) {
+ reply(533, "ENC protection not supported. Retry command under MIC.");
+ *s = '\0';
+ return(s);
+ }
+#endif /* NOENCRYPTION */
+ if ((cp = strpbrk(cs, " \r\n")))
+ *cp = '\0';
+ if (kerror = radix_encode(cs, out, &len, 1)) {
+ reply(501, "Can't base 64 decode argument to %s command (%s)",
+ mic ? "MIC" : "ENC", radix_error(kerror));
+ *s = '\0';
+ return(s);
+ }
+ if (debug) syslog(LOG_DEBUG, "getline got %d from %s <%s>\n",
+ len, cs, mic?"MIC":"ENC");
+#ifdef KERBEROS
+ if (strcmp(auth_type, "KERBEROS_V4") == 0) {
+ if ((kerror = mic ?
+ krb_rd_safe((unsigned char *)out, len, &kdata.session,
+ &his_addr, &ctrl_addr, &msg_data)
+ : krb_rd_priv((unsigned char *)out, len, schedule,
+ &kdata.session, &his_addr, &ctrl_addr, &msg_data))
+ != KSUCCESS) {
+ reply(535, "%s! (%s)",
+ mic ? "MIC command modified" : "ENC command garbled",
+ krb_get_err_text(kerror));
+ syslog(LOG_ERR,"%s failed: %s",
+ mic ? "MIC krb_rd_safe" : "ENC krb_rd_priv",
+ krb_get_err_text(kerror));
+ *s = '\0';
+ return(s);
+ }
+ (void) memcpy(s, msg_data.app_data, msg_data.app_length);
+ (void) strcpy(s+msg_data.app_length, "\r\n");
+ }
+#endif /* KERBEROS */
+#ifdef GSSAPI
+/* we know this is a MIC or ENC already, and out/len already has the bits */
+ if (strcmp(auth_type, "GSSAPI") == 0) {
+ gss_buffer_desc xmit_buf, msg_buf;
+ OM_uint32 maj_stat, min_stat;
+ int conf_state;
+
+ xmit_buf.value = out;
+ xmit_buf.length = len;
+ /* decrypt the message */
+ conf_state = !mic;
+ maj_stat = gss_unseal(&min_stat, gcontext, &xmit_buf,
+ &msg_buf, &conf_state, NULL);
+ if (maj_stat == GSS_S_CONTINUE_NEEDED) {
+ if (debug) syslog(LOG_DEBUG, "%s-unseal continued",
+ mic?"MIC":"ENC");
+ reply(535, "%s-unseal continued, oops",
+ mic?"MIC":"ENC");
+ *s = 0; return s;
+ }
+ if (maj_stat != GSS_S_COMPLETE) {
+ reply_gss_error(535, maj_stat, min_stat,
+ mic? "failed unsealing MIC message":
+ "failed unsealing ENC message");
+ *s = 0;
+ return s;
+ }
+
+ memcpy(s, msg_buf.value, msg_buf.length);
+ strcpy(s+msg_buf.length-(s[msg_buf.length-1]?0:1), "\r\n");
+ gss_release_buffer(&min_stat, &msg_buf);
+ }
+#endif /* GSSAPI */
+ /* Other auth types go here ... */
+ }
+#if defined KERBEROS || defined GSSAPI /* or other auth types */
+ else { /* !auth_type */
+ if ( (!(strncmp(s, "ENC", 3))) || (!(strncmp(s, "MIC", 3)))
+#ifndef NOCONFIDENTIAL
+ || (!(strncmp(s, "CONF", 4)))
+#endif
+ ) {
+ reply(503, "Must perform authentication before sending protected commands");
+ *s = '\0';
+ return(s);
+ }
+ }
+#endif /* KERBEROS */
+
+ if (debug)
+ syslog(LOG_DEBUG, "command: <%s>(%d)", s, strlen(s));
+ return (s);
+}
+
+static krb5_sigtype
+toolong(sig)
+ int sig;
+{
+ time_t now;
+
+ reply(421,
+ "Timeout (%d seconds): closing control connection.", timeout);
+ (void) time(&now);
+ if (logging) {
+ syslog(LOG_INFO,
+ "User %s timed out after %d seconds at %s",
+ (pw ? pw -> pw_name : "unknown"), timeout, ctime(&now));
+ }
+ dologout(1);
+}
+
+yylex()
+{
+ static int cpos, state;
+ register char *cp, *cp2;
+ register struct tab *p;
+ int n;
+ char c, *copy();
+
+ for (;;) {
+ switch (state) {
+
+ case CMD:
+ (void) signal(SIGALRM, toolong);
+ (void) alarm((unsigned) timeout);
+ if (getline(cbuf, sizeof(cbuf)-1, stdin) == NULL) {
+ reply(221, "You could at least say goodbye.");
+ dologout(0);
+ }
+ (void) alarm(0);
+#ifdef SETPROCTITLE
+ if (strncasecmp(cbuf, "PASS", 4) != NULL)
+ setproctitle("%s: %s", proctitle, cbuf);
+#endif /* SETPROCTITLE */
+ if ((cp = strchr(cbuf, '\r'))) {
+ *cp++ = '\n';
+ *cp = '\0';
+ }
+ if ((cp = strpbrk(cbuf, " \n")))
+ cpos = cp - cbuf;
+ if (cpos == 0)
+ cpos = 4;
+ c = cbuf[cpos];
+ cbuf[cpos] = '\0';
+ upper(cbuf);
+ p = lookup(cmdtab, cbuf);
+ cbuf[cpos] = c;
+ if (p != 0) {
+ if (p->implemented == 0) {
+ nack(p->name);
+ longjmp(errcatch,0);
+ /* NOTREACHED */
+ }
+ state = p->state;
+ yylval.str = p->name;
+ return (p->token);
+ }
+ break;
+
+ case SITECMD:
+ if (cbuf[cpos] == ' ') {
+ cpos++;
+ return (SP);
+ }
+ cp = &cbuf[cpos];
+ if ((cp2 = strpbrk(cp, " \n")))
+ cpos = cp2 - cbuf;
+ c = cbuf[cpos];
+ cbuf[cpos] = '\0';
+ upper(cp);
+ p = lookup(sitetab, cp);
+ cbuf[cpos] = c;
+ if (p != 0) {
+ if (p->implemented == 0) {
+ state = CMD;
+ nack(p->name);
+ longjmp(errcatch,0);
+ /* NOTREACHED */
+ }
+ state = p->state;
+ yylval.str = p->name;
+ return (p->token);
+ }
+ state = CMD;
+ break;
+
+ case OSTR:
+ if (cbuf[cpos] == '\n') {
+ state = CMD;
+ return (CRLF);
+ }
+ /* FALLTHROUGH */
+
+ case STR1:
+ case ZSTR1:
+ dostr1:
+ if (cbuf[cpos] == ' ') {
+ cpos++;
+ state = state == OSTR ? STR2 : ++state;
+ return (SP);
+ }
+ break;
+
+ case ZSTR2:
+ if (cbuf[cpos] == '\n') {
+ state = CMD;
+ return (CRLF);
+ }
+ /* FALLTHROUGH */
+
+ case STR2:
+ cp = &cbuf[cpos];
+ n = strlen(cp);
+ cpos += n - 1;
+ /*
+ * Make sure the string is nonempty and \n terminated.
+ */
+ if (n > 1 && cbuf[cpos] == '\n') {
+ cbuf[cpos] = '\0';
+ yylval.str = copy(cp);
+ cbuf[cpos] = '\n';
+ state = ARGS;
+ return (STRING);
+ }
+ break;
+
+ case NSTR:
+ if (cbuf[cpos] == ' ') {
+ cpos++;
+ return (SP);
+ }
+ if (isdigit(cbuf[cpos])) {
+ cp = &cbuf[cpos];
+ while (isdigit(cbuf[++cpos]))
+ ;
+ c = cbuf[cpos];
+ cbuf[cpos] = '\0';
+ yylval.num = atoi(cp);
+ cbuf[cpos] = c;
+ state = STR1;
+ return (NUMBER);
+ }
+ state = STR1;
+ goto dostr1;
+
+ case ARGS:
+ if (isdigit(cbuf[cpos])) {
+ cp = &cbuf[cpos];
+ while (isdigit(cbuf[++cpos]))
+ ;
+ c = cbuf[cpos];
+ cbuf[cpos] = '\0';
+ yylval.num = atoi(cp);
+ cbuf[cpos] = c;
+ return (NUMBER);
+ }
+ switch (cbuf[cpos++]) {
+
+ case '\n':
+ state = CMD;
+ return (CRLF);
+
+ case ' ':
+ return (SP);
+
+ case ',':
+ return (COMMA);
+
+ case 'A':
+ case 'a':
+ return (A);
+
+ case 'B':
+ case 'b':
+ return (B);
+
+ case 'C':
+ case 'c':
+ return (C);
+
+ case 'E':
+ case 'e':
+ return (E);
+
+ case 'F':
+ case 'f':
+ return (F);
+
+ case 'I':
+ case 'i':
+ return (I);
+
+ case 'L':
+ case 'l':
+ return (L);
+
+ case 'N':
+ case 'n':
+ return (N);
+
+ case 'P':
+ case 'p':
+ return (P);
+
+ case 'R':
+ case 'r':
+ return (R);
+
+ case 'S':
+ case 's':
+ return (S);
+
+ case 'T':
+ case 't':
+ return (T);
+
+ }
+ break;
+
+ default:
+ fatal("Unknown state in scanner.");
+ }
+ yyerror((char *) 0);
+ state = CMD;
+ longjmp(errcatch,0);
+ }
+}
+
+upper(s)
+ register char *s;
+{
+ while (*s != '\0') {
+ if (islower(*s))
+ *s = toupper(*s);
+ s++;
+ }
+}
+
+char *
+copy(s)
+ char *s;
+{
+ char *p;
+
+ p = malloc((unsigned) strlen(s) + 1);
+ if (p == NULL)
+ fatal("Ran out of memory.");
+ (void) strcpy(p, s);
+ return (p);
+}
+
+help(ctab, s)
+ struct tab *ctab;
+ char *s;
+{
+ register struct tab *c;
+ register int width, NCMDS;
+ char str[80];
+ char *type;
+
+ if (ctab == sitetab)
+ type = "SITE ";
+ else
+ type = "";
+ width = 0, NCMDS = 0;
+ for (c = ctab; c->name != NULL; c++) {
+ int len = strlen(c->name);
+
+ if (len > width)
+ width = len;
+ NCMDS++;
+ }
+ width = (width + 8) &~ 7;
+ if (s == 0) {
+ register int i, j, w;
+ int columns, lines;
+
+ lreply(214, "The following %scommands are recognized %s.",
+ type, "(* =>'s unimplemented)");
+ columns = 76 / width;
+ if (columns == 0)
+ columns = 1;
+ lines = (NCMDS + columns - 1) / columns;
+ for (i = 0; i < lines; i++) {
+ strcpy(str, " ");
+ for (j = 0; j < columns; j++) {
+ c = ctab + j * lines + i;
+ sprintf(&str[strlen(str)], "%s%c", c->name,
+ c->implemented ? ' ' : '*');
+ if (c + lines >= &ctab[NCMDS])
+ break;
+ w = strlen(c->name) + 1;
+ while (w < width) {
+ strcat(str, " ");
+ w++;
+ }
+ }
+ reply(0, "%s", str);
+ }
+ reply(214, "Direct comments to ftp-bugs@%s.", hostname);
+ return;
+ }
+ upper(s);
+ c = lookup(ctab, s);
+ if (c == (struct tab *)0) {
+ reply(502, "Unknown command %s.", s);
+ return;
+ }
+ if (c->implemented)
+ reply(214, "Syntax: %s%s %s", type, c->name, c->help);
+ else
+ reply(214, "%s%-*s\t%s; unimplemented.", type, width,
+ c->name, c->help);
+}
+
+sizecmd(filename)
+char *filename;
+{
+ switch (type) {
+ case TYPE_L:
+ case TYPE_I: {
+ struct stat stbuf;
+ if (stat(filename, &stbuf) < 0 ||
+ (stbuf.st_mode&S_IFMT) != S_IFREG)
+ reply(550, "%s: not a plain file.", filename);
+ else
+ reply(213, "%lu", stbuf.st_size);
+ break;}
+ case TYPE_A: {
+ FILE *fin;
+ register int c;
+ register long count;
+ struct stat stbuf;
+ fin = fopen(filename, "r");
+ if (fin == NULL) {
+ perror_reply(550, filename);
+ return;
+ }
+ if (fstat(fileno(fin), &stbuf) < 0 ||
+ (stbuf.st_mode&S_IFMT) != S_IFREG) {
+ reply(550, "%s: not a plain file.", filename);
+ (void) fclose(fin);
+ return;
+ }
+
+ count = 0;
+ while((c=getc(fin)) != EOF) {
+ if (c == '\n') /* will get expanded to \r\n */
+ count++;
+ count++;
+ }
+ (void) fclose(fin);
+
+ reply(213, "%ld", count);
+ break;}
+ default:
+ reply(504, "SIZE not implemented for Type %c.", "?AEIL"[type]);
+ }
+}
--- /dev/null
+.\" Copyright (c) 1985, 1988, 1991 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)ftpd.8 6.9 (Berkeley) 3/16/91
+.\"
+.so man1/tmac.doc
+.Dd March 16, 1991
+.Dt FTPD 8
+.Os BSD 4.2
+.Sh NAME
+.Nm ftpd
+.Nd
+.Tn DARPA
+Internet File Transfer Protocol server
+.Sh SYNOPSIS
+.Nm ftpd
+.Op Fl d
+.Op Fl l
+.Op Fl t Ar timeout
+.Op Fl T Ar maxtimeout
+.Op Fl p Ar port
+.Op Fl r Ar realm-file
+.Op Fl s Ar srvtab
+.Sh DESCRIPTION
+.Nm Ftpd
+is the
+.Tn DARPA
+Internet File Transfer Protocol
+server process. The server uses the
+.Tn TCP
+protocol
+and listens at the port specified in the
+.Dq ftp
+service specification; see
+.Xr services 5 .
+.Pp
+Available options:
+.Bl -tag -width Ds
+.It Fl d
+Debugging information is written to the syslog.
+.It Fl l
+Each
+.Xr ftp 1
+session is logged in the syslog.
+.It Fl t
+The inactivity timeout period is set to
+.Ar timeout
+seconds (the default is 15 minutes).
+.It Fl T
+A client may also request a different timeout period;
+the maximum period allowed may be set to
+.Ar timeout
+seconds with the
+.Fl T
+option.
+The default limit is 2 hours.
+.It Fl a
+Only permit Kerberos authenticated or anonymous logins.
+.It Fl p Ar port
+Run as a server and accept a connection on
+.Ar port.
+Normally the ftp server is invoked by
+.Xr inetd 8 .
+.It Fl r Ar realm-file
+Sets the name of the
+.Pa krb.conf
+file to use. The default value is normally
+.Pa /usr/kerberos/lib/krb.conf.
+.It Fl s Ar srvtab
+Sets the name of the
+.Pa srvtab
+file to use. The default value is normally
+.Pa /etc/krb-srvtab.
+.El
+.Pp
+The ftp server currently supports the following ftp
+requests; case is not distinguished.
+.Bl -column "Request" -offset indent
+.It Request Ta "Description"
+.It ABOR Ta "abort previous command"
+.It ACCT Ta "specify account (ignored)"
+.It ADAT Ta "send an authentication protocol message"
+.It ALLO Ta "allocate storage (vacuously)"
+.It APPE Ta "append to a file"
+.It AUTH Ta "specify an authentication protocol to be performed"
+.It CDUP Ta "change to parent of current working directory"
+.It CWD Ta "change working directory"
+.It DELE Ta "delete a file"
+.It ENC Ta "send a privacy and integrity protected command (given in argument)"
+.It HELP Ta "give help information"
+.It LIST Ta "give list files in a directory" Pq Dq Li "ls -lgA"
+.It MIC Ta "send an integrity protected command (given in argument)"
+.It MKD Ta "make a directory"
+.It MDTM Ta "show last modification time of file"
+.It MODE Ta "specify data transfer" Em mode
+.It NLST Ta "give name list of files in directory"
+.It NOOP Ta "do nothing"
+.It PASS Ta "specify password"
+.It PASV Ta "prepare for server-to-server transfer"
+.It PBSZ Ta "specify a protection buffer size"
+.It PORT Ta "specify data connection port"
+.It PROT Ta "specify a protection level under which to protect data transfers"
+.It PWD Ta "print the current working directory"
+.It QUIT Ta "terminate session"
+.It REST Ta "restart incomplete transfer"
+.It RETR Ta "retrieve a file"
+.It RMD Ta "remove a directory"
+.It RNFR Ta "specify rename-from file name"
+.It RNTO Ta "specify rename-to file name"
+.It SITE Ta "non-standard commands (see next section)"
+.It SIZE Ta "return size of file"
+.It STAT Ta "return status of server"
+.It STOR Ta "store a file"
+.It STOU Ta "store a file with a unique name"
+.It STRU Ta "specify data transfer" Em structure
+.It SYST Ta "show operating system type of server system"
+.It TYPE Ta "specify data transfer" Em type
+.It USER Ta "specify user name"
+.It XCUP Ta "change to parent of current working directory (deprecated)"
+.It XCWD Ta "change working directory (deprecated)"
+.It XMKD Ta "make a directory (deprecated)"
+.It XPWD Ta "print the current working directory (deprecated)"
+.It XRMD Ta "remove a directory (deprecated)"
+.El
+.Pp
+The following non-standard or
+.Tn UNIX
+specific commands are supported
+by the
+SITE request.
+.Pp
+.Bl -column Request -offset indent
+.It Sy Request Ta Sy Description
+.It UMASK Ta change umask. Em E.g. SITE UMASK 002
+.It IDLE Ta set idle-timer. Em E.g. SITE IDLE 60
+.It CHMOD Ta change mode of a file. Em E.g.
+SITE CHMOD 755 filename
+.It HELP Ta give help information. Em E.g. SITE HELP
+.El
+.Pp
+The remaining ftp requests specified in Internet
+.%T "RFC 959"
+are
+recognized, but not implemented.
+MDTM and SIZE are not specified in
+.%T "RFC 959" ,
+but will appear in the next updated FTP RFC.
+.Pp
+The ftp server will abort an active file transfer only when the
+ABOR
+command is preceded by a Telnet "Interrupt Process" (IP)
+signal and a Telnet "Synch" signal in the command Telnet stream,
+as described in Internet
+.%T "RFC 959" .
+If a
+STAT
+command is received during a data transfer, preceded by a Telnet IP
+and Synch, transfer status will be returned.
+.Pp
+.Nm Ftpd
+interprets file names according to the
+.Dq globbing
+conventions used by
+.Xr csh 1 .
+This allows users to utilize the metacharacters
+.Dq Li \&*?[]{}~ .
+.Pp
+.Nm Ftpd
+authenticates users according to the following rules:
+.Pp
+.Bl -enum -offset indent
+.It
+The user name must be in the password data base,
+.Pa /etc/passwd .
+.It
+An
+.Dv AUTH
+command must be accepted, the ensuing authentication protocol
+(conducted via
+.Dv ADAT
+commands and replies)
+must successfully complete, and the authenticated user must permitted
+access. Otherwise, a valid password which is not null
+must be provided by the client.
+.It
+The user name must not appear in the file
+.Pa /etc/ftpusers .
+.It
+The user must have a standard shell returned by
+.Xr getusershell 3 .
+.It
+If the user name is
+.Dq anonymous
+or
+.Dq ftp ,
+an
+anonymous ftp account must be present in the password
+file (user
+.Dq ftp ) .
+In this case the user is allowed
+to log in by specifying any password (by convention this
+is given as the client host's name).
+.El
+.Pp
+In the last case,
+.Nm ftpd
+takes special measures to restrict the client's access privileges.
+The server performs a
+.Xr chroot 2
+command to the home directory of the
+.Dq ftp
+user.
+In order that system security is not breached, it is recommended
+that the
+.Dq ftp
+subtree be constructed with care; the following
+rules are recommended.
+.Bl -tag -width "~ftp/pub" -offset indent
+.It Pa ~ftp
+Make the home directory owned by
+.Dq ftp
+and unwritable by anyone.
+.It Pa ~ftp/bin
+Make this directory owned by the super-user and unwritable by
+anyone. The program
+.Xr ls 1
+must be present to support the list command. This
+program should have mode 111.
+.It Pa ~ftp/etc
+Make this directory owned by the super-user and unwritable by
+anyone. The files
+.Xr passwd 5
+and
+.Xr group 5
+must be present for the
+.Xr ls
+command to be able to produce owner names rather than numbers.
+The password field in
+.Xr passwd
+is not used, and should not contain real encrypted passwords.
+These files should be mode 444.
+.It Pa ~ftp/pub
+Make this directory mode 777 and owned by
+.Dq ftp .
+Users
+should then place files which are to be accessible via the
+anonymous account in this directory.
+.El
+.Pp
+If an
+.Dv ADAT
+command succeeds, the control channel must be either
+integrity or privacy protected.
+In this case, the
+.Dv MIC
+and
+.Dv ENC
+commands are the only commands allowed over the control channel.
+The argument to the
+.Dv MIC
+command is a base 64 encoded string which, when decoded, is an
+ftp command integrity protected with a cryptographic checksum.
+The argument to the
+.Dv ENC
+command is a base 64 encoded string which, when decoded, is an
+ftp command privacy and integrity protected with encryption.
+.Pp
+If an
+.Dv ADAT
+command succeeds,
+ftp replies will also be either integrity or privacy protected.
+.Pp
+If an
+.Dv ADAT
+command succeeds, the data channel can also be integrity or privacy protected.
+The
+.Dv PROT
+command accepts S for integrity and P for privacy protection.
+Unless an
+.Dv ADAT
+command succeeds, the only protection level accepted by the
+.Dv PROT
+command is C (clear).
+.Sh SEE ALSO
+.Xr ftp 1 ,
+.Xr getusershell 3 ,
+.Xr syslogd 8
+.Pp
+Lunt, S. J.,
+FTP Security Extensions,
+Internet Draft,
+November 1993.
+.Sh BUGS
+The anonymous account is inherently dangerous and should
+avoided when possible.
+.Pp
+The server must run as the super-user
+to create sockets with privileged port numbers. It maintains
+an effective user id of the logged in user, reverting to
+the super-user only when binding addresses to sockets. The
+possible security holes have been extensively
+scrutinized, but are possibly incomplete.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
--- /dev/null
+/*
+ * Copyright (c) 1985, 1988, 1990 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1985, 1988, 1990 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)ftpd.c 5.40 (Berkeley) 7/2/91";
+#endif /* not lint */
+
+/*
+ * FTP server.
+ */
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/file.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+
+#define FTP_NAMES
+#include <arpa/ftp.h>
+#include <arpa/inet.h>
+#include <arpa/telnet.h>
+
+#include <signal.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <time.h>
+#include <pwd.h>
+#include <setjmp.h>
+#include <netdb.h>
+#include <errno.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef STDARG
+#if defined(__STDC__) && ! defined(VARARGS)
+#define STDARG
+#endif
+#endif
+#ifdef STDARG
+#include <stdarg.h>
+#endif
+#include "pathnames.h"
+
+#ifndef L_SET
+#define L_SET 0
+#endif
+#ifndef L_INCR
+#define L_INCR 1
+#endif
+
+#define strerror(error) (sys_errlist[error])
+extern char *sys_errlist[];
+
+extern char *mktemp ();
+
+#ifndef HAVE_SETEUID
+#ifdef HAVE_SETRESUID
+#define seteuid(e) setresuid(-1,e,-1)
+#define setegid(e) setresgid(-1,e,-1)
+#endif
+#endif
+
+#ifdef STDARG
+extern reply(int, char *, ...);
+extern lreply(int, char *, ...);
+#endif
+
+#ifdef KERBEROS
+#include <krb.h>
+
+AUTH_DAT kdata;
+KTEXT_ST ticket;
+MSG_DAT msg_data;
+Key_schedule schedule;
+int kerb_ok; /* Kerberos authentication and authorization succeeded */
+char *keyfile = KEYFILE;
+#endif /* KERBEROS */
+
+#ifdef GSSAPI
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+gss_ctx_id_t gcontext;
+gss_buffer_desc client_name;
+int gss_ok; /* GSSAPI authentication and userok authorization succeeded */
+char* gss_services[] = { "ftp", "host", 0 };
+#endif /* GSSAPI */
+
+char *auth_type; /* Authentication succeeded? If so, what type? */
+static char *temp_auth_type;
+
+/*
+ * File containing login names
+ * NOT to be used on this machine.
+ * Commonly used to disallow uucp.
+ */
+extern int errno;
+extern char *crypt();
+extern char version[];
+extern char *home; /* pointer to home directory for glob */
+extern FILE *ftpd_popen(), *fopen(), *freopen();
+extern int ftpd_pclose(), fclose();
+extern char *getline();
+extern char cbuf[];
+extern off_t restart_point;
+
+struct sockaddr_in ctrl_addr;
+struct sockaddr_in data_source;
+struct sockaddr_in data_dest;
+struct sockaddr_in his_addr;
+struct sockaddr_in pasv_addr;
+
+int data;
+jmp_buf errcatch, urgcatch;
+int logged_in;
+struct passwd *pw;
+int debug;
+int timeout = 900; /* timeout after 15 minutes of inactivity */
+int maxtimeout = 7200;/* don't allow idle time to be set beyond 2 hours */
+int logging;
+int authenticate;
+int guest;
+int type;
+int level;
+int form;
+int stru; /* avoid C keyword */
+int mode;
+int usedefault = 1; /* for data transfers */
+int pdata = -1; /* for passive mode */
+int transflag;
+off_t file_size;
+off_t byte_count;
+#if !defined(CMASK) || CMASK == 0
+#undef CMASK
+#define CMASK 027
+#endif
+int defumask = CMASK; /* default umask value */
+char tmpline[FTP_BUFSIZ];
+char hostname[MAXHOSTNAMELEN];
+char remotehost[MAXHOSTNAMELEN];
+
+/*
+ * Timeout intervals for retrying connections
+ * to hosts that don't accept PORT cmds. This
+ * is a kludge, but given the problems with TCP...
+ */
+#define SWAITMAX 90 /* wait at most 90 seconds */
+#define SWAITINT 5 /* interval between retries */
+
+int swaitmax = SWAITMAX;
+int swaitint = SWAITINT;
+
+void lostconn(), myoob();
+FILE *getdatasock(), *dataconn();
+
+#ifdef SETPROCTITLE
+char **Argv = NULL; /* pointer to argument vector */
+char *LastArgv = NULL; /* end of argv */
+char proctitle[FTP_BUFSIZ]; /* initial part of title */
+#endif /* SETPROCTITLE */
+
+#ifdef __SCO__
+/* sco has getgroups and setgroups but no initgroups */
+int initgroups(char* name, gid_t basegid) {
+ gid_t others[NGROUPS_MAX+1];
+ int ngrps;
+
+ others[0] = basegid;
+ ngrps = getgroups(NGROUPS_MAX, others+1);
+ return setgroups(ngrps+1, others);
+}
+#endif
+
+main(argc, argv, envp)
+ int argc;
+ char *argv[];
+ char **envp;
+{
+ int addrlen, on = 1, tos, port = -1;
+ char *cp;
+
+ debug = 0;
+#ifdef SETPROCTITLE
+ /*
+ * Save start and extent of argv for setproctitle.
+ */
+ Argv = argv;
+ while (*envp)
+ envp++;
+ LastArgv = envp[-1] + strlen(envp[-1]);
+#endif /* SETPROCTITLE */
+
+ argc--, argv++;
+ while (argc > 0 && *argv[0] == '-') {
+ for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
+
+ case 'v':
+ debug = 1;
+ break;
+
+ case 'd':
+ debug = 1;
+ break;
+
+ case 'l':
+ logging = 1;
+ break;
+
+ case 'a':
+ authenticate = 1;
+ break;
+
+ case 'p':
+ if (*++cp != '\0')
+ port = atoi(cp);
+ else if (argc > 1) {
+ argc--, argv++;
+ port = atoi(*argv);
+ }
+ else
+ fprintf(stderr, "ftpd: -p expects argument\n");
+ goto nextopt;
+
+ case 'r':
+ if (*++cp != '\0')
+ setenv("KRB_CONF", cp, 1);
+ else if (argc > 1) {
+ argc--, argv++;
+ setenv("KRB_CONF", *argv, 1);
+ }
+ else
+ fprintf(stderr, "ftpd: -r expects argument\n");
+ goto nextopt;
+
+#ifdef KERBEROS
+ case 's':
+ if (*++cp != '\0')
+ keyfile = cp;
+ else if (argc > 1) {
+ argc--, argv++;
+ keyfile = *argv;
+ }
+ else
+ fprintf(stderr, "ftpd: -s expects argument\n");
+ goto nextopt;
+
+#endif /* KERBEROS */
+ case 't':
+ timeout = atoi(++cp);
+ if (maxtimeout < timeout)
+ maxtimeout = timeout;
+ goto nextopt;
+
+ case 'T':
+ maxtimeout = atoi(++cp);
+ if (timeout > maxtimeout)
+ timeout = maxtimeout;
+ goto nextopt;
+
+ case 'u':
+ {
+ int val = 0;
+
+ while (*++cp && *cp >= '0' && *cp <= '9')
+ val = val*8 + *cp - '0';
+ if (*cp)
+ fprintf(stderr, "ftpd: Bad value for -u\n");
+ else
+ defumask = val;
+ goto nextopt;
+ }
+
+ default:
+ fprintf(stderr, "ftpd: Unknown flag -%c ignored.\n",
+ *cp);
+ break;
+ }
+nextopt:
+ argc--, argv++;
+ }
+
+ if (port != -1) {
+ struct sockaddr_in sin;
+ int s, ns, sz;
+
+ /* Accept an incoming connection on port. */
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+ sin.sin_port = htons(port);
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0) {
+ perror("socket");
+ exit(1);
+ }
+ (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&on, sizeof(on));
+ if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
+ perror("bind");
+ exit(1);
+ }
+ if (listen(s, 1) < 0) {
+ perror("listen");
+ exit(1);
+ }
+ sz = sizeof sin;
+ ns = accept(s, (struct sockaddr *)&sin, &sz);
+ if (ns < 0) {
+ perror("accept");
+ exit(1);
+ }
+ (void) close(s);
+ (void) dup2(ns, 0);
+ (void) dup2(ns, 1);
+ (void) dup2(ns, 2);
+ if (ns > 2)
+ (void) close(ns);
+ }
+
+ /*
+ * LOG_NDELAY sets up the logging connection immediately,
+ * necessary for anonymous ftp's that chroot and can't do it later.
+ */
+#ifndef LOG_NDELAY
+/* Ultrix syslog does not support NDELAY. */
+#define LOG_NDELAY 0
+#endif
+#ifndef LOG_DAEMON
+#define LOG_DAEMON 0
+#endif
+ openlog("ftpd", LOG_PID | LOG_NDELAY, LOG_DAEMON);
+ addrlen = sizeof (his_addr);
+ if (getpeername(0, (struct sockaddr *)&his_addr, &addrlen) < 0) {
+ syslog(LOG_ERR, "getpeername (%s): %m",argv[0]);
+ exit(1);
+ }
+ addrlen = sizeof (ctrl_addr);
+ if (getsockname(0, (struct sockaddr *)&ctrl_addr, &addrlen) < 0) {
+ syslog(LOG_ERR, "getsockname (%s): %m",argv[0]);
+ exit(1);
+ }
+#ifdef IP_TOS
+#ifdef IPTOS_LOWDELAY
+ tos = IPTOS_LOWDELAY;
+ if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *)&tos, sizeof(int)) < 0)
+ syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+#endif
+#endif
+ port = ntohs(ctrl_addr.sin_port);
+ data_source.sin_port = htons(port - 1);
+
+ (void) freopen("/dev/null", "w", stderr);
+ (void) signal(SIGPIPE, lostconn);
+ (void) signal(SIGCHLD, SIG_IGN);
+#ifdef SIGURG
+ if ((long)signal(SIGURG, myoob) < 0)
+ syslog(LOG_ERR, "signal: %m");
+#endif
+
+ /* Try to handle urgent data inline */
+#ifdef SO_OOBINLINE
+ if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt: %m");
+#endif
+
+#ifdef F_SETOWN
+ if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
+ syslog(LOG_ERR, "fcntl F_SETOWN: %m");
+#endif
+ dolog(&his_addr);
+ /*
+ * Set up default state
+ */
+ data = -1;
+ level = PROT_C;
+ type = TYPE_A;
+ form = FORM_N;
+ stru = STRU_F;
+ mode = MODE_S;
+ tmpline[0] = '\0';
+ (void) gethostname(hostname, sizeof (hostname));
+ reply(220, "%s FTP server (%s) ready.", hostname, version);
+ (void) setjmp(errcatch);
+ for (;;)
+ (void) yyparse();
+ /* NOTREACHED */
+}
+
+void
+lostconn()
+{
+ if (debug)
+ syslog(LOG_DEBUG, "lost connection");
+ dologout(-1);
+}
+
+static char ttyline[20];
+
+/*
+ * Helper function for sgetpwnam().
+ */
+char *
+sgetsave(s)
+ char *s;
+{
+ char *new = malloc((unsigned) strlen(s) + 1);
+
+ if (new == NULL) {
+ perror_reply(421, "Local resource failure: malloc");
+ dologout(1);
+ /* NOTREACHED */
+ }
+ (void) strcpy(new, s);
+ return (new);
+}
+
+/*
+ * Save the result of a getpwnam. Used for USER command, since
+ * the data returned must not be clobbered by any other command
+ * (e.g., globbing).
+ */
+struct passwd *
+sgetpwnam(name)
+ char *name;
+{
+ static struct passwd save;
+ register struct passwd *p;
+ char *sgetsave();
+
+ if ((p = getpwnam(name)) == NULL)
+ return (p);
+ if (save.pw_name) {
+ free(save.pw_name);
+ free(save.pw_passwd);
+ free(save.pw_gecos);
+ free(save.pw_dir);
+ free(save.pw_shell);
+ }
+ save = *p;
+ save.pw_name = sgetsave(p->pw_name);
+ save.pw_passwd = sgetsave(p->pw_passwd);
+ save.pw_gecos = sgetsave(p->pw_gecos);
+ save.pw_dir = sgetsave(p->pw_dir);
+ save.pw_shell = sgetsave(p->pw_shell);
+ return (&save);
+}
+
+setlevel(prot_level)
+int prot_level;
+{
+ switch (prot_level) {
+ case PROT_S:
+#ifndef NOENCRYPTION
+ case PROT_P:
+#endif
+ if (auth_type)
+ case PROT_C:
+ reply(200, "Protection level set to %s.",
+ (level = prot_level) == PROT_S ?
+ "Safe" : level == PROT_P ?
+ "Private" : "Clear");
+ else
+ default: reply(536, "%s protection level not supported.",
+ levelnames[prot_level]);
+ }
+}
+
+int login_attempts; /* number of failed login attempts */
+int askpasswd; /* had user command, ask for passwd */
+
+/*
+ * USER command.
+ * Sets global passwd pointer pw if named account exists and is acceptable;
+ * sets askpasswd if a PASS command is expected. If logged in previously,
+ * need to reset state. If name is "ftp" or "anonymous", the name is not in
+ * _PATH_FTPUSERS, and ftp account exists, set guest and pw, then just return.
+ * If account doesn't exist, ask for passwd anyway. Otherwise, check user
+ * requesting login privileges. Disallow anyone who does not have a standard
+ * shell as returned by getusershell(). Disallow anyone mentioned in the file
+ * _PATH_FTPUSERS to allow people such as root and uucp to be avoided.
+ */
+user(name)
+ char *name;
+{
+ register char *cp;
+ char *shell;
+#ifdef HAVE_GETUSERSHELL
+ char *getusershell();
+#endif
+
+ /* Some paranoid sites may want the client to authenticate
+ * before accepting the USER command. If so, uncomment this:
+
+ if (!auth_type) {
+ reply(530,
+ "Must perform authentication before identifying USER.");
+ return;
+ */
+ if (logged_in) {
+ if (guest) {
+ reply(530, "Can't change user from guest login.");
+ return;
+ }
+ end_login();
+ }
+
+ guest = 0;
+ if (strcmp(name, "ftp") == 0 || strcmp(name, "anonymous") == 0) {
+ if (checkuser("ftp") || checkuser("anonymous"))
+ reply(530, "User %s access denied.", name);
+ else if ((pw = sgetpwnam("ftp")) != NULL) {
+ guest = 1;
+ askpasswd = 1;
+ reply(331, "Guest login ok, send ident as password.");
+ } else
+ reply(530, "User %s unknown.", name);
+ return;
+ }
+ if (pw = sgetpwnam(name)) {
+ if ((shell = pw->pw_shell) == NULL || *shell == 0)
+ shell = "/bin/sh";
+#ifdef HAVE_GETUSERSHELL
+ while ((cp = getusershell()) != NULL)
+ if (strcmp(cp, shell) == 0)
+ break;
+ /* endusershell(); */ /* this breaks on solaris 2.4 */
+#else
+ cp = shell;
+#endif
+ if (cp == NULL || checkuser(name)) {
+ reply(530, "User %s access denied.", name);
+ if (logging)
+ syslog(LOG_NOTICE,
+ "FTP LOGIN REFUSED FROM %s, %s",
+ remotehost, name);
+ pw = (struct passwd *) NULL;
+ return;
+ }
+ }
+#ifdef KERBEROS
+ if (auth_type && strcmp(auth_type, "KERBEROS_V4") == 0) {
+ char buf[FTP_BUFSIZ];
+ kerb_ok = kuserok(&kdata,name) == 0;
+ if (! kerb_ok && authenticate) {
+ reply(530, "User %s access denied.", name);
+ if (logging)
+ syslog(LOG_NOTICE,
+ "FTP KERBEROS LOGIN REFUSED FROM %s, %s",
+ remotehost, name);
+ pw = (struct passwd *) NULL;
+ return;
+ }
+ sprintf(buf, "Kerberos user %s%s%s@%s is%s authorized as %s%s",
+ kdata.pname, *kdata.pinst ? "." : "",
+ kdata.pinst, kdata.prealm,
+ kerb_ok ? "" : " not",
+ name, kerb_ok ? "" : "; Password required.");
+ reply(kerb_ok ? 232 : 336, "%s", buf);
+ syslog(kerb_ok ? LOG_INFO : LOG_ERR, "%s", buf);
+ } else
+#endif /* KERBEROS */
+#ifdef GSSAPI
+ if (auth_type && strcmp(auth_type, "GSSAPI") == 0) {
+ char buf[FTP_BUFSIZ];
+ gss_ok = ftpd_userok(&client_name, name) == 0;
+ if (! gss_ok && authenticate) {
+ reply(530, "User %s access denied.", name);
+ if (logging)
+ syslog(LOG_NOTICE,
+ "FTP GSSAPI LOGIN REFUSED FROM %s, %s",
+ remotehost, name);
+ pw = (struct passwd *) NULL;
+ return;
+ }
+ sprintf(buf, "GSSAPI user %s is%s authorized as %s%s",
+ client_name.value,
+ gss_ok ? "" : " not",
+ name, gss_ok ? "" : "; Password required.");
+ /* 232 is per draft-8, but why 331 not 53z? */
+ reply(gss_ok ? 232 : 331, "%s", buf);
+ syslog(gss_ok ? LOG_INFO : LOG_ERR, "%s", buf);
+ } else
+#endif /* GSSAPI */
+ /* Other auth types go here ... */
+ if (authenticate) {
+ reply(530, "User %s access denied: authentication required.",
+ name);
+ if (logging)
+ syslog(LOG_NOTICE,
+ "FTP LOGIN REFUSED FROM %s, %s",
+ remotehost, name);
+ pw = (struct passwd *) NULL;
+ return;
+ } else
+ reply(331, "Password required for %s.", name);
+ askpasswd = 1;
+ /*
+ * Delay before reading passwd after first failed
+ * attempt to slow down passwd-guessing programs.
+ */
+ if (login_attempts)
+ sleep((unsigned) login_attempts);
+}
+
+/*
+ * Check if a user is in the file _PATH_FTPUSERS
+ */
+checkuser(name)
+ char *name;
+{
+ register FILE *fd;
+ register char *p;
+ char line[FTP_BUFSIZ];
+
+ if ((fd = fopen(_PATH_FTPUSERS, "r")) != NULL) {
+ while (fgets(line, sizeof(line), fd) != NULL)
+ if ((p = strchr(line, '\n')) != NULL) {
+ *p = '\0';
+ if (line[0] == '#')
+ continue;
+ if (strcmp(line, name) == 0)
+ return (1);
+ }
+ (void) fclose(fd);
+ }
+ return (0);
+}
+
+/*
+ * Terminate login as previous user, if any, resetting state;
+ * used when USER command is given or login fails.
+ */
+end_login()
+{
+
+ (void) seteuid((uid_t)0);
+ if (logged_in)
+ logwtmp(ttyline, "", "");
+ pw = NULL;
+ logged_in = 0;
+ guest = 0;
+}
+
+#ifdef KERBEROS
+static char *services[] = { "ftp", "rcmd", NULL };
+
+kpass(name, passwd)
+char *name, *passwd;
+{
+ char **service;
+ char instance[INST_SZ];
+ char realm[REALM_SZ];
+ char tkt_file[20];
+ KTEXT_ST ticket;
+ AUTH_DAT authdata;
+ des_cblock key;
+ unsigned long faddr;
+ struct hostent *hp;
+
+ if (krb_get_lrealm(realm, 1) != KSUCCESS)
+ return(0);
+
+ strcpy(tkt_file, TKT_ROOT);
+ strcat(tkt_file, "_ftpdXXXXXX");
+ krb_set_tkt_string(mktemp(tkt_file));
+
+ (void) strncpy(instance, krb_get_phost(hostname), sizeof(instance));
+
+ if ((hp = gethostbyname(instance)) == NULL)
+ return(0);
+
+ memcpy((char *) &faddr, (char *)hp->h_addr, sizeof(faddr));
+
+ if (krb_get_pw_in_tkt(name, "", realm, "krbtgt", realm, 1, passwd)) {
+ for (service = services; *service; service++)
+ if (!read_service_key(*service, instance, realm, 0, keyfile, key)) {
+ (void) memset(key, 0, sizeof(key));
+ if (krb_mk_req(&ticket, *service, instance, realm, 33) ||
+ krb_rd_req(&ticket, *service, instance, faddr, &authdata,keyfile)||
+ kuserok(&authdata, name)) {
+ dest_tkt();
+ return(0);
+ } else {
+ dest_tkt();
+ return(1);
+ }
+ }
+ dest_tkt();
+ return(0);
+ }
+ dest_tkt();
+ return(1);
+}
+#endif /* KERBEROS */
+
+pass(passwd)
+ char *passwd;
+{
+ char *xpasswd, *salt;
+
+ if (logged_in || askpasswd == 0) {
+ reply(503, "Login with USER first.");
+ return;
+ }
+ askpasswd = 0;
+ if (
+#ifdef KERBEROS
+ !kerb_ok &&
+#endif /* KERBEROS */
+#ifdef GSSAPI
+ !gss_ok &&
+#endif /* GSSAPI */
+ !guest) { /* "ftp" is only account allowed no password */
+ if (pw == NULL)
+ salt = "xx";
+ else
+ salt = pw->pw_passwd;
+#ifdef __SCO__
+ /* SCO does not provide crypt. */
+ xpasswd = "";
+#else
+ xpasswd = crypt(passwd, salt);
+#endif
+#ifdef KERBEROS
+ /* null pw_passwd ok if Kerberos password ok */
+ if (pw == NULL ||
+ (*pw->pw_passwd && strcmp(xpasswd, pw->pw_passwd) &&
+ !kpass(pw->pw_name, passwd)) ||
+ (!*pw->pw_passwd && !kpass(pw->pw_name, passwd))) {
+#else
+ /* The strcmp does not catch null passwords! */
+ if (pw == NULL || *pw->pw_passwd == '\0' ||
+ strcmp(xpasswd, pw->pw_passwd)) {
+#endif /* KERBEROS */
+ reply(530, "Login incorrect.");
+ pw = NULL;
+ if (login_attempts++ >= 5) {
+ syslog(LOG_NOTICE,
+ "repeated login failures from %s",
+ remotehost);
+ exit(0);
+ }
+ return;
+ }
+ }
+ login_attempts = 0; /* this time successful */
+ (void) setegid((gid_t)pw->pw_gid);
+ (void) initgroups(pw->pw_name, pw->pw_gid);
+
+ /* open wtmp before chroot */
+ (void)sprintf(ttyline, "ftp%d", getpid());
+ logwtmp(ttyline, pw->pw_name, remotehost);
+ logged_in = 1;
+
+ if (guest) {
+ /*
+ * We MUST do a chdir() after the chroot. Otherwise
+ * the old current directory will be accessible as "."
+ * outside the new root!
+ */
+ if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
+ reply(550, "Can't set guest privileges.");
+ goto bad;
+ }
+ } else if (chdir(pw->pw_dir) < 0) {
+ if (chdir("/") < 0) {
+ reply(530, "User %s: can't change directory to %s.",
+ pw->pw_name, pw->pw_dir);
+ goto bad;
+ } else
+ lreply(230, "No directory! Logging in with home=/");
+ }
+ if (seteuid((uid_t)pw->pw_uid) < 0) {
+ reply(550, "Can't set uid.");
+ goto bad;
+ }
+ if (guest) {
+ reply(230, "Guest login ok, access restrictions apply.");
+#ifdef SETPROCTITLE
+ sprintf(proctitle, "%s: anonymous/%.*s", remotehost,
+ sizeof(proctitle) - sizeof(remotehost) -
+ sizeof(": anonymous/"), passwd);
+ setproctitle(proctitle);
+#endif /* SETPROCTITLE */
+ if (logging)
+ syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
+ remotehost, passwd);
+ } else {
+ reply(230, "User %s logged in.", pw->pw_name);
+#ifdef SETPROCTITLE
+ sprintf(proctitle, "%s: %s", remotehost, pw->pw_name);
+ setproctitle(proctitle);
+#endif /* SETPROCTITLE */
+ if (logging)
+ syslog(LOG_INFO, "FTP LOGIN FROM %s, %s",
+ remotehost, pw->pw_name);
+ }
+ home = pw->pw_dir; /* home dir for globbing */
+ (void) umask(defumask);
+ return;
+bad:
+ /* Forget all about it... */
+ end_login();
+}
+
+retrieve(cmd, name)
+ char *cmd, *name;
+{
+ FILE *fin, *dout;
+ struct stat st;
+ int (*closefunc)();
+
+ if (cmd == 0) {
+ fin = fopen(name, "r"), closefunc = fclose;
+ st.st_size = 0;
+ } else {
+ char line[FTP_BUFSIZ];
+
+ (void) sprintf(line, cmd, name), name = line;
+ fin = ftpd_popen(line, "r"), closefunc = ftpd_pclose;
+ st.st_size = -1;
+#ifndef NOSTBLKSIZE
+ st.st_blksize = FTP_BUFSIZ;
+#endif
+ }
+ if (fin == NULL) {
+ if (errno != 0)
+ perror_reply(550, name);
+ return;
+ }
+ if (cmd == 0 &&
+ (fstat(fileno(fin), &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
+ reply(550, "%s: not a plain file.", name);
+ goto done;
+ }
+ if (restart_point) {
+ if (type == TYPE_A) {
+ register int i, n, c;
+
+ n = restart_point;
+ i = 0;
+ while (i++ < n) {
+ if ((c=getc(fin)) == EOF) {
+ perror_reply(550, name);
+ goto done;
+ }
+ if (c == '\n')
+ i++;
+ }
+ } else if (lseek(fileno(fin), restart_point, L_SET) < 0) {
+ perror_reply(550, name);
+ goto done;
+ }
+ }
+ dout = dataconn(name, st.st_size, "w");
+ if (dout == NULL)
+ goto done;
+#ifndef NOSTBLKSIZE
+ send_data(fin, dout, st.st_blksize);
+#else
+ send_data(fin, dout, FTP_BUFSIZ);
+#endif
+ (void) fclose(dout);
+ data = -1;
+ pdata = -1;
+done:
+ (*closefunc)(fin);
+}
+
+store(name, mode, unique)
+ char *name, *mode;
+ int unique;
+{
+ FILE *fout, *din;
+ struct stat st;
+ int (*closefunc)();
+ char *gunique();
+
+ if (unique && stat(name, &st) == 0 &&
+ (name = gunique(name)) == NULL)
+ return;
+
+ if (restart_point)
+ mode = "r+w";
+ fout = fopen(name, mode);
+ closefunc = fclose;
+ if (fout == NULL) {
+ perror_reply(553, name);
+ return;
+ }
+ if (restart_point) {
+ if (type == TYPE_A) {
+ register int i, n, c;
+
+ n = restart_point;
+ i = 0;
+ while (i++ < n) {
+ if ((c=getc(fout)) == EOF) {
+ perror_reply(550, name);
+ goto done;
+ }
+ if (c == '\n')
+ i++;
+ }
+ /*
+ * We must do this seek to "current" position
+ * because we are changing from reading to
+ * writing.
+ */
+ if (fseek(fout, 0L, L_INCR) < 0) {
+ perror_reply(550, name);
+ goto done;
+ }
+ } else if (lseek(fileno(fout), restart_point, L_SET) < 0) {
+ perror_reply(550, name);
+ goto done;
+ }
+ }
+ din = dataconn(name, (off_t)-1, "r");
+ if (din == NULL)
+ goto done;
+ if (receive_data(din, fout) == 0) {
+ if (unique)
+ reply(226, "Transfer complete (unique file name:%s).",
+ name);
+ else
+ reply(226, "Transfer complete.");
+ }
+ (void) fclose(din);
+ data = -1;
+ pdata = -1;
+done:
+ (*closefunc)(fout);
+}
+
+FILE *
+getdatasock(mode)
+ char *mode;
+{
+ int s, on = 1, tries;
+
+ if (data >= 0)
+ return (fdopen(data, mode));
+ (void) seteuid((uid_t)0);
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0)
+ goto bad;
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+ (char *) &on, sizeof (on)) < 0)
+ goto bad;
+ /* anchor socket to avoid multi-homing problems */
+ data_source.sin_family = AF_INET;
+ data_source.sin_addr = ctrl_addr.sin_addr;
+ for (tries = 1; ; tries++) {
+ if (bind(s, (struct sockaddr *)&data_source,
+ sizeof (data_source)) >= 0)
+ break;
+ if (errno != EADDRINUSE || tries > 10)
+ goto bad;
+ sleep(tries);
+ }
+ (void) seteuid((uid_t)pw->pw_uid);
+#ifdef IP_TOS
+#ifdef IPTOS_THROUGHPUT
+ on = IPTOS_THROUGHPUT;
+ if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&on, sizeof(int)) < 0)
+ syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
+#endif
+#endif
+ return (fdopen(s, mode));
+bad:
+ (void) seteuid((uid_t)pw->pw_uid);
+ (void) close(s);
+ return (NULL);
+}
+
+FILE *
+dataconn(name, size, mode)
+ char *name;
+ off_t size;
+ char *mode;
+{
+ char sizebuf[32];
+ FILE *file;
+ int retry = 0, tos;
+
+ file_size = size;
+ byte_count = 0;
+ if (size != (off_t) -1)
+ (void) sprintf (sizebuf, " (%ld bytes)", size);
+ else
+ (void) strcpy(sizebuf, "");
+ if (pdata >= 0) {
+ int s, fromlen = sizeof(data_dest);
+
+ s = accept(pdata, (struct sockaddr *)&data_dest, &fromlen);
+ if (s < 0) {
+ reply(425, "Can't open data connection.");
+ (void) close(pdata);
+ pdata = -1;
+ return(NULL);
+ }
+ (void) close(pdata);
+ pdata = s;
+#ifdef IP_TOS
+#ifdef IPTOS_LOWDELAY
+ tos = IPTOS_LOWDELAY;
+ (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&tos,
+ sizeof(int));
+#endif
+#endif
+ reply(150, "Opening %s mode data connection for %s%s.",
+ type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
+ return(fdopen(pdata, mode));
+ }
+ if (data >= 0) {
+ reply(125, "Using existing data connection for %s%s.",
+ name, sizebuf);
+ usedefault = 1;
+ return (fdopen(data, mode));
+ }
+ if (usedefault)
+ data_dest = his_addr;
+ usedefault = 1;
+ file = getdatasock(mode);
+ if (file == NULL) {
+ reply(425, "Can't create data socket (%s,%d): %s.",
+ inet_ntoa(data_source.sin_addr),
+ ntohs(data_source.sin_port), strerror(errno));
+ return (NULL);
+ }
+ data = fileno(file);
+ while (connect(data, (struct sockaddr *)&data_dest,
+ sizeof (data_dest)) < 0) {
+ if (errno == EADDRINUSE && retry < swaitmax) {
+ sleep((unsigned) swaitint);
+ retry += swaitint;
+ continue;
+ }
+ perror_reply(425, "Can't build data connection");
+ (void) fclose(file);
+ data = -1;
+ return (NULL);
+ }
+ reply(150, "Opening %s mode data connection for %s%s.",
+ type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
+ return (file);
+}
+
+#ifdef STDARG
+secure_error(char *fmt, ...)
+#else
+/* VARARGS1 */
+secure_error(fmt, p1, p2, p3, p4, p5)
+ char *fmt;
+#endif
+{
+ char buf[FTP_BUFSIZ];
+#ifdef STDARG
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ va_end(ap);
+#else
+ sprintf(buf, fmt, p1, p2, p3, p4, p5);
+#endif
+ reply(535, "%s", buf);
+ syslog(LOG_ERR, "%s", buf);
+}
+
+/*
+ * Tranfer the contents of "instr" to
+ * "outstr" peer using the appropriate
+ * encapsulation of the data subject
+ * to Mode, Structure, and Type.
+ *
+ * NB: Form isn't handled.
+ */
+send_data(instr, outstr, blksize)
+ FILE *instr, *outstr;
+ off_t blksize;
+{
+ register int c, cnt;
+ register char *buf;
+ int netfd, filefd;
+ int ret = 0;
+
+ transflag++;
+ if (setjmp(urgcatch)) {
+ transflag = 0;
+ return;
+ }
+ switch (type) {
+
+ case TYPE_A:
+ while ((c = getc(instr)) != EOF) {
+ byte_count++;
+ if (c == '\n') {
+ if (ferror(outstr) ||
+ (ret = secure_putc('\r', outstr)) < 0)
+ goto data_err;
+ }
+ if ((ret = secure_putc(c, outstr)) < 0)
+ goto data_err;
+ }
+ transflag = 0;
+ if (ferror(instr))
+ goto file_err;
+ if (ferror(outstr) ||
+ (ret = secure_flush(fileno(outstr))) < 0)
+ goto data_err;
+ reply(226, "Transfer complete.");
+ return;
+
+ case TYPE_I:
+ case TYPE_L:
+ if ((buf = malloc((u_int)blksize)) == NULL) {
+ transflag = 0;
+ perror_reply(451, "Local resource failure: malloc");
+ return;
+ }
+ netfd = fileno(outstr);
+ filefd = fileno(instr);
+ while ((cnt = read(filefd, buf, (u_int)blksize)) > 0 &&
+ (ret = secure_write(netfd, buf, cnt)) == cnt)
+ byte_count += cnt;
+ transflag = 0;
+ (void)free(buf);
+ if (cnt != 0) {
+ if (cnt < 0)
+ goto file_err;
+ goto data_err;
+ }
+ if ((ret = secure_flush(netfd)) < 0)
+ goto data_err;
+ reply(226, "Transfer complete.");
+ return;
+ default:
+ transflag = 0;
+ reply(550, "Unimplemented TYPE %d in send_data", type);
+ return;
+ }
+
+data_err:
+ transflag = 0;
+ if (ret != -2) perror_reply(426, "Data connection");
+ return;
+
+file_err:
+ transflag = 0;
+ perror_reply(551, "Error on input file");
+}
+
+/*
+ * Transfer data from peer to
+ * "outstr" using the appropriate
+ * encapulation of the data subject
+ * to Mode, Structure, and Type.
+ *
+ * N.B.: Form isn't handled.
+ */
+receive_data(instr, outstr)
+ FILE *instr, *outstr;
+{
+ register int c;
+ int cnt, bare_lfs = 0;
+ char buf[FTP_BUFSIZ];
+ int ret = 0;
+
+ transflag++;
+ if (setjmp(urgcatch)) {
+ transflag = 0;
+ return (-1);
+ }
+ switch (type) {
+
+ case TYPE_I:
+ case TYPE_L:
+ while ((cnt = secure_read(fileno(instr), buf, sizeof buf)) > 0) {
+ if (write(fileno(outstr), buf, cnt) != cnt)
+ goto file_err;
+ byte_count += cnt;
+ }
+ transflag = 0;
+ ret = cnt;
+ if (cnt < 0)
+ goto data_err;
+ return (0);
+
+ case TYPE_E:
+ reply(553, "TYPE E not implemented.");
+ transflag = 0;
+ return (-1);
+
+ case TYPE_A:
+ while ((c = secure_getc(instr)) >= 0) {
+ byte_count++;
+ if (c == '\n')
+ bare_lfs++;
+ while (c == '\r') {
+ if (ferror(outstr))
+ goto data_err;
+ if ((c = secure_getc(instr)) != '\n') {
+ (void) putc ('\r', outstr);
+ if (c == '\0')
+ goto contin2;
+ }
+ }
+ if (c < 0) break;
+ (void) putc(c, outstr);
+ contin2: ;
+ }
+ fflush(outstr);
+ ret = c;
+ if (c == -2 || ferror(instr))
+ goto data_err;
+ if (ferror(outstr))
+ goto file_err;
+ transflag = 0;
+ if (bare_lfs) {
+ lreply(226, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs);
+ reply(0, " File may not have transferred correctly.");
+ }
+ return (0);
+ default:
+ reply(550, "Unimplemented TYPE %d in receive_data", type);
+ transflag = 0;
+ return (-1);
+ }
+
+data_err:
+ transflag = 0;
+ if (ret != -2) perror_reply(426, "Data Connection");
+ return (-1);
+
+file_err:
+ transflag = 0;
+ perror_reply(452, "Error writing file");
+ return (-1);
+}
+
+statfilecmd(filename)
+ char *filename;
+{
+ char line[FTP_BUFSIZ];
+ FILE *fin;
+ int c;
+ char str[FTP_BUFSIZ], *p;
+
+ (void) sprintf(line, "/bin/ls -lgA %s", filename);
+ fin = ftpd_popen(line, "r");
+ lreply(211, "status of %s:", filename);
+ p = str;
+ while ((c = getc(fin)) != EOF) {
+ if (c == '\n') {
+ if (ferror(stdout)){
+ perror_reply(421, "control connection");
+ (void) ftpd_pclose(fin);
+ dologout(1);
+ /* NOTREACHED */
+ }
+ if (ferror(fin)) {
+ perror_reply(551, filename);
+ (void) ftpd_pclose(fin);
+ return;
+ }
+ *p = '\0';
+ reply(0, "%s", str);
+ p = str;
+ } else *p++ = c;
+ }
+ if (p != str) {
+ *p = '\0';
+ reply(0, "%s", str);
+ }
+ (void) ftpd_pclose(fin);
+ reply(211, "End of Status");
+}
+
+statcmd()
+{
+ struct sockaddr_in *sin;
+ u_char *a, *p;
+ char str[FTP_BUFSIZ];
+
+ lreply(211, "%s FTP server status:", hostname, version);
+ reply(0, " %s", version);
+ sprintf(str, " Connected to %s", remotehost);
+ if (!isdigit(remotehost[0]))
+ sprintf(&str[strlen(str)], " (%s)", inet_ntoa(his_addr.sin_addr));
+ reply(0, "%s", str);
+ if (auth_type) reply(0, " Authentication type: %s", auth_type);
+ if (logged_in) {
+ if (guest)
+ reply(0, " Logged in anonymously");
+ else
+ reply(0, " Logged in as %s", pw->pw_name);
+ } else if (askpasswd)
+ reply(0, " Waiting for password");
+ else if (temp_auth_type)
+ reply(0, " Waiting for authentication data");
+ else
+ reply(0, " Waiting for user name");
+ reply(0, " PROTection level: %s", levelnames[level]);
+ sprintf(str, " TYPE: %s", typenames[type]);
+ if (type == TYPE_A || type == TYPE_E)
+ sprintf(&str[strlen(str)], ", FORM: %s", formnames[form]);
+ if (type == TYPE_L)
+#if 1
+ strcat(str, " 8");
+#else
+/* this is silly. -- eichin@cygnus.com */
+#if NBBY == 8
+ sprintf(&str[strlen(str)], " %d", NBBY);
+#else
+ sprintf(&str[strlen(str)], " %d", bytesize); /* need definition! */
+#endif
+#endif
+ sprintf(&str[strlen(str)], "; STRUcture: %s; transfer MODE: %s",
+ strunames[stru], modenames[mode]);
+ reply(0, "%s", str);
+ if (data != -1)
+ strcpy(str, " Data connection open");
+ else if (pdata != -1) {
+ strcpy(str, " in Passive mode");
+ sin = &pasv_addr;
+ goto printaddr;
+ } else if (usedefault == 0) {
+ strcpy(str, " PORT");
+ sin = &data_dest;
+printaddr:
+ a = (u_char *) &sin->sin_addr;
+ p = (u_char *) &sin->sin_port;
+#define UC(b) (((int) b) & 0xff)
+ sprintf(&str[strlen(str)], " (%d,%d,%d,%d,%d,%d)", UC(a[0]),
+ UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
+#undef UC
+ } else
+ strcpy(str, " No data connection");
+ reply(0, "%s", str);
+ reply(211, "End of status");
+}
+
+fatal(s)
+ char *s;
+{
+ reply(451, "Error in server: %s", s);
+ reply(221, "Closing connection due to server error.");
+ dologout(0);
+ /* NOTREACHED */
+}
+
+char cont_char = ' ';
+
+#ifdef STDARG
+reply(int n, char *fmt, ...)
+#else
+/* VARARGS2 */
+reply(n, fmt, p0, p1, p2, p3, p4, p5)
+ int n;
+ char *fmt;
+#endif
+{
+ char buf[FTP_BUFSIZ];
+#ifdef STDARG
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ va_end(ap);
+#else
+ sprintf(buf, fmt, p0, p1, p2, p3, p4, p5);
+#endif
+
+ if (auth_type) {
+ char in[FTP_BUFSIZ], out[FTP_BUFSIZ];
+ int length, kerror;
+ /*
+ * File protection level also determines whether
+ * replies are 631 or 632. Should be independent ...
+ */
+ if (n) sprintf(in, "%d%c", n, cont_char);
+ else in[0] = '\0';
+ strcat(in, buf);
+#ifdef KERBEROS
+ if (strcmp(auth_type, "KERBEROS_V4") == 0)
+ if ((length = level == PROT_P ?
+ krb_mk_priv((unsigned char *)in, (unsigned char *)out,
+ strlen(in), schedule, &kdata.session,
+ &ctrl_addr, &his_addr)
+ : krb_mk_safe((unsigned char *)in, (unsigned char *)out,
+ strlen(in), &kdata.session,
+ &ctrl_addr, &his_addr)) == -1) {
+ syslog(LOG_ERR, "krb_mk_%s failed for KERBEROS_V4",
+ level == PROT_P ? "priv" : "safe");
+ fputs(in,stdout);
+ } else
+#endif /* KERBEROS */
+#ifdef GSSAPI
+ /* reply (based on level) */
+ if (strcmp(auth_type, "GSSAPI") == 0) {
+ gss_buffer_desc in_buf, out_buf;
+ OM_uint32 maj_stat, min_stat;
+ int conf_state;
+
+ in_buf.value = in;
+ in_buf.length = strlen(in) + 1;
+ maj_stat = gss_seal(&min_stat, gcontext,
+ level == PROT_P, /* confidential */
+ GSS_C_QOP_DEFAULT,
+ &in_buf, &conf_state,
+ &out_buf);
+ if (maj_stat != GSS_S_COMPLETE) {
+ /* generally need to deal */
+ secure_gss_error(maj_stat, min_stat,
+ (level==PROT_P)?
+ "gss_seal ENC didn't complete":
+ "gss_seal MIC didn't complete");
+ } else if ((level == PROT_P) && !conf_state) {
+ secure_error("GSSAPI didn't encrypt message");
+ } else {
+ memcpy(out, out_buf.value,
+ length=out_buf.length);
+ gss_release_buffer(&min_stat, &out_buf);
+ }
+ }
+#endif /* GSSAPI */
+ /* Other auth types go here ... */
+ if (kerror = radix_encode(out, in, &length, 0)) {
+ syslog(LOG_ERR, "Couldn't encode reply (%s)",
+ radix_error(kerror));
+ fputs(in,stdout);
+ } else
+ printf("%s%c%s", level == PROT_P ? "632" : "631",
+ n ? cont_char : '-', in);
+ } else {
+ if (n) printf("%d%c", n, cont_char);
+ fputs(buf, stdout);
+ }
+ printf("\r\n");
+ (void)fflush(stdout);
+ if (debug) {
+ if (n) syslog(LOG_DEBUG, "<--- %d%c", n, cont_char);
+ syslog(LOG_DEBUG, "%s", buf);
+ }
+}
+
+#ifdef STDARG
+lreply(int n, char *fmt, ...)
+#else
+/* VARARGS2 */
+lreply(n, fmt, p0, p1, p2, p3, p4, p5)
+ int n;
+ char *fmt;
+#endif
+{
+ char buf[FTP_BUFSIZ];
+#ifdef STDARG
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ va_end(ap);
+#else
+ sprintf(buf, fmt, p0, p1, p2, p3, p4, p5);
+#endif
+ cont_char = '-';
+ reply(n, "%s", buf);
+ cont_char = ' ';
+}
+
+ack(s)
+ char *s;
+{
+ reply(250, "%s command successful.", s);
+}
+
+nack(s)
+ char *s;
+{
+ reply(502, "%s command not implemented.", s);
+}
+
+/* ARGSUSED */
+yyerror(s)
+ char *s;
+{
+ char *cp;
+
+ if (cp = strchr(cbuf,'\n'))
+ *cp = '\0';
+ reply(500, "'%s': command not understood.", cbuf);
+}
+
+delete(name)
+ char *name;
+{
+ struct stat st;
+
+ if (stat(name, &st) < 0) {
+ perror_reply(550, name);
+ return;
+ }
+ if ((st.st_mode&S_IFMT) == S_IFDIR) {
+ if (rmdir(name) < 0) {
+ perror_reply(550, name);
+ return;
+ }
+ goto done;
+ }
+ if (unlink(name) < 0) {
+ perror_reply(550, name);
+ return;
+ }
+done:
+ ack("DELE");
+}
+
+cwd(path)
+ char *path;
+{
+ if (chdir(path) < 0)
+ perror_reply(550, path);
+ else
+ ack("CWD");
+}
+
+makedir(name)
+ char *name;
+{
+ if (mkdir(name, 0777) < 0)
+ perror_reply(550, name);
+ else
+ reply(257, "MKD command successful.");
+}
+
+removedir(name)
+ char *name;
+{
+ if (rmdir(name) < 0)
+ perror_reply(550, name);
+ else
+ ack("RMD");
+}
+
+pwd()
+{
+ char path[MAXPATHLEN + 1];
+
+ if (getcwd(path, sizeof path) == (char *)NULL)
+#ifdef POSIX
+ perror_reply(550, path);
+#else
+ reply(550, "%s.", path);
+#endif
+ else
+ reply(257, "\"%s\" is current directory.", path);
+}
+
+char *
+renamefrom(name)
+ char *name;
+{
+ struct stat st;
+
+ if (stat(name, &st) < 0) {
+ perror_reply(550, name);
+ return ((char *)0);
+ }
+ reply(350, "File exists, ready for destination name");
+ return (name);
+}
+
+renamecmd(from, to)
+ char *from, *to;
+{
+ if (rename(from, to) < 0)
+ perror_reply(550, "rename");
+ else
+ ack("RNTO");
+}
+
+dolog(sin)
+ struct sockaddr_in *sin;
+{
+ struct hostent *hp = gethostbyaddr((char *)&sin->sin_addr,
+ sizeof (struct in_addr), AF_INET);
+ time_t t, time();
+ extern char *ctime();
+
+ if (hp)
+ (void) strncpy(remotehost, hp->h_name, sizeof (remotehost));
+ else
+ (void) strncpy(remotehost, inet_ntoa(sin->sin_addr),
+ sizeof (remotehost));
+#ifdef SETPROCTITLE
+ sprintf(proctitle, "%s: connected", remotehost);
+ setproctitle(proctitle);
+#endif /* SETPROCTITLE */
+
+ if (logging) {
+ t = time((time_t *) 0);
+ syslog(LOG_INFO, "connection from %s at %s",
+ remotehost, ctime(&t));
+ }
+}
+
+/*
+ * Record logout in wtmp file
+ * and exit with supplied status.
+ */
+dologout(status)
+ int status;
+{
+ if (logged_in) {
+ (void) seteuid((uid_t)0);
+ logwtmp(ttyline, "", "");
+ }
+ /* beware of flushing buffers after a SIGPIPE */
+ _exit(status);
+}
+
+void
+myoob()
+{
+ char *cp, *cs;
+ extern char *strpbrk();
+
+ /* only process if transfer occurring */
+ if (!transflag)
+ return;
+ cp = tmpline;
+ if (getline(cp, sizeof(tmpline), stdin) == NULL) {
+ reply(221, "You could at least say goodbye.");
+ dologout(0);
+ }
+ upper(cp);
+ if ((cs = strpbrk(cp, "\r\n")))
+ *cs++ = '\0';
+ if (strcmp(cp, "ABOR") == 0) {
+ tmpline[0] = '\0';
+ reply(426, "Transfer aborted. Data connection closed.");
+ reply(226, "Abort successful");
+ longjmp(urgcatch, 1);
+ }
+ if (strcmp(cp, "STAT") == 0) {
+ if (file_size != (off_t) -1)
+ reply(213, "Status: %lu of %lu bytes transferred",
+ byte_count, file_size);
+ else
+ reply(213, "Status: %lu bytes transferred", byte_count);
+ }
+}
+
+/*
+ * Note: a response of 425 is not mentioned as a possible response to
+ * the PASV command in RFC959. However, it has been blessed as
+ * a legitimate response by Jon Postel in a telephone conversation
+ * with Rick Adams on 25 Jan 89.
+ */
+passive()
+{
+ int len;
+ register char *p, *a;
+
+ pdata = socket(AF_INET, SOCK_STREAM, 0);
+ if (pdata < 0) {
+ perror_reply(425, "Can't open passive connection");
+ return;
+ }
+ pasv_addr = ctrl_addr;
+ pasv_addr.sin_port = 0;
+ (void) seteuid((uid_t)0);
+ if (bind(pdata, (struct sockaddr *)&pasv_addr, sizeof(pasv_addr)) < 0) {
+ (void) seteuid((uid_t)pw->pw_uid);
+ goto pasv_error;
+ }
+ (void) seteuid((uid_t)pw->pw_uid);
+ len = sizeof(pasv_addr);
+ if (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0)
+ goto pasv_error;
+ if (listen(pdata, 1) < 0)
+ goto pasv_error;
+ a = (char *) &pasv_addr.sin_addr;
+ p = (char *) &pasv_addr.sin_port;
+
+#define UC(b) (((int) b) & 0xff)
+
+ reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
+ UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
+ return;
+
+pasv_error:
+ (void) close(pdata);
+ pdata = -1;
+ perror_reply(425, "Can't open passive connection");
+ return;
+}
+
+/*
+ * Generate unique name for file with basename "local".
+ * The file named "local" is already known to exist.
+ * Generates failure reply on error.
+ */
+char *
+gunique(local)
+ char *local;
+{
+ static char new[MAXPATHLEN];
+ struct stat st;
+ char *cp = strrchr(local, '/');
+ int count = 0;
+
+ if (cp)
+ *cp = '\0';
+ if (stat(cp ? local : ".", &st) < 0) {
+ perror_reply(553, cp ? local : ".");
+ return((char *) 0);
+ }
+ if (cp)
+ *cp = '/';
+ (void) strcpy(new, local);
+ cp = new + strlen(new);
+ *cp++ = '.';
+ for (count = 1; count < 100; count++) {
+ (void) sprintf(cp, "%d", count);
+ if (stat(new, &st) < 0)
+ return(new);
+ }
+ reply(452, "Unique file name cannot be created.");
+ return((char *) 0);
+}
+
+/*
+ * Format and send reply containing system error number.
+ */
+perror_reply(code, string)
+ int code;
+ char *string;
+{
+ reply(code, "%s: %s.", string, strerror(errno));
+}
+
+auth(type)
+char *type;
+{
+ if (auth_type)
+ reply(534, "Authentication type already set to %s", auth_type);
+ else
+#ifdef KERBEROS
+ if (strcmp(type, "KERBEROS_V4") == 0)
+ reply(334, "Using authentication type %s; ADAT must follow",
+ temp_auth_type = type);
+ else
+#endif /* KERBEROS */
+#ifdef GSSAPI
+ if (strcmp(type, "GSSAPI") == 0)
+ reply(334, "Using authentication type %s; ADAT must follow",
+ temp_auth_type = type);
+ else
+#endif /* KERBEROS */
+ /* Other auth types go here ... */
+ reply(504, "Unknown authentication type: %s", type);
+}
+
+auth_data(data)
+char *data;
+{
+ int kerror, length;
+#ifdef KERBEROS
+ int i;
+ static char *service;
+ char instance[INST_SZ];
+ u_long cksum;
+ char buf[FTP_BUFSIZ];
+ u_char out_buf[sizeof(buf)];
+#endif /* KERBEROS */
+#ifdef GSSAPI
+ OM_uint32 maj_stat, min_stat;
+ gss_OID mechid;
+ gss_buffer_desc tok, out_tok;
+ char gbuf[FTP_BUFSIZ];
+ u_char gout_buf[sizeof(gbuf)];
+#endif
+
+ if (auth_type) {
+ reply(503, "Authentication already established");
+ return(0);
+ }
+ if (!temp_auth_type) {
+ reply(503, "Must identify AUTH type before ADAT");
+ return(0);
+ }
+#ifdef KERBEROS
+ if (strcmp(temp_auth_type, "KERBEROS_V4") == 0) {
+ if (kerror = radix_encode(data, out_buf, &length, 1)) {
+ reply(501, "Couldn't decode ADAT (%s)",
+ radix_error(kerror));
+ syslog(LOG_ERR, "Couldn't decode ADAT (%s)",
+ radix_error(kerror));
+ return(0);
+ }
+ (void) memcpy((char *)ticket.dat, (char *)out_buf, ticket.length = length);
+ strcpy(instance, "*");
+ if (!service) {
+ char realm[REALM_SZ];
+ des_cblock key;
+
+ service = "ftp";
+ if (krb_get_lrealm(realm, 1) == KSUCCESS &&
+ read_service_key(service, instance, realm, 0, keyfile, key))
+ service = "rcmd";
+ else (void) memset(key, 0, sizeof(key));
+ }
+ if (kerror = krb_rd_req(&ticket, service, instance,
+ his_addr.sin_addr.s_addr, &kdata, keyfile)) {
+ secure_error("ADAT: Kerberos V4 krb_rd_req: %s",
+ krb_get_err_text(kerror));
+ return(0);
+ }
+ /* add one to the (formerly) sealed checksum, and re-seal it */
+ cksum = kdata.checksum + 1;
+ cksum = htonl(cksum);
+ key_sched(kdata.session,schedule);
+ if ((length = krb_mk_safe((u_char *)&cksum, out_buf, sizeof(cksum),
+ &kdata.session,&ctrl_addr, &his_addr)) == -1) {
+ secure_error("ADAT: krb_mk_safe failed");
+ return(0);
+ }
+ if (kerror = radix_encode(out_buf, buf, &length, 0)) {
+ secure_error("Couldn't encode ADAT reply (%s)",
+ radix_error(kerror));
+ return(0);
+ }
+ reply(235, "ADAT=%s", buf);
+ /* Kerberos V4 authentication succeeded */
+ auth_type = temp_auth_type;
+ temp_auth_type = NULL;
+ return(1);
+ }
+#endif /* KERBEROS */
+#ifdef GSSAPI
+ if (strcmp(temp_auth_type, "GSSAPI") == 0) {
+ int replied = 0;
+ gss_cred_id_t server_creds;
+ gss_name_t client;
+ int ret_flags;
+ struct gss_channel_bindings_struct chan;
+ gss_buffer_desc name_buf;
+ gss_name_t server_name;
+ OM_uint32 maj_stat, min_stat;
+ char localname[MAXHOSTNAMELEN];
+ char service_name[MAXHOSTNAMELEN+10];
+ char **service;
+
+ chan.initiator_addrtype = GSS_C_AF_INET;
+ chan.initiator_address.length = 4;
+ chan.initiator_address.value = &his_addr.sin_addr.s_addr;
+ chan.acceptor_addrtype = GSS_C_AF_INET;
+ chan.acceptor_address.length = 4;
+ chan.acceptor_address.value = &ctrl_addr.sin_addr.s_addr;
+ chan.application_data.length = 0;
+ chan.application_data.value = 0;
+
+ if (kerror = radix_encode(data, gout_buf, &length, 1)) {
+ reply(501, "Couldn't decode ADAT (%s)",
+ radix_error(kerror));
+ syslog(LOG_ERR, "Couldn't decode ADAT (%s)",
+ radix_error(kerror));
+ return(0);
+ }
+ tok.value = gout_buf;
+ tok.length = length;
+
+ if (gethostname(localname, MAXHOSTNAMELEN)) {
+ reply(501, "couldn't get local hostname (%d)\n", errno);
+ syslog(LOG_ERR, "Couldn't get local hostname (%d)", errno);
+ return 0;
+ }
+
+ for (service = gss_services; *service; service++) {
+ sprintf(service_name, "%s@%s", *service, localname);
+ name_buf.value = service_name;
+ name_buf.length = strlen(name_buf.value) + 1;
+ if (debug) syslog(LOG_INFO, "importing <%s>", service_name);
+ maj_stat = gss_import_name(&min_stat, &name_buf,
+ gss_nt_service_name,
+ &server_name);
+ if (maj_stat != GSS_S_COMPLETE) {
+ reply_gss_error(501, maj_stat, min_stat,
+ "importing name");
+ syslog(LOG_ERR, "gssapi error importing name");
+ return 0;
+ }
+
+ maj_stat = gss_acquire_cred(&min_stat, server_name, 0,
+ GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
+ &server_creds, NULL, NULL);
+ if (maj_stat != GSS_S_COMPLETE) {
+ reply_gss_error(501, maj_stat, min_stat,
+ "acquiring credentials");
+ syslog(LOG_ERR, "gssapi error acquiring credentials");
+ return 0;
+ }
+ if (server_creds == GSS_C_NO_CREDENTIAL) {
+ syslog(LOG_ERR, "acquire return GSS_C_NO_CREDENTIAL");
+ }
+ (void) gss_release_name(&min_stat, &server_name);
+ break;
+ }
+
+ gcontext = GSS_C_NO_CONTEXT;
+
+ maj_stat = gss_accept_sec_context(&min_stat,
+ &gcontext, /* context_handle */
+ server_creds, /* verifier_cred_handle */
+ &tok, /* input_token */
+ &chan, /* channel bindings */
+ &client, /* src_name */
+ &mechid, /* mech_type */
+ &out_tok, /* output_token */
+ &ret_flags,
+ NULL, /* ignore time_rec */
+ NULL /* ignore del_cred_handle */
+ );
+ if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED) {
+ reply_gss_error(535, maj_stat, min_stat,
+ "accepting context");
+ syslog(LOG_ERR, "failed accepting context");
+ (void) gss_release_cred(&min_stat, &server_creds);
+ return 0;
+ }
+
+ if (out_tok.length) {
+ if (kerror = radix_encode(out_tok.value, gbuf, &out_tok.length, 0)) {
+ secure_error("Couldn't encode ADAT reply (%s)",
+ radix_error(kerror));
+ syslog(LOG_ERR, "couldn't encode ADAT reply");
+ return(0);
+ }
+ if (maj_stat == GSS_S_COMPLETE) {
+ reply(235, "ADAT=%s", gbuf);
+ replied = 1;
+ } else {
+ /* If the server accepts the security data, and
+ requires additional data, it should respond with
+ reply code 335. */
+ reply(335, "ADAT=%s", gbuf);
+ }
+ (void) gss_release_buffer(&min_stat, &out_tok);
+ }
+ if (maj_stat == GSS_S_COMPLETE) {
+ /* GSSAPI authentication succeeded */
+ maj_stat = gss_display_name(&min_stat, client, &client_name,
+ &mechid);
+ if (maj_stat != GSS_S_COMPLETE) {
+ /* "If the server rejects the security data (if
+ a checksum fails, for instance), it should
+ respond with reply code 535." */
+ reply_gss_error(535, maj_stat, min_stat,
+ "extracting GSSAPI identity name");
+ syslog(LOG_ERR, "gssapi error extracting identity");
+ (void) gss_release_cred(&min_stat, &server_creds);
+ return 0;
+ }
+ /* If the server accepts the security data, but does
+ not require any additional data (i.e., the security
+ data exchange has completed successfully), it must
+ respond with reply code 235. */
+ if (!replied) reply(235, "GSSAPI Authentication succeeded");
+
+ auth_type = temp_auth_type;
+ temp_auth_type = NULL;
+
+ (void) gss_release_cred(&min_stat, &server_creds);
+ return(1);
+ } else if (maj_stat == GSS_S_CONTINUE_NEEDED) {
+ /* If the server accepts the security data, and
+ requires additional data, it should respond with
+ reply code 335. */
+ reply(335, "more data needed");
+ (void) gss_release_cred(&min_stat, &server_creds);
+ return(0);
+ } else {
+ /* "If the server rejects the security data (if
+ a checksum fails, for instance), it should
+ respond with reply code 535." */
+ reply_gss_error(535, maj_stat, min_stat,
+ "GSSAPI failed processing ADAT");
+ syslog(LOG_ERR, "GSSAPI failed prossing ADAT");
+ (void) gss_release_cred(&min_stat, &server_creds);
+ return(0);
+ }
+ }
+#endif /* GSSAPI */
+ /* Other auth types go here ... */
+ /* Also need to check authorization, but that is done in user() */
+}
+
+static char *onefile[] = {
+ "",
+ 0
+};
+
+/* returns:
+ * n>=0 on success
+ * -1 on error
+ * -2 on security error
+ */
+secure_fprintf(stream, fmt, p1, p2, p3, p4, p5)
+FILE *stream;
+char *fmt;
+{
+ char s[FTP_BUFSIZ];
+
+ if (level == PROT_C)
+ return(fprintf(stream, fmt, p1, p2, p3, p4, p5));
+ sprintf(s, fmt, p1, p2, p3, p4, p5);
+ return(secure_write(fileno(stream), s, strlen(s)));
+}
+
+send_file_list(whichfiles)
+ char *whichfiles;
+{
+ struct stat st;
+ DIR *dirp = NULL;
+ struct dirent *dir;
+ FILE *dout = NULL;
+ register char **dirlist, *dirname;
+ int simple = 0;
+ char *strpbrk();
+ int ret = 0;
+
+ if (strpbrk(whichfiles, "~{[*?") != NULL) {
+ extern char **ftpglob(), *globerr;
+
+ globerr = NULL;
+ dirlist = ftpglob(whichfiles);
+ if (globerr != NULL) {
+ reply(550, globerr);
+ return;
+ } else if (dirlist == NULL) {
+ errno = ENOENT;
+ perror_reply(550, whichfiles);
+ return;
+ }
+ } else {
+ onefile[0] = whichfiles;
+ dirlist = onefile;
+ simple = 1;
+ }
+
+ if (setjmp(urgcatch)) {
+ transflag = 0;
+ return;
+ }
+ while (dirname = *dirlist++) {
+ if (stat(dirname, &st) < 0) {
+ /*
+ * If user typed "ls -l", etc, and the client
+ * used NLST, do what the user meant.
+ */
+ if (dirname[0] == '-' && *dirlist == NULL &&
+ transflag == 0) {
+ retrieve("/bin/ls %s", dirname);
+ return;
+ }
+ perror_reply(550, whichfiles);
+ if (dout != NULL) {
+ (void) fclose(dout);
+ transflag = 0;
+ data = -1;
+ pdata = -1;
+ }
+ return;
+ }
+
+ if ((st.st_mode&S_IFMT) == S_IFREG) {
+ if (dout == NULL) {
+ dout = dataconn("file list", (off_t)-1, "w");
+ if (dout == NULL)
+ return;
+ transflag++;
+ }
+ if ((ret = secure_fprintf(dout, "%s%s\n", dirname,
+ type == TYPE_A ? "\r" : "")) < 0)
+ goto data_err;
+ byte_count += strlen(dirname) + 1;
+ continue;
+ } else if ((st.st_mode&S_IFMT) != S_IFDIR)
+ continue;
+
+ if ((dirp = opendir(dirname)) == NULL)
+ continue;
+
+ while ((dir = readdir(dirp)) != NULL) {
+ char nbuf[MAXPATHLEN];
+
+ if (dir->d_name[0] == '.' && dir->d_name[1] == '\0')
+ continue;
+ if (dir->d_name[0] == '.' && dir->d_name[1] == '.' &&
+ dir->d_name[2] == '\0')
+ continue;
+
+ sprintf(nbuf, "%s/%s", dirname, dir->d_name);
+
+ /*
+ * We have to do a stat to insure it's
+ * not a directory or special file.
+ */
+ if (simple || (stat(nbuf, &st) == 0 &&
+ (st.st_mode&S_IFMT) == S_IFREG)) {
+ if (dout == NULL) {
+ dout = dataconn("file list", (off_t)-1,
+ "w");
+ if (dout == NULL)
+ return;
+ transflag++;
+ }
+ if (nbuf[0] == '.' && nbuf[1] == '/')
+ {
+ if ((ret = secure_fprintf(dout, "%s%s\n", &nbuf[2],
+ type == TYPE_A ? "\r" : "")) < 0)
+ goto data_err;
+ }
+ else
+ if ((ret = secure_fprintf(dout, "%s%s\n", nbuf,
+ type == TYPE_A ? "\r" : "")) < 0)
+ goto data_err;
+ byte_count += strlen(nbuf) + 1;
+ }
+ }
+ (void) closedir(dirp);
+ }
+ ret = secure_write(fileno(dout), "", 0);
+data_err:
+ if (dout == NULL)
+ reply(550, "No files found.");
+ else if (ferror(dout) != 0 || ret == EOF)
+ perror_reply(550, "Data connection");
+ else if (ret != -2)
+ reply(226, "Transfer complete.");
+
+ transflag = 0;
+ if (dout != NULL)
+ (void) fclose(dout);
+ data = -1;
+ pdata = -1;
+}
+
+#ifdef SETPROCTITLE
+/*
+ * clobber argv so ps will show what we're doing.
+ * (stolen from sendmail)
+ * warning, since this is usually started from inetd.conf, it
+ * often doesn't have much of an environment or arglist to overwrite.
+ */
+
+setproctitle(buf)
+char *buf;
+{
+ register char *p, *bp, ch;
+ register int i;
+
+ /* make ps print our process name */
+ p = Argv[0];
+ *p++ = '-';
+
+ i = strlen(buf);
+ if (i > LastArgv - p - 2) {
+ i = LastArgv - p - 2;
+ buf[i] = '\0';
+ }
+ bp = buf;
+ while (ch = *bp++)
+ if (ch != '\n' && ch != '\r')
+ *p++ = ch;
+ while (p < LastArgv)
+ *p++ = ' ';
+}
+#endif /* SETPROCTITLE */
+#ifdef GSSAPI
+reply_gss_error(code, maj_stat, min_stat, s)
+int code;
+OM_uint32 maj_stat, min_stat;
+char *s;
+{
+ /* a lot of work just to report the error */
+ OM_uint32 gmaj_stat, gmin_stat;
+ gss_buffer_desc msg;
+ int msg_ctx;
+ msg_ctx = 0;
+ while (!msg_ctx) {
+ gmaj_stat = gss_display_status(&gmin_stat, maj_stat,
+ GSS_C_GSS_CODE,
+ GSS_C_NULL_OID,
+ &msg_ctx, &msg);
+ if ((gmaj_stat == GSS_S_COMPLETE)||
+ (gmaj_stat == GSS_S_CONTINUE_NEEDED)) {
+ lreply(code, "GSSAPI error major: %s",
+ (char*)msg.value);
+ (void) gss_release_buffer(&gmin_stat, &msg);
+ }
+ if (gmaj_stat != GSS_S_CONTINUE_NEEDED)
+ break;
+ }
+ msg_ctx = 0;
+ while (!msg_ctx) {
+ gmaj_stat = gss_display_status(&gmin_stat, min_stat,
+ GSS_C_MECH_CODE,
+ GSS_C_NULL_OID,
+ &msg_ctx, &msg);
+ if ((gmaj_stat == GSS_S_COMPLETE)||
+ (gmaj_stat == GSS_S_CONTINUE_NEEDED)) {
+ lreply(code, "GSSAPI error minor: %s",
+ (char*)msg.value);
+ (void) gss_release_buffer(&gmin_stat, &msg);
+ }
+ if (gmaj_stat != GSS_S_CONTINUE_NEEDED)
+ break;
+ }
+ reply(code, "GSSAPI error: %s", s);
+}
+
+secure_gss_error(maj_stat, min_stat, s)
+OM_uint32 maj_stat, min_stat;
+char *s;
+{
+ return reply_gss_error(535, maj_stat, min_stat, s);
+}
+
+
+#include <krb5.h>
+/* ftpd_userok -- hide details of getting the name and verifying it */
+/* returns 0 for OK */
+ftpd_userok(client_name, name)
+ gss_buffer_t client_name;
+ char *name;
+{
+ int retval = -1;
+ krb5_boolean k5ret;
+ krb5_context kc;
+ krb5_principal p;
+ krb5_error_code kerr;
+ krb5_init_context(&kc);
+
+ kerr = krb5_parse_name(kc, client_name->value, &p);
+ if (kerr) { retval = -1; goto fail; }
+ k5ret = krb5_kuserok(kc, p, name);
+ if (k5ret == TRUE)
+ retval = 0;
+ else
+ retval = 1;
+ krb5_free_principal(kc, p);
+ fail:
+ krb5_free_context(kc);
+ return retval;
+}
+#endif /* GSSAPI */
--- /dev/null
+/*
+ * Copyright (c) 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)logwtmp.c 5.7 (Berkeley) 2/25/91";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <utmp.h>
+#include <unistd.h>
+#include <string.h>
+
+#ifdef WTMP_FILE
+#define WTMPFILE WTMP_FILE
+#endif
+
+#ifndef WTMPFILE
+#define WTMPFILE "/usr/adm/wtmp"
+#endif
+
+static int fd = -1;
+
+/*
+ * Modified version of logwtmp that holds wtmp file open
+ * after first call, for use with ftp (which may chroot
+ * after login, but before logout).
+ */
+logwtmp(line, name, host)
+ char *line, *name, *host;
+{
+ struct utmp ut;
+ struct stat buf;
+ time_t time();
+
+ if (fd < 0 && (fd = open(WTMPFILE, O_WRONLY|O_APPEND, 0)) < 0)
+ return;
+ if (fstat(fd, &buf) == 0) {
+ (void)strncpy(ut.ut_line, line, sizeof(ut.ut_line));
+ (void)strncpy(ut.ut_name, name, sizeof(ut.ut_name));
+#ifndef NO_UT_HOST
+ (void)strncpy(ut.ut_host, host, sizeof(ut.ut_host));
+#endif
+ (void)time(&ut.ut_time);
+ if (write(fd, (char *)&ut, sizeof(struct utmp)) !=
+ sizeof(struct utmp))
+ (void)ftruncate(fd, buf.st_size);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 5.2 (Berkeley) 6/1/90
+ */
+
+#define _PATH_FTPUSERS "/etc/ftpusers"
--- /dev/null
+/*
+ * Copyright (c) 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software written by Ken Arnold and
+ * published in UNIX Review, Vol. 6, No. 8.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)popen.c 5.9 (Berkeley) 2/25/91";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_VFORK_H
+#include <vfork.h>
+#endif
+
+/*
+ * Special version of popen which avoids call to shell. This insures noone
+ * may create a pipe to a hidden program as a side effect of a list or dir
+ * command.
+ */
+static int *pids;
+static int fds;
+
+
+FILE *
+ftpd_popen(program, type)
+ char *program, *type;
+{
+ register char *cp;
+ FILE *iop;
+ int argc, gargc, pdes[2], pid;
+ char **pop, *argv[100], *gargv[1000], *vv[2];
+ extern char **ftpglob(), **copyblk();
+
+ if (*type != 'r' && *type != 'w' || type[1])
+ return(NULL);
+
+ if (!pids) {
+ if ((fds = getdtablesize()) <= 0)
+ return(NULL);
+ if ((pids = (int *)malloc((u_int)(fds * sizeof(int)))) == NULL)
+ return(NULL);
+ memset((char *)pids, 0, fds * sizeof(int));
+ }
+ if (pipe(pdes) < 0)
+ return(NULL);
+
+ /* break up string into pieces */
+ for (argc = 0, cp = program;; cp = NULL)
+ if (!(argv[argc++] = strtok(cp, " \t\n")))
+ break;
+ for (argc = 0; argv[argc]; argc++) argv[argc] = strdup(argv[argc]);
+
+ /* glob each piece */
+ gargv[0] = argv[0];
+ for (gargc = argc = 1; argv[argc]; argc++) {
+ if (!(pop = ftpglob(argv[argc]))) { /* globbing failed */
+ vv[0] = argv[argc];
+ vv[1] = NULL;
+ pop = copyblk(vv);
+ }
+ argv[argc] = (char *)pop; /* save to free later */
+ while (*pop && gargc < 1000)
+ gargv[gargc++] = *pop++;
+ }
+ gargv[gargc] = NULL;
+
+ iop = NULL;
+ switch(pid = vfork()) {
+ case -1: /* error */
+ (void)close(pdes[0]);
+ (void)close(pdes[1]);
+ goto pfree;
+ /* NOTREACHED */
+ case 0: /* child */
+ if (*type == 'r') {
+ if (pdes[1] != 1) {
+ dup2(pdes[1], 1);
+ dup2(pdes[1], 2); /* stderr, too! */
+ (void)close(pdes[1]);
+ }
+ (void)close(pdes[0]);
+ } else {
+ if (pdes[0] != 0) {
+ dup2(pdes[0], 0);
+ (void)close(pdes[0]);
+ }
+ (void)close(pdes[1]);
+ }
+ execv(gargv[0], gargv);
+ _exit(1);
+ }
+ /* parent; assume fdopen can't fail... */
+ if (*type == 'r') {
+ iop = fdopen(pdes[0], type);
+ (void)close(pdes[1]);
+ } else {
+ iop = fdopen(pdes[1], type);
+ (void)close(pdes[0]);
+ }
+ pids[fileno(iop)] = pid;
+
+pfree: for (argc = 1; argv[argc] != NULL; argc++) {
+ blkfree((char **)argv[argc]);
+ free((char *)argv[argc]);
+ }
+ return(iop);
+}
+
+ftpd_pclose(iop)
+ FILE *iop;
+{
+ register int fdes;
+#ifdef USE_SIGPROCMASK
+ sigset_t old, new;
+#else
+ int omask;
+#endif
+#ifdef WAIT_USES_INT
+ int stat_loc;
+#else
+ union wait stat_loc;
+#endif
+ int pid;
+
+ /*
+ * pclose returns -1 if stream is not associated with a
+ * `popened' command, or, if already `pclosed'.
+ */
+ if (pids == 0 || pids[fdes = fileno(iop)] == 0)
+ return(-1);
+ (void)fclose(iop);
+#ifdef USE_SIGPROCMASK
+ sigemptyset(&old);
+ sigemptyset(&new);
+ sigaddset(&new,SIGINT);
+ sigaddset(&new,SIGQUIT);
+ sigaddset(&new,SIGHUP);
+ sigprocmask(SIG_BLOCK, &new, &old);
+ while ((pid = wait((int *)&stat_loc)) != pids[fdes] && pid != -1);
+ sigprocmask(SIG_SETMASK, &old, NULL);
+#else
+ omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
+ while ((pid = wait((int *)&stat_loc)) != pids[fdes] && pid != -1);
+ sigsetmask(omask);
+#endif
+ pids[fdes] = 0;
+#ifdef WAIT_USES_INT
+ return(pid == -1 ? -1 : stat_loc);
+#else
+ return(pid == -1 ? -1 : stat_loc.w_status);
+#endif
+}
--- /dev/null
+#define CRED_DECL extern AUTH_DAT kdata;
+#define SESSION &kdata.session
+#define myaddr ctrl_addr
+#define hisaddr data_dest
--- /dev/null
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)vers.c 5.1 (Berkeley) 6/24/90";
+#endif /* not lint */
+
+char version[] = "Version 5.60";