9/2 Grundlagen

Transcrição

9/2 Grundlagen
Grundlagen
Teil 9/2.1 Seite 1
Einführung
9/2
Grundlagen
9/2.1
Einführung
Um genau zu verstehen, warum manche Sachen gerade so
funktionieren und nicht anders, ist es hilfreich, zumindest
grundlegende Kenntnisse über die internen Abläufe der Kommunikation via Internet zu haben.
Abläufe
In den folgenden Kapiteln wollen wir Ihnen zeigen, wie diese
Kommunikation über UNIX-Sockets zu Stande kommt und
welche Daten in welchen Formaten und welchen Protokollen
wie und wann über das Internet verschickt werden.
So erkennt man plötzlich, dass Passworte im Klartext über
relativ einfach „anzapfbare“ Internet-Verbindungen übertragen werden, dass es bei Kenntnis der Kommunikationsstati
möglich ist, bestehende Verbindungen „feindlich“ zu übernehmen, was passiert, wenn plötzlich einzelne Server ausfallen und vieles andere mehr.
Neben den Grundlagen finden Sie natürlich auch viele praktische Tipps und Hinweise, wie Sie z.B. selbst Internet-Clients
und -Server programmieren können oder wie Sie sich einfach
über Ihre Tastatur direkt mit einem {HTTP, SMTP, POP3,
...}-Server irgendwo im Internet auf „Protokoll-Niveau“ unterhalten können.
Praxistips
Teil 9/2.1 Seite 2
Einführung
Grundlagen
Grundlagen
Teil 9/2.2 Seite 1
Socket-Programmierung
9/2.2
Socket-Programmierung
Die Kommunikation im Internet läuft über Ports, die von Dämonen bzw. Servern auf einer bestimmten Maschine bedient
werden.
Ports
Der Ablauf der Kommunikation zwischen einem Client und
einem Server geschieht immer nach dem gleichen Prinzip:
• Zuerst wird das Protokoll festgelegt, über das kommuniziert werden soll (z. B. TCP).
• Dann wird der Port bestimmt, über den kommuniziert
werden soll (für HTTP z. B. 80).
• Als Letztes wird festgelegt, mit welchem Rechner kommuniziert werden soll.
Jetzt liegen die Informationen Protokoll-ID, Portnummer und
IP-Adresse des Gegenübers sowie die eigene IP-Adresse vor.
Socket
• Aus diesen Informationen werden jetzt die Adressen der
beiden Socket-Endpunkte bestimmt.
• Dann wird ein Socket erzeugt, der wie eine Art Pipe
(Rohr) angesehen werden kann, auf deren beide Enden allerdings nur die beiden Kommunikationspartner lesend
und schreibend zugreifen können.
• Mit bind wird das eine Ende des Sockets dem erzeugenden
Prozess als Stream zur Verfügung gestellt.
• Mit connect wird das andere Ende des Sockets mit dem
Server, der den angegebenen Port auf dem anderen Rechner bedient, verbunden.
Nun kann vom Client-Prozess aus in den Socket geschrieben
und aus ihm gelesen werden, als ob es sich um eine reguläre
Datei handeln würde. Die geschriebenen Daten werden jedoch
über das definierte Protokoll an den für den angegebenen Port
zuständigen Server auf dem anderen Rechner übermittelt. Ebenso werden Daten, die von diesem Server kommen, im Socket zum Lesen bereitgestellt.
Kommunikation
Teil 9/2.2 Seite 2
Grundlagen
Socket-Programmierung
#!/usr/bin/perl
($them,$port) = @ARGV;
$port = 2345 unless $port;
$them = ’localhost’ unless $them;
$SIG{’INT’} = ’dokill’;
sub dokill { kill 9,$child if $child; }
use Socket;
$sockaddr = ’S n a4 x8’;
chop($hostname = ‘hostname‘);
($name, $aliases, $proto) = getprotobyname(’tcp’);
($name, $aliases, $port) = getservbyname($port, ’tcp’)
unless $port =~ /^\d+$/;
($name, $aliases, $type, $len, $thisaddr) = gethostbyname($hostname);
($name, $aliases, $type, $len, $thataddr) = gethostbyname($them);
$this = pack($sockaddr, AF_INET, 0, $thisaddr);
$that = pack($sockaddr, AF_INET, $port, $thataddr);
$me = join(".",unpack("C4",$thisaddr));
$you = join(".",unpack("C4",$thataddr));
print
print
print
print
"Create Socket proto=$proto, ";
"port=$port,\n";
"this=($me/$hostname), ";
"that=($you/$them)\n";
socket(S, PF_INET, SOCK_STREAM, $proto) || die "socket: $!";
bind(S, $this) || die "bind: $!";
connect(S, $that) || die "connect: $!";
select(S); $| = 1; select(stdout); $| = 1;
if ($child = fork)
{ while (<STDIN>) { print S; }
sleep 3;
do dokill();
} else
{ while (<S>) { print; }
}
Grundlagen
Teil 9/2.2 Seite 3
Socket-Programmierung
Beispiel-Scripts zur Socket-Programmierung
Wie das konkret funktioniert sei an den beiden einfachsten
Perl-Scripts aus dem Perl-Handbuch demonstriert, die auf der
gegenüberliegenden und auf der folgenden Seite abgedruckt
sind. Das erste Script zeigt einen einfachen Client, das zweite
einen einfachen Server, der auf einem bestimmten Port auf
eingehende Verbindungen wartet.
Einfacher
TCP-Client
Im ersten Block werden die optional übergebenen Parameter
(das Script kann mit den Parametern Host und Portnummer
aufgerufen werden) in den Variablen $them und $port abgelegt. Falls nichts eingegeben wurde, wird als Host localhost
und als Port 2345 verwendet.
Beschreibung
des Scripts
Im zweiten Abschnitt wird die Signalbehandlungsroutine im
Fall des Abbrechens (Ctrl-C, entspricht SIG_INT) umdefiniert, indem der später gestartete Child-Prozess, falls er existiert, erst gekillt und dann erst das Programm beendet wird.
• getprotobyname liefert die Nummer des im Klartext angegebenen Protokolls (tcp) zurück, die unter UNIX z. B. in
/etc/protocols definiert sind (s. Kapitel 1/6.2 Protokolle).
• getservbyname liefert die Port-Nummer des im Klartext
angegebenen Services. Diese sind unter UNIX z. B. in der
Datei /etc/services definiert (s. Kapitel 1/6.3 Services)
• gethostbyname liefert schließlich die IP-Adresse des im
Klartext angegebenen Hosts.
$sockaddr = 'S n a4 x8' legt das Format für den SocketAddress-String fest, der dann mit pack gefüllt wird. Dieser
String stellt eine generische Socket-Adresse dar, die aus folgenden Elementen besteht:
• S: Unsigned Short (16 Bit) mit der Konstanten AF_INET,
die die IP-Prokollfamilie identifiziert (=2)
• N: Short in Network-Order (Big-Endian), der die Portnummer bzw. 0 (je nach Ende des Sockets) enthält.
Teil 9/2.2 Seite 4
Grundlagen
Socket-Programmierung
• a4: ASCII-String mit 4 Zeichen (mit Nullen aufgefüllt).
Dieser String enthält die 4 Byte der IP-Adresse des jeweiligen Kommunikationspartners. 192.168.1.1 wird z.B. als
String mit den ASCII-Zeichen 192, 168, 1 und 1 codiert.
• x8: 8 Nullbytes (reserviert u. a. für IP6 (AF_INET6=10),
das dann 8-Byte-IP-Adressen unterstützen wird).
#!/usr/bin/perl
($port) = @ARGV;
$port = 2345 unless $port;
use Socket;
$sockaddr = ’S n a4 x8’;
($name, $aliases, $proto) = getprotobyname(’tcp’);
($name, $aliases, $port) = getservbyname($port, ’tcp’)
unless $port =~ /^\d+$/;
$this = pack($sockaddr, AF_INET, $port, "\0\0\0\0");
select(NS); $| = 1; select(stdout);
socket(S, PF_INET, SOCK_STREAM, $proto) || die "socket: $!";
bind(S, $this) || die "bind: $!";
listen(S, 5) || die "connect: $!";
select(S); $| = 1; select(stdout); $| = 1;
for (;;)
{ print "Listening again\n";
($addr = accept(NS,S)) || die $!;
print "accept ok\n";
($af,$port,$inetaddr) = unpack($sockaddr,$addr);
@inetaddr = unpack(’C4’,$inetaddr);
print "$af $port @inetaddr\n";
while (<NS>)
{ print;
print NS;
}
}
Grundlagen
Teil 9/2.2 Seite 5
Socket-Programmierung
Nachdem wir verbunden sind und der Socket auf unbuffered
($|=1) umgestellt wurde, teilen wir unseren Prozess in zwei
Prozesse, von denen der eine (der von fork die Prozess-ID
des neuen zurückerhält) alles, was auf der Standardeingabe
eingegeben wird an den Socket schickt. Der andere Prozess
(der Child-Prozess, der von fork eine 0 zurückgeliefert bekommt) gibt einfach alles, was über den Socket ankommt, auf
dem Terminal aus.
Einfacher
TCP-Server
Das Script für die Implementation des Servers ist analog zu
dem des Client aufgebaut. Zuerst wird das optionale Argument Port ausgewertet. Daraufhin wird wieder die ProtokollID und die Portnummer bestimmt.
Beschreibung
des Scripts
Nun erscheint bereits der erste Unterschied: Wir binden zwar
wieder das lokale Ende des Sockets, aber diesmal mit der IPAdresse 0.0.0.0 (IN_ADDR_ANY, d. h. wir akzeptieren Verbindungen von beliebigen IP-Adressen) und der richtigen
Port-Nummer. Das andere Ende des Sockets wird jedoch
nicht connectet, sondern wir setzen einen listen ab (die 5 bedeutet, dass maximal 5 Verbindungen warten dürfen. Werden
es mehr, erhalten diese Clients einen „connection refused error“). Um nun die Verbindung zu etablieren, warten wir mit
accept auf einen eingehenden connect und bearbeiten diesen
dann, bis das Dateiende an diesem Eingang (NS) erreicht ist.
Verwendung der Beispielscripts
Auch ohne das SimpleClient-Script von oben kann mit dem
Systemprogramm telnet bereits eine Verbindung zu einem
beliebigen Port aufgebaut werden. Dazu muss telnet neben
dem Host nur noch eine Portnummer mit übergeben werden
(Eingaben in fett):
Port-Verbindung
mit Telnet
Teil 9/2.2 Seite 6
Grundlagen
Socket-Programmierung
$ telnet localhost 80
Trying 127.0.0.1...
Connected to localhost.
Escape character is ’^]’.
HEAD / HTTP/1.0
HTTP/1.1 200 OK
Date: Thu, 18 Jun 1998 06:42:01 GMT
Server: Apache/1.2.5
Last-Modified: Mon, 09 Mar 1998 15:26:45 GMT
ETag: "4002-174f-35040a35"
Content-Length: 5967
Accept-Ranges: bytes
Connection: close
Content-Type: text/html
Connection closed by foreign host.
Auf Port 80 des lokalen Rechners wurde eine Telnet-Session
gestartet. Hier erwartete uns ein Apache HTTP-Server, der
auf unsere Anfrage nach der Information zur Rootpage (Methode HEAD, Datei /, Protokoll HTTP/1.0, s. Kapitel 9/4.6
HTTP) mit dem entsprechenden Reply antwortete und danach die Verbindung geschlossen hat (die Meldung „Connection closed by foreign host“ stammt von telnet).
SimpleClient
Analog funktioniert das SimpleClient-Script, das nach einem
SimpleClient localhost 80 folgende Ausgaben macht:
Create Socket proto=6, port=80,
this=(127.0.0.1/localhost),
that=(127.0.0.1/localhost)
HEAD / HTTP/1.0
HTTP/1.1 200 OK
Date: Thu, 18 Jun 1998 06:42:01 GMT
Server: Apache/1.2.5
Last-Modified: Mon, 09 Mar 1998 15:26:45 GMT
ETag: "4002-174f-35040a35"
Content-Length: 5967
Accept-Ranges: bytes
Connection: close
Content-Type: text/html
Grundlagen
Teil 9/2.2 Seite 7
Socket-Programmierung
Setzen wir statt eines HEAD-Kommandos ein GET ab, erhalten wir die angegebene Web-Seite geliefert.
Create Socket proto=6, port=80,
this=(127.0.0.1/localhost),
that=(127.0.0.1/localhost)
GET / HTTP/1.0
HTTP/1.1 200 OK
Date: Thu, 18 Jun 1998 06:42:01 GMT
Server: Apache/1.2.5
Last-Modified: Mon, 09 Mar 1998 15:26:45 GMT
ETag: "4002-174f-35040a35"
Content-Length: 5967
Accept-Ranges: bytes
Connection: close
Content-Type: text/html
<HTML>
<HEAD><TITLE>Titelseite</TITLE></HEAD>
<BODY> ... </BODY>
</HTML>
Hierbei erkennt man, dass der HTTP-Server bei dem zurückgelieferten Informationskopf jede Zeile mit einem CRLF
(ASCII 13 + ASCII 10) abschließt, die Datei jedoch unverändert übermittelt.
Um nun das SimpleServer-Script zu testen, starten wir den
Server mit SimpleServer und connecten mit einem WebBrowser (hier einfach Lynx) unter Angabe des Ports (URL:
http://localhost:2345/) auf diesen Server.
SimpleServer
Teil 9/2.2 Seite 8
Grundlagen
Socket-Programmierung
Listening again
accept ok
2 1063 127 0 0 1
GET /?0,0 HTTP/1.0
Host: localhost:2345
Accept: text/html, text/plain, text/sgml,
*/*;q=0.01
Accept-Encoding: gzip, compress
Accept-Language: en
Negotiate: trans
User-Agent: Lynx/2.8rel.2 libwww-FM/2.14
Referer: http://localhost/test.html
Listening again
accept ok
2 1064 127 0 0 1
GET /subdir/file.html HTTP/1.0
Host: localhost:2345
Accept: text/html, text/plain, text/sgml,
*/*;q=0.01
Accept-Encoding: gzip, compress
Accept-Language: en
Negotiate: trans
User-Agent: Lynx/2.8rel.2 libwww-FM/2.14
Listening again
Diese Log-Datei zeigt zwei Verbindungen: Als Erste die Anfrage auf eine serverseitige Imagemap der Art
<A HREF="http://localhost:2345"><IMG
SRC="myimage.gif" ISMAP></A>
die als Argumente (nach dem ?) die Koordinaten 0,0 zurückgeliefert hat. Die zweite Verbindung war die einfache Abfrage einer Datei /subdir/file.html. Die Texte „Listening again“
und, „accept ok“ und die Angabe der Socketadresse (2 1064
127 0 0 1) stammen dabei vom SimpleServer-Script, alles
andere sind Texte, die Lynx an den Server geschickt hat.
Grundlagen
Teil 9/2.3 Seite 1
Windows-Sockets-API
9/2.3
Windows-Sockets-API
Autor: Andreas Ecker
Dieser Artikel gibt Ihnen einen Überblick über die Konstanten, Datenstrukturen und Funktionen, die das WindowsSockets-API (kurz WinSock-API) zur Verfügung stellt.
WinSock-API
Die meisten auf dem Markt verfügbaren Internet-Applikationen stellen ausschließlich die WinSock-API der Version 1.1,
oft in Form einer DLL in der Datei WINSOCK.DLL, zur
Verfügung. Von Microsoft wird eine Implementierung des
WinSock-API der Version 1.1 angeboten, die für alle Windows-Betriebssysteme ab Windows for Workgroups 3.11
kostenlos verfügbar ist. In Windows 95 und NT ist sie sogar
fest ins Betriebssystem integriert, allerdings handelt es sich
dabei um eine 16-Bit-DLL, die nur von 16-Bit-Applikationen
eingebunden werden kann.
Verfügbarkeit
Unter Windows 3.1 muss man auf eine der vielen SharewareVarianten ausweichen. Die stabilste Shareware-Implementierung stammt nach unseren Erfahrungen von Peter Tattam und
heißt Trumpet WinSocket. Diese Implementierung hat eine
Startapplikation mit integriertem RAS (zum Aufbau einer
Modem-Verbindung zum ISP) und unterstützt die Verbindungsprotokolle SLIP und PPP.
Eine Implementierung der Version 2.0 des WinSock-API ist
in Windows 95 und NT integriert und wird im Systemverzeichnis in Form der Datei WSOCK32.DLL bereitgestellt.
Das WinSock-API ist ein offener Standard. Die schon recht
umfangreiche Dokumentation von Version 1.1 der WinSockAPI ist in der Version 2.0 nochmals erheblich angewachsen.
Das lässt sich schon an der Dateigröße der Dokumentation
(einer ASCII-Datei) ablesen: während für die Version 1.1
noch 330 kB ausreichten, benötigt die Version 2.0 schon mehr
als das Doppelte. Hinzu kommen noch einmal knapp 400 kB
für das seit 2.0 verfügbare Dokument zum Service Provider
Interface.
Versionsumfang
Teil 9/2.3 Seite 2
Grundlagen
Windows-Sockets-API
Lassen Sie sich nicht vom Umfang der API-Dokumente abschrecken. Ein Großteil der Texte beschreibt Funktionen, die
ausschließlich für Sonderfälle vorgesehen sind, oder ist – vor
allem bei gleichartigen Funktionen – extrem redundant.
Applikationen
Mit wenigen Kernfunktionen sind bereits professionelle, asynchrone Netz-Applikationen wie ein Web-Browser oder ein
E-Mail-Client entwickelbar. Auch die Erweiterungen der
Version 2.0 sind dazu nicht notwendig.
Entsprechend dem Umfang des API geht dieser Artikel nur auf
die wichtigsten Funktionen des WinSock-API 1.1 näher ein.
Internet-BasisProtokolle
Das WinSock-API stellt die Transport-Protokolle der IPS zur
Verfügung, die vor allem für die Internet-Programmierung
benötigt werden. Das WinSock-API kapselt die LowLevelbzw. Basisprotokolle der IPS bis einschließlich zur Transportschicht. Dazu gehören vor allem die Protokolle TCP und
UDP sowie das Internet-Protokoll (IP).
DNS
Die im Protokoll-Stack höher liegenden Internet-Protokolle
müssen selbst programmiert oder mit Hilfe einer Komponente
abgedeckt werden. Eine Ausnahme bildet das Domain-Protokoll des DNS (Domain Name Service). WinSock-ProgrammiererInnen können diesen Dienst ohne Kenntnisse des Protokolls nutzen. Die dafür zuständigen Funktionen werden in
den WinSock-Dokumentationen als Datenbankfunktionen bezeichnet.
ICMP
Ausgefallene WinSock-Implementierungen bieten auch den
Zugriff auf tiefer liegende Protokolle wie ICMP (Internet
Control Message Protocol) oder unterstützen (LowLevel-)
Techniken zur direkten Erstellung und Manipulation der zu
verschickenden Datenpakete.
Raw-Sockets
Der Zugriff auf die tiefer liegenden Schichten erfolgt mit Hilfe von Raw-Sockets, die jedoch nur in speziellen WinSockVersionen implementiert sind und nicht Thema dieses Artikels sind.
Grundlagen
Teil 9/2.3 Seite 3
Windows-Sockets-API
WinSock-API 1.1 – Konstanten
Für C-ProgrammiererInnen werden in der Headerdatei WINSOCK.H Deklarationen der Parameter- oder Rückgabewerte
des WinSock-API der Version 1.1 bereitgestellt. Diese mit
30 kB nicht gerade kleine Header-Datei ist frei verfügbar und
standardisiert. Für Visual Basic gibt es bisher keine standardisierte Deklarationsdatei.
Winsock.h
Das Socket-API stellt mit seiner Vielzahl von Konstanten und
Datenstrukturen und den ca. 30 Funktionen den größten Teil
dieser Deklarationsdatei. Leider haben nicht alle vom API
verwendeten Werte (vor allem Nullwerte) eine ihnen zugeordnete Konstante. Zudem hält sich das Socket-API kaum an
Namenskonventionen, was die Übersicht über dieses API zusätzlich erschwert.
Beim Entwurf des WinSock-API mussten die Namen der Socket-API-Deklarationen unter Berücksichtigung des Berkeley
Software License Agreement größtenteils unverändert in das
WinSock-API übernommen werden. Die WinSock-Erweiterungen sind dagegen eindeutig an den drei führenden Buchstaben WSA zu erkennen. Sie ergänzen die Deklarationsdatei um zusätzliche Konstanten.
Zusätzliche
Konstanten
Das Socket-API definiert mehr als fünfzig verschiedene Fehlernummern. Auch die Namen dieser Konstanten wurden aus
den o. a. Gründen in das WinSock-API übernommen. Zusätzlich werden für die Freunde von Namenskonventionen die
Fehlernummern ein zweites Mal mit dem Namensvorsatz
WSAE deklariert. Ergänzt werden die WinSock-Fehlernummern um Fehlerkonstanten für Windows-spezifische Fehler.
Fehlernummern
Alle WinSock-Fehlernummern liegen im numerischen Bereich zwischen 10 000 und 20 000, um eine Überschneidung
mit den Fehlernummern des Betriebssystems auszuschließen.
Die Fehlerkonstante wird in WINSOCK.H mit Hilfe einer
Basisnummer (WSAEBASEERR) deklariert.
Basisnummer
Teil 9/2.3 Seite 4
Grundlagen
Windows-Sockets-API
Dies würde in Visual Basic formuliert wie folgt aussehen:
Const WSABASEERR = 10000
...
Const WSAEWOULDBLOCK = WSABASEERR + 35
Die Basisnummer in der Konstanten WSAEBASEERR wird
in allen uns bekannten WinSock-Implementierungen mit der
Zahl 10 000 vorbelegt. Die WinSock-Fehlerkonstanten
(WSAE…) werden durch Addition der Basisnummer mit einem Offset-Wert gebildet. Bei Verschiebungen der Fehlernummern in zukünftigen WinSock-Versionen ermöglicht die
Basisnummer eine schnelle Anpassung der FehlerkonstantenDeklarationen.
Konstanten
des WinSock-API
Die gebräuchlichsten Konstanten des WinSock-API sind
nachfolgend zusammengefasst:
Name
Bedeutung
Einsatzgebiet
FD_ACCEPT
Verbindungsangebot
Parameter von WSAAsyncSelect()
und Rückmeldefunktion
FD_CLOSE
Verbindungsende
Parameter von WSAAsyncSelect()
und Rückmelde-Ereignis
FD_CONNECT
Verbindungsanfrage
Parameter von WSAAsyncSelect()
und Rückmelde-Ereignis
FD_READ
Empfangsanfrage
Parameter von WSAAsyncSelect()
und Rückmelde-Ereignis
MAXGETHOSTSTRUCT
Byteanzahl
Maximale Größe der Socket-HostDatenstrukturen
MSG_PEEK
Pufferabfrage
Parameter von recv(), Empfangspuffer kopieren, nicht auslesen
SOCKET_ERROR
Fehleranzeige
Returnwert der meisten API-Funktionen
WSAEWOULDBLOCK
Warnung
Netzressource nicht verfügbar,
API-Fehler
Tabelle 9/2.3-1: Häufig verwendete Konstanten des WinSock-API
Grundlagen
Teil 9/2.3 Seite 5
Windows-Sockets-API
Benutzerdefinierte Datentypen
Von den mehr als zehn benutzerdefinierten Datentypen
(Strukturen) des WinSock-API reichen für die meisten Aufgabenstellungen die vier Strukturen HostEnt, ServEnt, Sock
Addr und WSAData aus.
Die HostEnt-Struktur wird bei der Nutzung der Datenbankfunktionen des WinSock-API benötigt. HostEnt enthält den
Domain- und den Aliasnamen sowie die IP-Adressen eines
Hosts. Nachfolgend finden Sie eine Nachbildung dieser
Struktur in Visual-Basic-Syntax:
HostEnt
Type HostEnt
h_name As Long
’ Zeiger auf offiziellen Namen des Host
h_aliases As Long
’ Zeiger auf Zeigerarray, dessen Elemente
’ .. auf die Aliasnamen des Host zeigen
h_addrtype As Integer
’ AddressType des Host (==AF_INET)
h_length As Integer
’ AddressLen des Host (==AF_INET_LENGTH)
h_addr_list As Long
’ Zeiger auf Zeigerarray, dessen Elemente
’ .. auf alle IP-Adressen des Host zeigen
End Type
Die Server-Entry-Struktur speichert die wichtigsten Verbindungsdaten eines Netzwerk-Diensts (Service) und wird ebenso zum Aufruf bestimmter Datenbankfunktionen benötigt.
Die Nachbildung der ServEnt-Struktur mit Visual Basic sehen Sie nachfolgend. Die wichtigsten Felder dieser Struktur
sind der Name s_name und der Port s_port eines Dienstes.
Type ServEnt
s_name As Long
’ Zeiger auf offiziellen Namen des Service
s_aliases As Long
ServEnt
Teil 9/2.3 Seite 6
Grundlagen
Windows-Sockets-API
’ Zeiger auf Zeigerarray, dessen Elemente
’ .. auf die Aliasnamen des Service zeigen
s_port As Integer
’ Portnummer des Services
s_proto As Long
’ Zeiger auf Protokollstring
End Type
SockAddr
Die Struktur SockAddr wird von den Funktionen des WinSock-API benutzt, um eine Socket-Adresse anzugeben oder
zu erfragen. Beispielsweise sind die elementaren WinSockFunktionen recvfrom(), sendto(), connect() und bind() mit einem SockAddr-Parameter ausgestattet.
Type sockaddr_in
sin_family As Integer
’ AddressFamily (AF_*-Konstanten, z. B.
AF_INET)
sin_port As Integer
’ Portnummer im Netzformat (big endian
byte order)
sin_addr As Long
’ IP-Address (IPv4: 4 mal 8 Bit)
sin_zero(0 To 7) As Byte
’ ungenutzt bzw. reserviert
End Type
WSAData
Die Struktur WSAData wird von einer API-Funktion (WSAStartup()) zur Rückgabe von Implementierungsdetails des
verwendeten WinSock-API eingesetzt.
Byte-Anordnung
Die Felder der vorgestellten Strukturen enthalten Daten, die
in einem besonderen Zahlenformat abgelegt sind. Bei der Übernahme und Übergabe von Zahlenwerten aus dem bzw. ins
Internet ist zu beachten, dass das im Internet verwendete
Zahlenformat (Big Endian) sich vom PC-Format (Little Endian) unterscheidet.
Im Folgenden werden die Funktionen des WinSock-API vorgestellt, die die Umwandlung für Sie erledigen.
Grundlagen
Teil 9/2.3 Seite 7
Windows-Sockets-API
API-Funktionen
Die Funktionen des WinSock-API sind in Tabelle 9/2.3-2 ihrem Namen nach sortiert aufgelistet.
Die mit einem „*“ markierten API-Funktionen decken alle
Bedürfnisse eines soliden Internet-Client vollkommen ab. Zur
Entwicklung eines Servers kommen noch die Funktionen accept(), bind() und listen() hinzu. Die restlichen API-Funktionen werden nur für spezielle Anforderungen benötigt.
Grundfunktionen
Fast alle DLL-Funktionen des WinSock-API sind in allen
Versionen von Visual Basic direkt deklarierbar und aufrufbar.
Nur manche der eher selten benötigten API-Funktionen (z. B.
die …BlockingHook…()-Funktionen) können ausschließlich
mit C-ähnlichen Sprachen (wie Visual Basic ab der Version
5.0, Delphi, CA-VO) genutzt werden.
Visual Basic
Funktionen des WinSock-API
Funktion
G.
Aufgabe
accept()
K
eintreffende Verbindungsanfrage beantworten, Verbindung aufbauen
bind()
K
Socket initialisieren, Socket-Adresse festlegen
closesocket()*
K
Socket schließen
connect()*
K
Verbindungsanfrage abschicken, Verbindung aufbauen
gethostbyaddr()#
D
Host- bzw. Servernamen und -adressen erfragen, Eingabe: IP-Adresse
gethostbyname()#
D
Host- bzw. Servernamen und -adressen erfragen, Eingabe: Hostname
gethostname()
D
lokale Host- bzw. Servernamen erfragen
getpeername()
K
Peer-Name des Socket erfragen
getprotobyname()#
D
Protokollname und -nummer erfragen, Eingabe: Protokollname
Teil 9/2.3 Seite 8
Grundlagen
Windows-Sockets-API
Funktion
G.
Aufgabe
getprotobynumber()#
D
Protokollname und -nummer erfragen, Eingabe: Protokollnummer
getservbyname()#
D
Dienstname und zugehörige Portnummer
erfragen, Eingabe: Dienstname
getservbyport()#
D
Dienstname und zugehörige Portnummer
erfragen, Eingabe: Portnummer
getsockname()
K
Socket-Adresse erfragen
getsockopt()
S
Socket-Option erfragen
htonl()*
W
konvertiert 32-Bit-Wert vom lokalen Format
ins Netzformat
htons()*
W
konvertiert 16-Bit-Wert vom lokalen Format
ins Netzformat
inet_addr()*
W
wandelt IP-Adresse vom Zeichenfolgen- ins
numerische (32-Bit-)Format
inet_ntoa()*
W
konvertiert IP-Adresse vom numerischen ins
Zeichenfolgenformat
ioctlsocket()*
S
Socket-Steuerung bzw. -Konfiguration
listen()
K
auf eingehende Verbindungsanfrage warten
ntohl()*
W
konvertiert 32-Bit-Wert vom Netz- ins lokale
Format
ntohs()*
W
konvertiert 16-Bit-Wert vom Netz- ins lokale
Format
recv()*
K
Daten (in verbindungsorientierter Kommunikation) empfangen
recvfrom()*
K
Datenpaket empfangen
select()
K
Statusabfrage eines oder mehrerer Sockets
send()*
K
Daten (in verbindungsorientierter Kommunikation) verschicken
sendto()*
K
Datenpaket verschicken
setsockopt()
S
Socket-Option einstellen
Grundlagen
Teil 9/2.3 Seite 9
Windows-Sockets-API
Funktion
G.
Aufgabe
shutdown()*
K
verbindungsorientierte Kommunikation abbrechen bzw. beenden
socket()*
K
Socket erstellen, Empfangs- und Sendepuffer einrichten
WSAAsyncSelect()*
K
Ereignis bei Änderung des VerbindungsStatus beantragen
WSACancelAsyncRequest()*
S
Antrag einer asynchronen Datenbankfunktion zurückziehen
WSACancelBlockingCall()
S
blockierende Socket-Funktion abbrechen
WSACleanup()*
S
Windows-Sockets-DLL abmelden
WSAGetLastError()*
S
Fehlernummer des letzten Socket-Fehlers
erfragen
WSAGet<X>By<Y>()*
D
asynchrone Datenbank-Funktionen (wie
Funktionen mit #)
WSAIsBlocking()
K
Socket-Blockierung bzw. -Bereitschaft erfragen
WSASetBlockingHook()
S
applikationseigene Überwachungs-Funktion
(Hook) installieren
WSASetLastError()
S
Socket-Fehlernummer einstellen
WSAStartup()*
S
Windows-Sockets-DLL anmelden, mit Applikation bekannt machen
WSAUnhookBlockingHook()
S
applikationseigene Überwachungs-Funktion
(Hook) deinstallieren
Tabelle 9/2.3-2: Funktionen des WinSock-API der Version 1.1
Die durchgängig aus Kleinbuchstabenen zusammengesetzten
Funktionensnamen sind von den Funktionen der BerkeleySockets (Socket-API) abgeleitet.
Berkeley-SocketFunktionen
Zusätzlich wurden 16 asynchrone Erweiterungsfunktionen
des Socket-API speziell für die Windows-Umgebung entwickelt, deren Funktionsnamen mit der Vorsilbe WSA beginnen.
WindowsErweiterungen
Teil 9/2.3 Seite 10
Grundlagen
Windows-Sockets-API
Funktionsgruppen
Die Funktionen des WinSock-API lassen sich in 4 Gruppen
einteilen. In Tabelle 9/2.3-2 ist in Spalte G. die Zugehörigkeit
zu einer Funktionsgruppe durch Buchstaben angegeben.
•
•
•
•
D
K
S
W
=
=
=
=
Datenbank
Kommunikation
Steuerung und Konfiguration
Wandlung, Konvertierung
Von den Konvertierungsfunktionen (W-Gruppe) werden die
Funktionen der Form ntoh…() und hton…() am häufigsten
eingesetzt. Spätestens beim Debugging oder beim Ausdruck
der Werte einer Socket-Adresse (IP-Adresse oder Portnummer) werden Sie merken, dass ohne Zuhilfenahme der Konvertierungsfunktionen scheinbar unsinnige Zahlenwerte angezeigt werden.
Beachten Sie, dass manuell ermittelte Portnummern und IPAdressen vor der Übergabe an das API (als Parameter oder
Strukturfeld) mit hton…() konvertiert werden müssen, um die
numerischen Werte vom lokalen Format (Little Endian) ins Internet-Format (Big Endian) zu transferieren. Umgekehrt ist eine
Wandlung mit einer ntoh…()-Funktion nötig, wenn eine 16- oder 32-Bit-Zahl aus dem API bzw. dem Internet übernommen
und angezeigt bzw. manuell korrigiert werden soll.
Grundlagen
Teil 9/2.4 Seite 1
Proxies
9/2.4
Proxies
Ein Proxy ist ein Computer, der die Verbindungen, die über
ihn laufen, so weiterleitet, dass es scheint, als ob die Anfrage
direkt von ihm käme (unter der Website von JunkBuster.com
(http://internet.junkbuster.com/) findet sich dazu eine recht
interessante Software . Außerdem speichert ein Proxy abgefragte Web-Seiten zwischen und liefert sie bei einer Anfrage
aus, um unnötigen Netz-Traffic zu vermeiden.
WWW-Proxy
Generell wirkt jeder Firewall zusätzlich als Proxy-Server.
Das heißt, jede Anfrage vom sicheren internen Netz wird an
den Firewall (Proxy) gerichtet, der dann seinerseits unter Angabe seiner Adresse erst die Verbindung zum endgültigen
Zielrechner aufnimmt. Auf diese Weise erhält der Zielrechner
als Reply-Adresse die des Proxies, und nur der Proxy kennt
den ursprünglichen Absender der Anfrage und schickt ihm
die vom Zielrechner übermittelte Information zurück.
Firewalls
Normalerweise funktioniert eine HTTP-Verbindung wie folgt
(Beispiel zum Laden der Seite http://www.yahoo .com/):
HTTP via Proxy
• Es wird eine Socketverbindung zu dem in der URL angegebenen Host geöffnet (z. B. www.yahoo.com).
• An diesen wird die Abfrage nach einer bestimmten Seite
geschickt (z. B. „GET / HTTP/1.0“ plus eventuell weitere
Header- und Body-Zeilen).
• Der Host antwortet darauf mit einem Status-Code, dem
Header und der angeforderten Datei.
Um mit HTTP nun eine Verbindung über einen Proxy aufzubauen, ist eine etwas andere Vorgehensweise nötig:
• Eine Socket-Verbindung zum entsprechenden Port des Proxy wird aufgebaut (z. B. firewall.mynet.de auf Port 8000).
Teil 9/2.4 Seite 2
Grundlagen
Proxies
• An diesen wird jetzt eine modifizierte Abfrage geschickt:
„GET http://www.yahoo.com/ HTTP/1.0“ plus eventuell
weitere Header- und Body-Zeilen – einziger Unterschied
ist also, dass in der Anfrage nicht nur der Pfad, sondern
auch Protokoll und Server angegeben werden müssen.
• Der Proxy zerlegt diese Angabe, baut seinerseits die Verbindung zum hier angegebenen Host auf und schickt diesem die Anfrage ohne Servernamen weiter („GET /
HTTP/1.0“)
• Der Host antwortet darauf mit einem Status-Code, dem
Header und der angeforderten Datei.
• Diese Datei wird dann vom Server über den noch offenen
Socket – nach eventuellen Viren- und Content-Checks – an
den anfragenden Client weitergereicht.
Grundlagen
Teil 9/2.5 Seite 1
Authentifizierung
9/2.5
Authentifizierung
Viele HTTP-Server erlauben die Sicherung bestimmter Verzeichnisse durch eine User/Passwort-Kombination. Unter
dem Apache-Web-Server kann dies z. B. mit einer .htaccessDatei im zu sichernden Verzeichnis geschehen.
HTTP-Server
AuthUserFile /home/httpd/x-users
AuthGroupFile /dev/null
AuthName Sicherheitsbereich
AuthType Basic
<Limit GET POST>
require valid-user
</Limit>
Das Listing zeigt eine .htaccess-Datei, die das Verzeichnis, in
dem sie liegt, durch eine Passwortabfrage vor unberechtigten
Zugriffen schützt. Die Usernamen und Passworte der Benutzer sind in der Form „user:verschlüsseltes_passwort“ in der
oben angegebenen Datei „/home/httpd/x-users“ abgelegt (hier
eine Beispielzeile als Ausschnitt).
:
luser:Qxno61okNQxpM
:
Der Benutzername lautet „luser“, das Passwort „secret“. Dieses Passwort wurde mit der UNIX-Crypt-Funktion unter Angabe eines „Salt“, also eines Startwertes, auf dem die Verschlüsselung basiert, verschlüsselt.
crypt("secret","Qx")
Salt ist hier im Beispiel „Qx“. Diese beiden Buchstaben erscheinen dann als erste Buchstaben des verschlüsselten Passworts. So kann man relativ einfach ein vom Benutzer einge-
Verschlüsselung
von Passwörtern
Teil 9/2.5 Seite 2
Grundlagen
Authentifizierung
gebenes Passwort wieder mit diesem „Salt“ verschlüsseln und
mit dem verschlüsselt abgespeicherten Passwort vergleichen
(Beispiel in Perl, Passwortdatei geöffnet als Stream PWD).
$_ = <PWD>;
chop;
# Zeile aus Passwortdatei lesen
# Zeilenende abschneiden
# Felder an : aufteilen und zuweisen
($user,$password) = split(/:/);
# benoetigtes salt bestimmen
$salt = substr($password,0,2);
# Vom Benutzer eingegebenes Passwort steht
# in $pwd, dieses verschluesseln wir
$encrypted = crypt($pwd,$salt)
# vergleichen der verschlüsselten Passwörter
if ($encrypted eq $password)
{ print "Passwort richtig.\n";
...
} else
{ print "Tut mir Leid ...\n"; }
HTTP-Request
Doch zurück zur Abfrage einer Datei vom HTTP-Server.
Schickt man an den Server mit
GET /users_only/index.html HTTP/1.0
Unauthorized
eine Anfrage nach einer Datei in einem derartig geschützten
Verzeichnis „users_only“, so antwortet der Server mit
HTTP/1.0 401 Unauthorized
Date: Fri, 31 Jul 1998 11:12:00 GMT
Server: Apache/1.1.3
WWW-Authenticate: Basic realm="Sicherheitsbereich"
Content-type: text/html
<HEAD><TITLE>Authorization Required</TITLE></HEAD>
<BODY><H1>Authorization Required</H1>
This server could not verify that you are authorized to access the
document you requested. Either you supplied the wrong credentials
(e.g., bad password), or your browser doesn’t understand how to
supply the credentials required.<P>
</BODY>
Grundlagen
Teil 9/2.5 Seite 3
Authentifizierung
Um doch Zugriff auf diese Datei zu erhalten erkennt der
Browser den Status-Code 401 und frägt den User unter Angabe des im Error-Header angegebenen „realm“ nach Username und Passwort. Dies wird dann vom Server Base64Encoded und in einem neuerlichen HTTP-Request mitübermittelt.
Passwortabfrage
GET /users_only/index.html HTTP/1.0
Authorization: Basic dXNlcjpzZWNyZXQ=
Nun sollte der Server korrekt mit einem Status 200 OK antworten und das Dokument liefern.
HTTP/1.0 200 OK
Date: Fri, 31 Jul 1998 11:35:55 GMT
Server: Apache/1.1.3
Content-type: text/html
<HTML>
<HEAD><TITLE>Users-Only-Bereich</TITLE>
</HEAD>
<BODY>
...
</BODY>
</HTML>
Es ist allerdings zu beachten, dass Benutzername und Passwort im Klartext (nur Base64-Codiert) übertragen werden.
Das heißt, dass jeder von jedem Rechner aus, über den das
TCP-Paket mit diesem Inhalt geroutet wird, mit einem einfachen Package-Sniffer diesen Text auslesen und Base64Decodieren kann, um dann selbst auf diesen vermeintlich
„geschützten“ Bereich zuzugreifen.
Achtung
Teil 9/2.5 Seite 4
Authentifizierung
Grundlagen

Documentos relacionados