void statfilecmd (const char *filename) { FILE *fin; int c; char line[LINE_MAX]; snprintf (line, sizeof (line), "/bin/ls -lgA %s", filename); fin = ftpd_popen (line, "r"); lreply (211, "status of %s:", filename); while ((c = getc (fin)) != EOF) { if (c == '\n') { if (ferror (stdout)) { perror_reply (421, "control connection"); ftpd_pclose (fin); dologout (1); } if (ferror (fin)) { perror_reply (551, filename); ftpd_pclose (fin); return; } putc ('\r', stdout); } putc (c, stdout); } ftpd_pclose (fin); reply (211, "End of Status"); }
void cwd(const char *path) { if (chdir(path) < 0) perror_reply(550, path); else { show_chdir_messages(250); ack("CWD"); if (getcwd(cached_path, MAXPATHLEN) == NULL) { discover_path(cached_path, path); } } }
/* Helper function. */ char * sgetsave (const char *s) { char *string; size_t len; if (s == NULL) s = ""; len = strlen (s) + 1; string = malloc (len); if (string == NULL) { perror_reply (421, "Local resource failure: malloc"); dologout (1); } /* (void) strcpy (string, s); */ memcpy (string, s, len); return string; }
void store (const char *name, const char *mode, int unique) { FILE *fout, *din; struct stat st; int (*closefunc) (FILE *); if (unique && stat (name, &st) == 0) { const char *name_unique = gunique (name); if (name_unique) name = name_unique; else { LOGCMD (*mode == 'w' ? "put" : "append", name); return; } } if (restart_point) mode = "r+"; fout = fopen (name, mode); closefunc = fclose; if (fout == NULL) { perror_reply (553, name); LOGCMD (*mode == 'w' ? "put" : "append", name); return; } byte_count = -1; if (restart_point) { if (type == TYPE_A) { off_t i, n; int c; n = restart_point; i = 0; while (i++ < n) { c = getc (fout); if (c == 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, SEEK_CUR) < 0) { perror_reply (550, name); goto done; } } else if (lseek (fileno (fout), restart_point, SEEK_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."); } fclose (din); data = -1; pdata = -1; done: LOGBYTES (*mode == 'w' ? "put" : "append", name, byte_count); (*closefunc) (fout); }
void retrieve (const char *cmd, const char *name) { FILE *fin, *dout; struct stat st; int (*closefunc) (FILE *); size_t buffer_size = 0; if (cmd == 0) { fin = fopen (name, "r"), closefunc = fclose; st.st_size = 0; } else { char line[BUFSIZ]; snprintf (line, sizeof line, cmd, name); name = line; fin = ftpd_popen (line, "r"), closefunc = ftpd_pclose; st.st_size = -1; buffer_size = BUFSIZ; } if (fin == NULL) { if (errno != 0) { perror_reply (550, name); if (cmd == 0) { LOGCMD ("get", name); } } return; } byte_count = -1; if (cmd == 0 && (fstat (fileno (fin), &st) < 0 || !S_ISREG (st.st_mode))) { reply (550, "%s: not a plain file.", name); goto done; } if (restart_point) { if (type == TYPE_A) { off_t i, n; int c; n = restart_point; i = 0; while (i++ < n) { c = getc (fin); if (c == EOF) { perror_reply (550, name); goto done; } if (c == '\n') i++; } } else if (lseek (fileno (fin), restart_point, SEEK_SET) < 0) { perror_reply (550, name); goto done; } } dout = dataconn (name, st.st_size, "w"); if (dout == NULL) goto done; send_data (fin, dout, buffer_size); fclose (dout); data = -1; pdata = -1; done: if (cmd == 0) LOGBYTES ("get", name, byte_count); (*closefunc) (fin); }
int main (int argc, char *argv[], char **envp) { int index; set_program_name (argv[0]); #ifdef HAVE_TZSET tzset (); /* In case no timezone database in ~ftp. */ #endif #ifdef HAVE_INITSETPROCTITLE /* Save start and extent of argv for setproctitle. */ initsetproctitle (argc, argv, envp); #endif /* HAVE_INITSETPROCTITLE */ /* Parse the command line */ iu_argp_init ("ftpd", default_program_authors); argp_parse (&argp, argc, argv, 0, &index, NULL); /* Bail out, wrong usage */ argc -= index; if (argc != 0) error (1, 0, "surplus arguments; try `%s --help' for more info", program_name); /* LOG_NDELAY sets up the logging connection immediately, necessary for anonymous ftp's that chroot and can't do it later. */ openlog ("ftpd", LOG_PID | LOG_NDELAY, LOG_FTP); freopen (PATH_DEVNULL, "w", stderr); /* If not running via inetd, we detach and dup(fd, 0), dup(fd, 1) the fd = accept(). tcpd is check if compile with the support */ if (daemon_mode) { if (server_mode (pid_file, &his_addr) < 0) exit (1); } else { socklen_t addrlen = sizeof (his_addr); if (getpeername (STDIN_FILENO, (struct sockaddr *) &his_addr, &addrlen) < 0) { syslog (LOG_ERR, "getpeername (%s): %m", program_name); exit (1); } } signal (SIGHUP, sigquit); signal (SIGINT, sigquit); signal (SIGQUIT, sigquit); signal (SIGTERM, sigquit); signal (SIGPIPE, lostconn); signal (SIGCHLD, SIG_IGN); if (signal (SIGURG, myoob) == SIG_ERR) syslog (LOG_ERR, "signal: %m"); /* Get info on the ctrl connection. */ { socklen_t addrlen = sizeof (ctrl_addr); if (getsockname (STDIN_FILENO, (struct sockaddr *) &ctrl_addr, &addrlen) < 0) { syslog (LOG_ERR, "getsockname (%s): %m", program_name); exit (1); } } #if defined (IP_TOS) && defined (IPTOS_LOWDELAY) && defined (IPPROTO_IP) /* To minimize delays for interactive traffic. */ { int tos = IPTOS_LOWDELAY; if (setsockopt (STDIN_FILENO, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof (int)) < 0) syslog (LOG_WARNING, "setsockopt (IP_TOS): %m"); } #endif #ifdef SO_OOBINLINE /* Try to handle urgent data inline. */ { int on = 1; if (setsockopt (STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof (on)) < 0) syslog (LOG_ERR, "setsockopt: %m"); } #endif #ifdef SO_KEEPALIVE /* Set keepalives on the socket to detect dropped connections. */ { int keepalive = 1; if (setsockopt (STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE, (char *) &keepalive, sizeof (keepalive)) < 0) syslog (LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); } #endif #ifdef F_SETOWN if (fcntl (STDIN_FILENO, F_SETOWN, getpid ()) == -1) syslog (LOG_ERR, "fcntl F_SETOWN: %m"); #endif dolog (&his_addr, &cred); /* Deal with login disable. */ if (display_file (PATH_NOLOGIN, 530) == 0) { reply (530, "System not available."); exit (0); } /* Display a Welcome message if exists, N.B. reply(220,) must follow. */ display_file (PATH_FTPWELCOME, 220); hostname = localhost (); if (!hostname) perror_reply (550, "Local resource failure: malloc"); /* Tell them we're ready to roll. */ if (!no_version) reply (220, "%s FTP server (%s %s) ready.", hostname, PACKAGE_NAME, PACKAGE_VERSION); else reply (220, "%s FTP server ready.", hostname); /* Set the jump, if we have an error parsing, come here and start fresh. */ setjmp (errcatch); /* Roll. */ for (;;) yyparse (); }
/* 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. */ static int receive_data (FILE * instr, FILE * outstr) { int c; int cnt, bare_lfs = 0; char buf[BUFSIZ]; transflag++; if (setjmp (urgcatch)) { transflag = 0; return -1; } switch (type) { case TYPE_I: case TYPE_L: while ((cnt = read (fileno (instr), buf, sizeof (buf))) > 0) { if (write (fileno (outstr), buf, cnt) != cnt) goto file_err; byte_count += cnt; } if (cnt < 0) goto data_err; transflag = 0; return 0; case TYPE_E: reply (553, "TYPE E not implemented."); transflag = 0; return -1; case TYPE_A: while ((c = getc (instr)) != EOF) { byte_count++; if (c == '\n') bare_lfs++; while (c == '\r') { if (ferror (outstr)) goto data_err; c = getc (instr); if (c != '\n') { putc ('\r', outstr); if (c == '\0' || c == EOF) goto contin2; } } putc (c, outstr); contin2:; } fflush (outstr); if (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); printf (" File may not have transferred correctly.\r\n"); } return (0); default: reply (550, "Unimplemented TYPE %d in receive_data", type); transflag = 0; return -1; } data_err: transflag = 0; perror_reply (426, "Data Connection"); return -1; file_err: transflag = 0; perror_reply (452, "Error writing file"); return -1; }
/* 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. */ static void send_data (FILE * instr, FILE * outstr, off_t blksize) { int c, cnt, filefd, netfd; char *buf, *bp; off_t curpos; size_t len, filesize; transflag++; if (setjmp (urgcatch)) { transflag = 0; return; } netfd = fileno (outstr); filefd = fileno (instr); #ifdef HAVE_MMAP if (file_size > 0) { curpos = lseek (filefd, 0, SEEK_CUR); if (curpos >= 0) { filesize = file_size - curpos; buf = mmap (0, filesize, PROT_READ, MAP_SHARED, filefd, curpos); } } #endif switch (type) { case TYPE_A: #ifdef HAVE_MMAP if (file_size > 0 && curpos >= 0 && buf != MAP_FAILED) { len = 0; while (len < filesize) { byte_count++; if (buf[len] == '\n') { if (ferror (outstr)) break; putc ('\r', outstr); } putc (buf[len], outstr); len++; } fflush (outstr); transflag = 0; munmap (buf, filesize); if (ferror (outstr)) goto data_err; reply (226, "Transfer complete."); return; } #endif while ((c = getc (instr)) != EOF) { byte_count++; if (c == '\n') { if (ferror (outstr)) goto data_err; putc ('\r', outstr); } putc (c, outstr); } fflush (outstr); transflag = 0; if (ferror (instr)) goto file_err; if (ferror (outstr)) goto data_err; reply (226, "Transfer complete."); return; case TYPE_I: case TYPE_L: #ifdef HAVE_MMAP if (file_size > 0 && curpos >= 0 && buf != MAP_FAILED) { bp = buf; len = filesize; do { cnt = write (netfd, bp, len); len -= cnt; bp += cnt; if (cnt > 0) byte_count += cnt; } while (cnt > 0 && len > 0); transflag = 0; munmap (buf, (size_t) filesize); if (cnt < 0) goto data_err; reply (226, "Transfer complete."); return; } #endif buf = malloc ((u_int) blksize); if (buf == NULL) { transflag = 0; perror_reply (451, "Local resource failure: malloc"); return; } while ((cnt = read (filefd, buf, (u_int) blksize)) > 0 && write (netfd, buf, cnt) == cnt) byte_count += cnt; transflag = 0; free (buf); if (cnt != 0) { if (cnt < 0) goto file_err; 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; perror_reply (426, "Data connection"); return; file_err: transflag = 0; perror_reply (551, "Error on input file"); }
static FILE * dataconn (const char *name, off_t size, const char *mode) { char sizebuf[32]; FILE *file; int retry = 0; file_size = size; byte_count = 0; if (size != (off_t) - 1) snprintf (sizebuf, sizeof (sizebuf), " (%s bytes)", off_to_str (size)); else *sizebuf = '\0'; if (pdata >= 0) { struct sockaddr_in from; socklen_t s; socklen_t fromlen = sizeof (from); signal (SIGALRM, toolong); alarm ((unsigned) timeout); s = accept (pdata, (struct sockaddr *) &from, &fromlen); alarm (0); if (s < 0) { reply (425, "Can't open data connection."); close (pdata); pdata = -1; return NULL; } close (pdata); pdata = s; #if defined (IP_TOS) && defined (IPTOS_THROUGHPUT) && defined (IPPROTO_IP) /* Optimize throughput. */ { int tos = IPTOS_THROUGHPUT; setsockopt (s, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof (int)); } #endif #ifdef SO_KEEPALIVE /* Set keepalives on the socket to detect dropped conns. */ { int keepalive = 1; setsockopt (s, SOL_SOCKET, SO_KEEPALIVE, (char *) &keepalive, sizeof (int)); } #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"); 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; }