date and time created 83/01/13 23:57:01 by sam
authorSam Leffler <sam@ucbvax.Berkeley.EDU>
Fri, 14 Jan 1983 15:57:01 +0000 (07:57 -0800)
committerSam Leffler <sam@ucbvax.Berkeley.EDU>
Fri, 14 Jan 1983 15:57:01 +0000 (07:57 -0800)
SCCS-vsn: libexec/ftpd/ftpd.c 4.1

usr/src/libexec/ftpd/ftpd.c [new file with mode: 0644]

diff --git a/usr/src/libexec/ftpd/ftpd.c b/usr/src/libexec/ftpd/ftpd.c
new file mode 100644 (file)
index 0000000..822afea
--- /dev/null
@@ -0,0 +1,656 @@
+#ifndef lint
+static char sccsid[] = "@(#)ftpd.c     4.1 (Berkeley) 83/01/13";
+#endif
+
+/*
+ * FTP server.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <signal.h>
+#include <wait.h>
+#include <pwd.h>
+#include <setjmp.h>
+#include <netdb.h>
+
+#include "ftp.h"
+
+extern int errno;
+extern char *sys_errlist[];
+extern char *crypt();
+extern char version[];
+extern char *home;             /* pointer to home directory for glob */
+extern FILE *popen(), *fopen();
+extern int pclose(), fclose();
+
+struct sockaddr_in ctrl_addr;
+struct sockaddr_in data_source;
+struct sockaddr_in data_dest;
+struct sockaddr_in his_addr;
+
+struct hostent *hp;
+
+int    data;
+jmp_buf        errcatch;
+int    logged_in;
+struct passwd *pw;
+int    debug;
+int    logging = 1;
+int    guest;
+int    type;
+int    form;
+int    stru;                   /* avoid C keyword */
+int    mode;
+char   hostname[32];
+char   *remotehost;
+
+int    lostconn();
+FILE   *getdatasock(), *dataconn();
+char   *ntoa();
+
+main(argc, argv)
+       int argc;
+       char *argv[];
+{
+       int ctrl, s, options = 0;
+       struct servent *sp;
+       union wait status;
+       char *cp;
+
+       sp = getservbyname("ftp", "tcp");
+       if (sp == 0) {
+               fprintf(stderr, "ftpd: fpt/tcp: unknown service\n");
+               exit(1);
+       }
+       ctrl_addr.sin_port = sp->s_port;
+       data_source.sin_port = htons(ntohs(sp->s_port) - 1);
+       signal(SIGPIPE, lostconn);
+       debug = 0;
+       argc--, argv++;
+       while (argc > 0 && *argv[0] == '-') {
+               for (cp = &argv[0][1]; *cp; cp++) switch (*cp) {
+
+               case 'd':
+                       debug = 1;
+                       options |= SO_DEBUG;
+                       break;
+
+               default:
+                       fprintf(stderr, "Unknown flag -%c ignored.\n", cp);
+                       break;
+               }
+               argc--, argv++;
+       }
+#ifndef DEBUG
+       if (fork())
+               exit(0);
+       for (s = 0; s < 10; s++)
+               if (s != 2)             /* don't screw stderr */
+                       (void) close(s);
+       (void) open("/dev/null", 0);
+       (void) dup2(0, 1);
+       { int tt = open("/dev/tty", 2);
+         if (tt > 0) {
+               ioctl(tt, TIOCNOTTY, 0);
+               close(tt);
+         }
+       }
+#endif
+       while ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+               perror("ftpd: socket");
+               sleep(5);
+       }
+       while (bind(s, &ctrl_addr, sizeof (ctrl_addr), 0) < 0) {
+               perror("ftpd: bind");
+               sleep(5);
+       }
+       for (;;) {
+               int hisaddrlen = sizeof (his_addr);
+
+               ctrl = accept(s, &his_addr, &hisaddrlen, 0);
+               if (ctrl < 0) {
+                       perror("ftpd: accept");
+                       sleep(5);
+                       continue;
+               }
+               data_dest = his_addr;
+               if (fork() == 0) {
+                       if (logging)
+                               dolog(&his_addr);
+                       close(s);
+                       dup2(ctrl, 0), close(ctrl), dup2(0, 1);
+                       /* do telnet option negotiation here */
+                       logged_in = 0;
+                       data = -1;
+                       gethostname(hostname, sizeof (hostname));
+                       reply(220, "%s FTP server (%s) ready.",
+                               hostname, version);
+                       for (;;) {
+                               setjmp(errcatch);
+                               yyparse();
+                       }
+               }
+               close(ctrl);
+               while (wait3(status, WNOHANG, 0) > 0)
+                       continue;
+       }
+}
+
+lostconn()
+{
+
+       fatal("Connection closed.");
+}
+
+pass(passwd)
+       char *passwd;
+{
+       char *xpasswd;
+
+       if (logged_in || pw == NULL) {
+               reply(503, "Login with USER first.");
+               return;
+       }
+       if (!guest) {           /* "ftp" is only account allowed no password */
+               xpasswd = crypt(passwd, pw->pw_passwd);
+               if (strcmp(xpasswd, pw->pw_passwd) != 0) {
+                       reply(530, "Login incorrect.");
+                       pw = NULL;
+                       return;
+               }
+       }
+       home = pw->pw_dir;              /* home dir for globbing */
+       setreuid(-1, pw->pw_uid);
+       setregid(-1, pw->pw_gid);
+       initgroups(pw->pw_name, pw->pw_gid);
+       if (chdir(pw->pw_dir)) {
+               reply(550, "User %s: can't change directory to $s.",
+                       pw->pw_name, pw->pw_dir);
+               pw = NULL;
+               return;
+       }
+       if (guest && chroot(pw->pw_dir) < 0){
+               reply(550, "Can't set guest privileges.");
+               pw = NULL;
+               return;
+       }
+       if (!guest)
+               reply(230, "User %s logged in.", pw->pw_name);
+       else
+               reply(230, "Guest login ok, access restrictions apply.");
+       logged_in = 1;
+}
+
+retrieve(cmd, name)
+       char *cmd, *name;
+{
+       FILE *fin, *dout;
+       struct stat st;
+       int (*closefunc)();
+
+       if (cmd == 0) {
+               if (*name == '!')
+                       fin = popen(name + 1, "r"), closefunc = pclose;
+               else
+                       fin = fopen(name, "r"), closefunc = fclose;
+       } else {
+               char line[BUFSIZ];
+
+               sprintf(line, cmd, name);
+               fin = popen(line, "r"), closefunc = pclose;
+       }
+       if (fin == NULL) {
+               reply(550, "%s: %s.", name, sys_errlist[errno]);
+               return;
+       }
+       st.st_size = 0;
+       if (cmd == 0 &&
+           (stat(name, &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)) {
+               reply(550, "%s: not a plain file.", name);
+               goto done;
+       }
+       dout = dataconn(name, st.st_size, "w");
+       if (dout == NULL)
+               goto done;
+       if (!send_data(fin, dout) || ferror(dout))
+               reply(550, "%s: %s.", name, sys_errlist[errno]);
+       else
+               reply(226, "Transfer complete.");
+       if (mode == MODE_S)
+               /* indicate EOF by closing connection */
+               fclose(dout), data = -1;
+done:
+       (*closefunc)(fin);
+}
+
+store(name, mode)
+       char *name, *mode;
+{
+       FILE *fout, *din;
+       int (*closefunc)();
+
+       if (name[0] == '!')
+               fout = popen(&name[1], "w"), closefunc = pclose;
+       else
+               fout = fopen(name, mode), closefunc = fclose;
+       if (fout == NULL) {
+               reply(550, "%s: %s.", name, sys_errlist[errno]);
+               return;
+       }
+       din = dataconn(name, -1, "r");
+       if (din == NULL)
+               goto done;
+       if (!receive_data(din, fout) || ferror(fout))
+               reply(550, "%s: %s.", name, sys_errlist[errno]);
+       else
+               reply(226, "Transfer complete.");
+       fclose(din), data = -1;
+done:
+       (*closefunc)(fout);
+}
+
+FILE *
+getdatasock(mode)
+       char *mode;
+{
+       int retrytime, s;
+
+       if (data >= 0)
+               return (fdopen(data, mode));
+       retrytime = 1;
+       while ((s = socket(AF_INET, SOCK_STREAM, 0, 0)) < 0) {
+               if (retrytime < 5) {
+                       sleep(retrytime);
+                       retrytime <<= 1;
+                       continue;
+               }
+               return (NULL);
+       }
+       retrytime = 1;
+       seteuid(0);
+       while (bind(s, &data_source, sizeof (data_source), 0) < 0) {
+               if (retrytime < 5) {
+                       sleep(retrytime);
+                       retrytime <<= 1;
+                       continue;
+               }
+               seteuid(0);
+               close(s);
+               return (NULL);
+       }
+       seteuid(0);
+       return (fdopen(s, mode));
+}
+
+FILE *
+dataconn(name, size, mode)
+       char *name;
+       int size;
+       char *mode;
+{
+       char sizebuf[32];
+       FILE *file;
+
+       if (size >= 0)
+               sprintf(sizebuf, " (%d bytes)", size);
+       else
+               (void) strcpy(sizebuf, "");
+       if (data >= 0) {
+               reply(125, "Using existing data connection for %s%s.",
+                   name, sizebuf);
+               return (fdopen(data, mode));
+       }
+       reply(150, "Opening data connection for %s (%s,%d)%s.",
+           name, ntoa(data_dest.sin_addr.s_addr),
+           ntohs(data_dest.sin_port), sizebuf);
+       file = getdatasock(mode);
+       if (file == NULL) {
+               reply(425, "Can't create data socket (%s,%d): %s.",
+                   ntoa(data_source.sin_addr),
+                   ntohs(data_source.sin_port),
+                   sys_errlist[errno]);
+               return (NULL);
+       }
+       data = fileno(file);
+       if (connect(data, &data_dest, sizeof (data_dest), 0) < 0) {
+               reply(425, "Can't build data connection: %s.",
+                   sys_errlist[errno]);
+               (void) fclose(file);
+               data = -1;
+               return (NULL);
+       }
+       return (file);
+}
+
+/*
+ * Tranfer the contents of "instr" to
+ * "outstr" peer using the appropriate
+ * encapulation of the date subject
+ * to Mode, Structure, and Type.
+ *
+ * NB: Form isn't handled.
+ */
+send_data(instr, outstr)
+       FILE *instr, *outstr;
+{
+       register int c;
+       int netfd, filefd, cnt;
+       char buf[BUFSIZ];
+
+       switch (type) {
+
+       case TYPE_A:
+               while ((c = getc(instr)) != EOF) {
+                       if (c == '\n')
+                               putc('\r', outstr);
+                       if (putc(c, outstr) == EOF)
+                               return (1);
+               }
+               return (0);
+               
+       case TYPE_I:
+       case TYPE_L:
+               netfd = fileno(outstr);
+               filefd = fileno(instr);
+
+               while ((cnt = read(filefd, buf, sizeof buf)) > 0)
+                       if (write(netfd, buf, cnt) < 0)
+                               return (1);
+               return (cnt < 0);
+       }
+       reply(504,"Unimplemented TYPE %d in send_data", type);
+       return (1);
+}
+
+/*
+ * 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 cr, escape, eof;
+       int netfd, filefd, cnt;
+       char buf[BUFSIZ];
+
+
+       switch (type) {
+
+       case TYPE_I:
+       case TYPE_L:
+               netfd = fileno(instr);
+               netfd = fileno(outstr);
+               while ((cnt = read(netfd, buf, sizeof buf)) > 0)
+                       if (write(filefd, buf, cnt) < 0)
+                               return (1);
+               return (cnt < 0);
+
+       case TYPE_E:
+               reply(504, "TYPE E not implemented.");
+               return (1);
+
+       case TYPE_A:
+               cr = 0;
+               while ((c = getc(instr)) != EOF) {
+                       if (cr) {
+                               if (c != '\r' && c != '\n')
+                                       putc('\r', outstr);
+                               putc(c, outstr);
+                               cr = c == '\r';
+                               continue;
+                       }
+                       if (c == '\r') {
+                               cr = 1;
+                               continue;
+                       }
+                       putc(c, outstr);
+               }
+               if (cr)
+                       putc('\r', outstr);
+               return (0);
+       }
+       fatal("Unknown type in receive_data.");
+       /*NOTREACHED*/
+}
+
+fatal(s)
+       char *s;
+{
+       reply(451, "Error in server: %s\n", s);
+       reply(221, "Closing connection due to server error.");
+       exit(0);
+}
+
+reply(n, s, args)
+       int n;
+       char *s;
+{
+
+       printf("%d ", n);
+       _doprnt(s, &args, stdout);
+       printf("\r\n");
+       fflush(stdout);
+       if (debug) {
+               fprintf(stderr, "<--- %d ", n);
+               _doprnt(s, &args, stderr);
+               fprintf(stderr, "\n");
+               fflush(stderr);
+       }
+}
+
+lreply(n, s, args)
+       int n;
+       char *s;
+{
+       printf("%d-", n);
+       _doprnt(s, &args, stdout);
+       printf("\r\n");
+       fflush(stdout);
+       if (debug) {
+               fprintf(stderr, "<--- %d-", n);
+               _doprnt(s, &args, stderr);
+               fprintf(stderr, "\n");
+       }
+}
+
+replystr(s)
+       char *s;
+{
+       printf("%s\r\n", s);
+       fflush(stdout);
+       if (debug)
+               fprintf(stderr, "<--- %s\n", s);
+}
+
+ack(s)
+       char *s;
+{
+       reply(200, "%s command okay.", s);
+}
+
+nack(s)
+       char *s;
+{
+       reply(502, "%s command not implemented.", s);
+}
+
+yyerror()
+{
+       reply(500, "Command not understood.");
+}
+
+delete(name)
+       char *name;
+{
+       struct stat st;
+
+       if (stat(name, &st) < 0) {
+               reply(550, "%s: %s.", name, sys_errlist[errno]);
+               return;
+       }
+       if ((st.st_mode&S_IFMT) == S_IFDIR) {
+               if (rmdir(name) < 0) {
+                       reply(550, "%s: %s.", name, sys_errlist[errno]);
+                       return;
+               }
+               goto done;
+       }
+       if (unlink(name) < 0) {
+               reply(550, "%s: %s.", name, sys_errlist[errno]);
+               return;
+       }
+done:
+       ack("DELE");
+}
+
+cwd(path)
+       char *path;
+{
+
+       if (chdir(path) < 0) {
+               reply(550, "%s: %s.", path, sys_errlist[errno]);
+               return;
+       }
+       ack("CWD");
+}
+
+do_mkdir(name)
+       char *name;
+{
+       
+       if (mkdir(name, 0777) < 0) {
+               reply(550, "%s: %s.", name, sys_errlist[errno]);
+               return;
+       }
+       ack("MKDIR");
+}
+
+do_rmdir(name)
+       char *name;
+{
+
+       if (rmdir(name) < 0) {
+               reply(550, "%s: %s.", name, sys_errlist[errno]);
+               return;
+       }
+       ack("RMDIR");
+}
+
+do_pwd()
+{
+       char path[1024];
+       char *p;
+
+       if (getwd(path) == NULL) {
+               reply(451, "%s.", path);
+               return;
+       }
+       reply(251, "\"%s\" is current directory.", path);
+}
+
+char *
+renamefrom(name)
+       char *name;
+{
+       struct stat st;
+
+       if (stat(name, &st) < 0) {
+               reply(550, "%s: %s.", name, sys_errlist[errno]);
+               return ((char *)0);
+       }
+       ack("RNFR");
+       return (name);
+}
+
+renamecmd(from, to)
+       char *from, *to;
+{
+
+       if (rename(from, to) < 0) {
+               reply(550, "rename: %s.", sys_errlist[errno]);
+               return;
+       }
+       ack("RNTO");
+}
+
+int guest;
+/*
+ * Test pathname for guest-user safety.
+ */
+inappropriate_request(name)
+       char *name;
+{
+       int bogus = 0, depth = 0, length = strlen(name);
+       char *p, *s;
+
+       if (!guest)
+               return (0);
+       if (name[0] == '/' || name[0] == '|')
+               bogus = 1;
+       for (p = name; p < name+length;) {
+               s = p;                          /* start of token */
+               while ( *p && *p!= '/')
+                       p++;
+               *p = 0;
+               if (strcmp(s, "..") == 0)
+                       depth -= 1;             /* backing up */
+               else if (strcmp(s, ".") == 0)
+                       depth += 0;             /* no change */
+               else
+                       depth += 1;             /* descending */
+               if (depth < 0) {
+                       bogus = 1;
+                       break;
+               }
+       }
+       if (bogus)
+               reply(553, "%s: pathname disallowed guest users", name);
+       return (bogus);
+}
+
+/*
+ * Convert network-format internet address
+ * to base 256 d.d.d.d representation.
+ */
+char *
+ntoa(in)
+       struct in_addr in;
+{
+       static char b[18];
+       register char *p;
+
+       in.s_addr = ntohl(in.s_addr);
+       p = (char *)&in;
+#define        UC(b)   (((int)b)&0xff)
+       sprintf(b, "%d.%d.%d.%d", UC(p[0]), UC(p[1]), UC(p[2]), UC(p[3]));
+       return (b);
+}
+
+dolog(sin)
+       struct sockaddr_in *sin;
+{
+       struct hostent *hp = gethostbyaddr(&sin->sin_addr,
+               sizeof (struct in_addr), AF_INET);
+       char *remotehost;
+       time_t t;
+
+       if (hp)
+               remotehost = hp->h_name;
+       else
+               remotehost = "UNKNOWNHOST";
+       t = time(0);
+       fprintf(stderr,"FTP %d: connection from %s at %s",
+               getpid(), remotehost, ctime(&t));
+       fflush(stderr);
+}