xreef / SimpleFTPServer

A simple FTP server for Arduino, ArduinoSAMD WiFiNINA, esp8266, esp32, stm32 and Raspberry Pi Pico W

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Passive mode from another subnet not working

nagyrobi opened this issue · comments

Congrats for this nice project!

The issue we have is that we're connecting to ESP32 nodes from a different subnet (in PASV mode) and that fails.

Filezilla client:

Status:	Connection established, waiting for welcome message...
Status:	Plain FTP is insecure. Please switch to FTP over TLS.
Status:	Logged in
Status:	Retrieving directory listing...
Command:	PWD
Response:	257 "/" is your current directory
Command:	TYPE I
Response:	200 TYPE is now 8-bit binary
Command:	PASV
Response:	227 Entering Passive Mode (0,0,0,0,195,89)
Command:	MLSD
Error:	The data connection could not be established: ECONNREFUSED - Connection refused by server
Response:	425 No data connection
Error:	Failed to retrieve directory listing

Command line client:

Connected to 192.168.1.12.
220---Welcome to Simply FTP server ---
220---   By Renzo Mischianti   ---
220 --    Version 2021-11-09    --
Name (192.168.1.12:user): admin
331 Ok. Password required
Password:
230 Ok
ftp> passive
Passive mode on.
ftp> dir
227 Entering Passive Mode (0,0,0,0,195,89)
ftp: connect: Connection refused
ftp> 

If we move with the client in the same network as the ESP32 everything is fine:

Connected to 192.168.1.12.
220---Welcome to Simply FTP server ---
220---   By Renzo Mischianti   ---
220 --    Version 2021-11-09    --
Name (192.168.1.12:user): admin
331 Ok. Password required
Password:
230 Ok
ftp> passive
Passive mode on.
ftp> dir
227 Entering Passive Mode (192,168,1,12,195,89)
150 Accepted data connection to port 50009
+/,	682,	config.json
+/,	7853,	pages.jsonl
226 2 matches total
ftp> quit
221 Goodbye

The problem is caused by this: 227 Entering Passive Mode (0,0,0,0,195,89) when the server is on a different subnet than the client.
If the message 227 would be identical in both cases, there would be no problems at all.

Don't know why the localIp is 0.0.0.0 in the server when client is connecting from a different subnet.

A bit of background about the network environment:

  • ESP32 nodes run on their own IoT subnet (192.168.1.0/24), they don't have permission to connect outside the network, firewall on the main router blocks outgoing connections
  • other clients (like the ones running Filezilla) run on other open subnet (192.168.2.0/24) from where they can make connections anywhere, including with ESP32's subnet
  • the subnets connect to the same gateway and connection is permitted from client subnet to IoT subnet but not from IoT subnet to other subnets. This blocks the usage of FTP Active mode, but has to work using Passive mode, because both data and control connection is initiated by the client towards the server. Firewall is not blocking communication from client to server.

Hi @nagyrobi,
It seems that the FTP server can't understand the IP; it receives the 0.0.0.0 that It's the "all IP" identifier probably given from the routing rule.
I don't have a network suitable for that, and I think It isn't a library problem, but a network setting.
I try to find more information.
Bye Renzo

Unfortunately the FTP clients in Passive mode can't handle IP address 0.0.0.0. They need to know what is the address to connect to in the direction of the server. If the server is behind a NAT, this address should usually be the address of the WAN port.

But here it's NO NAT between the networks, they are routed directly. In this case the standard address to be sent in the PASV message by the server towards the client is the address of the server itself, not 0.0.0.0. Just like it is when it's in the same network.

The client class that gives us the IP is the ESP32 WiFi, so apparently, there are only two possibilities:

  • or there is a bug in the Wifi class of ESP32;
  • or there is a network setting that masquerades the IP.
    But I need more time to check if there are other problems.

Bye Renzo

There's no advanced masquerading or anythig it's just pure and simple routing, nothing else. The address reported in PASV message should simply be the address of the network interface of the ESP itself, nothing else.

If ESP32 can't retrieve the IP, I think the only way to bypass this problem is to give the possibility to manually add the IP on the begin method.

I think about that.

