/* FEATHERY FTP-Server * Copyright (C) 2005-2010 Andreas Martin (andreas.martin@linuxmail.org) * * * 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 3 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, see . */ #ifndef WIN32 #include #include #include #include #include #include #include #if defined(__APPLE__) #include #include #endif #include #if defined(__FreeBSD__) #include #else //#include #endif #include #include #include #include #include #include "ftpTypes.h" #include "ftpConfig.h" #include "ftp.h" #include "errno.h" ip_t ownIp; LOCAL fd_set watchedSockets; LOCAL fd_set signaledSockets; LOCAL int maxSockNr; void ftpArchInit() { ownIp = 0; maxSockNr = 0; FD_ZERO(&watchedSockets); FD_ZERO(&signaledSockets); } void ftpArchCleanup(void) { } int ftpGetLocalTime(ftpTime_S *t) { struct tm *pTime; time_t tim; time(&tim); pTime = localtime(&tim); if (pTime) { t->year = (uint16_t) pTime->tm_year + 1900; t->month = (uint8_t) pTime->tm_mon + 1; t->day = (uint8_t) pTime->tm_mday; t->hour = (uint8_t) pTime->tm_hour; t->minute = (uint8_t) pTime->tm_min; t->second = (uint8_t) pTime->tm_sec; return 0; } return -1; } void* ftpOpenDir(const char* path) { return opendir(path); } const char* ftpReadDir(void* dirHandle) { struct dirent* dirEntry; dirEntry = readdir(dirHandle); if (dirEntry) return dirEntry->d_name; else return NULL; } int ftpCloseDir(void* dirHandle) { return closedir(dirHandle); } int ftpStat(const char* path, ftpPathInfo_S *info) { struct stat fileInfo = { 0 }; struct tm *pTime; struct passwd *pw; struct group *gr; if (!stat(path, &fileInfo)) { if (S_ISREG(fileInfo.st_mode)) info->type = TYPE_FILE; else if (S_ISDIR(fileInfo.st_mode)) info->type = TYPE_DIR; else info->type = TYPE_LINK; pTime = localtime(&fileInfo.st_mtime); if (pTime) { info->mTime.year = (uint16_t) pTime->tm_year + 1900; info->mTime.month = (uint8_t) pTime->tm_mon + 1; info->mTime.day = (uint8_t) pTime->tm_mday; info->mTime.hour = (uint8_t) pTime->tm_hour; info->mTime.minute = (uint8_t) pTime->tm_min; info->mTime.second = (uint8_t) pTime->tm_sec; } pTime = localtime(&fileInfo.st_ctime); if (pTime) { info->cTime.year = (uint16_t) pTime->tm_year + 1900; info->cTime.month = (uint8_t) pTime->tm_mon + 1; info->cTime.day = (uint8_t) pTime->tm_mday; info->cTime.hour = (uint8_t) pTime->tm_hour; info->cTime.minute = (uint8_t) pTime->tm_min; info->cTime.second = (uint8_t) pTime->tm_sec; } pTime = localtime(&fileInfo.st_atime); if (pTime) { info->aTime.year = (uint16_t) pTime->tm_year + 1900; info->aTime.month = (uint8_t) pTime->tm_mon + 1; info->aTime.day = (uint8_t) pTime->tm_mday; info->aTime.hour = (uint8_t) pTime->tm_hour; info->aTime.minute = (uint8_t) pTime->tm_min; info->aTime.second = (uint8_t) pTime->tm_sec; } info->size = (uint32_t) fileInfo.st_size; info->links = (int) fileInfo.st_nlink; pw = getpwuid(fileInfo.st_uid); if (pw) strncpy(info->user, pw->pw_name, sizeof(info->user) - 1); else snprintf(info->user, 20, "%04d", fileInfo.st_uid); gr = getgrgid(fileInfo.st_gid); if (gr) strncpy(info->group, gr->gr_name, sizeof(info->group) - 1); else snprintf(info->group, 20, "%04d", fileInfo.st_gid); return 0; } return -1; } int ftpMakeDir(const char* path) { return mkdir(path, S_IRUSR | S_IWUSR); } int ftpRemoveDir(const char* path) { return rmdir(path); } int ftpCloseSocket(socket_t *s) { if (VERBOSE_MODE_ENABLED) printf("\nClosing socket: %d\n", *s); int ret = 0; if (*s > 0) { shutdown(*s, 2); ret = close(*s); *s = 0; } return ret; } int ftpSend(socket_t s, const void *data, int len) { int currLen = 0; do { #if defined(__APPLE__) && defined(SO_NOSIGPIPE) currLen = send(s, data, len, SO_NOSIGPIPE); #elif defined(MSG_NOSIGNAL) currLen = send(s, data, len, MSG_NOSIGNAL); #else currLen = send(s, data, len, 0); #endif if (currLen >= 0) { if (currLen == 0) { int errorNumber = getLastSocketError(); const char *errText = getLastSocketErrorText(&errorNumber); if (VERBOSE_MODE_ENABLED) printf("\nERROR #1 ftpExecute ERROR ON SEND for socket = %d, data len = %d, error = %d [%s]\n", s, len, errorNumber, errText); } len -= currLen; data = (uint8_t*) data + currLen; } else { int errorNumber = getLastSocketError(); const char *errText = getLastSocketErrorText(&errorNumber); if (VERBOSE_MODE_ENABLED) printf("\nERROR #2 ftpExecute ERROR ON SEND for socket = %d, data len = %d, currLen = %d, error = %d [%s]\n", s, len, currLen, errorNumber, errText); return -1; } } while (len > 0); return 0; } int ftpReceive(socket_t s, void *data, int len) { return recv(s, data, len, 0); } socket_t ftpEstablishDataConnection(int passive, ip_t *ip, port_t *port, int sessionId) { socket_t dataSocket; struct sockaddr_in clientAddr; struct sockaddr_in myAddr; int on = 1; unsigned len; dataSocket = socket(AF_INET, SOCK_STREAM, 0); if (dataSocket < 0) return -1; if (!passive) { if (setsockopt(dataSocket, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) { if (VERBOSE_MODE_ENABLED) printf("In ftpEstablishDataConnection #1 about to Close socket = %d, for sessionId = %d\n", dataSocket, sessionId); ftpUntrackSocket(dataSocket); ftpCloseSocket(&dataSocket); return -1; } myAddr.sin_family = AF_INET; myAddr.sin_addr.s_addr = INADDR_ANY; myAddr.sin_port = htons(20); myAddr.sin_zero[0] = 0; if (bind(dataSocket, (struct sockaddr *)&myAddr, sizeof(myAddr))) { if (VERBOSE_MODE_ENABLED) printf("In ftpEstablishDataConnection #2 about to Close socket = %d, for sessionId = %d\n", dataSocket, sessionId); ftpUntrackSocket(dataSocket); ftpCloseSocket(&dataSocket); return -1; } clientAddr.sin_family = AF_INET; clientAddr.sin_addr.s_addr = htonl(*ip); clientAddr.sin_port = htons(*port); clientAddr.sin_zero[0] = 0; if (connect(dataSocket, (struct sockaddr *)&clientAddr, sizeof(clientAddr))) { if (VERBOSE_MODE_ENABLED) printf("In ftpEstablishDataConnection #3 about to Close socket = %d, for sessionId = %d\n", dataSocket, sessionId); ftpUntrackSocket(dataSocket); ftpCloseSocket(&dataSocket); return -1; } } else { int passivePort = ftpGetPassivePort() + sessionId; if (VERBOSE_MODE_ENABLED) printf("\nPASSIVE CONNECTION for sessionId = %d using port #: %d for socket = %d\n", sessionId, passivePort, dataSocket); myAddr.sin_family = AF_INET; myAddr.sin_addr.s_addr = INADDR_ANY; myAddr.sin_port = htons(passivePort); myAddr.sin_zero[0] = 0; //myAddr.sin_port = htons(ftpGetPassivePort() + sessionId); if (setsockopt(dataSocket, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) { if (VERBOSE_MODE_ENABLED) printf("PASSIVE CONNECTION In ftpEstablishDataConnection setsockopt failed about to Close socket = %d, for sessionId = %d\n", dataSocket, sessionId); ftpUntrackSocket(dataSocket); ftpCloseSocket(&dataSocket); return -1; } if (bind(dataSocket, (struct sockaddr *)&myAddr, sizeof(myAddr))) { if (VERBOSE_MODE_ENABLED) printf("\nERROR In ftpEstablishDataConnection passive bind failed for sessionId = %d using port #: %d about to close socket = %d\n", sessionId, passivePort, dataSocket); ftpUntrackSocket(dataSocket); ftpCloseSocket(&dataSocket); return -1; } if (VERBOSE_MODE_ENABLED) printf("\nPASSIVE CONNECTION for sessionId = %d using port #: %d bound ok for socket = %d\n", sessionId, passivePort, dataSocket); len = sizeof(myAddr); if (getsockname(dataSocket, (struct sockaddr *)&myAddr, &len)) // Port des Server-Sockets ermitteln { if (VERBOSE_MODE_ENABLED) printf("\nERROR In ftpEstablishDataConnection passive getsockname failed for sessionId = %d using port #: %d about to close socket = %d\n", sessionId, passivePort, dataSocket); ftpUntrackSocket(dataSocket); ftpCloseSocket(&dataSocket); return -1; } *port = ntohs(myAddr.sin_port); *ip = ownIp; if (VERBOSE_MODE_ENABLED) printf("\nPASSIVE CONNECTION for sessionId = %d using port #: %d about to listen on port: %d using listener socket: %d\n", sessionId, passivePort, *port, dataSocket); if (listen(dataSocket, 100)) { if (VERBOSE_MODE_ENABLED) printf("\nERROR In ftpEstablishDataConnection passive listen failed for sessionId = %d using port #: %d about to close socket = %d\n", sessionId, passivePort, dataSocket); ftpUntrackSocket(dataSocket); ftpCloseSocket(&dataSocket); return -1; } //*port = ftpGetPassivePort(); //*ip = ownIp; //dataSocket = ftpGetServerPassivePortListenSocket(); } return dataSocket; } socket_t ftpAcceptDataConnection(socket_t listner) { struct sockaddr_in clientinfo; unsigned len; socket_t dataSocket; len = sizeof(clientinfo); dataSocket = accept(listner, (struct sockaddr *)&clientinfo, &len); if (dataSocket < 0) { if (VERBOSE_MODE_ENABLED) printf("ERROR In ftpAcceptDataConnection accept failed, dataSocket = %d, listner = %d\n", dataSocket, listner); dataSocket = -1; } else { if (VERBOSE_MODE_ENABLED) printf("In ftpAcceptDataConnection accept new socket = %d, listner =%d\n", dataSocket, listner); } if (VERBOSE_MODE_ENABLED) printf("\nIn ftpAcceptDataConnection about to close listener socket = %d\n", listner); //ftpUntrackSocket(listner); //ftpCloseSocket(&listner); // Server-Socket wird nicht mehr gebrauch deshalb schließen ip_t remoteIP = ntohl(clientinfo.sin_addr.s_addr); if (ftpIsValidClient && ftpIsValidClient(remoteIP) == 0) { if (VERBOSE_MODE_ENABLED) printf("ERROR: Connection with %s is NOT a valid trusted client, dropping connection closing socket = %d.\n", inet_ntoa(clientinfo.sin_addr), dataSocket); ftpUntrackSocket(dataSocket); ftpCloseSocket(&dataSocket); dataSocket = -1; } return dataSocket; } socket_t ftpCreateServerSocket(int portNumber) { int theServer; struct sockaddr_in serverinfo; unsigned len; int val = 1; int opt_result = 0; theServer = socket(AF_INET, SOCK_STREAM, 0); if (theServer < 0) return -1; serverinfo.sin_family = AF_INET; serverinfo.sin_addr.s_addr = INADDR_ANY; serverinfo.sin_port = htons(portNumber); serverinfo.sin_zero[0] = 0; len = sizeof(serverinfo); opt_result = setsockopt(theServer, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); if (bind(theServer, (struct sockaddr *)&serverinfo, len)) { if (VERBOSE_MODE_ENABLED) printf("\nERROR In ftpCreateServerSocket bind FAILED about to close listener socket = %d opt_result = %d\n", theServer, opt_result); ftpUntrackSocket(theServer); ftpCloseSocket(&theServer); return -2; } if (listen(theServer, 100)) { if (VERBOSE_MODE_ENABLED) printf("\nERROR In ftpCreateServerSocket listen FAILED about to close listener socket = %d\n", theServer); ftpUntrackSocket(theServer); ftpCloseSocket(&theServer); return -3; } return (socket_t) theServer; } socket_t ftpAcceptServerConnection(socket_t server, ip_t *remoteIP, port_t *remotePort) { struct sockaddr_in sockinfo; socket_t clientSocket; unsigned len; len = sizeof(sockinfo); clientSocket = accept(server, (struct sockaddr *)&sockinfo, &len); if (clientSocket < 0) { if (VERBOSE_MODE_ENABLED) printf("ftpAcceptServerConnection accept failed for socket = %d\n", clientSocket); } else { if (VERBOSE_MODE_ENABLED) printf("ftpAcceptServerConnection accept new socket = %d\n", clientSocket); } *remoteIP = ntohl(sockinfo.sin_addr.s_addr); *remotePort = ntohs(sockinfo.sin_port); if (!ownIp) // kennen wir schon die eigene IP? { len = sizeof(sockinfo); if (clientSocket >= 0 && getsockname(clientSocket, (struct sockaddr *)&sockinfo, &len)) { if (VERBOSE_MODE_ENABLED) printf("getsockname error\n"); } ownIp = ntohl(sockinfo.sin_addr.s_addr); // eigene IP-Adresse abspeichern (wird dir PASV benätigt) } if (VERBOSE_MODE_ENABLED) printf("Connection with %s on Port %d accepted.\n", inet_ntoa(sockinfo.sin_addr), *remotePort); if (ftpIsValidClient && ftpIsValidClient(*remoteIP) == 0) { if (VERBOSE_MODE_ENABLED) printf("Connection with %s on Port %d is NOT a valid trusted client, dropping connection closing socket = %d.\n", inet_ntoa(sockinfo.sin_addr), *remotePort, clientSocket); ftpUntrackSocket(clientSocket); ftpCloseSocket(&clientSocket); clientSocket = -1; } return clientSocket; } int ftpTrackSocket(socket_t s) { if (s > maxSockNr) maxSockNr = s; FD_SET(s, &watchedSockets); return 0; } int ftpUntrackSocket(socket_t s) { if (s > 0) { FD_CLR(s, &watchedSockets); } return 0; } int ftpTestSocket(socket_t s) { return FD_ISSET(s, &signaledSockets); } int ftpSelect(int poll) { signaledSockets = watchedSockets; if (poll) { struct timeval t = { 0 }; //t.tv_usec = 100; return select(maxSockNr + 1, &signaledSockets, NULL, NULL, &t); } else { struct timeval t = { 0 }; t.tv_usec = 1000; return select(maxSockNr + 1, &signaledSockets, NULL, NULL, &t); } } int getLastSocketError() { return errno; } const char * getLastSocketErrorText(int *errNumber) { int errId = (errNumber != NULL ? *errNumber : getLastSocketError()); return strerror(errId); } #endif