> -----Message d'origine-----
> De : Maarten Bakker [mailto:m.j.bak...@planet.nl]
> Envoyé : mardi 18 octobre 2011 19:23
> À : synalist-public@lists.sourceforge.net
> Objet : [Synalist] FTP Server PASV mode
>
> Does anyone know how
> the "PASV" command should be handled?
>
> if cmd = 'PASV' then
> begin
> .......
> continue;
> end;
>
>
> Thanks in advance,
>
> Maarten
>
Attached a modified ftpthrd.pas demo file that supports passive mode. Error
handling could probably be improved.
Ludo
unit FtpThrd;
{$IFDEF FPC}
{$mode delphi}
{$endif}
interface
uses
{$IFDEF LINUX}
Libc,
{$ELSE}
LCLIntf, LCLType, LMessages,
{$ENDIF}
Classes, SysUtils, blcksock, synsock, synautil, filectrl, FileUtil;
type
TFtpServerThread = class(TThread)
private
clients: TSocket;
FDataIP, FDataPort: string;
protected
procedure Execute; override;
procedure send(const sock: TTcpBlocksocket; value: string);
procedure ParseRemote(Value: string);
function buildname(dir, value: string): string;
function buildrealname(value: string): string;
function buildlist(value: string): string;
public
constructor Create(sock: TSocket);
end;
implementation
const
timeout = 60000;
MyMonthNames: array[1..12] of AnsiString =
('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
{==============================================================================}
{ TFtpServerThread }
constructor TFtpServerThread.create(sock: TSocket);
begin
inherited create(false);
FreeOnTerminate := true;
clients := sock;
// Priority := tpNormal;
end;
procedure TFtpServerThread.send(const sock: TTcpBlocksocket; value: string);
begin
sock.SendString(value + CRLF);
end;
procedure TFtpServerThread.ParseRemote(Value: string);
var
n: integer;
nb, ne: integer;
s: string;
x: integer;
begin
Value := trim(Value);
nb := Pos('(',Value);
ne := Pos(')',Value);
if (nb = 0) or (ne = 0) then
begin
nb:=RPos(' ',Value);
s:=Copy(Value, nb + 1, Length(Value) - nb);
end
else
begin
s:=Copy(Value,nb+1,ne-nb-1);
end;
for n := 1 to 4 do
if n = 1 then
FDataIP := Fetch(s, ',')
else
FDataIP := FDataIP + '.' + Fetch(s, ',');
x := StrToIntDef(Fetch(s, ','), 0) * 256;
x := x + StrToIntDef(Fetch(s, ','), 0);
FDataPort := IntToStr(x);
end;
function TFtpServerThread.buildname(dir, value: string): string;
begin
if value = '' then
begin
result := dir;
exit;
end;
if value[1] = '/' then
result := value
else
if (dir <> '') and (dir[length(dir)] = '/') then
Result := dir + value
else
Result := dir + '/' + value;
end;
function TFtpServerThread.buildrealname(value: string): string;
begin
value := replacestring(value, '..', '.');
value := replacestring(value, '/', '\');
result := '.\data' + value;
end;
function fdate(value: integer): string;
var
st: tdatetime;
wYear, wMonth, wDay: word;
begin
st := filedatetodatetime(value);
DecodeDate(st, wYear, wMonth, wDay);
Result:= Format('%d %s %d', [wday, MyMonthNames[wMonth], wyear]);
end;
function TFtpServerThread.buildlist(value: string): string;
var
SearchRec: TSearchRec;
r: integer;
s: string;
begin
result := '';
if value = '' then
exit;
if value[length(value)] <> '\' then
value := value + '\';
R := FindFirstUTF8(value + '*.*',faanyfile,SearchRec); { *Converted from
FindFirst* }
while r = 0 do
begin
if ((searchrec.Attr and faHidden) = 0)
and ((searchrec.Attr and faSysFile) = 0)
and ((searchrec.Attr and faVolumeID) = 0) then
begin
s := '';
if (searchrec.Attr and faDirectory) > 0 then
begin
if (searchrec.Name <> '.') and (searchrec.Name <> '..') then
begin
s := s + 'drwxrwxrwx 1 root root 1 ';
s := s + fdate(searchrec.time) + ' ';
s := s + searchrec.name;
end;
end
else
begin
s := s + '-rwxrwxrwx 1 root other ';
s := s + inttostr(searchrec.Size) + ' ';
s := s + fdate(searchrec.time) + ' ';
s := s + searchrec.name;
end;
if s <> '' then
Result := Result + s + CRLF;
end;
r := FindNextUTF8(SearchRec); { *Converted from FindNext* }
end;
FindCloseUTF8(searchrec); { *Converted from FindClose* }
end;
procedure TFtpServerThread.Execute;
var
sock, dsock, psock: TTCPBlockSocket;
s, t: string;
authdone: boolean;
user: string;
cmd, par: string;
pwd: string;
st: TFileStream;
bPassiveMode:boolean;
begin
sock := TTCPBlockSocket.Create;
dsock := TTCPBlockSocket.Create;
bPassiveMode:=false;
try
sock.Socket := clients;
send(sock, '220 welcome ' + sock.GetRemoteSinIP + '!');
authdone := false;
user := '';
repeat
s := sock.RecvString(timeout);
cmd := uppercase(separateleft(s, ' '));
par := separateright(s, ' ');
if sock.lasterror <> 0 then
exit;
if terminated then
exit;
if cmd = 'USER' then
begin
user := par;
send(sock, '331 Please specify the password.');
continue;
end;
if cmd = 'PASS' then
begin
//user verification...
if ((user = 'username') and (par = 'password'))
or (user = 'anonymous') then
begin
send(sock, '230 Login successful.');
authdone := true;
continue;
end;
end;
send(sock, '500 Syntax error, command unrecognized.');
until authdone;
pwd := '/';
repeat
s := sock.RecvString(timeout);
cmd := uppercase(separateleft(s, ' '));
par := separateright(s, ' ');
if par = s then
par := '';
if sock.lasterror <> 0 then
exit;
if terminated then
exit;
if cmd = 'QUIT' then
begin
send(sock, '221 Service closing control connection.');
break;
end;
if cmd = 'NOOP' then
begin
send(sock, '200 tjadydadydadydaaaaa!');
continue;
end;
if cmd = 'PWD' then
begin
send(sock, '257 ' + Quotestr(pwd, '"'));
continue;
end;
if cmd = 'CWD' then
begin
t := unquotestr(par, '"');
t := buildname(pwd, t);
if DirectoryExistsUTF8(Buildrealname(t)) { *Converted from
DirectoryExists* } then
begin
pwd := t;
send(sock, '250 OK ' + t);
end
else
send(sock, '550 Requested action not taken.');
continue;
end;
if cmd = 'MKD' then
begin
t := unquotestr(par, '"');
t := buildname(pwd, t);
if CreateDirUTF8(Buildrealname(t)) { *Converted from CreateDir* } then
begin
pwd := t;
send(sock, '257 "' + t + '" directory created');
end
else
send(sock, '521 "' + t + '" Requested action not taken.');
continue;
end;
if cmd = 'CDUP' then
begin
pwd := '/';
send(sock, '250 OK');
continue;
end;
if (cmd = 'TYPE')
or (cmd = 'ALLO')
or (cmd = 'STRU')
or (cmd = 'MODE') then
begin
send(sock, '200 OK');
continue;
end;
if cmd = 'PORT' then
begin
Parseremote(par);
send(sock, '200 OK');
continue;
end;
if cmd = 'PASV' then
begin
dsock.CloseSocket;
psock := TTCPBlockSocket.Create;
psock.bind(sock.GetLocalSinIP, '0');
psock.setLinger(true, 10000);
psock.listen;
if psock.LastError = 0 then
begin
send(sock, format('227 Entering Passive Mode (%s,%d,%d)',
[StringReplace(sock.GetLocalSinIP,'.',',',[rfReplaceAll]),psock.GetLocalSinPort
div 256,psock.GetLocalSinPort mod 256]));
bPassiveMode:=sock.LastError = 0;
dsock.socket:=psock.Accept;
end;
continue;
end;
if cmd = 'LIST' then
begin
t := unquotestr(par, '"');
t := buildname(pwd, t);
if bPassiveMode then
begin
try
send(sock, '150 OK ' + t);
dsock.SendString(buildlist(buildrealname(t)));
send(sock, '226 OK ' + t);
finally
dsock.CloseSocket;
psock.Free;
end;
end
else
begin
dsock.CloseSocket;
dsock.Connect(Fdataip, Fdataport);
if dsock.LastError <> 0 then
send(sock, '425 Can''t open data connection.')
else
begin
send(sock, '150 OK ' + t);
dsock.SendString(buildlist(buildrealname(t)));
send(sock, '226 OK ' + t);
end;
dsock.CloseSocket;
end;
continue;
end;
if cmd = 'RETR' then
begin
t := unquotestr(par, '"');
t := buildname(pwd, t);
if FileExistsUTF8(buildrealname(t)) { *Converted from FileExists* }
then
begin
if bPassiveMode then
begin
try
send(sock, '150 OK ' + t);
try
st := TFileStream.Create(buildrealname(t), fmOpenRead or
fmShareDenyWrite);
try
dsock.SendStreamRaw(st);
finally
st.free;
end;
send(sock, '226 OK ' + t);
except
on exception do
send(sock, '451 Requested action aborted: local error in
processing.');
end;
finally
dsock.CloseSocket;
psock.Free;
end;
end
else
begin
dsock.CloseSocket;
dsock.Connect(Fdataip, Fdataport);
dsock.SetLinger(true, 10000);
if dsock.LastError <> 0 then
send(sock, '425 Can''t open data connection.')
else
begin
send(sock, '150 OK ' + t);
try
st := TFileStream.Create(buildrealname(t), fmOpenRead or
fmShareDenyWrite);
try
dsock.SendStreamRaw(st);
finally
st.free;
end;
send(sock, '226 OK ' + t);
except
on exception do
send(sock, '451 Requested action aborted: local error in
processing.');
end;
end;
dsock.CloseSocket;
end;
end
else
send(sock, '550 File unavailable. ' + t);
continue;
end;
if cmd = 'STOR' then
begin
t := unquotestr(par, '"');
t := buildname(pwd, t);
if DirectoryExistsUTF8(extractfiledir(buildrealname(t))) { *Converted
from DirectoryExists* } then
begin
if bPassiveMode then
begin
try
send(sock, '150 OK ' + t);
try
st := TFileStream.Create(buildrealname(t), fmCreate or
fmShareDenyWrite);
try
dsock.RecvStreamRaw(st, timeout);
finally
st.free;
end;
send(sock, '226 OK ' + t);
except
on exception do
send(sock, '451 Requested action aborted: local error in
processing.');
end;
finally
dsock.CloseSocket;
psock.Free;
end;
end
else
begin
dsock.CloseSocket;
dsock.Connect(Fdataip, Fdataport);
dsock.SetLinger(true, 10000);
if dsock.LastError <> 0 then
send(sock, '425 Can''t open data connection.')
else
begin
send(sock, '150 OK ' + t);
try
st := TFileStream.Create(buildrealname(t), fmCreate or
fmShareDenyWrite);
try
dsock.RecvStreamRaw(st, timeout);
finally
st.free;
end;
send(sock, '226 OK ' + t);
except
on exception do
send(sock, '451 Requested action aborted: local error in
processing.');
end;
end;
dsock.CloseSocket;
end;
end
else
send(sock, '553 Directory not exists. ' + t);
continue;
end;
send(sock, '500 Syntax error, command unrecognized.');
until false;
finally
dsock.free;
sock.free;
end;
end;
{==============================================================================}
end.
------------------------------------------------------------------------------
The demand for IT networking professionals continues to grow, and the
demand for specialized networking skills is growing even more rapidly.
Take a complimentary Learning@Cisco Self-Assessment and learn
about Cisco certifications, training, and career opportunities.
http://p.sf.net/sfu/cisco-dev2dev
_______________________________________________
synalist-public mailing list
synalist-public@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/synalist-public