#include "system.h" #if !defined(HAVE_CONFIG_H) #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 #ifndef __LCLINT__ #if HAVE_MACHINE_TYPES_H # include #endif #endif #if HAVE_NETINET_IN_SYSTM_H # include # include #endif #if ! HAVE_HERRNO extern int h_errno; #endif #include #include #include #include #include #include "rpmio.h" #if !defined(HAVE_INET_ATON) int inet_aton(const char *cp, struct in_addr *inp); #endif #define TIMEOUT_SECS 60 #define BUFFER_SIZE 4096 #ifndef IPPORT_FTP # define IPPORT_FTP 21 #endif #if defined(USE_ALT_DNS) && USE_ALT_DNS #include "dns.h" #endif #include "url.h" #include "ftp.h" static int ftpDebug = 0; static int ftpTimeoutSecs = TIMEOUT_SECS; static int ftpCheckResponse(urlinfo *u, char ** str); static int ftpCommand(urlinfo *u, char * command, ...); static int copyData(FD_t sfd, FD_t tfd); static int getHostAddress(const char * host, struct in_addr * address); static int ftpCheckResponse(urlinfo *u, 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(u->ftpControl, &readSet); timeout.tv_sec = ftpTimeoutSecs; timeout.tv_usec = 0; rc = select(u->ftpControl + 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(u->ftpControl, 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 (ftpDebug) fprintf(stderr, "<- %s\n", buf); if (*errorCode == '4' || *errorCode == '5') { if (!strncmp(errorCode, "550", 3)) return FTPERR_FILE_NOT_FOUND; if (!strncmp(errorCode, "552", 3)) return FTPERR_NIC_ABORT_IN_PROGRESS; return FTPERR_BAD_SERVER_RESPONSE; } if (rc) return rc; return 0; } int ftpCommand(urlinfo *u, char * command, ...) { va_list ap; int len; char * s; char * buf; 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 (ftpDebug) fprintf(stderr, "-> %s", buf); if (write(u->ftpControl, buf, len) != len) { return FTPERR_SERVER_IO_ERROR; } return ftpCheckResponse(u, NULL); } #if !defined(USE_ALT_DNS) || !USE_ALT_DNS static int mygethostbyname(const char * host, struct in_addr * address) { struct hostent * hostinfo; hostinfo = gethostbyname(host); if (!hostinfo) return 1; memcpy(address, hostinfo->h_addr_list[0], hostinfo->h_length); return 0; } #endif static int getHostAddress(const char * host, struct in_addr * address) { if (isdigit(host[0])) { if (!inet_aton(host, address)) { return FTPERR_BAD_HOST_ADDR; } } else { if (mygethostbyname(host, address)) { errno = h_errno; return FTPERR_BAD_HOSTNAME; } } return 0; } static int tcpConnect(const char *host, int port) { struct sockaddr_in sin; int sock = -1; int rc; sin.sin_family = AF_INET; sin.sin_port = htons(port); sin.sin_addr.s_addr = INADDR_ANY; do { if ((rc = getHostAddress(host, &sin.sin_addr)) < 0) break; if ((sock = socket(sin.sin_family, SOCK_STREAM, IPPROTO_IP)) < 0) { rc = FTPERR_FAILED_CONNECT; break; } if (connect(sock, (struct sockaddr *) &sin, sizeof(sin))) { rc = FTPERR_FAILED_CONNECT; break; } } while (0); if (rc < 0 && sock >= 0) { close(sock); return rc; } if (ftpDebug) fprintf(stderr,"++ connect %s:%d on fd %d\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), sock); return sock; } int httpOpen(urlinfo *u) { int sock; const char *host; int port; char *buf; size_t len; if (u == NULL || ((host = u->host) == NULL)) return FTPERR_BAD_HOSTNAME; if ((port = u->port) < 0) port = 80; if ((sock = tcpConnect(host, port)) < 0) return sock; len = strlen(u->path) + sizeof("GET \r\n"); buf = alloca(len); strcpy(buf, "GET "); strcat(buf, u->path); strcat(buf, "\r\n"); if (write(sock, buf, len) != len) { close(sock); return FTPERR_SERVER_IO_ERROR; } if (ftpDebug) fprintf(stderr, "-> %s", buf); return sock; } int ftpOpen(urlinfo *u) { const char * host; const char * user; const char * password; int port; int rc; if (u == NULL || ((host = u->host) == NULL)) return FTPERR_BAD_HOSTNAME; if ((port = u->port) < 0) port = IPPORT_FTP; if ((user = u->user) == NULL) user = "anonymous"; if ((password = u->password) == NULL) { if (getuid()) { struct passwd * pw = getpwuid(getuid()); char *myp = alloca(strlen(pw->pw_name) + sizeof("@")); strcpy(myp, pw->pw_name); strcat(myp, "@"); password = myp; } else { password = "root@"; } } if ((u->ftpControl = tcpConnect(host, port)) < 0) return u->ftpControl; /* ftpCheckResponse() assumes the socket is nonblocking */ if (fcntl(u->ftpControl, F_SETFL, O_NONBLOCK)) { close(u->ftpControl); u->ftpControl = -1; return FTPERR_FAILED_CONNECT; } if ((rc = ftpCheckResponse(u, NULL))) { return rc; } if ((rc = ftpCommand(u, "USER", user, NULL))) { close(u->ftpControl); u->ftpControl = -1; return rc; } if ((rc = ftpCommand(u, "PASS", password, NULL))) { close(u->ftpControl); u->ftpControl = -1; return rc; } if ((rc = ftpCommand(u, "TYPE", "I", NULL))) { close(u->ftpControl); u->ftpControl = -1; return rc; } return u->ftpControl; } int copyData(FD_t sfd, FD_t tfd) { char buf[BUFFER_SIZE]; fd_set emptySet, readSet; struct timeval timeout; int bytesRead; int bytesCopied = 0; int rc; while (1) { FD_ZERO(&emptySet); FD_ZERO(&readSet); FD_SET(fdFileno(sfd), &readSet); timeout.tv_sec = ftpTimeoutSecs; timeout.tv_usec = 0; rc = select(fdFileno(sfd) + 1, &readSet, &emptySet, &emptySet, &timeout); if (rc == 0) { rc = FTPERR_SERVER_TIMEOUT; break; } else if (rc < 0) { rc = FTPERR_UNKNOWN; break; } bytesRead = fdRead(sfd, buf, sizeof(buf)); if (bytesRead == 0) { rc = 0; break; } if (fdWrite(tfd, buf, bytesRead) != bytesRead) { rc = FTPERR_FILE_IO_ERROR; break; } bytesCopied += bytesRead; } if (ftpDebug) fprintf(stderr, "++ copied %d bytes: %s\n", bytesCopied, ftpStrerror(rc)); fdClose(sfd); return rc; } int ftpAbort(FD_t fd) { urlinfo *u = (urlinfo *)fd->fd_url; char buf[BUFFER_SIZE]; int rc; int tosecs = ftpTimeoutSecs; if (ftpDebug) fprintf(stderr, "-> ABOR\n"); sprintf(buf, "%c%c%c", IAC, IP, IAC); send(u->ftpControl, buf, 3, MSG_OOB); sprintf(buf, "%cABOR\r\n", DM); if (write(u->ftpControl, buf, 7) != 7) { return FTPERR_SERVER_IO_ERROR; } if (fdFileno(fd) >= 0) { while(read(fdFileno(fd), buf, sizeof(buf)) > 0) ; } ftpTimeoutSecs = 10; if ((rc = ftpCheckResponse(u, NULL)) == FTPERR_NIC_ABORT_IN_PROGRESS) { rc = ftpCheckResponse(u, NULL); } rc = ftpCheckResponse(u, NULL); ftpTimeoutSecs = tosecs; if (fdFileno(fd) >= 0) fdClose(fd); return 0; } int ftpGetFileDesc(FD_t fd) { urlinfo *u; const char *remotename; struct sockaddr_in dataAddress; int i, j; char * passReply; char * chptr; char * retrCommand; int rc; u = (urlinfo *)fd->fd_url; remotename = u->path; if (ftpDebug) fprintf(stderr, "-> PASV\n"); if (write(u->ftpControl, "PASV\r\n", 6) != 6) { return FTPERR_SERVER_IO_ERROR; } if ((rc = ftpCheckResponse(u, &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; fd->fd_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); if (fdFileno(fd) < 0) { return FTPERR_FAILED_CONNECT; } retrCommand = alloca(strlen(remotename) + 20); sprintf(retrCommand, "RETR %s\r\n", remotename); i = strlen(retrCommand); while (connect(fdFileno(fd), (struct sockaddr *) &dataAddress, sizeof(dataAddress)) < 0) { if (errno == EINTR) continue; fdClose(fd); return FTPERR_FAILED_DATA_CONNECT; } if (ftpDebug) fprintf(stderr, "-> %s", retrCommand); if (write(u->ftpControl, retrCommand, i) != i) { return FTPERR_SERVER_IO_ERROR; } if ((rc = ftpCheckResponse(u, NULL))) { fdClose(fd); return rc; } return 0; } static int ftpGetFileDone(urlinfo *u) { if (ftpCheckResponse(u, NULL)) return FTPERR_BAD_SERVER_RESPONSE; return 0; } int httpGetFile(FD_t sfd, FD_t tfd) { return copyData(sfd, tfd); } int ftpGetFile(FD_t sfd, FD_t tfd) { urlinfo *u; int rc; /* XXX sfd will be freed by copyData -- grab sfd->fd_url now */ u = (urlinfo *)sfd->fd_url; /* XXX normally sfd = ufdOpen(...) and this code does not execute */ if (fdFileno(sfd) < 0 && (rc = ftpGetFileDesc(sfd)) < 0) { fdClose(sfd); return rc; } rc = copyData(sfd, tfd); if (rc < 0) return rc; return ftpGetFileDone(u); } int ftpClose(FD_t fd) { int fdno = ((urlinfo *)fd->fd_url)->ftpControl; if (fdno >= 0) close(fdno); return 0; } const char *ftpStrerror(int errorNumber) { switch (errorNumber) { case 0: return _("Success"); case FTPERR_BAD_SERVER_RESPONSE: return _("Bad server response"); case FTPERR_SERVER_IO_ERROR: return _("Server IO error"); case FTPERR_SERVER_TIMEOUT: return _("Server timeout"); case FTPERR_BAD_HOST_ADDR: return _("Unable to lookup server host address"); case FTPERR_BAD_HOSTNAME: return _("Unable to lookup server host name"); case FTPERR_FAILED_CONNECT: return _("Failed to connect to server"); case FTPERR_FAILED_DATA_CONNECT: return _("Failed to establish data connection to 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_NIC_ABORT_IN_PROGRESS: return _("Abort in progress"); case FTPERR_UNKNOWN: default: return _("Unknown or unexpected error"); } }