Version: 0.7.99 (using KDE KDE 3.4.2) Installed from: SuSE RPMs Compiler: gcc 4.0.2 20050901 OS: Linux When trying to connect from my machine (IPv4 address 213.41.243.33, IpV6 address 2001:7a8:b321::1) to ftp.u-strasbg.fr (2001:660:2402::6) as anonymous user in active FTP mode, the remote directory listing does not appear. Looking at the log window gives the explanation: [21:28:31] PORT 2001:7a8:b321::1,12,229 [21:28:31] 501 Illegal PORT command Then I shut down the IPv6 link with ip addr del 2001:7a8:b321::1 dev dsl0 and it goes much better: [21:55:24] PORT 213,41,243,33,4,26 [21:55:24] 200 PORT command successful I get the impression there is something wrong with syntax of PORT command with IPv6 addresses. In the same situation (same hosts, I mean) all the other FTP clients seem to have no problem.
0.7.0-beta1 from the archive downloaded on the site has not the problem. The problem only occurs with the version from SVN repository (numbered 0.7.99). Both are compiled by me the same way. That makes me think this bug has probably been introduced very recently...
So if I understand you correctly, you are connecting to this server via it's IPv6 address ? If so, I will have to implement the commands needed to support IPv6 connections - I'll do that later today.
I didn't ask to use the IPv6 link ;-). Some software component chose to use the IPv6 connectivity instead of the IPv4 one, I don't know why. I have just asked the host name "ftp.u-strasbg.fr" in kftpgrabber. This host name happens to have both A (IPv4 DNS) and AAAA (IPv6) DNS entries: # dig -t AAAA ftp.u-strasbg.fr (...) ;; ANSWER SECTION: ftp.u-strasbg.fr. 3238 IN CNAME anubis.u-strasbg.fr. anubis.u-strasbg.fr. 3238 IN AAAA 2001:660:2402::6 (...) # dig -t A ftp.u-strasbg.fr (...) ;; ANSWER SECTION: ftp.u-strasbg.fr. 3182 IN CNAME anubis.u-strasbg.fr. anubis.u-strasbg.fr. 3182 IN A 130.79.200.5 (...) I don't know why it chose the AAAA record instead of the A one. Anyway, that makes kftpgrabber unusable, unless you think at shutting down your IPv6 connectivity. I hope that helps.
SVN commit 573853 by kostko: Added support for EPRT. CCBUG: 132522 M +44 -23 ftpsocket.cpp M +1 -1 ftpsocket.h M +1 -0 socket.cpp --- trunk/extragear/network/kftpgrabber/src/engine/ftpsocket.cpp #573852:573853 @@ -719,21 +719,43 @@ { if (currentState == NegotiateActive) { if (!socket()->isResponse("2")) { - // Negotiation failed, reset since active is the last fallback - socket()->resetCommandClass(Failed); + if (socket()->getConfigInt("feat.eprt")) { + socket()->setConfig("feat.eprt", 0); + } else { + // Negotiation failed, reset since active is the last fallback + socket()->resetCommandClass(Failed); + return; + } + } else { + currentState = HaveConnection; + socket()->nextCommandAsync(); return; } - - currentState = HaveConnection; - socket()->nextCommandAsync(); - } else { - // Setup the socket and set the apropriate port command - currentState = NegotiateActive; - - QString address = socket()->setupActiveTransferSocket(); - if (!address.isNull()) - socket()->sendCommand("PORT " + address); } + + // Setup the socket and set the apropriate port command + currentState = NegotiateActive; + + KNetwork::KSocketAddress address = socket()->setupActiveTransferSocket(); + if (address.address()) { + if (socket()->getConfigInt("feat.eprt")) { + QString ianaFamily = QString::number(address.ianaFamily()); + + socket()->sendCommand("EPRT |" + ianaFamily + "|" + address.nodeName() + "|" + address.serviceName() + "|"); + } else if (address.ianaFamily() == 1) { + QString format = address.nodeName().replace(".", ","); + + format.append(","); + format.append(QString::number((unsigned char) address.address()->sa_data[0])); + format.append(","); + format.append(QString::number((unsigned char) address.address()->sa_data[1])); + + socket()->sendCommand("PORT " + format); + } else { + socket()->emitEvent(Event::EventMessage, i18n("Incompatible address family for PORT, but EPRT not supported, aborting!")); + socket()->resetCommandClass(Failed); + } + } } }; @@ -773,7 +795,7 @@ m_transferSocket->connect(realHost, QString::number(port)); } -QString FtpSocket::setupActiveTransferSocket() +KNetwork::KSocketAddress FtpSocket::setupActiveTransferSocket() { if (!m_serverSocket) m_serverSocket = new KNetwork::KServerSocket(); @@ -800,7 +822,7 @@ if (!found) { emitEvent(Event::EventMessage, i18n("Unable to establish a listening socket.")); resetCommandClass(Failed); - return QString::null; + return KNetwork::KSocketAddress(); } } else { m_serverSocket->setAddress("0"); @@ -808,13 +830,13 @@ if (!m_serverSocket->listen()) { emitEvent(Event::EventMessage, i18n("Unable to establish a listening socket.")); resetCommandClass(Failed); - return QString::null; + return KNetwork::KSocketAddress(); } } KNetwork::KSocketAddress serverAddr = m_serverSocket->localAddress(); KNetwork::KSocketAddress controlAddr = localAddress(); - QString request; + KNetwork::KSocketAddress request; if (KFTPCore::Config::portForceIp()) { // Force a specified IP/hostname to be used in PORT @@ -824,21 +846,20 @@ if (resolverResults.error() < 0) { // Well, we are unable to resolve the name, so we should use what we got // from control socket - request.append(controlAddr.nodeName().replace(".", ",")); + request = controlAddr; } else { // The name has been resolved and we have the address, so we should // use it - request.append(resolverResults[0].address().nodeName().replace(".", ",")); + request = resolverResults[0].address(); } } else { // Just use our IP we bound to when connecting to the remote server - request.append(controlAddr.nodeName().replace(".", ",")); + request = controlAddr; } - request.append(","); - request.append(QString::number((unsigned char) serverAddr.address()->sa_data[0])); - request.append(","); - request.append(QString::number((unsigned char) serverAddr.address()->sa_data[1])); + // Set the proper port + request.address()->sa_data[0] = serverAddr.address()->sa_data[0]; + request.address()->sa_data[1] = serverAddr.address()->sa_data[1]; emitEvent(Event::EventMessage, i18n("Waiting for data connection on port %1...").arg(serverAddr.serviceName())); QObject::connect(m_serverSocket, SIGNAL(readyAccept()), this, SLOT(slotDataAccept())); --- trunk/extragear/network/kftpgrabber/src/engine/ftpsocket.h #573852:573853 @@ -95,7 +95,7 @@ void resetCommandClass(ResetCode code = Ok); void setupPassiveTransferSocket(const QString &host, int port); - QString setupActiveTransferSocket(); + KNetwork::KSocketAddress setupActiveTransferSocket(); QFile *getTransferFile() { return &m_transferFile; } void checkTransferEnd(); --- trunk/extragear/network/kftpgrabber/src/engine/socket.cpp #573852:573853 @@ -68,6 +68,7 @@ // Fill in some default values setConfig("feat.epsv", 1); + setConfig("feat.eprt", 1); setConfig("feat.pasv", 1); setConfig("feat.size", 1); setConfig("ssl.prot_mode", 'C');
It works for me: [13:27:18] EPRT |2|2001:7a8:b321::1|3397| [13:27:18] 200 EPRT command successful Thank you very much for the EPRT support. I still can't get anything from SVN version to go through the data connection, neither in active nor in passive mode. That's probably a second, independant, bug, and this second bug is probably linked to IPv6 too.
Nope, that's not linked to IPv6, it happens with IPv4 too. That's probably an inter-thread communication problem. In kftpgrabber/src/engine/thread.cpp, method Thread::run(), // Deliver internal events while any while (m_internalEvents.count() > 0) { count() returns 0 even though an event has been queued by the method Thread::queueInternalEvent().
SVN commit 574216 by kostko: Removed internal thread events and used manual polling instead (at least until ported to Qt 4). Better handling of transfer socket closures. BUG: 132522 M +40 -41 ftpsocket.cpp M +2 -2 ftpsocket.h --- trunk/extragear/network/kftpgrabber/src/engine/ftpsocket.cpp #574215:574216 @@ -87,10 +87,32 @@ slotControlTryRead(); if (m_transferSocket) { - if (getCurrentCommand() == Commands::CmdPut) - slotDataTryWrite(); - else - slotDataTryRead(); + if (m_transferConnecting && m_transferSocket->state() == Connected) { + m_transferConnecting = false; + slotDataConnected(); + } else if (!m_transferConnecting) { + if (getCurrentCommand() == Commands::CmdPut) + slotDataTryWrite(); + else { + bool input; + m_transferSocket->socketDevice()->poll(&input, 0, 0, 0); + + if (input) + slotDataTryRead(); + } + } + } else if (m_serverSocket) { + bool input; + m_serverSocket->socketDevice()->poll(&input, 0, 0, 0); + + if (input) { + KNetwork::KActiveSocketBase *socket = m_serverSocket->accept(); + + if (socket) { + slotDataAccept(static_cast<KNetwork::KStreamSocket*>(socket)); + m_transferConnecting = false; + } + } } // Check for timeouts @@ -200,9 +222,6 @@ timeoutWait(false); if (m_transferSocket && code != Ok) { - QObject::disconnect(m_transferSocket, SIGNAL(connected(const KResolverEntry&)), this, SLOT(slotDataConnected())); - QObject::disconnect(m_transferSocket, SIGNAL(gotError(int)), this, SLOT(slotDataError())); - // Invalidate the socket closeDataTransferSocket(); @@ -761,9 +780,7 @@ void FtpSocket::initializeTransferSocket() { - QObject::connect(m_transferSocket, SIGNAL(connected(const KResolverEntry&)), this, SLOT(slotDataConnected())); - QObject::connect(m_transferSocket, SIGNAL(gotError(int)), this, SLOT(slotDataError())); - + m_transferConnecting = true; m_transferEnd = 0; m_transferBytes = 0; m_transferBufferSize = 4096; @@ -862,19 +879,13 @@ request.address()->sa_data[1] = serverAddr.address()->sa_data[1]; emitEvent(Event::EventMessage, i18n("Waiting for data connection on port %1...").arg(serverAddr.serviceName())); - QObject::connect(m_serverSocket, SIGNAL(readyAccept()), this, SLOT(slotDataAccept())); return request; } -void FtpSocket::slotDataAccept() +void FtpSocket::slotDataAccept(KNetwork::KStreamSocket *socket) { - ENGINE_ENSURE_THREAD(slotDataAccept()); - - if (!m_serverSocket) - return; - - m_transferSocket = static_cast<KNetwork::KStreamSocket*>(m_serverSocket->accept()); + m_transferSocket = socket; initializeTransferSocket(); // Socket has been accepted so the server is not needed anymore @@ -903,9 +914,6 @@ void FtpSocket::closeDataTransferSocket() { - QObject::disconnect(m_transferSocket, SIGNAL(connected(const KResolverEntry&)), this, SLOT(slotDataConnected())); - QObject::disconnect(m_transferSocket, SIGNAL(gotError(int)), this, SLOT(slotDataError())); - if (m_dataSsl) { m_dataSsl->close(); delete m_dataSsl; @@ -936,29 +944,10 @@ void FtpSocket::slotDataConnected() { - ENGINE_ENSURE_THREAD(slotDataConnected()); - emitEvent(Event::EventMessage, i18n("Data connection established.")); nextCommand(); } -void FtpSocket::slotDataError() -{ - ENGINE_ENSURE_THREAD(slotDataError()); - - if (!m_transferSocket) - return; - - if (isFatalError(m_transferSocket->error())) { - emitEvent(Event::EventMessage, i18n("Data connection failed (%1).").arg(errorString(m_transferSocket->error()))); - - // We received an error, reset with failure - resetCommandClass(Failed); - } else if (m_transferSocket->error() == RemotelyDisconnected) { - transferCompleted(); - } -} - void FtpSocket::variableBufferUpdate(Q_LONG size) { if (size > m_transferBufferSize - 64) { @@ -1051,8 +1040,18 @@ } else size = m_transferSocket->readBlock(m_transferBuffer, m_transferBufferSize); - if (size <= 0) + // Check if the connection has been closed + if (m_transferSocket->error() != NoError) { + if (m_transferSocket->error() != WouldBlock) { + transferCompleted(); + return; + } + } + + if (size <= 0) { + transferCompleted(); return; + } timeoutPing(); --- trunk/extragear/network/kftpgrabber/src/engine/ftpsocket.h #574215:574216 @@ -120,6 +120,7 @@ char *m_transferBuffer; int m_transferBufferSize; int m_transferEnd; + bool m_transferConnecting; KSSL *m_controlSsl; KSSL *m_dataSsl; @@ -129,9 +130,8 @@ void slotControlTryRead(); void slotError(); - void slotDataAccept(); + void slotDataAccept(KNetwork::KStreamSocket *socket); void slotDataConnected(); - void slotDataError(); void slotDataTryRead(); void slotDataTryWrite(); };