rpm/ftp.c

467 lines
9.8 KiB
C

#if HAVE_CONFIG_H
# include "config.h"
#else
#define HAVE_MACHINE_TYPES_H 1
#define HAVE_ALLOCA_H 1
#define HAVE_NETINET_IN_SYSTM_H 1
#define HAVE_SYS_SOCKET_H 1
#endif
#if HAVE_MACHINE_TYPES_H
# include <machine/types.h>
#endif
#if HAVE_ALLOCA_H
# include <alloca.h>
#endif
#if HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#if HAVE_NETINET_IN_SYSTM_H
# include <sys/types.h>
# include <netinet/in_systm.h>
#endif
#if ! HAVE_HERRNO
extern int h_errno;
#endif
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <pwd.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include "inet_aton.h" /* for systems too stupid to provide this */
#define TIMEOUT_SECS 60
#define BUFFER_SIZE 4096
#ifndef IPPORT_FTP
# define IPPORT_FTP 21
#endif
#include "ftp.h"
static int ftpCheckResponse(int sock, char ** str);
static int ftpCommand(int sock, char * command, ...);
static int ftpReadData(int sock, int out);
static int getHostAddress(const char * host, struct in_addr * address);
static int ftpCheckResponse(int sock, char ** str) {
static char buf[BUFFER_SIZE + 1];
int bufLength = 0;
fd_set emptySet, readSet;
char * chptr, * start;
struct timeval timeout;
int bytesRead, rc = 0;
int doesContinue = 1;
char errorCode[4];
errorCode[0] = '\0';
do {
FD_ZERO(&emptySet);
FD_ZERO(&readSet);
FD_SET(sock, &readSet);
timeout.tv_sec = TIMEOUT_SECS;
timeout.tv_usec = 0;
rc = select(sock + 1, &readSet, &emptySet, &emptySet, &timeout);
if (rc < 1) {
if (rc==0)
return FTPERR_BAD_SERVER_RESPONSE;
else
rc = FTPERR_UNKNOWN;
} else
rc = 0;
bytesRead = read(sock, buf + bufLength, sizeof(buf) - bufLength - 1);
bufLength += bytesRead;
buf[bufLength] = '\0';
/* divide the response into lines, checking each one to see if
we are finished or need to continue */
start = chptr = buf;
do {
while (*chptr != '\n' && *chptr) chptr++;
if (*chptr == '\n') {
*chptr = '\0';
if (*(chptr - 1) == '\r') *(chptr - 1) = '\0';
if (str) *str = start;
if (errorCode[0]) {
if (!strncmp(start, errorCode, 3) && start[3] == ' ')
doesContinue = 0;
} else {
strncpy(errorCode, start, 3);
errorCode[3] = '\0';
if (start[3] != '-') {
doesContinue = 0;
}
}
start = chptr + 1;
chptr++;
} else {
chptr++;
}
} while (*chptr);
if (doesContinue && chptr > start) {
memcpy(buf, start, chptr - start - 1);
bufLength = chptr - start - 1;
} else {
bufLength = 0;
}
} while (doesContinue && !rc);
if (*errorCode == '4' || *errorCode == '5') {
if (!strncmp(errorCode, "550", 3)) {
return FTPERR_FILE_NOT_FOUND;
}
return FTPERR_BAD_SERVER_RESPONSE;
}
if (rc) return rc;
return 0;
}
int ftpCommand(int sock, char * command, ...) {
va_list ap;
int len;
char * s;
char * buf;
int rc;
va_start(ap, command);
len = strlen(command) + 2;
s = va_arg(ap, char *);
while (s) {
len += strlen(s) + 1;
s = va_arg(ap, char *);
}
va_end(ap);
buf = alloca(len + 1);
va_start(ap, command);
strcpy(buf, command);
strcat(buf, " ");
s = va_arg(ap, char *);
while (s) {
strcat(buf, s);
strcat(buf, " ");
s = va_arg(ap, char *);
}
va_end(ap);
buf[len - 2] = '\r';
buf[len - 1] = '\n';
buf[len] = '\0';
if (write(sock, buf, len) != len) {
return FTPERR_SERVER_IO_ERROR;
}
if ((rc = ftpCheckResponse(sock, NULL)))
return rc;
return 0;
}
static int getHostAddress(const char * host, struct in_addr * address) {
struct hostent * hostinfo;
if (isdigit(host[0])) {
if (!inet_aton(host, address)) {
return FTPERR_BAD_HOST_ADDR;
}
} else {
hostinfo = gethostbyname(host);
if (!hostinfo) {
errno = h_errno;
return FTPERR_BAD_HOSTNAME;
}
memcpy(address, hostinfo->h_addr_list[0], hostinfo->h_length);
}
return 0;
}
int ftpOpen(char * host, char * name, char * password, char * proxy,
int port) {
static int sock;
/*static char * lastHost = NULL;*/
struct in_addr serverAddress;
struct sockaddr_in destPort;
struct passwd * pw;
char * buf;
int rc;
if (port < 0) port = IPPORT_FTP;
if (!name)
name = "anonymous";
if (!password) {
if (getuid()) {
pw = getpwuid(getuid());
password = alloca(strlen(pw->pw_name) + 2);
strcpy(password, pw->pw_name);
strcat(password, "@");
} else {
password = "root@";
}
}
if (proxy) {
buf = alloca(strlen(name) + strlen(host) + 5);
sprintf(buf, "%s@%s", name, host);
name = buf;
host = proxy;
}
if ((rc = getHostAddress(host, &serverAddress))) return rc;
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (sock < 0) {
return FTPERR_FAILED_CONNECT;
}
destPort.sin_family = AF_INET;
destPort.sin_port = htons(port);
destPort.sin_addr = serverAddress;
if (connect(sock, (struct sockaddr *) &destPort, sizeof(destPort))) {
close(sock);
return FTPERR_FAILED_CONNECT;
}
/* ftpCheckResponse() assumes the socket is nonblocking */
if (fcntl(sock, F_SETFL, O_NONBLOCK)) {
close(sock);
return FTPERR_FAILED_CONNECT;
}
if ((rc = ftpCheckResponse(sock, NULL))) {
return rc;
}
if ((rc = ftpCommand(sock, "USER", name, NULL))) {
close(sock);
return rc;
}
if ((rc = ftpCommand(sock, "PASS", password, NULL))) {
close(sock);
return rc;
}
if ((rc = ftpCommand(sock, "TYPE", "I", NULL))) {
close(sock);
return rc;
}
return sock;
}
int ftpReadData(int sock, int out) {
char buf[BUFFER_SIZE];
fd_set emptySet, readSet;
struct timeval timeout;
int bytesRead, rc;
while (1) {
FD_ZERO(&emptySet);
FD_ZERO(&readSet);
FD_SET(sock, &readSet);
timeout.tv_sec = TIMEOUT_SECS;
timeout.tv_usec = 0;
rc = select(sock + 1, &readSet, &emptySet, &emptySet, &timeout);
if (rc == 0) {
close(sock);
return FTPERR_SERVER_TIMEOUT;
} else if (rc < 0) {
close(sock);
return FTPERR_UNKNOWN;
}
bytesRead = read(sock, buf, sizeof(buf));
if (bytesRead == 0) {
close(sock);
return 0;
}
if (write(out, buf, bytesRead) != bytesRead) {
close(sock);
return FTPERR_FILE_IO_ERROR;
}
}
}
int ftpGetFileDesc(int sock, char * remotename) {
int dataSocket;
struct sockaddr_in dataAddress;
int i, j;
char * passReply;
char * chptr;
char * retrCommand;
int rc;
if (write(sock, "PASV\r\n", 6) != 6) {
return FTPERR_SERVER_IO_ERROR;
}
if ((rc = ftpCheckResponse(sock, &passReply)))
return FTPERR_PASSIVE_ERROR;
chptr = passReply;
while (*chptr && *chptr != '(') chptr++;
if (*chptr != '(') return FTPERR_PASSIVE_ERROR;
chptr++;
passReply = chptr;
while (*chptr && *chptr != ')') chptr++;
if (*chptr != ')') return FTPERR_PASSIVE_ERROR;
*chptr-- = '\0';
while (*chptr && *chptr != ',') chptr--;
if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
chptr--;
while (*chptr && *chptr != ',') chptr--;
if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
*chptr++ = '\0';
/* now passReply points to the IP portion, and chptr points to the
port number portion */
dataAddress.sin_family = AF_INET;
if (sscanf(chptr, "%d,%d", &i, &j) != 2) {
return FTPERR_PASSIVE_ERROR;
}
dataAddress.sin_port = htons((i << 8) + j);
chptr = passReply;
while (*chptr++) {
if (*chptr == ',') *chptr = '.';
}
if (!inet_aton(passReply, &dataAddress.sin_addr))
return FTPERR_PASSIVE_ERROR;
dataSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (dataSocket < 0) {
return FTPERR_FAILED_CONNECT;
}
retrCommand = alloca(strlen(remotename) + 20);
sprintf(retrCommand, "RETR %s\r\n", remotename);
i = strlen(retrCommand);
if (write(sock, retrCommand, i) != i) {
return FTPERR_SERVER_IO_ERROR;
}
if (connect(dataSocket, (struct sockaddr *) &dataAddress,
sizeof(dataAddress))) {
close(dataSocket);
return FTPERR_FAILED_DATA_CONNECT;
}
if ((rc = ftpCheckResponse(sock, NULL))) {
close(dataSocket);
return rc;
}
return dataSocket;
}
int ftpGetFileDone(int sock) {
if (ftpCheckResponse(sock, NULL)) {
return FTPERR_BAD_SERVER_RESPONSE;
}
return 0;
}
int ftpGetFile(int sock, char * remotename, int dest) {
int dataSocket, rc;
dataSocket = ftpGetFileDesc(sock, remotename);
if (dataSocket < 0) return dataSocket;
rc = ftpReadData(dataSocket, dest);
close(dataSocket);
if (rc) return rc;
return ftpGetFileDone(sock);
}
void ftpClose(int sock) {
close(sock);
}
const char *ftpStrerror(int errorNumber) {
switch (errorNumber) {
case FTPERR_BAD_SERVER_RESPONSE:
return ("Bad FTP server response");
case FTPERR_SERVER_IO_ERROR:
return("FTP IO error");
case FTPERR_SERVER_TIMEOUT:
return("FTP server timeout");
case FTPERR_BAD_HOST_ADDR:
return("Unable to lookup FTP server host address");
case FTPERR_BAD_HOSTNAME:
return("Unable to lookup FTP server host name");
case FTPERR_FAILED_CONNECT:
return("Failed to connect to FTP server");
case FTPERR_FAILED_DATA_CONNECT:
return("Failed to establish data connection to FTP server");
case FTPERR_FILE_IO_ERROR:
return("IO error to local file");
case FTPERR_PASSIVE_ERROR:
return("Error setting remote server to passive mode");
case FTPERR_FILE_NOT_FOUND:
return("File not found on server");
case FTPERR_UNKNOWN:
default:
return("FTP Unknown or unexpected error");
}
}