📄 ftp.c
字号:
/* File: ftpproxy/ftp.c Copyright (C) 1999, 2000 Wolfgang Zekoll <wzk@quietsche-entchen.de> Copyright (C) 2000, 2003 Andreas Schoenberg <asg@ftpproxy.org> This software is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <stdlib.h>#include <stdio.h>#include <string.h>#include <unistd.h>#include <stdarg.h>#include <time.h>#include <signal.h>#include <sys/wait.h>#include <ctype.h>#include <errno.h>#include <sys/types.h>#include <sys/stat.h>#include <sys/fcntl.h>#include <sys/socket.h>#include <netdb.h>#include <netinet/in.h>#include <netinet/tcp.h>#include <arpa/inet.h>#include <syslog.h>#include <sys/time.h>#include "ftp.h"#include "ip-lib.h"#include "lib.h"typedef struct _ftpcmd { char name[20]; int par, ispath, useccp; int resp; int log; } ftpcmd_t;ftpcmd_t cmdtab[] = { /* * Einfache FTP Kommandos. */ { "ABOR", 0, 0, 0, 225, 1 }, /* oder 226 */ { "ACCT", 1, 0, 0, 230, 0 }, { "CDUP", 1, 1, 1, 200, 1 }, { "CWD", 1, 1, 1, 250, 1 }, { "DELE", 1, 1, 1, 250, 1 }, { "NOOP", 0, 0, 0, 200, 0 }, { "MDTM", 1, 1, 1, 257, 1 }, { "MKD", 1, 1, 1, 257, 1 }, { "MODE", 1, 0, 0, 200, 0 }, { "PWD", 0, 0, 0, 257, 0 }, { "QUIT", 0, 0, 0, 221, 0 }, { "REIN", 0, 0, 0, 0, /* 220, */ 0 }, /* wird nicht unterstuetzt */ { "REST", 1, 0, 0, 350, 0 }, { "RNFR", 1, 1, 1, 350, 1 }, { "RNTO", 1, 1, 1, 250, 1 }, { "RMD", 1, 1, 1, 250, 1 }, { "SITE", 1, 0, 1, 200, 0 }, { "SIZE", 1, 1, 1, 213, 1 }, { "SMNT", 1, 0, 0, 250, 0 }, { "STAT", 1, 1, 1, 211, 0 }, /* oder 212, 213 */ { "STRU", 1, 0, 0, 0, /* 200, */ 0 }, /* wird nicht unterstuetzt */ { "SYST", 0, 0, 0, 215, 0 }, { "TYPE", 1, 0, 0, 200, 0 }, { "XCUP", 1, 1, 1, 200, 1 }, { "XCWD", 1, 1, 1, 250, 1 }, { "XMKD", 1, 1, 1, 257, 1 }, { "XPWD", 0, 0, 0, 257, 0 }, { "XRMD", 1, 1, 1, 250, 1 }, /* * Nur der Vollstaendigkeit halber: FTP Kommandos die gesondert * behandelt werden. */ { "LIST", 1, 1, 1, 0, 0 }, { "NLST", 1, 1, 1, 0, 0 }, { "PORT", 1, 0, 0, 0, /* 200, */ 0 }, { "PASV", 0, 0, 0, 0, /* 200, */ 0 }, { "ALLO", 1, 0, 0, 0, /* 200, */ 0 }, { "RETR", 1, 1, 1, 0, 0 }, { "STOR", 1, 1, 1, 0, 0 }, { "STOU", 0, 0, 1, 0, 0 }, { "APPE", 1, 1, 1, 0, 0 }, { "HELP", 0, 0, 0, 0, 0 }, { "FEAT", 0, 0, 0, 0, 0 }, { "", 0, 0, 0, 0, 0 } };unsigned get_interface_info(int pfd, char *ip, int max){ int size; unsigned int port; struct sockaddr_in saddr; size = sizeof(saddr); if (getsockname(pfd, (struct sockaddr *) &saddr, &size) < 0) { syslog(LOG_NOTICE, "-ERR: can't get interface info: %s", strerror(errno)); exit (-1); } copy_string(ip, (char *) inet_ntoa(saddr.sin_addr), max); port = ntohs(saddr.sin_port); return (port);}int get_client_info(ftp_t *x, int pfd){ int size; struct sockaddr_in saddr; struct in_addr *addr; struct hostent *hostp = NULL; *x->client = 0; size = sizeof(saddr); if (getpeername(pfd, (struct sockaddr *) &saddr, &size) < 0 ) return (-1); copy_string(x->client_ip, (char *) inet_ntoa(saddr.sin_addr), sizeof(x->client_ip)); if (x->config->numeric_only == 1) copy_string(x->client, x->client_ip, sizeof(x->client)); else { addr = &saddr.sin_addr, hostp = gethostbyaddr((char *) addr, sizeof (saddr.sin_addr.s_addr), AF_INET); copy_string(x->client, hostp == NULL? x->client_ip: hostp->h_name, sizeof(x->client)); } strlwr(x->client); return (0);} /* * Basic I/O functions */int close_ch(ftp_t *x, dtc_t *ch){ if (ch->isock >= 0) close(ch->isock); if (ch->osock >= 0) close (ch->osock); ch->isock = -1; ch->osock = -1; ch->state = 0; ch->operation = 0; ch->seen150 = 0; return (0);}int getc_fd(ftp_t *x, int fd){ int c; bio_t *bio; if (fd == 0) bio = &x->cbuf; else if (fd == x->fd.server) bio = &x->sbuf; else { syslog(LOG_NOTICE, "-ERR: internal bio/fd error"); exit (1); } if (bio->here >= bio->len) { int rc, max, bytes, earlyreported; struct timeval tov; fd_set available, fdset; bio->len = bio->here = 0; earlyreported = 0; FD_ZERO(&fdset); FD_SET(fd, &fdset);/* x->fd.max = fd; */ max = fd; if (x->ch.operation == 0) /* nichts */ ; else if (x->ch.state == PORT_LISTEN) { if (x->ch.mode == MODE_PORT) { FD_SET(x->ch.osock, &fdset); if (x->ch.osock > max) max = x->ch.osock; x->ch.active = x->ch.osock; } else if (x->ch.mode == MODE_PASSIVE) { FD_SET(x->ch.isock, &fdset); if (x->ch.isock > max) max = x->ch.isock; x->ch.active = x->ch.isock; } else { syslog(LOG_NOTICE, "-ERR: internal mode error"); exit (-1); } } else if (x->ch.state == PORT_CONNECTED && x->ch.seen150 == 1) { FD_SET(x->ch.active, &fdset); if (x->ch.active > max) max = x->ch.active; } bytes = 0; while (1) {/* memmove(&available, &fdset, sizeof(fd_set)); */ available = fdset; tov.tv_sec = x->config->timeout; tov.tv_usec = 0; if (debug >= 2) fprintf (stderr, "select max= %d\n", max); rc = select(max + 1, &available, (fd_set *) NULL, (fd_set *) NULL, &tov); if (rc < 0) { syslog(LOG_NOTICE, "select() error: %s\n", strerror(errno)); break; } else if (rc == 0) { syslog(LOG_NOTICE, "connection timed out: client= %s, server= %s:%u", x->client, x->server.name, x->server.port); return (-1); } if (FD_ISSET(fd, &available)) { if ((bytes = read(fd, bio->buffer, sizeof(bio->buffer) - 2)) <= 0) { if (debug != 0) { if (bytes == 0) fprintf (stderr, "received zero bytes on fd %d\n", fd); else fprintf (stderr, "received %d bytes on fd %d, errno= %d, error= %s\n", bytes, fd, errno, strerror(errno)); } return (-1); } break; } else if (FD_ISSET(x->ch.active, &available)) { if (x->ch.state == PORT_LISTEN) { int sock, adrlen; struct sockaddr_in adr; earlyreported = 0; adrlen = sizeof(struct sockaddr); sock = accept(x->ch.active, (struct sockaddr *) &adr, &adrlen); if (debug != 0) fprintf (stderr, "accept() on socket\n"); if (sock < 0) { syslog(LOG_NOTICE, "-ERR: accept error: %s", strerror(errno)); exit (1); } else { char remote[80]; copy_string(remote, inet_ntoa(adr.sin_addr), sizeof(remote)); if (debug) fprintf (stderr, "connection from %s\n", remote); /* * Gegenstelle ueberpruefen. */ if (x->ch.mode == MODE_PORT) { if (strcmp(x->server.ipnum, remote) != 0) { if (x->config->allow_anyremote != 0) /* configuration tells us not to care -- 31JAN02asg */ ; else { syslog(LOG_NOTICE, "-ERR: unexpected connect: %s, expected= %s", remote, x->server.ipnum); exit (1); } } } else { if (strcmp(x->client_ip, remote) != 0) { if (x->config->allow_anyremote != 0) /* ok -- 31JAN02asg */ ; else { syslog(LOG_NOTICE, "-ERR: unexpected connect: %s, expected= %s", remote, x->client_ip); exit (1); } } } } /* * Datenkanal zur anderen Seite aufbauen. */ if (x->ch.mode == MODE_PORT) { dup2(sock, x->ch.osock); close (sock); x->ch.state = PORT_CONNECTED; if (debug) fprintf (stderr, "osock= %d\n", x->ch.osock); if ((x->ch.isock = openip(x->ch.client.ipnum, x->ch.client.port, x->interface, x->config->dataport)) < 0) { syslog(LOG_NOTICE, "-ERR: can't connect to client: %s", strerror(errno)); exit (1); } if (debug) fprintf (stderr, "isock= %d\n", x->ch.isock); } else if (x->ch.mode == MODE_PASSIVE) { dup2(sock, x->ch.isock); close (sock); x->ch.state = PORT_CONNECTED; if (debug) fprintf (stderr, "isock= %d\n", x->ch.isock); if ((x->ch.osock = openip(x->ch.server.ipnum, x->ch.server.port, x->config->sourceip, 0)) < 0) { syslog(LOG_NOTICE, "-ERR: can't connect to server: %s", strerror(errno)); exit (1); } if (debug) fprintf (stderr, "osock= %d\n", x->ch.osock); } /* * Setzen der Datenquelle (Server oder Client). */ if (x->ch.operation == OP_GET) { x->ch.active = x->ch.osock; x->ch.other = x->ch.isock; } else if (x->ch.operation == OP_PUT) { x->ch.active = x->ch.isock; x->ch.other = x->ch.osock; } else { syslog(LOG_NOTICE, "-ERR: transfer operation error"); exit (1); } if (x->ch.seen150 == 0) { /* * And finally ... another attempt to solve the short * data transmission timing problem: If we didn't receive * the 150 response yet from the server we deactivate the * data channel until we have the 150 -- 030406asg */ if (debug >= 2) fprintf (stderr, "150 not seen, deactivating data channel\n"); FD_ZERO(&fdset); FD_SET(fd, &fdset); max = fd; } else { if (debug >= 2) fprintf (stderr, "150 already seen, activating data channel\n"); FD_ZERO(&fdset); FD_SET(fd, &fdset); FD_SET(x->ch.active, &fdset); max = (fd > x->ch.active)? fd: x->ch.active; } if (debug) fprintf (stderr, "active= %d, other= %d\n", x->ch.active, x->ch.other); x->ch.bytes = 0; x->ch.started = time(NULL); } else if (x->ch.state == PORT_CONNECTED) { int wrote; char buffer[FTPMAXBSIZE + 10]; if (x->ch.operation == 0) { if (earlyreported == 0) { earlyreported = 1; syslog(LOG_NOTICE, "early write/read event, sleeping 2 seconds"); sleep(2); continue; } } bytes = read(x->ch.active, buffer, x->config->bsize /* sizeof(buffer) */ ); /* * Handling servers that close the data connection -- 24APR02asg */ wrote = 0; if ((bytes > 0) && ((wrote = write(x->ch.other, buffer, bytes)) == bytes)) x->ch.bytes += bytes; else { if (wrote < 0) syslog(LOG_NOTICE, "error writing data channel, error= %s", strerror(errno)); if (debug) fprintf (stderr, "closing data connection\n"); close_ch(x, &x->ch); FD_ZERO(&fdset); FD_SET(fd, &fdset); max = fd; return (1); } } } } bio->len = bytes; bio->here = 0; } if (bio->here >= bio->len) return (-1); c = (unsigned char) bio->buffer[bio->here++]; return (c);}char *readline_fd(ftp_t *x, int fd, char *line, int size){ int c, k; *line = 0; size = size - 2; c = getc_fd(x, fd); if (c < 0) return (NULL); else if (c == 1) { strcpy(line, "\001"); return (line); } k = 0; while (c > 0 && c != '\n' && c != 0) { if (k < size) line[k++] = c; c = getc_fd(x, fd); } line[k] = 0; noctrl(line); k = 0; while ((c = (unsigned char ) line[k]) != 0 && c > 126) k++; if (k > 0) copy_string(line, &line[k], size); return (line);}char *cfgets(ftp_t *x, char *line, int size){ char *p; *line = 0; if ((p = readline_fd(x, 0, line, size)) == NULL) return (NULL); else if (debug != 0) fprintf (stderr, "CLI >>>: %s\n", p);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -