/* 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 . */ #ifdef WIN32 #include #include #include #include #include #include #include "ftpTypes.h" #include "ftpConfig.h" #include "ftp.h" #pragma comment(lib, "ws2_32") #pragma comment(lib, "MSWSOCK") #define MSG_NOSIGNAL 0 ip_t ownIp; LOCAL fd_set watchedSockets; LOCAL fd_set signaledSockets; LOCAL int maxSockNr; int wsaInitCount = 0; void ftpArchInit() { WSADATA wsaData; ownIp = 0; maxSockNr = 0; FD_ZERO(&watchedSockets); if (VERBOSE_MODE_ENABLED) printf("Feathery calling WSAStartup...\n"); WSAStartup(MAKEWORD(2, 0), &wsaData); wsaInitCount++; } void ftpArchCleanup(void) { if (VERBOSE_MODE_ENABLED) printf("Feathery calling WSACleanup...[%d]\n", wsaInitCount); if (wsaInitCount > 0) { WSACleanup(); // WINSOCK freigeben wsaInitCount--; } } 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; } typedef struct { HANDLE findHandle; char path[MAX_PATH_LEN]; }readDir_S; void* ftpOpenDir(const char* path) { readDir_S* p = malloc(sizeof(readDir_S)); if (p) { p->findHandle = INVALID_HANDLE_VALUE; strncpy(p->path, path, strlen(path)); } return p; } const char* ftpReadDir(void* dirHandle) { WIN32_FIND_DATA findData; readDir_S* p = (readDir_S *) dirHandle; if (dirHandle == NULL) return NULL; if (p->findHandle == INVALID_HANDLE_VALUE) { if (strcmp(p->path, "/")) strcat(p->path, "/*"); else strcat(p->path, "*"); p->findHandle = FindFirstFileA(p->path, &findData); if (p->findHandle != INVALID_HANDLE_VALUE) { strcpy(p->path, findData.cFileName); return p->path; } return NULL; } else { if (FindNextFileA(p->findHandle, &findData)) { strcpy(p->path, findData.cFileName); return p->path; } return NULL; } } int ftpCloseDir(void* dirHandle) { readDir_S* p = dirHandle; if (dirHandle) { FindClose(p->findHandle); free(p); } return 0; } int ftpStat(const char* path, ftpPathInfo_S *info) { struct stat fileInfo = { 0 }; struct tm *pTime; if (!stat(path, &fileInfo)) { if (fileInfo.st_mode & _S_IFREG) info->type = TYPE_FILE; else if (fileInfo.st_mode & _S_IFDIR) 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; strncpy(info->user, "ftp", sizeof(info->user)); strncpy(info->group, "ftp", sizeof(info->group)); return 0; } return -1; } int ftpMakeDir(const char* path) { return !CreateDirectoryA(path, NULL); } int ftpRemoveDir(const char* path) { return !RemoveDirectoryA(path); } int ftpCloseSocket(socket_t *s) { int ret = 0; if (VERBOSE_MODE_ENABLED) printf("\nClosing socket: %d\n", *s); if (*s > 0) { shutdown((SOCKET) *s, 2); ret = closesocket((SOCKET) *s); *s = 0; } return ret; } int ftpSend(socket_t s, const void *data, int len) { int currLen = 0; do { #ifdef __APPLE__ currLen = send((SOCKET) s, data, len, SO_NOSIGPIPE); #else currLen = send((SOCKET) s, data, len, MSG_NOSIGNAL); #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 dataSocket; struct sockaddr_in clientAddr; struct sockaddr_in myAddr; BOOL on = 1; unsigned len; dataSocket = socket(AF_INET, SOCK_STREAM, 0); if (dataSocket == INVALID_SOCKET) return -1; if (!passive) { if (setsockopt(dataSocket, SOL_SOCKET, SO_REUSEADDR, (const char*) &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, (const char *) &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 (socket_t) dataSocket; } socket_t ftpAcceptDataConnection(socket_t listner) { struct sockaddr_in clientinfo; unsigned len; SOCKET dataSocket; ip_t remoteIP; len = sizeof(clientinfo); dataSocket = accept(listner, (struct sockaddr *)&clientinfo, &len); if (dataSocket == INVALID_SOCKET) { 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 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 = INVALID_SOCKET; } return (socket_t) dataSocket; } socket_t ftpCreateServerSocket(int portNumber) { SOCKET theServer; struct sockaddr_in serverinfo; unsigned len; int val = 1; int opt_result = 0; theServer = socket(AF_INET, SOCK_STREAM, 0); if (theServer == INVALID_SOCKET) 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, (char *) &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 opt_result = %d\n", theServer, opt_result); 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; int 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 (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 WSAGetLastError(); } const char * getLastSocketErrorText(int *errNumber) { int errId = (errNumber != NULL ? *errNumber : getLastSocketError()); char* str = malloc(256); str[0] = '\0'; FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errNumber, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), str, 256, NULL); return str; } #endif