Retrieve from where? ESP32 should already know its own IP address. That's what is needed.

ESP32 return 0.0.0.0 according to your output

The line where It gets the IP is this
https://github.com/xreef/SimpleFTPServer/blob/master/FtpServer.cpp#L437
or this
https://github.com/xreef/SimpleFTPServer/blob/master/FtpServer.cpp#L439

in the two methods the command to get the IP is
WiFi.localIP()

@fvanroie can you please take a look, what do we use in OpenHASP to determine our own IP address?

ESP32 return 0.0.0.0 according to your output

Not always. When I connect to FTP from local network, it returns the real address. When I connect to FTP from neighbor network, it returns 0.0.0.0. Are you 100% sure WiFi.localIP() is not overridden by something else?

@fvanroie can you please take a look, what do we use in OpenHASP to determine our own IP address?

We also use localIP() like SimpleFTPServer does. You can easily verify this by enabling -D FTP_SERVER_DEBUG in user_setups\esp32\_esp32.ini. Here is my output:

-U-S-E-R- -h-a-s-p-
Command is: USER
-P-A-S-S- -t-e-s-t-
Command is: PASS
 Authentication Ok. Waiting for commands.
-P-W-D-
Command is: PWD
-T-Y-P-E- -I-
Command is: TYPE
-P-A-S-V-
Command is: PASV
 Connection management set to passive
 Listening at 10.11.12.32:50009
-M-L-S-D-
Command is: MLSD
List of file!!Dir opened!!Connected!!
DIR MLSD

I also connected over a VPN:

-U-S-E-R- -h-a-s-p-
Command is: USER
-P-A-S-S- -t-e-s-t-
Command is: PASS
 Authentication Ok. Waiting for commands.
-S-Y-S-T-
Command is: SYST
-T-Y-P-E- -I-
Command is: TYPE
-T-Y-P-E- -A-
Command is: TYPE
-P-A-S-V-
Command is: PASV
 Connection management set to passive
 Listening at 10.11.12.32:50009
Command is: LIST
List of file!!Dir opened!!

As you can see Listening at 10.11.12.32:50009 sends the correct IP address in my setup.
What does your debug log say when you connect locally vs. remotely?

I have this:

Prompt >  Client connected!
[20:24:07.024][65524/91440 28][41764/41860  1] FTP : Connected
-rompt > -U-S-E-R- -t-e-s-t-
 Command is: USER
-P-A-S-S- -t-e-s-t-
 Command is: PASS
 Authentication Ok. Waiting for commands.
-P-W-D-
 Command is: PWD
-T-Y-P-E- -I-
 Command is: TYPE
-P-A-S-V-
 Command is: PASV
 Connection management set to passive
 Listening at 0.0.0.0:50009
-M-L-S-D-
 Command is: MLSD
List of file!!

ESP32 return 0.0.0.0 according to your output

The line where It gets the IP is this https://github.com/xreef/SimpleFTPServer/blob/master/FtpServer.cpp#L437 or this https://github.com/xreef/SimpleFTPServer/blob/master/FtpServer.cpp#L439

in the two methods the command to get the IP is WiFi.localIP()

Thanks @xreef for the pointer to the code. I think I have figured out what is happening...

The FTP server is started at boot before WiFi is connected. This means localIP will store the a 0.0.0.0 address.
Then when PASV is received it sends the localIP of 0.0.0.0 stored at startup to the client on the same subnet...

I have tweaked the code a little bit to check for (uint32_t)localIp > 0. Otherwise the actual WiFi.localIP() is used instead.

I think localIP should only be used if it is set manually, to indicate the address of the remote NAT interface.
If it is not set (i.e. 0.0.0.0) use the current WiFi.localIP()... What do you think?

@fvanroie I tested your patch and now it works!

Hi @fvanroie,
Yes, I think I also add that change; I never consider starting services before the connection.

I release It next week, and I must also release some minor fixes (like support for ESP32 core 2.x.x).

I'd also like to ask you for some information about Home Automation; many people ask me to modify my LoRa gateway example with support for MQTT. Can it be a suitable project?

Thanks, Renzo

