Files
2019-02-01 04:31:10 +01:00

942 lines
29 KiB
C

/* FEATHERY FTP-Server
* Copyright (C) 2005-2010 Andreas Martin (andreas.martin@linuxmail.org)
* <https://sourceforge.net/projects/feathery>
*
* 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 <http://www.gnu.org/licenses/>.
*/
/*
DECLARATIVE SPECIFICATIONS
5.1. MINIMUM IMPLEMENTATION
In order to make FTP workable without needless error messages, the
following minimum implementation is required for all servers:
TYPE - ASCII Non-print
MODE - Stream
STRUCTURE - File, Record
COMMANDS - USER, QUIT, PORT,
TYPE, MODE, STRU,
for the default values
RETR, STOR,
NOOP.
The default values for transfer parameters are:
TYPE - ASCII Non-print
MODE - Stream
STRU - File
All hosts must accept the above as the standard defaults.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include "ftpTypes.h"
#include "ftpConfig.h"
#include "ftp.h"
#include "ftpIfc.h"
#include "ftpMessages.h"
LOCAL uint8_t scratchBuf[LEN_SCRATCHBUF];
int ftpSendMsg(msgmode_E mode, int sessionId, int ret, const char* msg) {
int sentlen = 0;
int len = (int) strlen(msg);
char buf[6];
if (mode == MSG_QUOTE) {
snprintf((char*) buf, 6, "%03d \"", ret);
sentlen += ftpSend(ftpGetSession(sessionId)->ctrlSocket, buf, 5);
sentlen += ftpSend(ftpGetSession(sessionId)->ctrlSocket, msg, len);
sentlen += ftpSend(ftpGetSession(sessionId)->ctrlSocket, "\"\r\n", 3);
} else {
snprintf((char*) buf, 6, "%03d ", ret);
sentlen += ftpSend(ftpGetSession(sessionId)->ctrlSocket, buf, 4);
sentlen += ftpSend(ftpGetSession(sessionId)->ctrlSocket, msg, len);
sentlen += ftpSend(ftpGetSession(sessionId)->ctrlSocket, "\r\n", 2);
}
if (VERBOSE_MODE_ENABLED) printf("%02d <-- %s%s\n", sessionId, buf, msg);
return sentlen;
}
int ftpExecTransmission(int sessionId) {
int finished = FALSE;
size_t len;
ftpSession_S *pSession = ftpGetSession(sessionId);
transmission_S *pTrans = &pSession->activeTrans;
int rxLen;
pSession->timeLastCmd = ftpGetUnixTime();
switch (pTrans->op) {
case OP_RETR:
len = ftpReadFile(scratchBuf, 1, LEN_SCRATCHBUF, pTrans->fsHandle);
if (len > 0) {
pTrans->fileSize -= (uint32_t) len;
if (ftpSend(pTrans->dataSocket, scratchBuf, (int) len)) {
ftpSendMsg(MSG_NORMAL, sessionId, 426, ftpMsg000);
finished = TRUE;
}
} else {
if (VERBOSE_MODE_ENABLED) printf("ERROR in ftpExecTransmission ftpReadFile returned = %d for sessionId = %d\n", (int) len, sessionId);
ftpSendMsg(MSG_NORMAL, sessionId, 451, ftpMsg001);
finished = TRUE;
}
if (pTrans->fileSize == 0) {
ftpSendMsg(MSG_NORMAL, sessionId, 226, ftpMsg002);
finished = TRUE;
}
break;
case OP_STOR:
rxLen = 0;
do {
len = ftpReceive(pTrans->dataSocket, &scratchBuf[rxLen], LEN_SCRATCHBUF - rxLen);
if (len < 1) {
int errorNumber = getLastSocketError();
const char *errText = getLastSocketErrorText(&errorNumber);
if (VERBOSE_MODE_ENABLED) printf("ftpExecTransmission ERROR ON RECEIVE for socket = %d, data len = %d, error = %d [%s]\n", pTrans->dataSocket, (LEN_SCRATCHBUF - rxLen), errorNumber, errText);
break;
}
rxLen += (int) len;
} while (rxLen < LEN_SCRATCHBUF);
if (rxLen > 0) {
size_t res = ftpWriteFile(scratchBuf, 1, rxLen, pTrans->fsHandle);
if (res != (size_t) rxLen) {
if (VERBOSE_MODE_ENABLED) printf("ERROR in ftpExecTransmission ftpWriteFile returned = %d for sessionId = %d\n", (int) res, sessionId);
ftpSendMsg(MSG_NORMAL, sessionId, 451, ftpMsg001);
finished = TRUE;
}
}
if (len < 1) {
ftpSendMsg(MSG_NORMAL, sessionId, 226, ftpMsg003);
finished = TRUE;
}
break;
case OP_LIST:
break;
default:
return -1;
}
if (finished) {
ftpCloseTransmission(sessionId);
return 1;
} else {
return 0;
}
}
LOCAL int ftpCmdUser(int sessionId, const char* args, int len) {
ftpSendMsg(MSG_NORMAL, sessionId, 331, ftpMsg004);
ftpGetSession(sessionId)->userId = ftpFindAccount(args);
return 0;
}
LOCAL int ftpCmdPass(int sessionId, const char* args, int len) {
if (!ftpCheckPassword(ftpGetSession(sessionId)->userId, args)) {
ftpAuthSession(sessionId);
ftpSendMsg(MSG_NORMAL, sessionId, 230, ftpMsg005);
} else {
ftpGetSession(sessionId)->userId = 0;
ftpSendMsg(MSG_NORMAL, sessionId, 530, ftpMsg006);
}
return 0;
}
LOCAL int ftpCmdSyst(int sessionId, const char* args, int len) {
ftpSendMsg(MSG_NORMAL, sessionId, 215, "UNIX Type: L8");
return 0;
}
LOCAL int ftpCmdPort(int sessionId, const char* args, int len) {
//char clientIp[16]="";
uint16_t clientPort = 0;
int commaCnt = 0;
int n;
char* p;
for (n = 0; args[n] != '\0'; n++) {
if (commaCnt <= 3) // Ip-Adresse
{
if (args[n] == ',') {
commaCnt++;
// if(commaCnt < 4)
// clientIp[n] = '.';
// else
// clientIp[n] = '\0';
}
// else
// clientIp[n] = args[n];
} else // Port-Nummer
{
p = (char*) &args[n];
clientPort = (uint16_t) (strtoul(p, &p, 0) << 8);
p++;
clientPort |= strtoul(p, &p, 0);
break;
}
}
if (ftpGetSession(sessionId)->passiveDataSocket >= 0) {
if (VERBOSE_MODE_ENABLED) printf("In ftpCmdPort about to Close socket = %d for sessionId = %d\n", ftpGetSession(sessionId)->passiveDataSocket, sessionId);
ftpUntrackSocket(ftpGetSession(sessionId)->passiveDataSocket);
ftpCloseSocket(&ftpGetSession(sessionId)->passiveDataSocket);
ftpGetSession(sessionId)->passiveDataSocket = -1;
}
//ftpGetSession(sessionId)->passiveDataSocket = -1;
ftpGetSession(sessionId)->remoteDataPort = clientPort;
ftpGetSession(sessionId)->passive = FALSE;
ftpSendMsg(MSG_NORMAL, sessionId, 200, ftpMsg007);
return 0;
}
LOCAL int ftpCmdNoop(int sessionId, const char* args, int len) {
ftpSendMsg(MSG_NORMAL, sessionId, 200, ftpMsg008);
return 0;
}
LOCAL int ftpCmdQuit(int sessionId, const char* args, int len) {
ftpSendMsg(MSG_NORMAL, sessionId, 221, ftpMsg009);
return -1;
}
LOCAL int ftpCmdAbor(int sessionId, const char* args, int len) {
ftpCloseTransmission(sessionId);
ftpSendMsg(MSG_NORMAL, sessionId, 226, ftpMsg040);
return 0;
}
#define ALL 0x80
#define LIST 1
#define NLST 2
#define MLST 4
#define MLSD 8
LOCAL int sendListing(socket_t dataSocket, int sessionId, const char* path, int format) {
void *dir;
const char monName[12][4] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
dir = ftpOpenDir(path);
if (dir) {
const char* dirEntry = NULL;
int len = 0;
int err = 0;
ftpTime_S currTime = { 0 };
ftpPathInfo_S fileInfo;
int haveAnySuccessfulFiles = 0;
ftpGetLocalTime(&currTime);
ftpSendMsg(MSG_NORMAL, sessionId, 150, ftpMsg010);
if (VERBOSE_MODE_ENABLED) printf("In sendListing about to read dir contents [%s] for sessionId = %d, dataSocket = %d\n", path, sessionId, dataSocket);
haveAnySuccessfulFiles = 0;
while ((dirEntry = ftpReadDir(dir)) != NULL) {
const char * realPath = ftpGetRealPath(sessionId, dirEntry, FALSE);
int statResult = ftpStat(realPath, &fileInfo);
if (VERBOSE_MODE_ENABLED) printf("ftpGetRealPath() returned [%s] stat() = %d\n", realPath, statResult);
if (statResult == 0) {
if ((format & ALL) == 0) {
if (dirEntry[0] == '.')
continue;
}
if (format & LIST) {
switch (fileInfo.type) {
default:
case TYPE_FILE:
scratchBuf[0] = '-';
break;
case TYPE_DIR:
scratchBuf[0] = 'd';
break;
case TYPE_LINK:
scratchBuf[0] = 'l';
break;
}
if (currTime.year == fileInfo.mTime.year) {
len = snprintf((char*) &scratchBuf[1], LEN_SCRATCHBUF - 1, "rwxrwxrwx %4u %-8s %-8s %8u %s %02d %02d:%02d %s\r\n",
fileInfo.links,
fileInfo.user,
fileInfo.group,
fileInfo.size,
monName[fileInfo.mTime.month - 1],
fileInfo.mTime.day,
fileInfo.mTime.hour,
fileInfo.mTime.minute,
dirEntry);
} else {
len = snprintf((char*) &scratchBuf[1], LEN_SCRATCHBUF - 1, "rwxrwxrwx %4u %-8s %-8s %8u %s %02d %5d %s\r\n",
fileInfo.links,
fileInfo.user,
fileInfo.group,
fileInfo.size,
monName[fileInfo.mTime.month - 1],
fileInfo.mTime.day,
fileInfo.mTime.year,
dirEntry);
}
ftpSend(dataSocket, scratchBuf, len + 1);
haveAnySuccessfulFiles = 1;
} else if (format & NLST) {
len = snprintf((char*) scratchBuf, LEN_SCRATCHBUF, "%s\r\n", dirEntry);
ftpSend(dataSocket, scratchBuf, len);
haveAnySuccessfulFiles = 1;
} else if (format & MLSD) {
if (!strcmp("..", dirEntry))
len = snprintf((char*) scratchBuf, LEN_SCRATCHBUF, "Type=pdir");
else {
switch (fileInfo.type) {
default:
case TYPE_FILE:
len = snprintf((char*) scratchBuf, LEN_SCRATCHBUF, "Type=file");
break;
case TYPE_DIR:
len = snprintf((char*) scratchBuf, LEN_SCRATCHBUF, "Type=dir");
break;
case TYPE_LINK:
len = snprintf((char*) scratchBuf, LEN_SCRATCHBUF, "Type=OS.unix=slink");
break;
}
}
ftpSend(dataSocket, scratchBuf, len);
len = snprintf((char*) scratchBuf, LEN_SCRATCHBUF, ";Size=%u;Modify=%04d%02d%02d%02d%02d%02d;Perm=r; %s\r\n",
fileInfo.size,
fileInfo.mTime.year,
fileInfo.mTime.month,
fileInfo.mTime.day,
fileInfo.mTime.hour,
fileInfo.mTime.minute,
fileInfo.mTime.second,
dirEntry);
ftpSend(dataSocket, scratchBuf, len);
haveAnySuccessfulFiles = 1;
}
} else {
err = 1;
//break;
}
}
ftpCloseDir(dir);
if (err && haveAnySuccessfulFiles == 0) {
if (VERBOSE_MODE_ENABLED) printf("ERROR in sendListing err = %d, path = [%s] for sessionId = %d, dataSocket = %d\n", err, path, sessionId, dataSocket);
ftpSendMsg(MSG_NORMAL, sessionId, 451, ftpMsg039);
} else {
ftpSendMsg(MSG_NORMAL, sessionId, 226, ftpMsg013);
}
} else {
if (VERBOSE_MODE_ENABLED) printf("ERROR opendir [%s] returned errno: %#x for sessionId = %d, dataSocket = %d\n", path, errno, sessionId, dataSocket);
ftpSendMsg(MSG_NORMAL, sessionId, 451, ftpMsg038);
}
return 0;
}
LOCAL int ftpCmdList(int sessionId, const char* args, int len) {
const char* realPath;
socket_t s;
//#### Funktioniert nicht wenn Pfad übergeben wird
/* if(args[0] != '\0')
{
if(args[0] != '-')
realPath = ftpGetRealPath(sessionId, args);
}
else*/
realPath = ftpGetRealPath(sessionId, ftpGetSession(sessionId)->workingDir, TRUE);
if (ftpGetSession(sessionId)->passive == FALSE) {
s = ftpEstablishDataConnection(FALSE, &ftpGetSession(sessionId)->remoteIp, &ftpGetSession(sessionId)->remoteDataPort, sessionId);
if (s < 0) {
ftpSendMsg(MSG_NORMAL, sessionId, 425, ftpMsg011);
return 1;
}
} else {
s = ftpAcceptDataConnection(ftpGetSession(sessionId)->passiveDataSocket);
if (s < 0) {
ftpSendMsg(MSG_NORMAL, sessionId, 425, ftpMsg012);
return 1;
}
}
sendListing(s, sessionId, realPath, LIST);
if (VERBOSE_MODE_ENABLED) printf("In ftpCmdList about to Close socket = %d for sessionId = %d\n", s, sessionId);
ftpUntrackSocket(s);
ftpCloseSocket(&s);
return 0;
}
LOCAL int ftpCmdNlst(int sessionId, const char* args, int len) {
//#### -List kann auch Argumente haben
const char* realPath;
socket_t s;
/* if(pfad übergeben)
realPath = ftpGetRealPath(sessionId, args);
else*/
realPath = ftpGetRealPath(sessionId, ftpGetSession(sessionId)->workingDir, TRUE);
if (ftpGetSession(sessionId)->passive == FALSE) {
s = ftpEstablishDataConnection(FALSE, &ftpGetSession(sessionId)->remoteIp, &ftpGetSession(sessionId)->remoteDataPort, sessionId);
if (s < 0) {
ftpSendMsg(MSG_NORMAL, sessionId, 425, ftpMsg011);
return 1;
}
} else {
s = ftpAcceptDataConnection(ftpGetSession(sessionId)->passiveDataSocket);
if (s < 0) {
ftpSendMsg(MSG_NORMAL, sessionId, 425, ftpMsg012);
return 1;
}
}
sendListing(s, sessionId, realPath, NLST);
if (VERBOSE_MODE_ENABLED) printf("In ftpCmdNlst about to Close socket = %d for sessionId = %d\n", s, sessionId);
ftpUntrackSocket(s);
ftpCloseSocket(&s);
return 0;
}
LOCAL int ftpCmdRetr(int sessionId, const char* args, int len) {
ftpPathInfo_S fileInfo;
const char* realPath = ftpGetRealPath(sessionId, args, TRUE);
socket_t s;
void *fp;
int statResult = 0;
if (VERBOSE_MODE_ENABLED) printf("In ftpCmdRetr args [%s] realPath [%s]\n", args, realPath);
statResult = ftpStat(realPath, &fileInfo);
if (VERBOSE_MODE_ENABLED) printf("stat() = %d fileInfo.type = %d\n", statResult, fileInfo.type);
if (statResult || (fileInfo.type != TYPE_FILE)) // file accessible?
{
if (VERBOSE_MODE_ENABLED) printf("ERROR In ftpCmdRetr [file not available] args [%s] realPath [%s]\n", args, realPath);
ftpSendMsg(MSG_NORMAL, sessionId, 550, ftpMsg032);
return 2;
}
if (ftpIsClientAllowedToGetFile != NULL) {
if (ftpIsClientAllowedToGetFile(ftpGetSession(sessionId)->remoteIp, ftpFindAccountById(ftpGetSession(sessionId)->userId), realPath) != 1) {
if (VERBOSE_MODE_ENABLED) printf("ERROR In ftpCmdRetr FILE DISALLOWED By server [file not available] args [%s] realPath [%s]\n", args, realPath);
ftpSendMsg(MSG_NORMAL, sessionId, 550, ftpMsg032);
return 2;
}
}
if (ftpGetSession(sessionId)->passive == FALSE) {
s = ftpEstablishDataConnection(FALSE, &ftpGetSession(sessionId)->remoteIp, &ftpGetSession(sessionId)->remoteDataPort, sessionId);
if (s < 0) {
ftpSendMsg(MSG_NORMAL, sessionId, 425, ftpMsg011);
return 1;
}
} else {
if (VERBOSE_MODE_ENABLED) printf("In ftpCmdRetr about accept passive data connection, args [%s] realPath [%s]\n", args, realPath);
s = ftpAcceptDataConnection(ftpGetSession(sessionId)->passiveDataSocket);
if (s < 0) {
if (VERBOSE_MODE_ENABLED) printf("ERROR In ftpCmdRetr failed to accept data connection, args [%s] realPath [%s]\n", args, realPath);
ftpSendMsg(MSG_NORMAL, sessionId, 425, ftpMsg012);
return 1;
}
}
ftpSendMsg(MSG_NORMAL, sessionId, 150, ftpMsg014);
fp = ftpOpenFile(realPath, "rb");
if (fp) {
if (VERBOSE_MODE_ENABLED) printf("In ftpCmdRetr opened realPath [%s] [%p] for sessionId = %d for socket = %d\n", realPath, fp, sessionId, s);
ftpOpenTransmission(sessionId, OP_RETR, fp, s, fileInfo.size);
ftpExecTransmission(sessionId);
} else {
if (VERBOSE_MODE_ENABLED) printf("ERROR in ftpCmdRetr could not open realPath [%s] for sessionId = %d for socket = %d\n", realPath, sessionId, s);
ftpSendMsg(MSG_NORMAL, sessionId, 451, ftpMsg015);
if (VERBOSE_MODE_ENABLED) printf("In ftpCmdRetr about to Close socket = %d for sessionId = %d\n", s, sessionId);
ftpUntrackSocket(s);
ftpCloseSocket(&s);
}
return 0;
}
LOCAL int ftpCmdStor(int sessionId, const char* args, int len) {
socket_t s;
void* fp;
const char* realPath = ftpGetRealPath(sessionId, args, TRUE);
if (ftpGetSession(sessionId)->passive == FALSE) {
s = ftpEstablishDataConnection(FALSE, &ftpGetSession(sessionId)->remoteIp, &ftpGetSession(sessionId)->remoteDataPort, sessionId);
if (s < 0) {
ftpSendMsg(MSG_NORMAL, sessionId, 425, ftpMsg011);
return 1;
}
} else {
s = ftpAcceptDataConnection(ftpGetSession(sessionId)->passiveDataSocket);
if (s < 0) {
ftpSendMsg(MSG_NORMAL, sessionId, 425, ftpMsg012);
return 1;
}
}
ftpSendMsg(MSG_NORMAL, sessionId, 150, ftpMsg016);
fp = ftpOpenFile(realPath, "wb");
if (fp) {
ftpOpenTransmission(sessionId, OP_STOR, fp, s, 0);
ftpExecTransmission(sessionId);
} else {
if (VERBOSE_MODE_ENABLED) printf("ERROR in ftpCmdStor could not open realPath [%s]\n", realPath);
ftpSendMsg(MSG_NORMAL, sessionId, 451, ftpMsg015);
if (VERBOSE_MODE_ENABLED) printf("In ftpCmdStor about to Close socket = %d for sessionId = %d\n", s, sessionId);
ftpUntrackSocket(s);
ftpCloseSocket(&s);
}
return 0;
}
LOCAL int ftpCmdDele(int sessionId, const char* args, int len) {
const char* realPath = ftpGetRealPath(sessionId, args, TRUE);
if (ftpRemoveFile(realPath))
ftpSendMsg(MSG_NORMAL, sessionId, 450, ftpMsg018);
else
ftpSendMsg(MSG_NORMAL, sessionId, 250, ftpMsg019);
return 0;
}
LOCAL int ftpCmdMkd(int sessionId, const char* args, int len) {
const char* realPath = ftpGetRealPath(sessionId, args, TRUE);
if (ftpMakeDir(realPath))
ftpSendMsg(MSG_NORMAL, sessionId, 550, ftpMsg020);
else
ftpSendMsg(MSG_NORMAL, sessionId, 250, ftpMsg021);
return 0;
}
LOCAL int ftpCmdRmd(int sessionId, const char* args, int len) {
const char* realPath = ftpGetRealPath(sessionId, args, TRUE);
if (ftpRemoveDir(realPath))
ftpSendMsg(MSG_NORMAL, sessionId, 550, ftpMsg022);
else
ftpSendMsg(MSG_NORMAL, sessionId, 250, ftpMsg023);
return 0;
}
LOCAL int ftpCmdPwd(int sessionId, const char* args, int len) {
if (ftpGetSession(sessionId)->workingDir[0])
ftpSendMsg(MSG_QUOTE, sessionId, 257, ftpGetSession(sessionId)->workingDir);
else
ftpSendMsg(MSG_QUOTE, sessionId, 257, "/");
return 0;
}
LOCAL int ftpCmdCwd(int sessionId, const char* args, int len) {
if (ftpChangeDir(sessionId, args) == 0)
ftpSendMsg(MSG_NORMAL, sessionId, 250, ftpMsg024);
else
ftpSendMsg(MSG_NORMAL, sessionId, 550, ftpMsg025);
return 0;
}
LOCAL int ftpCmdType(int sessionId, const char* args, int len) {
switch (args[0]) {
case 'I':
case 'i':
ftpSendMsg(MSG_NORMAL, sessionId, 200, ftpMsg026);
ftpGetSession(sessionId)->binary = TRUE;
break;
case 'A':
case 'a':
ftpSendMsg(MSG_NORMAL, sessionId, 200, ftpMsg027);
ftpGetSession(sessionId)->binary = FALSE;
break;
default:
ftpSendMsg(MSG_NORMAL, sessionId, 504, ftpMsg028);
break;
}
return 0;
}
LOCAL int ftpCmdPasv(int sessionId, const char* args, int len) {
uint16_t port;
uint32_t ip;
char str[50];
socket_t s;
uint32_t remoteFTPServerIp;
if (VERBOSE_MODE_ENABLED) printf("In ftpCmdPasv sessionId = %d, ftpGetSession(sessionId)->passiveDataSocket = %d\n", sessionId, ftpGetSession(sessionId)->passiveDataSocket);
if (ftpGetSession(sessionId)->passiveDataSocket <= 0) {
//if(VERBOSE_MODE_ENABLED) printf("In ftpCmdPasv about to Close socket = %d for sessionId = %d\n",ftpGetSession(sessionId)->passiveDataSocket,sessionId);
//ftpUntrackSocket(ftpGetSession(sessionId)->passiveDataSocket);
//ftpCloseSocket(&ftpGetSession(sessionId)->passiveDataSocket);
//}
//ftpGetSession(sessionId)->passiveDataSocket = -1;
s = ftpEstablishDataConnection(TRUE, &ip, &port, sessionId);
if (s < 0) {
ftpSendMsg(MSG_NORMAL, sessionId, 425, ftpMsg012);
return 1;
}
ftpGetSession(sessionId)->passiveDataSocket = s;
ftpGetSession(sessionId)->passiveIp = ip;
ftpGetSession(sessionId)->passivePort = port;
} else {
s = ftpGetSession(sessionId)->passiveDataSocket;
ip = ftpGetSession(sessionId)->passiveIp;
port = ftpGetSession(sessionId)->passivePort;
}
//if(VERBOSE_MODE_ENABLED) printf("In ftpCmdPasv sessionId = %d, client IP = %u, remote IP = %u, port = %d, ftpAddUPNPPortForward = %p, ftpRemoveUPNPPortForward = %p using listener socket = %d\n",
// sessionId, ftpGetSession(sessionId)->remoteIp, ftpFindExternalFTPServerIp(ftpGetSession(sessionId)->remoteIp), port,ftpAddUPNPPortForward,ftpRemoveUPNPPortForward,s);
if (VERBOSE_MODE_ENABLED) printf("In ftpCmdPasv sessionId = %d, client IP = %u, remote IP = %u, port = %d, using listener socket = %d\n",
sessionId, ftpGetSession(sessionId)->remoteIp, ftpFindExternalFTPServerIp(ftpGetSession(sessionId)->remoteIp), port, s);
if (ftpAddUPNPPortForward != NULL && ftpFindExternalFTPServerIp(ftpGetSession(sessionId)->remoteIp) != 0) {
ftpGetSession(sessionId)->remoteFTPServerPassivePort = port;
//if(VERBOSE_MODE_ENABLED) printf("In ftpCmdPasv sessionId = %d, adding UPNP port forward\n", sessionId);
//ftpAddUPNPPortForward(port, port);
remoteFTPServerIp = ftpFindExternalFTPServerIp(ftpGetSession(sessionId)->remoteIp);
snprintf(str, 50, "%s (%d,%d,%d,%u,%d,%d)",
ftpMsg029,
(remoteFTPServerIp >> 24) & 0xFF,
(remoteFTPServerIp >> 16) & 0xFF,
(remoteFTPServerIp >> 8) & 0xFF,
remoteFTPServerIp & 0xFF,
(port >> 8) & 0xFF,
port & 0xFF);
if (VERBOSE_MODE_ENABLED) printf("In ftpCmdPasv sessionId = %d, str [%s]\n", sessionId, str);
} else {
snprintf(str, 50, "%s (%d,%d,%d,%u,%d,%d)",
ftpMsg029,
(ip >> 24) & 0xFF,
(ip >> 16) & 0xFF,
(ip >> 8) & 0xFF,
ip & 0xFF,
(port >> 8) & 0xFF,
port & 0xFF);
}
if (VERBOSE_MODE_ENABLED) printf("In ftpCmdPasv sessionId = %d, ftpGetSession(sessionId)->passiveDataSocket = %d SENDING 227 to client\n", sessionId, ftpGetSession(sessionId)->passiveDataSocket);
ftpSendMsg(MSG_NORMAL, sessionId, 227, str);
ftpGetSession(sessionId)->passive = TRUE;
return 0;
}
LOCAL int ftpCmdCdup(int sessionId, const char* args, int len) {
return ftpCmdCwd(sessionId, "..", 2);
}
LOCAL int ftpCmdStru(int sessionId, const char* args, int len) {
switch (args[0]) {
case 'F':
case 'f':
ftpSendMsg(MSG_NORMAL, sessionId, 200, ftpMsg030);
break;
default:
ftpSendMsg(MSG_NORMAL, sessionId, 504, ftpMsg031);
break;
}
return 0;
}
#if RFC3659
LOCAL int ftpCmdSize(int sessionId, const char* args, int len) {
int ret;
char str[12];
ftpPathInfo_S fileInfo;
const char* realPath = ftpGetRealPath(sessionId, args, TRUE);
ret = ftpStat(realPath, &fileInfo);
if (ret) {
ftpSendMsg(MSG_NORMAL, sessionId, 550, ftpMsg032);
return 2;
}
snprintf(str, 12, "%d", fileInfo.size);
ftpSendMsg(MSG_NORMAL, sessionId, 213, str);
return 0;
}
LOCAL int ftpCmdMdtm(int sessionId, const char* args, int len) {
int ret;
char str[15];
ftpPathInfo_S fileInfo;
const char* realPath = ftpGetRealPath(sessionId, args, TRUE);
ret = ftpStat(realPath, &fileInfo);
if (ret) {
ftpSendMsg(MSG_NORMAL, sessionId, 550, ftpMsg032);
return 2;
}
snprintf(str, 15, "%04d%02d%02d%02d%02d%02d", fileInfo.mTime.year,
fileInfo.mTime.month,
fileInfo.mTime.day,
fileInfo.mTime.hour,
fileInfo.mTime.minute,
fileInfo.mTime.second);
ftpSendMsg(MSG_NORMAL, sessionId, 213, str);
return 0;
}
LOCAL int ftpCmdMlst(int sessionId, const char* args, int len) {
ftpSendMsg(MSG_NORMAL, sessionId, 550, "MLST command not implemented");
return 0;
}
/*LOCAL int ftpCmdMlsd(int sessionId, const char* args, int len)
{
ftpSendMsg(MSG_NORMAL, sessionId, 550, "MLSD command not implemented");
return 0;
}*/
LOCAL int ftpCmdMlsd(int sessionId, const char* args, int len) {
const char* realPath;
socket_t s;
//#### Funktioniert nicht wenn Pfad übergeben wird
/* if(args[0] != '\0')
{
if(args[0] != '-')
realPath = ftpGetRealPath(sessionId, args);
}
else*/
realPath = ftpGetRealPath(sessionId, ftpGetSession(sessionId)->workingDir, TRUE);
if (ftpGetSession(sessionId)->passive == FALSE) {
s = ftpEstablishDataConnection(FALSE, &ftpGetSession(sessionId)->remoteIp, &ftpGetSession(sessionId)->remoteDataPort, sessionId);
if (s < 0) {
ftpSendMsg(MSG_NORMAL, sessionId, 425, ftpMsg011);
return 1;
}
} else {
s = ftpAcceptDataConnection(ftpGetSession(sessionId)->passiveDataSocket);
if (s < 0) {
ftpSendMsg(MSG_NORMAL, sessionId, 425, ftpMsg012);
return 1;
}
}
sendListing(s, sessionId, realPath, MLSD);
if (VERBOSE_MODE_ENABLED) printf("In ftpCmdMlsd about to Close socket = %d for sessionId = %d\n", s, sessionId);
ftpUntrackSocket(s);
ftpCloseSocket(&s);
return 0;
}
#endif
typedef struct {
char cmdToken[5]; ///< command-string
int tokLen; ///< len of cmdToken
int neededRights; ///< required access rights for executing the command
int needLogin; ///< if TRUE, command needs successful authentication via USER and PASS
int reportFeat; ///< if TRUE, command is reported via FEAT
int duringTransfer; ///< if TRUE, command can be executed during a file transfer
int(*handler)(int, const char*, int); ///< handler function
}ftpCommand_S;
static const ftpCommand_S cmds[] = {
{"USER", 4, 0, FALSE, FALSE, FALSE, ftpCmdUser},
{"PASS", 4, 0, FALSE, FALSE, FALSE, ftpCmdPass},
{"SYST", 4, 0, TRUE, FALSE, FALSE, ftpCmdSyst},
{"PORT", 4, 0, TRUE, FALSE, FALSE, ftpCmdPort},
{"NOOP", 4, 0, TRUE, FALSE, FALSE, ftpCmdNoop},
{"QUIT", 4, 0, FALSE, FALSE, TRUE, ftpCmdQuit},
{"ABOR", 4, 0, FALSE, FALSE, TRUE, ftpCmdAbor},
{"LIST", 4, FTP_ACC_LS, TRUE, FALSE, FALSE, ftpCmdList},
{"NLST", 4, FTP_ACC_LS, TRUE, FALSE, FALSE, ftpCmdNlst},
{"PWD", 3, FTP_ACC_RD, TRUE, FALSE, FALSE, ftpCmdPwd},
{"XPWD", 4, FTP_ACC_DIR, TRUE, FALSE, FALSE, ftpCmdPwd},
{"TYPE", 4, 0, TRUE, FALSE, FALSE, ftpCmdType},
{"PASV", 4, 0, TRUE, FALSE, FALSE, ftpCmdPasv},
{"CWD", 3, FTP_ACC_DIR, TRUE, FALSE, FALSE, ftpCmdCwd},
{"CDUP", 4, FTP_ACC_DIR, TRUE, FALSE, FALSE, ftpCmdCdup},
{"STRU", 4, 0, TRUE, FALSE, FALSE, ftpCmdStru},
{"RETR", 4, FTP_ACC_RD, TRUE, FALSE, FALSE, ftpCmdRetr},
{"STOR", 4, FTP_ACC_WR, TRUE, FALSE, FALSE, ftpCmdStor},
{"DELE", 4, FTP_ACC_WR, TRUE, FALSE, FALSE, ftpCmdDele},
{"MKD" , 3, FTP_ACC_WR, TRUE, FALSE, FALSE, ftpCmdMkd},
{"XMKD", 4, FTP_ACC_WR, TRUE, FALSE, FALSE, ftpCmdMkd},
{"RMD" , 3, FTP_ACC_WR, TRUE, FALSE, FALSE, ftpCmdRmd},
{"XRMD", 4, FTP_ACC_WR, TRUE, FALSE, FALSE, ftpCmdRmd},
#if RFC3659
{"SIZE", 4, FTP_ACC_RD, TRUE, TRUE, FALSE, ftpCmdSize},
{"MDTM", 4, FTP_ACC_RD, TRUE, TRUE, FALSE, ftpCmdMdtm},
{"MLST", 4, FTP_ACC_RD, TRUE, TRUE, FALSE, ftpCmdMlst},
{"MLSD", 4, FTP_ACC_LS, TRUE, TRUE, FALSE, ftpCmdMlsd}
#endif
};
int execFtpCmd(int sessionId, const char* cmd, int cmdlen) {
int n;
int ret = 0;
ftpSession_S *pSession = ftpGetSession(sessionId);
//if(VERBOSE_MODE_ENABLED) printf("In execFtpCmd ARRAY_SIZE(cmds) = %lu for sessionId = %d\n",ARRAY_SIZE(cmds),sessionId);
if (VERBOSE_MODE_ENABLED) printf("In execFtpCmd cmd [%s] for sessionId = %d\n", cmd, sessionId);
for (n = 0; n < (int) ARRAY_SIZE(cmds); n++) {
if (!strncmp(cmds[n].cmdToken, cmd, cmds[n].tokLen)) {
int i = cmds[n].tokLen;
if (cmds[n].needLogin) {
if ((pSession->userId == 0) || (pSession->authenticated == FALSE)) {
if (VERBOSE_MODE_ENABLED) printf("In execFtpCmd User NOT loggedin for sessionId = %d\n", sessionId);
ftpSendMsg(MSG_NORMAL, sessionId, 530, ftpMsg033);
return 0;
}
if (ftpCheckAccRights(pSession->userId, cmds[n].neededRights)) {
if (VERBOSE_MODE_ENABLED) printf("In execFtpCmd User has no ACCESS for sessionId = %d\n", sessionId);
ftpSendMsg(MSG_NORMAL, sessionId, 550, ftpMsg034);
return 0;
}
}
if ((pSession->activeTrans.op != OP_NOP)) // transfer in progress?
{
if (cmds[n].duringTransfer == FALSE) // command during transfer allowed?
{
if (VERBOSE_MODE_ENABLED) printf("In execFtpCmd got command during transfer, discarding for sessionId = %d\n", sessionId);
return 0; // no => silently discard command
}
}
while (cmd[i] != '\0') {
if ((cmd[i] != ' ') && (cmd[i] != '\t'))
break;
i++;
}
if (VERBOSE_MODE_ENABLED) printf("About to execute cmds[n].cmdToken [%s] command [%s] for sessionId = %d\n", cmds[n].cmdToken, &cmd[i], sessionId);
ret = cmds[n].handler(sessionId, &cmd[i], (int) strlen(&cmd[i])); // execute command
if (VERBOSE_MODE_ENABLED) printf("Executed cmds[n].cmdToken [%s] command [%s] ret = %d for sessionId = %d\n", cmds[n].cmdToken, &cmd[i], ret, sessionId);
pSession->timeLastCmd = ftpGetUnixTime();
return ret;
} else {
//if(VERBOSE_MODE_ENABLED) printf("In execFtpCmd SKIPPED COMMAND cmds[n].cmdToken = [%s] n = %d for sessionId = %d\n",cmds[n].cmdToken,n,sessionId);
}
}
if (VERBOSE_MODE_ENABLED) printf("ERROR UNKNOWN COMMAND cmd [%s] for sessionId = %d\n", cmd, sessionId);
ftpSendMsg(MSG_NORMAL, sessionId, 500, cmd); // reject unknown commands
pSession->timeLastCmd = ftpGetUnixTime();
return ret;
}
void ftpParseCmd(int sessionId) {
ftpSession_S *pSession;
int len;
socket_t ctrlSocket;
pSession = ftpGetSession(sessionId);
len = pSession->rxBufWriteIdx;
ctrlSocket = pSession->ctrlSocket;
if ((pSession->rxBuf[len - 1] == '\n') &&
(pSession->rxBuf[len - 2] == '\r')) // command correctly terminated?
{
int c = 0;
pSession->rxBuf[len - 2] = '\0';
pSession->rxBufWriteIdx = 0;
for (c = 0; c < len; c++) // convert command token to uppercase
{
if (isspace(pSession->rxBuf[c]))
break;
pSession->rxBuf[c] = toupper(pSession->rxBuf[c]);
}
if (VERBOSE_MODE_ENABLED) printf("%02d --> %s for socket: %d\n", sessionId, pSession->rxBuf, ctrlSocket);
if (execFtpCmd(sessionId, pSession->rxBuf, len - 2) == -1) {
if (VERBOSE_MODE_ENABLED) printf("In execFtpCmd command triggered close for socket: %d!\n", ctrlSocket);
ftpUntrackSocket(ctrlSocket);
ftpCloseSession(sessionId);
}
} else {
if (VERBOSE_MODE_ENABLED) printf("ERROR In execFtpCmd problem with parsing string [%s] for socket: %d!\n", pSession->rxBuf, ctrlSocket);
}
if (pSession->rxBufWriteIdx >= LEN_RXBUF) // overflow of receive buffer?
{
pSession->rxBufWriteIdx = 0;
ftpSendMsg(MSG_NORMAL, sessionId, 500, ftpMsg035);
if (VERBOSE_MODE_ENABLED) printf("ERROR: Receive buffer overflow. Received data discarded.\n");
}
}