/* * Copyright (C) 1999-2004 Joachim Wieland * * This program 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., * 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ #include "jftpgw.h" #include #include /* reads a single line from fd and returns a pointer to a malloc()ed * char array. * * Parameters: fd: The file descriptor to read from * * Return values: (char*) 0 on error, a pointer to a malloc()ed char array * that contains the read data on success * * Called by: various functions */ extern int timeout; static char *readline_check(int, int); char* readline(int fd) { /* do not check for valid FTP responses */ return readline_check(fd, 0); } char* ftp_readline(int fd) { /* check for valid FTP responses */ return readline_check(fd, 1); } static char *readline_check(int fd, int check_ftp_format) { const int MAXSIZE = 3; int n, ret, length = 0; int linecnt; int data_read = 0; char *temp, *buffer; fd_set readset; struct timeval rtimeout; sigset_t sigset, oldset; rtimeout.tv_sec = config_get_ioption("commandtimeout", 300); rtimeout.tv_usec = 0; FD_ZERO(&readset); FD_SET(fd, &readset); timeout = 0; sigemptyset(&sigset); sigemptyset(&oldset); sigaddset(&sigset, SIGCHLD); ret = sigprocmask(SIG_BLOCK, &sigset, &oldset); if (ret < 0) { jlog(3, "sigprocmask() error: %s", strerror(errno)); } if ((ret = select(fd + 1, &readset, NULL, NULL, &rtimeout)) <= 0) { if (ret == 0) { timeout = 1; } /* save the errno value from sigprocmask() */ n = errno; ret = sigprocmask(SIG_UNBLOCK, &sigset, &oldset); if (ret < 0) { jlog(3, "sigprocmask() error releasing the blocked" " signals: %s", strerror(errno)); } errno = n; return 0; } ret = sigprocmask(SIG_UNBLOCK, &sigset, &oldset); if (ret < 0) { jlog(3, "sigprocmask() error releasing the blocked" " signals: %s", strerror(errno)); } if (fd < 0) { jlog(1, "readline_check: Not connected"); return 0; } buffer = (char *)malloc(MAXSIZE); enough_mem(buffer); temp = buffer; linecnt = 0; while ((n = read(fd, temp, 1)) > 0) { data_read = 1; if (*temp == '\r') { linecnt++; continue; } if (*temp == '\n') { linecnt++; break; } if (*temp == '\0') break; /* check for a valid FTP response. It must start with either * xxx (mind the space) * or * xxx- (with a dash) */ length++; if (check_ftp_format && linecnt == 0 && ((length <= 3 && !isdigit((int) *temp)) || (length == 4 && *temp != ' ' && *temp != '-'))) { buffer[length] = 0; jlog(4, "malformed FTP response: %s", buffer); jlog(9, "in line: %d", linecnt); set_errstr("malformed FTP response"); return 0; } if ((length+1) % MAXSIZE == 0) { buffer = (char*) realloc(buffer, length + 1 + MAXSIZE); enough_mem(buffer); temp = buffer + length - 1; } temp++; } if (n < 0 || (n == 0 && data_read == 0)) { if (n < 0) { set_errstr(strerror(errno)); jlog(2, "Error reading: %s", strerror(errno)); } free(buffer); return 0; } buffer[length] = '\0'; if (my_strcasestr(buffer, "PASS ") == (char*) 0) { jlog(9, "Read (%d): %s", fd, buffer); } else { jlog(9, "Read (%d): ***hidden***", fd); } return buffer; } /* reads from fd until it has a "xxx data" response and returns the * response code xxx. * If **data != 0, *data is set to the address of the malloc()ed array that * contains the read data. * * Parameters: fd: The file descriptor to read from * data: A pointer to a pointer that should be set to the address * of the read data. * * Return value: 0 on error * the response code on success * Called by: checkforabort() * */ int ftp_getrc(int fd, char **data) { char *line; char *tmp =0; int response; while ((line = ftp_readline(fd))) { if (line[0] < '0' || line[0] > '9') { free(line); continue; } if (line[3] == ' ') { tmp = strdup(line); enough_mem(tmp); break; } /* free the line if we continue in the loop */ free(line); } if (!line || !*line) return 0; sscanf(line, "%d ", &response); if (data) { *data = tmp; } else { free(tmp); } /* free the line that was not freed within the whlie loop because of * a break statement */ free(line); tmp =0; if (response >= 100) return response; return 0; } /* readall() reads from an fd and returnes the whole data in a structure * message. * * Parameters: sourcefd: The file descriptor to read from * * Return values: A struct message with * - both char* arrays in it set to NULL on error * - one char* array pointing to the whole message and the * other one pointing to the last line of it, starting with * the response code. * * Called by: login() to get the whole welcome message and to analyze * the authentication message * */ struct message readall(int sourcefd) { char* line =0, *buf =0, *tmp =0, *last =0, *linestart =0; struct message ret; size_t tmpsize; while ((line = readline(sourcefd))) { if (buf) { tmpsize = strlen(buf) + strlen(line) + 4; tmp = (char*) malloc(tmpsize); enough_mem(tmp); strncpy(tmp, buf, tmpsize); last = tmp + strlen(buf); free(buf); buf = 0; } else { tmpsize = strlen(line) + 4; tmp = (char*) malloc(tmpsize); enough_mem(tmp); tmp[0] = '\0'; last = tmp; } linestart = tmp + strlen(tmp); scnprintf(tmp, tmpsize, "%s\r\n", line); buf = tmp; tmp =0; if (line[0] < '0' || line[0] > '9') { free(line); continue; } if (line[3] == ' ') { break; } free(line); line = (char*) 0; } if (!line) { free(buf); ret.fullmsg = (char*) 0; ret.lastmsg = (char*) 0; return ret; } else { free(line); } ret.fullmsg = buf; jlog(9, "Readall (%d): %s", sourcefd, buf); ret.lastmsg = last; return ret; } /* passall() reads from sourcefd until there is no data left to read and writes * everything to targetfd * * Parameters: sourcefd: The file descriptor to read from * targetfd: The file descriptor to write to * * Return value: 0 on error * a pointer that contains the malloc()ed char array with the * passed data on success * * Called by: handlecmds() to pass the message sent after a QUIT * passcmd() to pass the control connection when passing a command * passcmd() to pass the control connection after having transmit * the file */ char* passall(int sourcefd, int targetfd) { char* line =0, *sendbuf =0; size_t sendbufsize; while ((line = readline(sourcefd))) { sendbufsize = strlen(line) + 3; sendbuf = (char*) malloc(sendbufsize); enough_mem(sendbuf); snprintf(sendbuf, sendbufsize, "%s\r\n", line); free(line); say(targetfd, sendbuf); if (strlen(sendbuf) > 4 && isdigit((int)sendbuf[0]) && isdigit((int)sendbuf[1]) && isdigit((int)sendbuf[2]) && sendbuf[3] == ' ') { /* the loop is left here */ break; } else { free(sendbuf); sendbuf = 0; } } return sendbuf; }