I'd also like to ask you for some information about Home Automation; many people ask me to modify my LoRa gateway example with support for MQTT. Can it be a suitable project?

I see you already implemented a Web interface and REST api.
MQTT is very similar and perfectly suited to send/receive messages.

I am not familiar with LoRa gateway but I think it can be a very nice project indeed.
MQTT is very popular Home Automation protocol with support in all major HA software.
Arduino has several good MQTT clients which should be easy to add to your project.

@fvanroie I already implemented MQTT in AWS and similar; I'd like to know if there are some standard authentication for Home Assistant and others or all can be used Mutual authentication with a certificate.
Bye Renzo

Dear Renzo;


I report here the discussion we started on your personal web site; I have some difficulties posting there and the discussion can be of general interest.

I’m using your last version of ftp library on esp32 with last arduino core and last stable arduino IDE. I’m having the same problem described here and on your github forum. my code, as regards wifi section, is similar to your last example here. ESP32 is in AP only mode. activating lib debug message, last part is exactly:

//
Command is: PASV
Connection management set to passive
Listening at 0.0.0.0:50009
-M-L-S-D-
/
/

reading here, I understand that the problem was solved, but in order to have a client connection from filezilla on win10, I had to modify FtpServer.cpp at row 497 (of last available version on github), as:

//
.
.
.
//
// PASV – Passive Connection management
//
else if( CommandIs( “PASV” ))
{
data.stop();
dataServer.begin();
if (((((uint32_t) NET_CLASS.localIP()) & ((uint32_t) NET_CLASS.subnetMask())) ==
(((uint32_t) client.remoteIP()) & ((uint32_t) NET_CLASS.subnetMask()))) && (uint32_t)localIp <= 0) {
dataIp = NET_CLASS.softAPIP(); //NET_CLASS.localIP(); //<– LD
} else {
dataIp = localIp;
}
dataPort = pasvPort;
DEBUG_PRINTLN( F(” Connection management set to passive”) );
.
.
.
/
/

replacing call to NET_CLASS.localIP() with a call to NET_CLASS.softAPIP()

with this replacement, the connection in fine&fast.
I tryed to use ftpSrv.setLocalIp(), with actual IP address, when the first wifi client connect to esp32 AP time after esp32 boot, but with no results. I have not tested the code with esp32 in STA mode.

just for information, I had some trial with few android ftp client app, but with no luck. some app have trouble in entering PASV mode, some have trouble with SYST and another have trouble with file path (adding a strange ./ in the middle of the file path, just before file name. the app is working with a filezilla server).

regards

Hi Renzo, here some few other consideration about the same issue;

yes, seems that the IP address the FTP class uses is wrong. As I wrote, my esp32 is used in AP mode only. I configured the network with default IP address (192.168.4.1), gateway and subnet mask. I have a web server on the esp32 that works normally. I tried to pass the right IP address to the ftp server, but with no results (ftpSrv.setLocalIp(softAPIP()); I tried just after the server begin and also just after the event of the first client is connected to esp32 AP WiFi. I tried also passing to ftpSrv.setLocalIp() the corrected IP address as constant.
In my opinion the two function softAPIP() and localIP() act differently in AP mode and in STA mode. The ftp server private variable localIp is initialized using localIP() and that (for some reasons) lead to localIp == 0.0.0.0. Even if the code use setLocalIp() after ftp begin, this is not used because at runtime, entering PASV mode, the code detect a 0.0.0.0 in localIp and try to obtain the correct IP, but always using localIP(), overwrinting user-assigned IP and obtaining again 0.0.0.0. At this point, using softAPIP() instead, the ftp server can report the correct IP.
In my opinion, a possible solution can be to act differently in STA mode and in AP mode, in a similar way as used in https://github.com/me-no-dev/ESPAsyncWebServer#using-filters; I used this feature in order to have two different web page accessing the esp32 from the AP or from the STA; I configured the two interfaces with two completely different parameters (IP address, gatway...). The two 'filters' are two functions that can be found in https://github.com/me-no-dev/ESPAsyncWebServer/blob/master/src/WebServer.cpp at row 24
bye

Hi Renzo
many thanks, last update solved the issue about ftp passive mode in ESP32 AP mode
Bye