/* * ftplib.cpp * Copyright (C) 2002 Florin Malita * * This file is part of LUFS, a free userspace filesystem implementation. * See http://lufs.sourceforge.net/ for updates. * * LUFS 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. * * LUFS 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-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include "ftplib.h" FTPConnection::FTPConnection(int act, char *h, unsigned short p, char *u, char *pw){ TRACE("in constructor"); host = string(h); port = p; user = string(u); pass = string(pw); last_cmd = string(""); active = act; csock = dsock = 0; cfd = dfd = NULL; } FTPConnection::~FTPConnection(){ TRACE("in destructor"); disconnect(); } void FTPConnection::disconnect(){ if(dfd) fclose(dfd); if(dsock) close(dsock); if(cfd) fclose(cfd); if(csock) close(csock); csock = dsock = 0; cfd = dfd = 0; } int FTPConnection::connect(){ struct hostent *hst; struct sockaddr_in addr; int res, i; disconnect(); if(!(hst = gethostbyname(host.c_str()))){ ERROR("could not resolve host " << host); return -1; } if((csock = socket(PF_INET, SOCK_STREAM, 0)) < 0){ ERROR("socket call failed!"); return -1; } memset(&addr, 0, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_port = htons(port); for(i = 0; hst->h_addr_list[i]; i++){ memcpy(&addr.sin_addr.s_addr, hst->h_addr_list[i], 4); TRACE("trying to connect to "<h_addr_list[i])<<"..."); if(!::connect(csock, (struct sockaddr*)&addr, sizeof(struct sockaddr_in))){ TRACE("connect succeded..."); break; } } if(! hst->h_addr_list[i]){ ERROR("could not connect to server!"); goto error; } if(!(cfd = fdopen(csock, "r+"))){ WARN("could not create filestream!"); goto error; } if(get_response() != 220){ WARN("protocol error!"); goto error; } if(execute(string("USER ") + user, 0, 0) < 0){ WARN("USER failed!"); goto error; } res = get_response(); if((res < 0) || ((res != 331) && (res != 230))){ WARN("invalid user!"); goto error; } if((res == 331) && (execute(string("PASS ") + pass, 230, 0) < 0)){ WARN("login failed!"); goto error; } if((res = execute(string("SYST"), 0, 0)) < 0){ WARN("SYST command failed!"); goto error; } if(! fgets(buf, FTP_MAXLINE, cfd)){ WARN("no response to SYST!"); goto error; } if((sscanf(buf, "%u %32s", &res, system) != 2) || (res != 215)){ WARN("bad response to SYST!"); goto error; } TRACE("logged in. system type is " << system << "."); return 0; error: disconnect(); return -1; } int FTPConnection::get_response(){ int res = 0; unsigned multiline = 0; if(!cfd) return -1; if(!fgets(buf, FTP_MAXLINE, cfd)){ WARN("broken control socket!"); return -1; } TRACE("line: " << buf); if(buf[3] == '-'){ if(!sscanf(buf, "%u-", &multiline)){ WARN("bad response!"); return -1; } TRACE("multiline: " << multiline); } if(multiline) do { if(!fgets(buf, FTP_MAXLINE, cfd)){ WARN("broken control socket!"); return -1; } TRACE("line: " << buf); if(buf[3] == ' ') sscanf(buf, "%u ", &res); }while((unsigned)res != multiline); else if(!sscanf(buf, "%u", &res)){ WARN("bad response!"); return -1; } return res; } int FTPConnection::close_data(){ if(dfd){ fclose(dfd); dfd = NULL; } if(dsock){ close(dsock); dsock = 0; return get_response(); } return 0; } int FTPConnection::execute_retry(string cmd, int r, int reconnect){ int res, tries = 0; do { res = execute(cmd, r, reconnect); }while((res == -EAGAIN) && (tries++ < FTP_MAXTRIES)); return res; } int FTPConnection::execute(string cmd, int r, int reconnect){ int res; close_data(); if(cfd == NULL){ if((!reconnect) || (connect() < 0)){ WARN("could not connect!"); return -1; } } TRACE("executing " << cmd); cmd += "\r\n"; if((res = fwrite(cmd.c_str(), 1, cmd.size(), cfd)) != (int)cmd.size() || (fflush(cfd))){ WARN("write error!"); if((!reconnect) || ((res = connect()) < 0)){ WARN("could not reconnect!"); return res; } } if(r){ if((res = get_response()) != r){ WARN("command failed!"); if((reconnect) && ((res < 0) || (res == 421))){ if((res = connect()) < 0) return res; else return -EAGAIN; }else return -1; } } return 0; } int FTPConnection::execute_open(string cmd, string type, long long offset){ if((!csock) || (!cfd)){ TRACE("control socket not connected."); disconnect(); if(connect() < 0) return -1; } if(active) return execute_open_active(cmd, type, offset); else return execute_open_passive(cmd, type, offset); } int FTPConnection::execute_open_active(string cmd, string type, long long offset){ struct sockaddr_in addr, ctrl; int res, ssock, tries = 0; if((dsock == 0) || (dfd == NULL) || (offset != last_off) || (cmd != last_cmd)){ again: if(tries++ > FTP_MAXTRIES){ WARN("too many failures!"); return -1; } close_data(); memset(&addr, 0, sizeof(struct sockaddr_in)); addr.sin_addr.s_addr = INADDR_ANY; addr.sin_port = 0; addr.sin_family = AF_INET; if((ssock = socket(PF_INET, SOCK_STREAM, 0)) < 0){ WARN("socket call failed!"); return ssock; } if((res = bind(ssock, (struct sockaddr*)&addr, sizeof(struct sockaddr_in))) < 0){ WARN("bind call failed!"); close(ssock); return res; } if((res = listen(ssock, 2)) < 0){ WARN("listen call failed!"); close(ssock); return res; } res = sizeof(struct sockaddr_in); if((res = getsockname(csock, (struct sockaddr*)&ctrl, (socklen_t*)&res)) < 0){ WARN("getsockname call failed!"); close(ssock); return res; } res = sizeof(struct sockaddr_in); if((res = getsockname(ssock, (struct sockaddr*)&addr, (socklen_t*)&res)) < 0){ WARN("getsockname call failed!"); close(ssock); return res; } sprintf(buf, "PORT %u,%u,%u,%u,%u,%u", ctrl.sin_addr.s_addr & 0xff, (ctrl.sin_addr.s_addr >> 8) & 0xff, (ctrl.sin_addr.s_addr >> 16) & 0xff, (ctrl.sin_addr.s_addr >> 24) & 0xff, ntohs(addr.sin_port) >> 8, ntohs(addr.sin_port) & 0xff); TRACE("cmd: " << buf); if((res = execute(string(buf), 200, 1)) < 0){ close(ssock); if(res == -EAGAIN) goto again; return res; } if((res = execute(string("TYPE ") + type, 200, 1)) < 0){ close(ssock); if(res == -EAGAIN) goto again; return res; } sprintf(buf, "REST %llu", offset); if(offset && ((res = execute(string(buf), 350,1)) < 0)){ close(ssock); if(res == -EAGAIN) goto again; return res; } if((res = execute(cmd, 150, 1)) < 0){ close(ssock); if(res == -EAGAIN) goto again; return res; } res = sizeof(struct sockaddr_in); if((res = accept(ssock, (struct sockaddr*)&addr, (socklen_t*)&res)) < 0){ WARN("accept call failed!"); close(ssock); return res; } close(ssock); dsock = res; if((dfd = fdopen(dsock, "r+")) == NULL){ WARN("fdopen failed!"); return -1; } last_cmd = cmd; last_off = offset; }else TRACE("keepalive..."); return 0; } int getIP(char *buf, unsigned long *ip, unsigned short *port){ int res; unsigned char i0,i1, i2, i3, p0, p1; if((res = sscanf(buf," (%hhu,%hhu,%hhu,%hhu,%hhu,%hhu)", &i0, &i1, &i2, &i3, &p0, &p1)) != 6){ WARN("bad format, res=" << res); return -1; } TRACE("buf: " << buf); TRACE("(i0,i1,i2,i3,p0,p1)=("<<(int)i0<<","<<(int)i1<<","<<(int)i2<<","<<(int)i3<<","<<(int)p0<<","<<(int)p1<<")"); *ip = ntohl((unsigned)i0 + (((unsigned)i1) << 8) + (((unsigned)i2) << 16) + (((unsigned)i3) << 24)); *port = ntohs((unsigned)p0 + (((unsigned)p1) << 8)); TRACE("IP: " << *ip << "(" << inet_ntoa(*(struct in_addr*)ip) << ")"); TRACE("port: " << *port); return 0; } int FTPConnection::execute_open_passive(string cmd, string type, long long offset){ int res, tries = 0; unsigned long ip; unsigned short port; struct sockaddr_in addr; TRACE("dsock: "< FTP_MAXTRIES){ WARN("too many failures!"); return -1; } TRACE("reopening data connection..."); if((res = execute(string("PASV"), 0, 1)) < 0){ WARN("PASV command failed!"); if(res == -EAGAIN) goto again; return res; } if(!fgets(buf, FTP_MAXLINE, cfd)){ WARN("no response!"); goto again; } if((!sscanf(buf, "%u", &res)) || (res != 227)){ WARN("bad response!"); goto again; } if(getIP(strchr(buf, '('), &ip, &port) < 0){ WARN("could not extract ip/port information!"); goto again; } if((res = execute(string("TYPE ") + type, 200, 1)) < 0){ if(res == -EAGAIN) goto again; return res; } sprintf(buf, "REST %llu", offset); if(offset && (res = execute(string(buf), 350, 1)) < 0){ WARN("could not set offset!"); if(res == -EAGAIN) goto again; return res; } if((res = execute(cmd, 0, 1)) < 0){ if(res == -EAGAIN) goto again; return res; } if((dsock = socket(PF_INET, SOCK_STREAM, 0)) < 0){ WARN("socket call failed!"); return dsock; } memset(&addr, 0, sizeof(struct sockaddr_in)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(ip); addr.sin_port = htons(port); if(::connect(dsock, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) < 0){ WARN("could not connect to server!"); return -1; } if(get_response() != 150){ WARN("bad server response!"); close(dsock); dsock = 0; return -1; } if((dfd = fdopen(dsock, "r+")) == NULL){ WARN("fdopen failed!"); close_data(); return -1; } last_cmd = cmd; last_off = offset; }else TRACE("keepalive..."); return 0; }