Sign in to follow this  
MatsVed

[.net] FTP protocol... LIST response?

Recommended Posts

Hi!
I'm building myself an FTP server, and I did alright until I got to the LIST command. I'm testing against the FileZilla client, and for some reason it insists on sending me LIST even though I've specified that my server supports MLSD.

No matter what I do, I can't get the client to accept my directory listing.
Here's the code:

                public static void HandleLISTCmd(string FTPCommand, FTPClient Client)
{
Client.Send(Encoding.ASCII.GetBytes("150 Opening binary data connection for file list\0"));

/*MemoryStream EPLFStream = new MemoryStream();
BinaryWriter Writer = new BinaryWriter(EPLFStream);

string[] Files = Directory.GetFiles(GlobalSettings.Default.WorkingDirectory);

foreach (string Fle in Files)
Writer.Write(Encoding.ASCII.GetBytes(GenerateFileInfo(Fle)));

Writer.Flush();*/


Client.ConnectionResetEvent.WaitOne();

Client.SendFile(EPLFStream.ToArray());

foreach (string Fle in Directory.GetFiles(GlobalSettings.Default.WorkingDirectory))
{
string FInfo = GenerateFileInfo(Fle);
Client.SendFile(Encoding.ASCII.GetBytes(FInfo));
}

//By now the filetransfer should be complete, so let the client know...
Client.Send(Encoding.ASCII.GetBytes("226 Transfer complete.\0"));
}

private static string GenerateFileInfo(string Filepath)
{
FileInfo FInfo = new FileInfo(Filepath);

string Size = FInfo.Length.ToString();
TimeSpan TSpan = (FInfo.LastWriteTime - new DateTime(1970, 1, 1));
string Modified = TSpan.TotalSeconds.ToString();

string ReturnStr = "+s" + Size + ",m" + Modified + ",r," + "\t" + FInfo.Name + "\0";

return ReturnStr;
}

private static string GenerateBinLS(string Filepath)
{
FileInfo FInfo = new FileInfo(Filepath);
string Result = "rw-r--r-- 1 owner group ";

Result += RightAlignString(FInfo.Length.ToString(), 13, ' ') + " ";
Result += GetMonth(FInfo.LastWriteTime.Month) + " ";

if (FInfo.LastWriteTime.Day.ToString().Length > 2)
Result += FInfo.LastWriteTime.Day.ToString() + " ";
else if (FInfo.LastWriteTime.Day.ToString().Length == 1)
Result += " " + FInfo.LastWriteTime.Day.ToString() + " ";
else
Result += " " + FInfo.LastWriteTime.Day.ToString() + " ";

Result += FInfo.LastWriteTime.Hour.ToString() + ":" + FInfo.LastWriteTime.Minute.ToString() + " ";

Result += FInfo.Name;

return Result;
}

private static string GetMonth(int Month)
{
switch (Month)
{
case 1:
return "Jan";
case 2:
return "Feb";
case 3:
return "Mar";
case 4:
return "Apr";
case 5:
return "May";
case 6:
return "Jun";
case 7:
return "Jul";
case 8:
return "Aug";
case 9:
return "Sep";
case 10:
return "Oct";
case 11:
return "Nov";
case 12:
return "Dec";
}

return "";
}

private static string RightAlignString(string sString, int nWidth, char cDelimiter)
{
System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder();

for (int nCharacter = 0; nCharacter < nWidth - sString.Length; nCharacter++)
stringBuilder.Append(cDelimiter);

stringBuilder.Append(sString);
return stringBuilder.ToString();
}


I've tried using both GenerateFileInfo() and GenerateBinLS() to generate my LIST response, but no matter which one I use I get an error about the connection having timed out and that the client couldn't receive my directory listing.
I've read that the server is supposed to close the data connection, but when I try to do that FileZilla complains that the server has closed the connection.

Here is how I open my data connection:

         /// <summary>
/// Handles a PASV command in the FTP protocol.
/// PASV asks the server to open a port for data-transfer and send
/// it to the client so it can connect.
/// </summary>
/// <param name="FTPCommand"></param>
/// <param name="IP"></param>
/// <param name="Client"></param>
public static void HandlePASVCmd(string FTPCommand, string IP, FTPClient Client)
{
IP = IP.Replace(".", ",");

if (Client.OpenDataPort((PortNum1 * 256 + PortNum2).ToString()))
{
Client.Send(Encoding.ASCII.GetBytes("227 Entering Passive Mode (" + IP + "," + CalculatePort() + ")\0"));
}
else //This should technically never occur. See http://kb.globalscape.com/KnowledgebaseArticle10142.aspx
//for error codes.
Client.Send(Encoding.ASCII.GetBytes("425 Cannot open data connection!\0"));
}

private static int PortNum1 = 10;
private static int PortNum2 = 10;

private static string CalculatePort()
{
//The calculation is PortNum1 x 256 + PortNum2
//26100 is the port being used to listen for initial FTP connections.
if ((PortNum1 * 256) + PortNum2 != 26100)
{
string Result = PortNum1.ToString() + "," + PortNum2.ToString();

PortNum1++;
PortNum2++;

return Result;
}
else
{
PortNum1 = PortNum1 + 2;
PortNum2 = PortNum2 + 2;
return PortNum1.ToString() + "," + PortNum2.ToString();
}

}


       public bool OpenDataPort(string Port)
{
IPHostEntry HostEntry = Dns.GetHostByName(Dns.GetHostName());
IPAddress Address = HostEntry.AddressList[0];
Logger.LogDebug("DataAddress: " + Address.ToString());
IPEndPoint LocalEP = new IPEndPoint(Address, int.Parse(Port));

m_DatListenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
m_ConnectingWaitEvent = new AutoResetEvent(false);

try
{
m_DatListenerSocket.Bind(LocalEP);
//Don't need a high backlog, since we'll only be listening for one incoming connection.
m_DatListenerSocket.Listen(10);
m_DatListenerSocket.BeginAccept(new AsyncCallback(OnAcceptDataconnection), m_DatListenerSocket);
}
catch (SocketException)
{
return false;
}

return true;
}

private void OnAcceptDataconnection(IAsyncResult AR)
{
Socket AcceptedSocket = m_DatListenerSocket.EndAccept(AR);

if (AcceptedSocket != null)
{
AcceptedSocket.Blocking = false;
m_DatSenderSocket = AcceptedSocket;
m_ConnectingWaitEvent.Set();
}
}


Can someone please help me out? :)



Share this post


Link to post
Share on other sites
I could be totally wrong here, but I think that the list response has to come back on the data channel and not on the control channel. Something to try at least.

Share this post


Link to post
Share on other sites
Quote:
Original post by MatsVed
I'm doing that!


Sry I assumed that 'Client' reference in your first block of code was a Socket object and your control channel. I would recommend logging everything you write to your control and data channels and examining that. You might also try executing client commands against a server that you know works and mimic its output. FTP is really old and as you probably noticed the documentation is ... unhelpful.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this