Ein Qt-Server
Serverapplikationen sind in Qt nicht von Haus aus IPv6-kompatibel: Die Klasse »QTcpServer«
, die eine Abstraktion für den Serversocket darstellt, horcht immer nur auf einer Adresse, die ungeschickterweise in der Methode »listen()«
mit der IPv4-Adresse »QHostAddress::Any«
als Standardargument vorbelegt ist.
Als Lösung kann der Anwendungsentwickler zunächst beim »listen()«
den IPv6-Parameter »QHostAddress::AnyIPv6«
angeben. Das funktioniert auf allen Plattformen und Distributionen, auf denen die Socketoption »IPV6_V6ONLY«
standardmäßig auf 0 steht. IPv4-Verbindungen erhält das resultierende Programm über V4-mapped-Adressen.
Leider gibt es keinen einfachen Weg, »QTcpServer«
beizubringen, die Socketoption »IPV6_V6ONLY«
zu verwenden. Eine Möglichkeit besteht darin, über »QTcpServer::setSocketDescriptor«
dem Programm einen mit C-Mitteln erzeugten Socket-Deskriptor unterzuschieben.
Eine alternative Lösung findet der Programmierer, indem er sich etwas im Fundus der »QtNetwork«
-Bibliothek umschaut. Dort findet sich die Klasse »QNetworkInterface«
, die alle Netzwerkgeräte aufzählt. Zu jedem Gerät erzeugt sie eine Liste von »QNetworkAddressEntry«
-Instanzen, von denen wiederum jede eine »QHostAddress«
besitzt. Damit lassen sich alle IPv4- und IPv6-Adressen des Systems finden.
Der Entwickler erhält zusätzlich die Möglichkeit, den Serverport nur für bestimmte Netzwerkkarten zu öffnen. Der Code des Qt-Servers (Listings 9 und 10) iteriert über alle Netzwerkgeräte und -adressen (Zeile 29 in Listing 10). Hier ließe sich im Grunde auch die statische Methode »QNetworkInterface::allAddresses()«
verwenden [10], doch leider liefert diese fehlerhafte Link-Local-Adressen.
Für jede Netzwerkadresse legt das Programm einen neuen »QTcpServer«
an (Zeile 46, Listing 10) und verbindet ihn über einen »QSignalMapper«
. Das ist notwendig, weil »QTcpServer«
nur ein Void-Signal »newConnection()«
sendet, was dem Empfänger nicht mitteilt, welcher von mehreren Sendern denn eine neue Verbindung bereithält. »QSignalMapper«
wertet das Signal mit der Senderinformation auf, sodass das Programm dem Besucher mitteilen kann, von welcher IP-Adresse er kommt.
Listing 9
greeter.h
01 #ifndef GREETER_H
02 #define GREETER_H
03
04 #include <QtCore/QObject>
05 #include <QtNetwork/QTcpServer>
06 #include <QtNetwork/QTcpSocket>
07
08
09 class Greeter : public QObject
10 {
11 Q_OBJECT
12
13 public:
14 Greeter(QObject *parent) : QObject(parent) {}
15
16 public slots:
17 void newConnection(QObject* serverObject) {
18 QTcpServer* server = static_cast<QTcpServer*>(serverObject);
19
20 QTcpSocket* connection = server->nextPendingConnection();
21 connect(connection, SIGNAL(disconnected()),
22 connection, SLOT(deleteLater()));
23
24 QHostAddress peerAddress = connection->peerAddress();
25 QString address = peerAddress.toString();
26
27 connection->write("Hello ");
28 connection->write(address.toAscii());
29 connection->write("\n");
30 connection->disconnectFromHost();
31 }
32
33 };
34
35 #endif
Listing 10
server.cpp
01 #include <QtCore/QCoreApplication>
02 #include <QtCore/QSignalMapper>
03 #include <QtCore/QStringList>
04 #include <QtNetwork/QNetworkInterface>
05 #include <QtNetwork/QHostAddress>
06 #include <QtNetwork/QTcpServer>
07 #include <iostream>
08 #include "greeter.h"
09
10
11 int main(int argc, char *argv[])
12 {
13 QCoreApplication app(argc, argv);
14 int port = app.arguments().at(1).toInt();
15
16 Greeter* greeter = new Greeter(&app);
17
18 QSignalMapper* sigMap;
19 sigMap = new QSignalMapper(&app);
20 greeter->connect(sigMap,
21 SIGNAL(mapped(QObject *)),
22 SLOT(newConnection(QObject *)));
23
24 QList<QTcpServer> servers;
25
26 QList<QNetworkInterface> ifs;
27 ifs = QNetworkInterface::allInterfaces();
28
29 foreach(const QNetworkInterface& i, ifs) {
30 QList<QNetworkAddressEntry> entries;
31 entries = i.addressEntries();
32
33 foreach(const QNetworkAddressEntry& entry, entries) {
34 QHostAddress address = entry.ip();
35
36 // fix scope of link-local addresses
37 Q_IPV6ADDR addr6;// = address.toIPv6Address();
38 addr6 = address.toIPv6Address();
39 if (addr6[0] == 0xfe &&
40 addr6[1] == 0x80) {
41 QString name=i.humanReadableName();
42 address.setScopeId(name);
43 }
44
45 QTcpServer* server;
46 server = new QTcpServer(&app);
47 sigMap->setMapping(server, server);
48 sigMap->connect(server,
49 SIGNAL(newConnection()),
50 SLOT(map()));
51
52 server->listen(address, port);
53 if (!server->isListening()) {
54 std::cout << "Cannot listen on "
55 << address.toString().toAscii().constData() << std::endl;
56 }
57 }
58 }
59
60 return app.exec();
61 }
IPv6 in der Perl-Programmierung
Wer in Perl IPv6-fähige Programme schreiben wollte, benötigte bisher das CPAN-Modul »Socket6«
, das Funktionen wie »getaddrinfo()«
bereitstellte. Seit Perl 5.14 (Mai 2011) steckt die nötige Funktionalität im Modul »Socket«
[11] und damit im Perl-Core. Listing 11 zeigt seine Anwendung. Komfortabler gerät die Socketprogrammierung mit »IO::Socket«
[12]. Das für IPv4 nötige »IO::Socket::INET«
ist schon lange Teil des Perl-Core, für IPv6 existiert das ausgereifte Modul »IO::Socket::INET6«
vom CPAN:
use IO::Socket::INET6; my $sock6 = IO::Socket::INET6->new( '[::1]:12345' ); my $sock4 = IO::Socket::INET6->new( '127.0.0.1:12345' );
Das Modul ist abwärtskompatibel zu »IO::Socket: :INET«
und kann auch IPv4-Verbindungen herstellen. Die Adressen akzeptiert es als Hostnamen und in IPv4- oder IPv6-Notation: Da »IO: :Socket::INET«
eine einfachere Programmierung als Sockets im Libc-Stil bietet, nutzten es bisher viele Programme und Perl-Module, die damit nicht IPv6-tauglich sind. Darunter finden sich auch Core-Module wie »Net::SMTP«
, »Net::FTP«
und wichtige CPAN-Module wie »LWP«
.
In vielen Fällen lassen sich diese Programme mit Hilfe von »Net::INET6Glue::INET_is_INET6«
[13] IPv6-fähig machen, da dieses »IO::Socket::INET«
mit »IO::Socket::INET6«
ersetzt:
use Net::INET6Glue::INET_is_INET6;
use LWP::Simple;
print get('http://ipv6.google.com/'); Folgendes Kommando macht ein vorhandenes Programm IPv6-fähig:
$ perl -MNet::INET6Glue::INET_is_INET6 ipv4_programm.pl
»Net::INET6Glue::FTP«
erweitert darüber hinaus auch »Net::FTP«
um die für IPv6 erforderlichen Kommandos »EPRT«
und »EPSV«
. »IO::Socket::SSL«
bietet eine einfache Nutzung von SSL und unterstützt automatisch IPv6, sofern »IO::Socket::INET6«
installiert ist.
Darüber hinaus gibt es in Perl noch mehrere Bibliotheken für einen nicht blockierenden Umgang mit Sockets. Diese unterstützen vielfach auch IPv6, benutzen aber wie beispielsweise »AnyEvent«
und POE für die Adressenauflösung nicht das blockierende »getaddrinfo()«
, sondern besitzen ihren eigenen Resolver. Dieser liefert allerdings die Ergebnisse möglicherweise in einer anderen Reihenfolge, als es mit »getaddrinfo()«
üblich ist.
Weitere Informationen zu IPv6 in der Perl-Programmierung finden sich unter anderem in Vorträgen auf den Deutschen Perl-Workshops 2009 [14] und 2010 [15]. (Steffen Ullrich)
Listing 11
Perl-Socket
01 #!/usr/bin/perl
02
03 use Socket6; # ab 5.14 reicht 'use Socket'
04 my @res = getaddrinfo('ipv6.google.com','http',AF_UNSPEC,SOCK_STREAM );
05 my $sock;
06 while (! $sock and @res) {
07 my ($fam,$type,$proto,$saddr,$cname) = splice(@res,0,5);
08 socket($sock,$fam,$type,$proto) or die $!;
09 connect($sock,$saddr) and last;
10 undef $sock;
11 }
12 $sock or die $!;
Fazit
Eigentlich ist es nicht schwer, ein Programm IPv6-kompatibel zu machen, wenn man weiß, worauf zu achten ist. Clientprogramme sind in der Regel unproblematisch, aber Programme mit Serverfunktionalität gewinnen an Komplexität, wenn der Entwickler alle Eventualitäten einkalkulieren möchte. Frameworks mit hohem Abstraktionsgrad können sich dabei als problematisch erweisen, wenn sie wichtige Funktionalität nicht in ihrer Abstraktionsschicht abbilden, wie etwa »IPV6_V6ONLY«
im Qt-Toolkit. (mhu)
Infos
- RFC 3493: http://tools.ietf.org/html/rfc3493
- Glibc-Bug: http://sourceware.org/bugzilla/show_bug.cgi?id=12377
- Bug in Ubuntu-Eglibc: https://bugs.launchpad.net/ubuntu/+source/eglibc/+bug/762512
- »
getaddrinfo()« in Python: http://docs.python.org/library/socket.html#socket.getaddrinfo - Sicherheitsprobleme von »
V4MAPPED« -Adressen: http://tools.ietf.org/html/draft-itojun-v6ops-v4mapped-harmful-02 - »
select()« in Python: http://docs.python.org/library/select.html#select.select - »
QtNetwork« -Bibliothek: http://doc.qt.nokia.com/4.6/network-programming.html - »
QHostAddress« : http://doc.qt.nokia.com/4.6/qhostaddress.html - »
QHostInfo« : http://doc.qt.nokia.com/4.6/qhostinfo.html - »
QNetworkInterface« : http://doc.qt.nokia.com/4.6/qnetworkinterface.html - Perl-Modul-Socket: http://perldoc.perl.org/Socket.html
- »
IO::Socket::INET« : http://perldoc.perl.org/IO/Socket/INET.html - »
Net::INET6Glue« : http://search.cpan.org/~sullr/Net-INET6Glue-0.5/ - Steffen Ullrich, "IPv6 und Perl": Vortrag 2009, http://maulwuff.de/pws/2009/s5/ipv6.html
- Steffen Ullrich, "Migration von Applikationen zu IPv6": Vortrag 2010, http://maulwuff.de/pws/2010/ipv6.html
- Listings zum Artikel samt Projektdateien für Qmake: http://www.linux-magazin.de/static/listings/magazin/2011/08/ipv6-programmieren/
Diesen Artikel als PDF kaufen
Express-Kauf als PDF
Umfang: 7 Heftseiten
Preis € 0,99
(inkl. 19% MwSt.)
Als digitales Abo
Weitere Produkte im Medialinx Shop »
Versandartikel
Onlineartikel
Alle Rezensionen aus dem Linux-Magazin
- Buecher/07 Bücher über 3-D-Programmierung sowie die Sprache Dart
- Buecher/06 Bücher über Map-Reduce und über die Sprache Erlang
- Buecher/05 Bücher über Scala und über Suchmaschinen-Optimierung
- Buecher/04 Bücher über Metasploit sowie über Erlang/OTP
- Buecher/03 Bücher über die LPI-Level-2-Zertifizierung
- Buecher/02 Bücher über Node.js und über nebenläufige Programmierung
- Buecher/01 Bücher über Linux-HA sowie über PHP-Webprogrammierung
- Buecher/12 Bücher über HTML-5-Apps sowie Computer Vision mit Python
- Buecher/11 Bücher über Statistik sowie über C++-Metaprogrammierung
- Buecher/10 Bücher zu PHP-Webbots sowie zur Emacs-Programmierung
Insecurity Bulletin
Im Insecurity Bulletin widmet sich Mark Vogelsberger aktuellen Sicherheitslücken sowie Hintergründen und Security-Grundlagen. mehr...





