[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kopete-devel
Subject:    Re: [kopete-devel] QHostAddress (Re: Patch for Bug 101164 ...)
From:       Thiago Macieira <thiago () kde ! org>
Date:       2005-05-30 12:11:29
Message-ID: 200505300911.41200.thiago () kde ! org
[Download RAW message or body]

[Attachment #2 (multipart/signed)]

[Attachment #4 (multipart/mixed)]


Martijn Klingens wrote:
>On Thursday 26 May 2005 16:18, Thiago Macieira wrote:
>> I'd rather you didn't use QHostAddress anywhere in KDE code.
>>
>> It's broken by design and I am trying to convince Trolltech of that.
>
>Out of curiosity, what's wrong with it?

See the attached email, which I sent to qt-bugs. QHostAddress is broken by 
design because it's too small. It needs to be 25% bigger (24 bytes) to 
properly hold an IPv6 address. In other words, an address is more than 
just an IP and a port number.

And that's not counting DCCP, which a friend of mine is adding to the 
Linux kernel, and which will require an extra 4 bytes.

The proper solution would be to keep the real sockaddr structure, just 
like I do with KSocketAddress.

-- 
  Thiago Macieira  -  thiago (AT) macieira (DOT) info
    PGP/GPG: 0x6EF45358; fingerprint:
    E067 918B B660 DBD1 105C  966C 33F5 F005 6EF4 5358

5. Swa he géanhwearf tó timbran, and hwonne he cóm, lá! Unix cwæð "Hello, 
World". Ǽfre ǽghwilc wæs glæd and seo woruld wæs fréo.

["forwarded message" (message/rfc822)]


[Attachment #9 (multipart/mixed)]


Hello,

This bug is about Qt4, with the snapshot 4.0.0-rc1-snapshot-20050525, as 
found in the KDE Subversion repository.

Short version:
  QHostAddress isn't enough to hold proper addresses

Long version:
  Some IPv6 addresses require more information than a simple 128-bit 
address, and Qt4 currently fails with them. Link-local addresses in 
stream connections require the sin6_scope_id field of sockaddr_in6 to be 
set when connect(2)ing, and that information is discarded by Qt4's 
QHostInfo (QHostInfoAgent::fromName in network/qhostinfo_unix.cpp).

That information must not be discarded.

Testcase:
  The following testcase (attached) is trying to connect to an Apache 2.0 
server running on a remote machine on the same network. The Apache httpd 
server has no special configuration, and is configured to listen with a 
directive:
=====
Listen 80
=====

Site-local addressing:
$ ./streamsockettest fec0::8000:200:21ff:fe69:43a7 80
Socket state changed to 1
Socket state changed to 2
Socket name lookup finished
Socket state changed to 3
Socket has connected

Socket is ready for writing; will write:
GET / HTTP/1.0


HTTP/1.1 200 OK
Date: Sat, 28 May 2005 15:59:44 GMT
Server: Apache/2.0.49 (Unix) mod_perl/1.99_13 Perl/v5.8.3 mod_ssl/2.0.49 
OpenSSL/0.9.7c
Last-Modified: Sat, 23 Aug 2003 20:25:08 GMT
ETag: "274f9-1-6193b100"
Accept-Ranges: bytes
Content-Length: 1
Connection: close
Content-Type: text/plain; charset=ISO-8859-1


Socket state changed to 6
Socket state changed to 0
Socket has closed

Link-local addressing, without scopeid:
$ ./streamsockettest fe80::200:21ff:fe69:43a7 80
Socket state changed to 1
Socket state changed to 2
Socket name lookup finished
Socket got error 10
Socket state changed to 6
Socket state changed to 0
Socket has closed

Link-local addressing, with scopeid:
$ ./streamsockettest fe80::200:21ff:fe69:43a7%eth0 80
Socket state changed to 1
Socket state changed to 2
Socket name lookup finished
Socket got error 10
Socket state changed to 6
Socket state changed to 0
Socket has closed

strace reveals in both cases:
$ strace ./streamsockettest fe80::200:21ff:fe69:43a7 80 2>&1 | grep 
connect
connect(7, {sa_family=AF_INET6, sin6_port=htons(80), inet_pton(AF_INET6, 
"fe80::200:21ff:fe69:43a7", &sin6_addr), sin6_flowinfo=0, 
sin6_scope_id=0}, 28) = -1 EINVAL (Invalid argument)

$ strace ./streamsockettest fe80::200:21ff:fe69:43a7%eth0 80 2>&1 | grep 
connect
connect(9, {sa_family=AF_INET6, sin6_port=htons(80), inet_pton(AF_INET6, 
"fe80::200:21ff:fe69:43a7", &sin6_addr), sin6_flowinfo=0, 
sin6_scope_id=0}, 28) = -1 EINVAL (Invalid argument)


Comparison testcase:
$ telnet fec0::8000:200:21ff:fe69:43a7 80
Trying fec0::8000:200:21ff:fe69:43a7...
Connected to fec0::8000:200:21ff:fe69:43a7.
Escape character is '^]'.

$ telnet fe80::200:21ff:fe69:43a7 80
Trying fe80::200:21ff:fe69:43a7...
telnet: connect to address fe80::200:21ff:fe69:43a7: Invalid argument

$ telnet fe80::200:21ff:fe69:43a7%eth0 80
Trying fe80::200:21ff:fe69:43a7%eth0...
Connected to fe80::200:21ff:fe69:43a7%eth0.
Escape character is '^]'.

strace:
$ strace telnet fe80::200:21ff:fe69:43a7 80 2>&1 | grep connect
connect(3, {sa_family=AF_INET6, sin6_port=htons(80), inet_pton(AF_INET6, 
"fe80::200:21ff:fe69:43a7", &sin6_addr), sin6_flowinfo=0, 
sin6_scope_id=0}, 28) = -1 EINVAL (Invalid argument)

$ strace telnet fe80::200:21ff:fe69:43a7%eth0 80 2>&1 | grep connect
connect(3, {sa_family=AF_INET6, sin6_port=htons(80), inet_pton(AF_INET6, 
"fe80::200:21ff:fe69:43a7", &sin6_addr), sin6_flowinfo=0, 
sin6_scope_id=if_nametoindex("eth0")}, 28) = 0


For comparison, the KDE version of this program works fine. It is 
available at 
http://websvn.kde.org/branches/work/kde4/kdelibs/kdecore/network/tests/streamsockettest.cpp
-- 
  Thiago Macieira  -  thiago (AT) macieira (DOT) info
    PGP/GPG: 0x6EF45358; fingerprint:
    E067 918B B660 DBD1 105C  966C 33F5 F005 6EF4 5358

2. Tó cennan his weorc gearu, ymbe se circolwyrde, wearð se cægbord and se 
leohtspeccabord, and þa mýs cómon lator. On þone dæg, he hine reste.

["streamsockettest.cpp" (text/x-c++src)]

#include <unistd.h>

#include <QtCore/QCoreApplication>
#include <QtCore/QString>
#include <QtCore/QByteArray>
#include <QtNetwork/QHostAddress>

#include "streamsockettest.h"

#include <iostream>
using namespace std;

int timeout = 0;
Test::Test(QString host, QString service, QString lhost, QString lservice, bool \
blocking) {
  QObject::connect(&socket, SIGNAL(stateChanged(SocketState)), this, \
SLOT(stateChangedSlot(SocketState)));  QObject::connect(&socket, \
SIGNAL(error(SocketError)), this, SLOT(gotErrorSlot(SocketError)));  \
QObject::connect(&socket, SIGNAL(hostFound()), this, SLOT(hostFoundSlot()));  \
QObject::connect(&socket, SIGNAL(connected()),   this, SLOT(connectedSlot()));
  QObject::connect(&socket, SIGNAL(disconnected()), this, SLOT(closedSlot()));
  QObject::connect(&socket, SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
  //QObject::connect(&socket, SIGNAL(readyWrite()), this, SLOT(readyWriteSlot()));
  socket.connectToHost(host, service.toUInt());
}

void Test::stateChangedSlot(QTcpSocket::SocketState newstate)
{
  cout << "Socket state changed to " << (int)newstate << endl;
}

void Test::gotErrorSlot(QTcpSocket::SocketError errorcode)
{
  cerr << "Socket got error " << (int)errorcode << endl;
  if (errorcode != QTcpSocket::RemoteHostClosedError)
    QCoreApplication::exit();
}

void Test::hostFoundSlot()
{
  cout << "Socket name lookup finished" << endl;
}

void Test::connectedSlot()
{
  cout << "Socket has connected" << endl;
  readyWriteSlot();
}

void Test::timedOutSlot()
{
  cout << "Socket timed out connecting" << endl;
  QCoreApplication::exit();
}

void Test::closedSlot()
{
  cout << "Socket has closed" << endl;
  QCoreApplication::exit();
}
    
void Test::readyReadSlot()
{
  char buf[512];
  qint64 retval;
  do
    {
      retval = socket.readLine(buf, sizeof buf);
      if (retval <= 0)
        socket.close();		// EOF?
      else
        cout << buf;
    }
  while (retval > 0);
}

void Test::readyWriteSlot()
{
  QByteArray data("GET / HTTP/1.0\r\n\r\n");
  
  cout << endl << "Socket is ready for writing; will write: " << endl;
  cout << data.data() << endl;
  
  socket.write(data.data(), data.length());
}

int main(int argc, char **argv)
{
  QCoreApplication a(argc, argv);

  bool blocking = false;
  int c;
  while ((c = getopt(argc, argv, "bt:")) != -1)
    switch (c)
      {
      case 'b':
	blocking = true;
	break;

      case 't':
	timeout = QString::fromLatin1(optarg).toInt();
	break;
      }

  if (argc - optind < 2)
    return 1;

  QString lhost, lservice;
  if (argc - optind >= 3)
    lhost = QString::fromLocal8Bit(argv[optind + 2]);
  if (argc - optind >= 4)
    lservice = QString::fromLocal8Bit(argv[optind + 3]);
  Test test(QString::fromLocal8Bit(argv[optind]), QString::fromLocal8Bit(argv[optind \
+ 1]),   lhost, lservice, blocking);

  return a.exec();
}

#include "streamsockettest.moc"


["streamsockettest.h" (text/x-c++hdr)]

#ifndef streamsockettest_h
#define streamsockettest_h

#include <QtNetwork/QTcpSocket>

class Test : public QObject
{
  Q_OBJECT

public:
  QTcpSocket socket;

  Test(QString host, QString service, QString lhost, QString lservice, bool blocking);

public slots:
  void stateChangedSlot(QTcpSocket::SocketState newstate);
  void gotErrorSlot(QTcpSocket::SocketError errorcode);
  void hostFoundSlot();
  void connectedSlot();
  void timedOutSlot();
  void closedSlot();    
  void readyReadSlot();
  void readyWriteSlot();
};

#endif

[Attachment #14 (application/pgp-signature)]
[Attachment #15 (application/pgp-signature)]

_______________________________________________
kopete-devel mailing list
kopete-devel@kde.org
https://mail.kde.org/mailman/listinfo/kopete-devel


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic