Sockets


Inhoud :

  1. Wat zijn Sockets ?
  2. Context
  3. Het concept 'Socket'
  4. Situering van SOCKETS binnen een gelaagd Netwerkmodel
  5. Interfaces
  6. Implementaties
  7. ADDRES FAMILY en PROTOCOL FAMILY
  8. Sockets in soorten
  9. Hoe werkt het
    1. Connection oriented sockets
    2. Connectionless sockets
    3. Sockets - een iets andere definitie
  10. SOCKET OPTIONS
    1. Broadcast
    2. Multicast (send)
    3. Multicast (receive)
  11. Byte Order
  12. Winsock
  13. Raw Sockets
  14. Secure Sockets
  15. Geraadpleegde Werken (links)
  16. Programmeren met Sockets (links)

Wat zijn Sockets ?

Socket : (definitie volgens Webopaedia)

  1. Engels voor 'stopcontact' of, meer algemeen, vrouwelijke connector. Aansluiting, component waar een kabel ingeplugd kan worden.
  2. Component waarmee een chip, een geÔntegreerd circuit of een andere hardware component op een printplaat bevestigd wordt. Typisch voorbeeld : de sockets op een moederbord, waarin een CPU bevestigd kan worden.
  3. Een software component die door programma's gebruikt wordt om te communiceren over een netwerk, of een software component die door programmeurs aangewend wordt om hun applicaties van netwerkconnectiviteit te voorzien.

In deze verhandeling wordt socket uitsluitend in de 3de betekenis behandeld.

Een socket is dus een software component die netwerkconnectiviteit biedt. Software componenten kunnen aangeleverd worden in de vorm van source code, en in gecompileerde vorm. Ze worden gebruikt om aan toepassingen netwerkfunctionaliteit te bezorgen, zodat deze via een netwerk kunnen communiceren met een andere toepassing, met een ander programma.

Achtergrond

Het begrip socket stamt uit de wereld van UNIX, en werd in het begin van de jaren zeventig ontwikkeld aan de University of California, in Berkeley. De universiteit verwierf een UNIX licentie die toeliet de source code te bewerken, en ontwikkelde mettertijd een eigen versie van UNIX, de Berkeley Standard Distribution (BSD UNIX), die o.a. voortleeft in de open source versies OpenBSD, NetBSD en FreeBSD, ťťn van de belangrijkste "concurrenten" van Linux. Sockets werden geÔntroduceerd in BSD versie 4.

Sockets waren oorspronkelijk opgevat als een manier om InterProcess Communication (IPC) te programmeren, dus om programma's (processen) toe te laten te communiceren met andere programma's. Andere mechanismen voor Inter Process Communication zijn o.a. semaforen, shared memory, en vooral de pipe en de named pipe (FIFO), welke op UNIX-achtige systemen nog veel worden gebruikt. Het best gekende voorbeeld hier is de pipe.

Stel dat je een programma runt, en je wil het resultaat daarvan kenbaar maken aan een ander programma om er mee verder te werken. De output van programma 1 moet dus input worden voor programma 2. Het pipe commando ( | ) zorgt daar voor. Hoewel dit een heel krachtig mechanisme is, en waanzinnig veel gebruikt op Unix, Linux en verwante systemen, heeft het toch een paar tekortkomingen : de programma's kunnen zelf geen pipe opzetten, kunnen m.a.w. niet zelf 'contact op,nemen' met een ander programma. De pipe moet opgezet worden door een 3de proces (bvb. de shell). Ook moet er voor iedere uitwisseling van informatie opnieuw een pipe opgezet worden, er is dus geen toestand waarin een programma wacht op informatie van andere programma's. Om dergelijke tekortkomingen op te vangen werd de socket ontwikkeld.

Merk op dat er in het voorgaande nog geen sprake is van netwerken. We hebben het enkel gehad over communicatie tussen processen.

Voor 'shared memory' is het wellicht duidelijk dat, om geheugen te delen, de processen op dezelfde machine moeten lopen. Iets gelijkaardigs geldt voor pipes. Sockets onderscheiden zich van andere IPC mechanismen door het feit dat de communicerende processen niet noodzakelijk op ťťn en hetzelfde systeem hoeven te lopen : sockets vormen een 'eindpunt voor communicatie' en kunnen op zichzelf bestaan. Binnen 1 systeem kunnen ze toegepast worden voor communicatie tussen processen op dat systeem, maar in een netwerkomgeving kunnen processen op verschillende computers lopen. Sockets kunnen zelfs bestaan zonder dat er daadwerkelijk communicatie plaatsvindt, en zelfs zonder dat er 'aan de andere kant' een socket bestaat. Een socket kan zich namelijk in de toestand 'listening' bevinden, dus wachtend op communicatie. Vandaar dat sockets moeiteloos toegepast kunnen worden om communicatie over een netwerk in een programma op te nemen.

Omwille van hun oorsprong worden sockets soms BSD Sockets genoemd.

Sockets kunnen aangeleverd worden onder de vorm van source code die in een programma gekopieerd wordt, of er aan gelinkt wordt tijdens het compilatieproces. Aangezien er verschillende programmeertalen bestaan, zijn sockets dus, als source code, in verschillende talen terug te vinden : als Java classes, als C header files en libraries, in Perl libraries, enz. Een bekende implementatie van sockets is de Winsock.dll. Dit is een binair bestand (dus : een component in gecompileerde vorm) die in de Microsoft Windows omgeving de netwerkconnectiviteit verzorgt.

Het concept 'Socket'

Aangezien sockets behoren tot het domein van de netwerkprogrammeurs, is voor een goed begrip van het concept socket, een woordje uitleg over programmeren hier wel op zijn plaats.

De standaard in- en output van een programma is meestal : het toetsenbord en het scherm. In zijn eenvoudigste vorm : De gebruiker levert informatie aan het programma door iets te tikken op het toetsenbord, het programma verwerkt de informatie op de manier die de programmeur geschreven heeft, en toont het resultaat op het scherm.

Programma's kunnen ook input ophalen uit bestanden, en/of output wegschrijven naar bestanden. Omdat dat vrij veel voorkomt, zijn in de meeste talen middelen voorhanden om dat gemakkelijk mogelijk te maken. Procedurele talen hebben meestal functies die specifiek bedoeld zijn om filetoegang te leveren. ObjectgeoriŽnteerde talen bieden klassen die filetoegang mogelijk maken. Voor toegang tot databases en dergelijke worden eveneens prefab componenten voorzien.

Sockets zijn, in grote mate, te vergelijken met componenten die filetoegang leveren. Beide vormen ze als het ware een doorgeefluik tussen het programma en de buitenwereld (in het ene geval : een bestand op de harde schijf, in het andere geval : het netwerk).

In een programma dat informatie moet wegschrijven naar een bestand zou een programmeur iets kunnen schrijven als :

	open bestand A
	lees de eerstvolgende lijn in het bestand en
	hou deze data bij in  stringvariabele a
	sluit bestand A

	open bestand B
	schrijf de inhoud van stringvariabele a naar bestand B
	sluit bestand B

Hij hoeft zich verder geen zorgen te maken over hoe de opdracht 'schrijf naar bestand' werkt, op welke manier het filesysteem of de harde schijf daarbij betrokken worden enz.

Op analoge wijze kan een socket 'geopend' worden, het programma kan de gegevens kunnen 'naar de socket schrijven', en de socket zorgt voor de verdere afhandeling (nl. zorgen dat de data op het netwerk terecht komen, in een vorm die er voor zorgt dat de data op hun bestemming kunnen aankomen).

Het is uiteraard mogelijk om netwerkconnectiviteit te implementeren zonder het gebruik van sockets. Een netwerkkaart kan benaderd worden via packet drivers. Packet drivers worden in het geheugen geladen, en zijn dan door toepassingen te benaderen via software interrupts.

het laden van een packet driver onder DOS. (screenshot)
Figuur 1 : het laden van een packet driver onder DOS. (screenshot)

De programmeur moet er in zijn programma voor gezorgd hebben dat deze packet driver op de juiste manier aangesproken wordt. Hij moet er met name voor zorgen dat zijn programma met de netwerkkaart kan communiceren via de software interrupt (97 of 0x61 in het gegeven voorbeeld), bijvoorbeeld door deze waarde op te slaan in een configuratiebestand, of door van de gebruiker te eisen dat de packet driver geÔnstalleerd werd met dezelfde software interrupt als die welke door het programma verondersteld wordt. Voorbeelden van deze werkwijze zijn Arachne (grafische webbrowser voor DOS) en WATTCP (TCP/IP software voor DOS).

Het is duidelijk dat het eenvoudiger is in een programma te werken met functies en/of objecten die dit soort hardware beslommeringen verbergen voor de programmeur, die zich dan kan concentreren op de goede werking van zijn programma. Dat is wat sockets doen : ze verbergen de hardware / driver kant van de datacommunicatie. De programmeur moet enkel weten hoe hij met de socket kan communiceren, wat relatief eenvoudig is. Sockets zijn dus ook te begrijpen als een API, een Application Program Interface.

Om een idee te geven waarover we het hier hebben volgt hier een eenvoudig voorbeeld. : Het opzetten van een connectie via Winsock.

Aan het vb project 'MijnEersteChatProgramma, een eenvoudig chat programma, wordt om te beginnen de Microsoft Winsock Component toegevoegd.

Winsock control in een Visual Basic project. (screenshot)
Figuur 2 - Winsock control in een Visual Basic project. (screenshot)

Vervolgens schrijven we iets als :

	'vraag aan de gebruiker met welke host hij wil connecteren, 
	'en op welke poort

	strRemotePC = InputBox(strRemotePC, "Remote PC ?")
	strRemotePort = InputBox(strRemotePort, "Remote port ?", 1000)

	'geef de geven waarden door aan Winsock, en laat winsock de functie
	'.Connect uitvoeren
	
    	Winsock1.RemoteHost = strRemotePC
   	Winsock1.RemotePort = Int(strRemotePort)
   	Winsock1.Connect
	
	'wacht tot de connectie tot stand is gekomen
	
   	Do Until Winsock1.State = sckConnected
		DoEvents: DoEvents: DoEvents: DoEvents:	
	Loop

Het versturen van een bericht is al net zo eenvoudig. Veronderstellen we dat de gebruiker tekst intypt in het(tekstvak) txtMyMessage :


	strMessageToSend = txtMyMessage.text
	Winsock1.send(strMessageToSend)

Bron: Using Winsock, Karl Moore

Zou eenvoudig is dat. Merk op dat de programmeur zich helemaal niet hoeft te bekommeren om het netwerkaspect van zijn programma; dat wordt allemaal afgehandeld door WinSock. De programmeur moet enkel aangeven wat Winsock moet doen, door de juiste functies (.connect, .send, ...) te kiezen, en de benodigde informatie mee te geven, (.remotehost = ..., .port = ..., ).

HOE Winsock deze opdrachten en gegevens verwerkt (de "implementatie") is gecodeerd in de respectievelijke functiedefinities. Naar buiten toe, naar de applicatie die van deze functionaliteit gebruik wil maken, hoeven enkel (de naam4 van) de functies en properties gekend te zijn. De verzameling van de namen van publieke functies en eigenschappen wordt 'interface' genoemd.

Tot zover deze introductie in interface based programming.

Naast BSD sockets bestaat een gelijkaardige werkwijze, de System V 'Transport Layer Interface' (TLI). Volgens FOLDOC (Free Online Dictionary of Computers) is TLI meer protocolonafhankelijk dan BSD Sockets, dat dichter aanleunt bij de TCP/IP protocol suite.

De keuze tussen de twee interfaces varieert van fabrikant tot fabrikant. De meeste Unix systemen ondersteunen beide interfaces, maar TLI wordt algemeen als superieur beschouwd. BSD Sockets zijn populair in Linux en in de BSD Unix familie (uiteraard), en hebben door de implementatie van Microsoft (WinSock) wellicht een groter aantal gebruikers.

Om sockets ook 'in het echt' te zien te krijgen (en dus niet slechts als een stukje source code) kan je op een Linux of (BSD-) UNIX systeem de opdracht netstat geven. Je krijgt dan een lijst van 'connecties', in de vorm van sockets, met hun status :'listening', 'connected', ...

Situering van SOCKETS binnen een gelaagd Netwerkmodel

Uit het voorgaande blijkt dat sockets een interface bieden aan programma's. In de het TCP/IP model heet dat de 'Application layer', ruwweg te vergelijken met Application Layer + Presentation Layer in het ISO-OSI model.

Sockets interfacen niet direct met de hardware, zelfs niet met de packet drivers. Ze interfacen met de host-to-host transport protocollen die we situeren in de transportlaag. Door hun ontwikkeling in het kader van BSD Unix sluiten sockets vrij goed aan bij het TCP/IP model. Het is dan ook eenvoudiger sockets een plaats te geven binnen het TCP/IP model dan binnen het OSI model.

Sockets in relatie tot de TCP/IP protocol suite
Figuur 3 : Sockets in relatie tot de TCP/IP protocol suite
(Linux System Administration, M.Carling e.a.)

Merk op dat sockets niet gezien moeten worden als een extra laag in het TCP/IP model (of het ISO-OSI model, enz.). Strikt genomen is het ook geen 'deel van' de transportlaag. Bovenstaande figuur heeft enkel tot doel aan te geven dat sockets ervoor zorgen dat Application Layer en Transport Layer met elkaar kunnen samenwerken.

Bij het verzenden van een bericht is het dan ook niet noodzakelijk dat aan de ontvangende kant ook sockets gebruikt worden. Virtueel verloopt de communicatie dan wel 'met de overeenstemmende laag", in de praktijk wordt data van de applicatielaag doorheen de verschillende lagen ingepakt tot er uiteindelijk een reeks bits op de netwerkkabel terechtkomt. Aan andere kant doorloopt deze reeks bits opnieuw alle lagen (stijgend), tot de data (ontdaan van headers en trailers) opnieuw op de applicatielaag terechtkomt. Er is dus aan de ontvangende kant wel een mechanisme nodig om de applicatie met de transportlaag te laten communiceren, maar dat hoeft niet noodzakelijk een BSD socket te zijn.

layers, interfaces en protocols
Figuur 4 : layers, interfaces en protocols (cursus Datacommunicatie, Jan Celis)

Voor de volledigheid moet ik er ook nog op wijzen dat sockets niet noodzakelijk met netwerken geassocieerd moeten worden, maar met datacommunicatie in het algemeen. Ze zijn tenslotte oorspronkelijk ontwikkeld als mechanisme voor InterProcess Communication.

Bij wijze van voorbeeld : op Unix machines worden sockets toegepast voor communicatie tussen de X Windows Server en client applications. X Windows is een server die een grafische modus mogelijk maakt zodat client applications een grafische user interface kunnen aanbieden. Communicatie tussen de client processen en de server processen verloopt via sockets. Er zijn dus verschillende soorten sockets te onderscheiden : Unix Domain Sockets en Internet Sockets. Het onderscheid tussen beide ligt in de wijze waarop ze geadresseerd worden : Unix Domain Sockets worden geadresseerd met de Unix Addres Family (AF_UNIX), Sockets voor datacommunicatie over het internet worden geadresseerd met adressen uit de Internet Address Family (AF_INET). Bij de creatie van sockets wordt de address family (AF_UNIX, AF_INET) als parameter meegegeven.

In onderstaande figuur - de schermoutput van netstat op een Linux systeem - zien we o.a. een aantal unix sockets die verwijzen naar X11, de X Windows server.

Screenshot : netstat toont sockets
Figuur 5 - Screenshot : netstat toont sockets

We zien onder 'Protocollen' echter ook tcp, dat al iets doet vermoeden over de verhouding tussen sockets en tcp/ip netwerken. In het vervolg van dit werk zal de nadruk dan ook vooral liggen op sockets en toepassingen ervan binnen de context van datacommunicatie over TCP/IP.

Verder zien we de types 'STREAM' en 'DGRAM' verschijnen. Dit zijn 2 verschillende types van sockets. De verschillende types sockets worden verder in dit werk nog besproken.

Interfaces

Op dit punt zijn we de term 'interface' in twee niet helemaal identieke betekenissen tegengekomen.

Aan de ene kant is er sprake van interface based programming : het gebruiken van 'prefab' source code (of van binaire componenten) via publieke / geŽxporteerde functies. Dit is het meest uitgesproken in objectgeblaseerde ontwikkelomgevingen zoals Visual Basic, en in objectgeoriŽnteerde programmeertalen zoals C++ en Java, maar wordt eigenlijk ook toegepast in een procedurele taal als C, door het includen en aanroepen van functies waarvan de implementatie terug te vinden is in de libraries. De functies die op deze manier source code uit libraries, classes en (binaire, gecompileerde) componenten beschikbaar maken, worden API's genoemd, Application Program Interface. Dit is dus een interface op het niveau van het ontwikkelen en uitvoeren van een programma. We vinden hier socket API's in de vorm van (functies die toegang geven tot) socket libraries, socket classes en (binaire, gecompileerde) socket components.

Aan de andere kant hebben we gezien hoe de verschillende 'lagen' van een gelaagd netwerkmodel, of van een protocol suite, verbonden zijn via interfaces. In deze context is interface te begrijpen als het mechanisme waardoor data doorgegeven worden van de ene laag naar de andere : output van de ene laag wordt input van de andere laag, en het mechanisme dat daarvoor zorgt is de interface. In deze betekenis zien we sockets als interface tussen de 2 protocollagen, met name tussen de application layer en de host to host communication layer - in een TCP/IP netwerk. Zoals gezegd zijn er andere interfaces mogelijk (bijv. de Transport Layer Interface van Unix System V ).

BSD sockets vormen dus een interface met de 'transportlaag' ofte de 'host-to-host communication layer' : sockets geven aan toepassingen toegang tot host-to-host communication protocollen. In een aantal specifieke gevallen geven sockets ook toegang tot de lagere lagen (IP, packet drivers, ...)

De socket is (voor de programmeur, of voor het programma) op zijn beurt toegankelijk via een aantal functies, die samen de API vormen.

Om sockets te zien in relatie tot datacommunicatieprotocollen, is het interessant de paragraaf 'Interfaces' uit RFC 793 (TRANSMISSION CONTROL PROTOCOL) te bekijken.

1.  INTRODUCTION
Interfaces
  The TCP interfaces on one side to user or application processes and on
  the other side to a lower level protocol such as Internet Protocol.
  This interface consists of a set of  calls much like the calls an 
  operating system provides to an application process for manipulating
  files.  For example, there are
  calls to open and close connections and to send and receive data on
  established connections.  It is also expected that the TCP can
  asynchronously communicate with application programs.  Although
  considerable freedom is permitted to TCP implementors to design
  interfaces which are appropriate to a particular operating system
  environment, a minimum functionality is required at the TCP/user
  interface for any valid implementation.


  The interface between TCP and lower level protocol is essentially
  unspecified except that it is assumed there is a mechanism whereby the
  two levels can asynchronously pass information to each other.
  Typically, one expects the lower level protocol to specify this

  interface.  TCP is designed to work in a very general environment of
  interconnected networks.  The lower level protocol which is assumed
  throughout this document is the Internet Protocol.

(...)

2.   PHILOSOPHY 
(...)

  The TCP is assumed to be a module in an operating system.  The users
  access the TCP much like they would access the file system.  The TCP
  may call on other operating system functions, for example, to manage
  data structures.  The actual interface to the network is assumed to be
  controlled by a device driver module.  The TCP does not call on the
  network device driver directly, but rather calls on the internet
  datagram protocol module which may in turn call on the device driver.


  2.4.  Interfaces

  The TCP/user interface provides for calls made by the user on the TCP
  to OPEN or CLOSE a connection, to SEND or RECEIVE data, or to obtain
  STATUS about a connection.  These calls are like other calls from user
  programs on the operating system, for example, the calls to open, read
  from, and close a file.

  The TCP/internet interface provides calls to send and receive
  datagrams addressed to TCP modules in hosts anywhere in the internet
  system.  These calls have parameters for passing the address, type of
  service, precedence, security, and other control information.

Het lijkt wel een recept voor BSD sockets. De nauwe band tussen BSD sockets en TCP/IP blijkt ook uit de volgende figuur van Microsoft. Hoewel bedoeld om de plaats van Winsock binnen Windows CE te illustreren, geeft het toch een mooi beeld van hoe BSD Sockets (geÔmplementeerd in Winsock) zich verhouden tot andere netwerksoftware.

Winsock en OSI
Figuur 6 - Winsock en OSI (MSDN Library)

Hoewel Microsoft deze figuur gebruikt om Winsock te situeren binnen het OSI-model, zien we dat de Winsock API expliciet bedoeld is als interface tussen Application (7) en Transport (4). De tussenliggende lagen die het OSI-model daarbij voorschrijft worden - net zoals in de TCP/IP protocol suite en het gelijknamige 'model', compleet genegeerd, de taken die tot die lagen behoren worden hetzij op de application layer, hetzij op de host-to-host communication layer opgelost.

Op laag 3 en 4 vinden we uiteraard TCP, UDP en IP, maar ook ICMP, IGMP en ARP. Merk op dat Microsoft onderscheid maakt tussen 'secure' en 'insecure'. Op 'secure sockets' komen we later nog terug. Daarnaast valt nog op te merken dat de Winsock API applicaties ook toelaat te communiceren over Infrarood volgens de IrDA standaard. Daartoe heeft Microsoft een extra Address Family gecreŽerd, nl AF_IRDA.

Op de datalink laag zien we tenslotte NDIS : De Network Driver Interface Specification. Windows Networking gebruikt NDIS om voor zijn netwerklaag een een uniforme interface met netwerk-hardware (netwerkkaarten, seriŽle poorten, ...) te creŽren.

Implementaties

Implementatie betekent hier : hoe wordt dit in de realiteit toegepast. Er zijn verschillende implementaties van sockets mogelijk.

Aangezien sockets voor het eerst geÔmplementeerd werden op een BSD (UNIX) systeem, werden ze oorspronkelijk geÔmplementeerd in een C library, nl. system/estlib.lib. Libraries zijn source code bestanden met functiedefinities die via header files in een source code bestand opgenomen worden. Vervolgens kunnen de functies in het programma aangeroepen worden, bijv. als volgt :

Code sample in C

The following function shows how to use the socket(), bind(), and listen() function
to establish a socket which can accept calls:

/* 
 *  code to establish a socket; originally from bzs@bu-cs.bu.edu
 */

 int establish(portnum) 
 u_short portnum;
 { 

   char myname[MAXHOSTNAME+1];			
   int s;
   struct sockaddr_in sa;
   struct hostent *hp;

 bzero(&sa,sizeof(struct sockaddr_in)); 	/* clear our address */
 gethostname(myname,MAXHOSTNAME); 		/* who are we? */
 hp= gethostbyname(myname); 			/* get our address info */
 
 if (hp == NULL) return(-1);			/* we don't exist !? */


 sa.sin_family= hp->h_addrtype; 		/* this is our host address */
 sa.sin_port= htons(portnum); 			/* this is our port number */

if ((s= socket(AF_INET,SOCK_STREAM,0)) < 0) 	/* create socket s */
 return(-1);
 
if (bind(s,&sa,sizeof sa,0) < 0) 
  {
   close(s);
   return(-1); 					/* bind address to socket */
   }

listen(s, 3); 					/* max # of queued connects*/
 return(s);

}
 

Hoewel C iets minder goed op Engels lijkty dan Visual Basic, en dit voorbeeld dus niet voor iedereen zo illustratief zal zijn als het eerder gegeven winsock voorbeeld, zien we hier toch dat de hier gedefinieerde functie achtereenvolgens :

  1. de naam van het systeem opvraagt met de functie gethostname()
  2. aan de hand van die naam het eigen IP adres ophaalt : gethostbyname()
  3. een socket creŽert : socket()
    parameters:
    1. AF_INET : de internet address family; de socket is bedoelt voor communicatie over het internet
    2. SOCK_STREAM : het is een stream socket, geen datagram socket (zie verder)
    3. 0 : hier kan dmv een nummer een protocol aangeduid worden. Hier wordt 0 gegeven, zodat het protocol zal volgen uit de Address Family en het type Socket (Internet Address Family => IP, stream socket => TCP)
  4. een relatie legt tussen 'deze socket'en het eigen adres (tuplet IP address : Port Nummer) : bind()
  5. luistert of iemand een verbinding probeert te maken :listen()

gethostname(), gethostbyname(),socket(), bind(), listen(), ... zijn functies. De implementatie ervan zit in libraries die bij het compileren aan dit programma gelinkt zullen worden. Bij de uitvoering ervan worden dit system calls : functies die instructies laten uitvoeren door de kernel (het operating system), en/of er informatie van opvragen.

Nog een code sample in C : een (werkende) echo server. Een echo server doet niets anders dan ontvangen berichten terugsturen naar de afzender en kan zo gebruikt worden om netwerkconnectiviteit te testen op applicatie-niveau : als je de echoserver laat luisteren op bijvoorbeeld tcp poort 80, kan je vanop een ander systeem een telnet-sessie naar die poort 80 starten. Alles wat je intikt in de telnetsessie zal door de server ontvangen worden en teruggestuurd. Zo kan je bijvoorbeeld in een complex netwerk met VLANs en firewalls tussen subnetten, nagaan of je webserver (of iets anders) nog bereikbaar zal zijn.

Toen objectgeoriŽnteerde talen hun opgang maken, werd in- en output geÔmplementeerd via klassen, met eigenschappen en methoden. Dit is - in grote lijnen - te vergelijken met het gebruik van functies, zoals in bovenstaand C voorbeeld. Zo heeft Java een socket pakkage (verzameling van klassen), en C++ de socketstream classes of de Csocket class uit de Microsoft Foundation Classes. Trouw aan het oorspronkelijk ontwerp, waar socket I/O gemodelleerd werd naar het voorbeeld van file I/O, wordt bij objectgeoriŽnteerde programmeertalen door middel van overerving en polymorfisme gezorgd dat socket I/O, console I/O, file I/O, enz. heel goed op elkaar lijken, wat het gebruiksgemak voor de programmeur sterk verhoogt.

Aangezien dit nog steeds source files zijn, is het voor de programmeurs ook mogelijk de implementatie, desgewenst, aan te passen. Voor eenvoudig application development (het creŽren van toepassingssoftware) kan de programmeur er zich toe beperken de publieke functies van deze klassen (de API) aan te roepen.

Tenslotte worden sockets ook geÔmplementeerd als Dynamic Link Library. In dit geval is de betreffende library gecompileerd (bijv. winsock.dll), en bied deze enkel een interface aan (een verzameling van functies) die door de programmeur aangeroepen kunnen worden bij het schrijven van zijn programma, en die dus ook tijdens de uitvoering van het programma aangeroepen zullen worden. In Visual Basic gebeurt dat door de Microsoft Winsock Component aan het project toe te voegen (zie vroeger),in andere talen kunnen de publieke functies van Winsock rechtstreeks aangesproken worden (zie verder). Dat betekend echter wel dat de Winsock.dll aanwezig moet zijn op het systeem waar het programma uitgevoerd wordt. Toepassingen die gebruik maken wan winsock.dll moeten dus tijdens de installatie een winsock.dll op het host systeem installeren.

ADDRES FAMILY en PROTOCOL FAMILY

Sockets bieden een proces toegang tot een ander proces, al dan niet op dezelfde host. Sockets bieden een programmeur dus de mogelijkheid netwerkfunctionaliteit, of datacommunicatiefunctionaliteit, te implementeren in een programma, zonder al te veel rekening te moeten houden met de hoe die communicatie tot stand komt. Desalniettemin moet een programmeur van communicatiesoftware (of, ruimer gezien,software die moet kunnen communiceren met andere software), in enige mate rekening houden met de context waarin die communicatie zal plaatsvinden.

Sockets zorgen er, in de eerste plaats, voor dat een proces data kan afleveren aan een ander proces. Dat proces moet echter gelokaliseerd kunnen worden, er is nood aan adressering. Voor communicatie met een proces op een UNIX machine kan bijvoorbeeld een filepathname gebruikt worden, omdat (vrijwel) alles op een UNIX machine als een file benaderd kan worden. Op het internet wordt voor adressering van hosts gebruik gemaakt van IP-nummers. Andere netwerkprotocollen hebben hun eigen manier van adressering.

In de definitie van BSD Sockets (socket.h - zie links) wordt dan ook onderscheid gemaakt tussen verschillende Address Families of Communication Domains.

Daarnaast voorziet de socket specification ook Protocol Families, maar aangezien protocollen nauw samenhangen met het communication domain, zijn de protocolfamilies identiek aan de Address Families (PF_UNIX, PF_INET, PF_SNA, ...).

Sockets in soorten

Sockets laten processen op verschillende hosts toe met elkaar te communiceren, via een netwerk. Uitgaande van een TCP/IP netwerk, vinden we op de 'host-to-host' laag 2 protocollen : TCP en UDP. Analoog daaraan bestaan er 2 types sockets. We zullen zien dat deze types aanleunen bij de respectievelijk het UDP en het TCP protocol.

stream socket

Een stream socket resulteert in datastroom. Deze is bidirectioneel (de socket kan zowel zenden als ontvangen). Na de creatie van de socket wordt via de connect() call een verbinding opgezet met de 'remote' socket, alvorens er data verstuurd wordt. De communicatie is betrouwbaar. Data worden ontvangen in de volgorde dat ze verstuurd zijn, en er is geen risico op duplicatie.

Het is duidelijk dat dit type socket, toegepast op een tcp/ip netwerk, verwijst naar het tcp protocol.

datagram socket

Anders dan de stream socket kent de datagram socket geen connect(). Er wordt enkel data verstuurd (send(), write()) of ontvangen (recv(), read()). Als delen van een bericht via meerdere send() opdrachten verstuurd worden, is er dus geen garantie dat ze aankomen in dezelfde volgorde aankomen als dat ze verstuurd zijn. De datagram socket wordt niet verondersteld betrouwbare of ongedupliceerde communicatie te leveren. De communicatie is wel bidirectioneel : dezelfde socket kan gebruikt worden voor zenden en ontvangen.

De datagram socket is o.a. bedoeld als interface met het udp protocol.

Naast de STREAM en DGRAM sockets, geassocieerd met respectievelijk het tcp en udp transport protocol, voorziet de BSD Socket Specification ook nog en Sequenced Packet Sockets, Reliably Delivered Message Sockets en Raw Sockets.

Deze worden op dezelfde manier gecreŽerd als STREAM en DGRAM sockets, namelijk door het meegeven van een parameter (SOCKET_STREAM, SOCKET_DGRAM, SOCKET_RAW, SOCK_SEQPACKET, SOCK_RDM) aan de socket() call.

Sequenced packet socket :
vergelijkbaar met de stream socket maar enkel bedoeld voor toegang tot het Sequenced Packet Protocol (SPP).
Reliably Delivered Message socket :
Gelijkaardig aan het Datagram Socket, maar met voorzieningen voor betrouwbare communicatie (reliable delivery). Wordt momenteel niet toegepast.
Raw Socket :
socket type dat toegang biedt tot de onderste netwerklagen (IP, datalink). Oorspronkelijk bedoelt voor experimentele en educatieve doeleinden, netwerk troubleshooting (vb ICMP) en om eventueel nieuwe protocollen te ontwikkelen.
Secure Socket :
In het kader van datacommunicatie worden soms ook 'Secure Sockets' vermeldt, zoals in SSL - Secure Socket Layer. Secure Sockets worden niet als apart type vermeld in unix / linux / BSD documentatie. We komen hier later nog even op terug.

Het type socket dat gebruikt kan worden hangt af van het communication domain.

Eerder zagen we nog dat Winsock datacommunicatie volgens de IrDA specificatie ondersteund en daarvoor AF_IRDA toegevoegd heeft. Andere types zijn speciaal ontwikkeld voor toegang tot specifieke netwerken. De Sequenced Packet socket is bijvoorbeeld bedoeld als interface met Xerox Network Systems, waar o.a. het Sequenced Packet Protocol toegepast wordt.

Algemeen geldt dat een socket alleen communiceert met een gelijksoortige socket (zelfde adresfamily of protocol familie, zelfde type), of in ieder geval een gelijksoortig mechanisme. Een STREAM socket zal dus niet met een DATAGRAM socket communiceren.

Typisch volgt er uit het Communication Domain en het Socket Type een protocol. Een Datagram Socket in het Internet domain verwijst naar het UDP protocol. Deze socket zal dus van UDP gebruik maken om te communiceren met de host. Wanneer deze relatie niet zo eenduidig is, kan een protocol gespecificeerd worden aan de hand van een nummer. De mapping van protocollen op protocol nummers is o.a. te vinden in de protocollen file van de host, of kan door een proces via system calls opgevraagd worden. RAW sockets kunnen tegen verschillende protocollen gebruikt worden en dienen dus een protocolnummer mee te krijgen.

Hoe werkt het

Connection oriented sockets

communicatie via sockets
Figuur 7 - Communicatie via sockets (Sockets, WPI)

The steps involved in establishing a socket on the server side are as follows:

  1. Create a socket with the socket() system call
  2. Bind the socket to an address using the bind() system call. For a server socket on the Internet, an address consists of a port number on the host machine.
  3. Listen for connections with the listen() system call
  4. Accept a connection with the accept() system call. This call typically blocks until a client connects with the server.
  5. Send and receive data

The steps involved in establishing a socket on the client side are as follows:

  1. Create a socket with the socket() system call
  2. Connect the socket to the address of the server using the connect() system call
  3. Send and receive data. There are a number of ways to do this, but the simplest is to use the read() and write() system calls.

In dit geval is er duidelijk sprake van een 'client process' en een 'server proces'. Het server proces creŽert een socket, die luistert op een gegeven poort, tot er een verbinding gemaakt wordt. Het is het client proces dat de verbinding initieert. Het connection-oriented blijkt duidelijk uit het feit dat de client een connect()uitvoert, die door de server (of de peer) accept() wordt, alvorens er sprake is van het verzenden of ontvangen van data. De Socket die hier gecreŽerd wordt is van het type SOCKET_STREAM, een stream socket.

Het adres waarvan sprake is een record (in C : een struct) waarin plaats voorzien is voor adresgegevens, zoals een IP adres en een poortnummer. Het IP adres van de server socket is het IP adres van de host waarop het proces draait. Het eigen IP adres kan worden aangeleverd door de kernel, via functies die de netwerkconfiguratie kunnen uitlezen. Deze waarde is bestemd voor het source address veld in de IP header. Het poortnummer wordt bepaald door de programmeur, en volgt bij voorkeur enige logica (well-know port numbers, registered port numbers). Deze waarde komt als source port terecht in de TCP header.

De client kan op dezelfde manier een adres en portnummer krijgen. Het poortnummer van de client, indien niet gespecificeerd door de programmeur, kan een willekeurig nummer zijn dat nog niet in gebruik is. Ook hier bestaan mechanismen om een poortnummer te bepalen.

Uiteraard moet het client proces het IP adres en poortnummer van de te connecteren server socket kennen. De applicatie waarin de socket zit zal dus voorzien moeten worden van een mechanisme om deze informatie te weten te komen, hetzij door input van de gebruiker, configuratiebestanden, parameters, etc. Een vorm van address resolution is wellicht ook noodzakelijk.

Daarnaast zijn er een aantal functies voorzien waarmee een applicatie deze informatie kan opvragen, zoals gethostbyname, gethostbyaddr, getnetbyname, getnetbyaddr, getservbyname, getservbyport enz. Deze functies kunnen gebruik maken van diverse lookup mechanismen van het besturingssysteem (ifconfig, dns daemon, hosts file, services file, ...) om poortnummers en IP adressen te weten te komen.

Uiteraard komen deze waarden uiteindelijk terecht in de IP header (destination address) en TCP header (destination port).

De functie listen() laat het server proces luisteren op zijn toegewezen poort. Listen() zorgt er ook voor dat incomende connecties in een wachtrij geplaatst worden. De accept() functie die daarop volgt is een 'blokkerende functie', een blocking call. Dat wil zeggen dat de opdracht pas beŽindigd wordt op het moment dat er (van een ander proces) een connect() ontvangen is. Connect() en accept() zorgen samen voor de 3-way handshake die het tot stand komen van een TCP verbinding karakteriseert. Deze functies zorgen dus voor het zetten van de SYN en/of ACK vlag in de TCP header.

Send() en Recv() worden herhaalt zolang als nodig is. Tenslotte wordt de communicatie beŽindigd met close().

De close() opdracht sluit de socket slechts als alle data (die eventueel nog aanwezig is in buffers) verzonden (of ontvangen) is. Eventueel kan de connectie vroegtijdig beŽindigd worden door shutdown() (maakt buffers leeg), gevolgd door close(). Close() zorgt dus dat de FIN vlag van de TCP header gezet wordt.

Tenslotte bied de stream socket ook nog voorzieningen voor out-of-band data. Dit zijn data die weliswaar meegestuurd worden in het TCP-pakket, maar om een of andere reden onmiddellijk moeten afgeleverd worden, dus buiten de volgorde van de andere pakketten om. Dat houdt wellicht verband met de URG of RSH vlag van de TCP header. URG betekent dat het pakket (onder andere) urgente data bevat (de locatie ervan wordt ook aangegeven in de TCP header). PSH betekent 'data die onmiddellijk doorgestuurd moeten worden'.

Connectionless sockets

Anders dan in connection-oriented datacommunicatie, wordt er voor connectionless protocollen (UDP) geen gebruik gemaakt van de functies listen(), connect() en accept(). Het mechanisme is nog eenvoudiger :

  1. creŽer een socket met de socket() call,
  2. zend data met de sendto() call, en/of ontvang data met de recvfrom() call.

Anders dan bij de send() en recv() functies (of write() en read()) die veronderstellen dat er een connectie is en communiceren met het adres waarmee een verbinding is tot stand gebracht, wordt bij sendto() en recvfrom() het adres van de tegenpartij expliciet als parameter meegegeven. De socket stuurt dus een gegeven bericht naar een gegeven adres (IP addres + port number), en daarmee is de kous af. Er is geen voorafgaande controle of er aan de andere kant een proces is dat de verzonden data kan, wil of zal ontvangen. Er wordt ook geen acknowledgement verwacht als de data zijn toegekomen. De communicatie is dus verbindingsloos en onbetrouwbaar. Als meerders sendto() opdrachten gebruikt worden (bijv voor transfer van data die langer is dan wat sendto() in ťťn keer kan verwerken), is er geen garantie dat alle datagrammen in de juiste volgorde toekomen.

Het type socket dat hier gebruikt wordt is SOCKET_DGRAM, een datagram socket. Raw Sockets (SOCKET_RAW) zijn ook geschikt voor dit soort communicatie.

Het is desalniettemin mogelijk connect() te gebruiken in combinatie met een SOCKET_DGRAM socket. Er wordt dan echter nog steeds geen connectie opgezet. Data verstuurd via de socket zal echter automatisch verstuurd worden naar het adres dat gebruikt is in het connect() statement, en enkel data van dit address zal door de socket aanvaard worden. Het connect() statement wacht in dit geval niet op een accept() van de communicatiepartner, maar onthoudt enkel het destination address.

Voorbeelden van socket programming in Perl, Java, C, C++ enz zijn te vinden in de in de bibliografie aangehaalde bronnen.

Sockets - een iets andere definitie

Uit het voorgaande blijkt dat sockets op een netwerk uniek geÔdentificeerd kunnen worden als een combinatie van een netwerknummer (host address) en een poortnummer (verwijzing naar een socket op een host). Het adres identificeert de host, de poortnummer identificeert een proces op die host. Zodoende kan een web server 2 client processen (browsers, browser sessies) onderscheiden en hun requests correct afhandelen.

Vandaar dat sommige auteurs 'socket' definiŽren als 'de combinatie van een hostadres en een poortnummer'. In sommige gevallen wordt daar nog 'protocolnummer' aan toegevoegd, maar meestal is dit overbodig omdat het protocol, by default, afgeleid uit het communication domain (addres family) en het socket type.
bijv.

Op een gelijkaardige manier worden sockets dikwijls omschreven als 'een endpunt van communicatie' : een proces op een host creŽert een socket, die communiceert met een (veronderstelde socket gecreŽerd door een) ander proces. De Sockets vormen een interface met de infrastructuur (netwerk, ... ) die de communicatie mogelijk maakt. Een vergelijking die hier dikwijls gehanteerd wordt is dat sockets te zien zijn als een telefoontoestel. Een proces 'telefoneert' via de socket naar een ander proces, als daar opgenomen wordt en de tegenpartij is bereid te praten, wordt er een gesprek gevoerd, waarna de communicatie (volgens de regels van het protocol) beŽindigd wordt. Voor verbindingsloze socket kan eventueel gezien worden als een brievenbus. Je gooit er een brief in (met adres van afzender en bestemmeling) en dat is dat.

De analogie met TCP respectievelijk UDP is duidelijk. In het TCP/IP model vinden we TCP en UDP trouwens op de host-to-host laag, wat ook mooi aansluit bij het concept socket als 'eindpunt van communicatie'.

SOCKET OPTIONS

We hebben gezien dat bij de creatie van een socket 3 parameters meegegeven worden :

  1. Communication Domain, vb 'Internet' -
  2. Type van Socket, vb stream socket => connection oriented
  3. Protocol

Daarnaast zijn er nog een aantal opties. De setsockopt ('Set Socket Option') neemt als parameters o.a. de socket waarop de options van toepassing zijn, de naam van de optie, de waarde van de optie (True/False, 1/0, of een andere waarde), en het option level. Het option level geeft aan door welk deel van het systeem de optie geinterpreteerd moet worden.

Deze levels worden gedefinieerd in de include/netinet/in.h header file :

SOL_IPPROTO_TCP geeft aan de gegeven optie bedoeld is voor het Transport Control Protocol (TCP protocol), enz. SOL_SOCKET (in socket.h) geeft aan dat de gegeven optie op socket-niveau geÔnterpreteerd moet worden,

Naast setsockopt is er uiteraard ook getsockopt(), een functie die toelaat een bepaalde optie van een gegeven socket op te vragen en uit te lezen.

Broadcasting.

Datagram sockets kunnen gebruikt worden om broadcasts te genereren. Bedoeling is om een proces toe te laten bronnen op het netwerk te lokaliseren zonder dat het adres ervan te kennen, of informatie doorgeven aan andere hosts (bijv. voor routing)

Hiertoe beschikken sockets over een mechanisme om het broadcast address (van het netwerk waartoe ze behoren) op te vragen, aan de hand van de network interface configuration van hun host.

Multicast (sending)

Multicast is het verzenden van een IP datagram naar een groep van meerdere hosts, via 1 address. Omwille van het onbetrouwbare en verbindingsloze karakter kan er niet van uit gegaan worden dat alle bedoelde hosts de communicatie ontvangen, of zelfs dat alleen maar de bedoelde hosts de communicatie ontvangen.

Via een TTL-nummer wordt de scope van de multicast aangegeven. Een TTL van 1 zorgt dat de multicast beperkt blijft tot hut subnet waar de zender deel van uitmaakt. Hogere nummers laten toe meerdere routers te passeren en dus ook andere subnetten te bereiken. Het maximum is 255 : unrestricted.

Multicast en Boadcast is alleen toepasbaar bij datagram sockets (of raw sockets).

Multicast (receiving)

Om multicast berichten te kunnen ontvangen moet de network interface card van de betreffende host multicast ondersteunen. Verder moet de host lid zijn van een IP multicast group.

Een proces kan lid worden van een multicast groep d.m.v. de socket option IP_ADD_MEMBERSHIP, waarbij het multicast address van de group en de betreffende network interface meegegeven worden. Meerdere multicast group memberships op dezelfde socket zijn mogelijk. Het is ook mogelijk van meer dan 1 proces te binden aan een multicast socket, d.w.z. het is mogelijk meerdere sockets te creŽren met hetzelfde multicast group address en portnummer.

Er zijn tevens opties die multicast mogelijk maken zonder gebruik te maken van IP, bijvoorbeeld door rechtstreeks gebruik te maken van de multicast-ondersteuning van de datalink. De address family die in dat geval gebruikt dient te woren, is AF_UNSPEC.

Verder zijn er opties als SO_KEEPALIVE (keep connections alive), SO_DONTROUTE (just use interface addresses), SO_USELOOPBACK, ... Deze, en andere opties, worden gedefinieerd in de socket.h file (zie bijlage).

Andere opties laten een proces toe protocol-specifieke informatie door te geven aan het netwerk, bijvoorbeeld ten behoeve van propriety protocollen (vb Xerox Network Systems protocols) en anderen non- TCP/IP protocollen.

Bijgevolg zijn niet alle opties toepasbaar op alle sockets : de beschikbare opties hangen o.a. af van het type socket, de Protocol of Address Family.

Byte Order

Een ander interessant aspect i.v.m. datacommunicatie is de byte order binnen een groep van bytes die bij elkaar horen (een "word"). Byte Order geeft aan hoe, in welke volgorde, bytes gelezen of opgeslagen worden. Denk 'van links naar rechts' of 'van rechts naar links'. Dit is vooral van belang voor de interpretatie van getallen, bijv. IP adressen.

We onderscheiden hier

Het kan gebeuren dat de host een andere byte order hanteert dat het netwerk. In dat geval is het noodzakelijk de byte order van te verzenden en ontvangen data te converteren naar de gepaste byte order.

Deze verschillende Byte - volgordes worden ook aangeduid met de termen 'Big Endian' en 'Little Endian'.

Computers op basis van Intel processoren gebruiken Little-Endian Byte Order. Macintosh computers (Motorola processor) gebruiken Big-Endian Byte Order. Op het netwerk wordt Big-Endian Byte order verondersteld. Datacommunicatie tussen, in dit voorbeeld, Macintosh software en Windows software vereist dus conversie tussen Big-Endian en Little-Endian Byte order. Sockets bevatten een aantal functies die deze conversie uitvoeren : htons() (host to network short integer), ntohs() (network to host short integer), htonl() (host to network long integer), and ntohl() (network to host long integer).

Het spreekt vanzelf dat een 'host to network' conversiefunctie op een Windows (Intel) machine dus een andere betekenis zal hebben dan op een Macintosh. In het laatste geval is de network byte order al gelijk aan de host byte order, in het eerste geval niet en moet de volgorde omgekeerd worden.

Dit illustreert alweer hoe sockets toegang geven tot netwerken en tevens abstractie maken van de onderliggende infrastructuur.

Winsock

BSD Socket implementatie

Windows Sockets9 is de Microsoft Windows versie van BSD sockets. Aangezien de Windows besturingssystemen van Microsoft een wijde verspreiding kennen en een groot aantal gebruikers hebben, kan Windows Sockets (WinSock) beschouwd worden als een belangrijke vertegenwoordiger van de BSD Sockets. Vandaar dat we Winsock even van dichtbij bekijken.

Windows Sockets volgt de BSD standaard, maar voegt er een aantal Microsoft Windows-specifieke functies aan toe. M.a.w. de Windows Socket Specification beschrijft de Windows Socket API. Voor programmeurs is het bijvoorbeeld goed te weten dat er een functie WSAStartup() en WSACleanup() bestaat die zou zorgen voor het correct initialiseren, respectievelijk afbreken van de sockets. Daarnaast wijkt Winsock af van de BSD standaard op het vlak van error-handling. Verder biedt de Winsock API dezelfde functies als de BSD standaard sockets, plus een aantal functies specifiek voor gebruik in Windows toepassingen en om conform te zijn met WOSA, een standaard voor Windows software ontwikkeling.

Uiteraard zegt dit niets over hoe die functies geÔmplementeerd zijn. Er zijn mensen die beweren dat de implementatie van Microsoft nogal gebrekkig is, en niet alle TCP/IP communicatie op correcte wijze verwerkt. Sommige programmeurs gaan zelfs zo ver dat ze de gebruiker de mogelijkheid bieden geen gebruik te maken van winsock, en in plaats daarvan andere methoden voor netwerkconnectiviteit andere oplossingen aanbieden.

Een voorbeeld uit de Help bij Pegasus Mail (10), over problemen veroorzaakt door de manier waarom de winsock.dll van Microsoft dial-up connecties afhandelt.

--- Unfortunately, the most commonly-used Windows Sockets implementation - the Microsoft version shipped with Windows 95 and NT - is also one of the poorest at handling dialup connections. To work around the deficiencies of Microsoft's code, Pegasus Mail provides a number of methods you can use to control dialling and hanging up on your Internet connections. ---

Microsoft zegt over Winsock :

--- Windows Sockets 2 (Winsock) enables programmers to create advanced Internet, intranet, and other network-capable applications to transmit application data across the wire, independent of the network protocol being used. With Winsock, programmers are provided access to advanced Microsoft(r) Windows(r) networking capabilities such as multicast and Quality of Service (QOS).

Winsock follows the Windows Open System Architecture (WOSA) model; it defines a standard service provider interface (SPI) between the application programming interface (API), with its exported functions and the protocol stacks. It uses the sockets paradigm that was first popularized by Berkeley Software Distribution (BSD) UNIX. It was later adapted for Windows in Windows Sockets 1.1, with which Windows Sockets 2 applications are backward compatible. Winsock programming previously centered around TCP/IP. Some programming practices that worked with TCP/IP do not work with every protocol. As a result, the Windows Sockets 2 API adds functions where necessary to handle several protocols. ---

Aangezien Microsoft Winsock implementeert als DLL, moet deze dll aanwezig zijn op het systeem waar de toepassingen die van winsock gebruik maken, uitgevoerd worden.

Anders dan bij de meeste dll's is er niet 1 officiŽle winsock.dll. Iedere producent van communicatiesoftware of netwerksoftware is vrij zelf een eigen winsock.dll te produceren, volgens de Microsoft WinSock Specification, een document dat beschrijft welke functies de API moet aanbieden en hoe ze te gebruiken.

Bekende producenten van WinSock zijn :

Niet alle winsock.dll's zijn dus identiek. Er kunnen verschillen bestaan in de wijze waarop de functies geÔmplementeerd zijn, maar ook in de protocollen die Winsock ondersteund. Zo zouden bepaalde winsock.dll's enkel ondersteuning bieden voor Ethernet netwerken, andere voor Ethernet + modemverbindingen (SLIP en / of PPP), nog andere winsocks bieden ook connectiviteit over Token_Ring of X.25, de Winsock bij Windows NT zou toelaten gelijktijdig over Ethernet en modemverbindingen te communiceren terwijl de Windows 95 Winsock slechts 1 soort verbinding tegelijk ondersteund, enzovoort.

De winsock.dll wordt verder ook meegeleverd met netwerksoftware zoals Novell Netware of het AOL internet access pakket. Een gevolg daarvan is dat er op een systeem uiteindelijk meerdere, verschillende winsock.dll's kunnen bestaan. Programma's gebruiken in dat geval de eerste dll die ze vinden, en dus niet noodzakelijk de dll waarvoor ze ontwikkeld zijn. Aangezien er verschillen kunnen zijn in de implementatie, kan dat als gevolg hebben dat sommige programma's niet of gebrekkig werken als ze toevallig de 'verkeerde' dll aanspreken.

Een gelijkaardig probleem ontstaat als bij de installatie van communicatiesoftware een bestaande winsock.dll overschreven wordt door de dll die bij de nieuw geÔnstalleerde software meegeleverd wordt. Als deze dll dan onvoldoende compatibel is met andere programma's kan het gebeuren dat een aantal programma's niet meer werken.

Zo is er een verhaal bekend over AOL dat bij de installatie van zijn internetsoftware een winsock.dll installeerde die blijkbaar iets te veel afgestemd was op uitsluitend AOL software, waardoor de mensen die dit internet access pakket installeerden, plots hun andere datacommunicatiesoftware dienst zagen weigeren.

Protocol Stack ?

Sommige bronnen ( o.a. Webopaedia) beschrijven Winsock ook als een protocol stack :

--- Protocol Stack : A set of network protocol layers that work together. The OSI Reference Model that defines seven protocol layers is often called a stack, as is the set of TCP/IP protocols that define communication over the internet.

The term stack also refers to the actual software that processes the protocols. So, for example, programmers sometimes talk about loading a stack, which means to load the software required to use a specific set of protocols. Another common phrase is binding a stack, which refers to linking a set of network protocols to a network interface card (NIC). Every NIC must have at least one stack bound to it.

In Windows, the TCP/IP stack is implemented by the Winsock DLL.
---

Deze benadering wijkt af van definitie die we tot noch toe gehanteerd hebben. We zijn er van uit gegaan dat

Hier wordt de Winsock dll echter beschreven als een (implementatie van een) protocol stack, dus als een concrete uitwerking van verschillende protocollen die, als lagen op elkaar, met elkaar samenwerken om netwerkconnectiviteit te voorzien voor toepassingsprogrammas.

De beschrijving van een aantal verschillende Winsocks in "Winsock programming" (zie bibliografie), suggereert echter dat ook deze benadering correct kan zijn. Als van deze of gene Winsock gezegd kan worden dat hij

  1. bepaalde datalink protocollen (Ethernet, Token-Ring, SLIP, PPP) wel of niet aanbiedt,
  2. naast TCP/IP ook andere netwerk en transport protocollen ondersteunt, en
  3. (uiteraard) een interface met de application layer aanbiedt,

lijkt het me duidelijk dat een winsock.dll inderdaad beschouwd kan worden als een concrete uitwerking van een aantal samenwerkende protocollen, een protocol stack.

Deze benadering lijkt ook te worden ondersteund door de veelgehoorde verklaring dat Windows oorspronkelijk niet ontworpen is voor gebruik in een netwerk, maar dat ondersteuning voor netwerking later aan het besturingssysteem is toegevoegd (door het toevoegen van een winsock.dll bestand), in tegenstelling tot bijvoorbeeld Linux, waar de TCP/IP stack is geÔmplementeerd in de kernel.

Het zou ons te ver leiden dit in detail te onderzoeken, maar het leek me wel belangrijk dit onderscheid aan te halen, omdat in de in dit werk geciteerde bronnen zowel de ene (WinSock = BSD sockets) als de andere benadering (Winsock = protocol stack) hanteren, zonder evenwel expliciet aan te geven vanuit welke benadering ze geschreven zijn.

Raw Sockets

Sockets (standaard BSD Sockets) vormen een interface met OSI laag 4, of, uitgaande van een TCP/IP netwerk, de host-to-host layer. In de definitie van de socket en verwante functies, maar ontoegankelijk voor de applicatie, is geÔmplementeerd hoe de input van de applicatie moet worden doorgegeven aan TCP of UDP. De TCP laag communiceert vervolgens met de IP laag van de host, die o.a. het IP adres van de host als 'source address' meegeeft. Deze laag communiceert met de datalink laag, enz.

Zoals eerder gezien, is het, vanuit het standpunt van een programmeur, ook mogelijk om rechtstreeks met de packet drivers te communiceren. De ontwerpers van BSD hebben tevens een interface voorzien die dit vereenvoudigd : de zogenaamde "raw sockets". Het concept is vergelijkbaar met standaard BSD sockets, maar standaard sockets leveren enkel toegang tot de TCP /UDP laag, terwijl raw sockets ook toegang geven tot de onderliggende lagen : IP en datalink.

De oorspronkelijke bedoeling hiervan was om het mogelijk te maken internettoepassingen te creŽren die geen gebruik maken van TCP of UDP, maar bijvoorbeeld rechtstreeks op IP niveau werken : ICMP (bijv. ping, traceroute, ...) e.d. Binnen het kader van de universiteit die BSD ontworpen heeft, gaf Raw Socket support ook een mogelijkheid om datacommunicatie in detail te bestuderen en er mee te experimenteren.

Raw Sockets, in combinatie met een een aantal socket options, bieden een programmeur de mogelijkheid zelf (IP-) pakketten samen te stellen, of de samenstelling ervan te manipuleren. Dit kan van pas komen bij het ontwikkelen van bepaalde toepassingen, zoals Network Address Translation.

Een andere toepassing hiervan is dat, omwille van de toegang tot de IP laag en de datalink, TCP/IP pakketjes 'gespoofd' kunnen worden, d.w.z. voorzien van informatie die niet aangeleverd wordt door de transport of netwerk laag, maar door de applicatie (dus door de programmeur of de gebruiker). Concreet wil dat zeggen dat het bijvoorbeeld mogelijk is pakketjes te verzenden met een willekeurig IP bronadres, in plaats van het echte IP adres van de host die de pakketjes verzendt.

Dit is de essentie van bijvoorbeeld de reflected denial of service attack14. Stuur een SYN pakketje (verzoek om TCP connectie te openen) naar een willekeurige server (de 'reflector'). Zet in het pakketje, als bron adres, het adres van een andere server (de 'victim'). Het antwoord op het SYN pakket (the ACKnowledgement) zal naar dit adres verstuurd worden in naar de echte afzender. Doe dit met een groot aantal (reflector) servers tegelijk, en de victim server wordt overspoelt met SYN/ACK pakketjes die allemaal beantwoord moeten worden. De pakketjes consumeren bandbreedte, het verwerken ervan gebruikt geheugen en processertijd. Met voldoende pakketjes haal je de victim server van het internet.

Een andere bekende toepassing is nmap (http://www.insecure.org/). nmap (Network Mapper) is een -naar men zegt - performante port scanner, die, naast het gewone 'proberen connecteren op een poort en zien of de connectie geaccepteerd wordt' (in socket terminologie : connect() en accept()), ook een aantal minder voor de hand liggende scan en probing technieken toepast, door via Raw Sockets zelf pakketjes 'samen te stellen'. Door de respons van de gescande host op deze pakketten kunnen poorten geŽvalueerd worden (open - filtered - closed) zonder dat er een connectie geÔnitieerd hoeft te worden.

De eenvoudigste toepassing hiervan is het scannen met pakketjes waar de ACK of FIN vlag gezet is (i.p.v.SYN), zodat firewalls dit niet interpreteren (en loggen) als een 'attempt to connect'. Ook is het mogelijk de 3-way handshake af te breken nadat een SYN/ACK ontvangen is, door een RST terug te sturen ipv de te verwachten ACK. Er wordt dan geen verbinding tot stand gebracht, wat met het gewone gebruik van connect() en accept() wel het geval zou zijn.

Een andere illustratieve toepassing is het spoofen van port numbers. Aangezien vele firewalls communicatie van poort 53 toe laten om verkeer met DNS servers toe te laten, kunnen de pakketjes die gebruikt worden om te scannen, gespoofd worden zodat ze afkomstig lijken van een DNS server (poort53). Het gebruik van zombies (een portscan laten uitvoeren door een andere computer, maar de resultaten laten toekomen op je eigen systeem) behoort ook tot de mogelijkheden.

Uit de reacties van het de gescande host op de gemanipuleerde pakketten kan tevens een vrij accurate gissing naar het gebruikte besturingssysteem (inclusief versie) gedaan worden.

Meer info in verband hiermee is te vinden in de nmap man page.

Secure Sockets

De term "secure socket" komt niet voor in enige documentatie die ik over sockets heb gevonden. Web queries i.v.m. secure sockets resulteren onveranderlijk in verwijzingen naar SSL - Secure Socket Layer.

Documentatie over SSL spreekt meestal over een 'protocol', wat suggereerd dat dit niet direct iets met BSD sockets te maken heeft. Het zou dus wel eens kunnen dat de 'socket' in 'secure socket' een andere betekenis heeft dan dat we tot hier toe gehanteerd hebben. Dit wordt ondersteund door het feit dat OSI encryptie situeert in de presentatielaag. In het TCP/IP model zou dat dan wellicht de application layer zijn, zodat we kunnen aannemen dat encryptie (en authenticatie en andere security issues) door de applicatie elf afgehandeld moeten worden.

Nochtans is het wel zo dat sockets te situeren zijn tussen de application layer (proces) en de transportlaag (datacommunicatie) : een socket bevindt zich in de applicatie maar geeft toegang tot het netwerk. In die zin kan ik me voorstellen dat een ietwat aangepaste implementatie van een socket voor bijvoorbeeld encryptie kan zorgen alvorens de data doorgegeven worden aan het onderliggende protocol.

Microsoft vermeldt - enigszins onverwacht, Microsoft wordt niet dikwijls geassocieerd met security, tenzij in negatieve zin - in zijn documentatie over winsock 17 het gebruik van secure sockets.

-----

Windows CE supports the Private Communication Technology protocol 1.0 and SSL versions 2.0 and 3.0 security protocols. These protocols are available either through WinInet or directly from Winsock. Adding security to an application using these Winsock extensions requires few changes to an application. Once a secure socket is connected, the application may send and receive data on that socket unaware that the data over the wire is encoded.

Dit lijkt in ieder geval heel sterk op het toepassen van sockets voor datacommunicatie, in de betekenis die er in dit werk aan gegeven is. Deze tekst suggereert inderdaad dat secure sockets een uitbreiding zijn van de 'gewone' sockets, om geŽncrypteerde datacommunicatie aan te bieden aan applicaties.

-----

Implementing a Secure Socket
To implement a secure socket

  1. Create a socket with the socket function.
  2. Set the socket in secure mode with the setsockopt function. Set the level parameter to SO_SOCKET, optname to SO_SECURE, and optval to a DWORD set to SO_SEC_SSL.
  3. Specify the certificate validation callback function by calling WSAIoctl with the SO_SSL_SET_VALIDATE_CERT_HOOK control code.
  4. To specify a particular security protocol, call WSAIoctl with the SO_SSL_GET_PROTOCOLS control code to determine the default protocols. Then call WSAIoctl with the SO_SSL_SET_PROTOCOLS control code to select the protocols to be enabled. Otherwise, Windows CE selects the protocol.
  5. Make a connection with the connect function.
    The certificate callback function is automatically called. The connection can be completed only if the callback function verifies the acceptability of the certificate by returning SSL_ERR_OKAY.
  6. Transmit and send.
    The send and recv functions automatically encode and decode data.
  7. When finished, close the socket with the closesocket function.

Ook dit lijkt als twee druppels water op de beschrijving van hoe een proces een socket creŽert, een connectie opzet, en data zend en ontvangt. Het enige verschil hier is dat d.m.v. een socket option (SO_SECURE) met waarde "SO_SEC_SSL" de socket 'secure' gemaakt wordt, waarna (punt 6) de functies send() en, recv() automatisch voor encryptie en decryptie zorgen.

Microsoft beschrijft gelijkaardige mechanismen voor authentication d.m.v. certificaten, waarbij een connectie via de secure socket pas aanvaard wordt als de remote host een certificaat van een trusted authority kan voorleggen. (zie ook punt 5 in de procedure).

De waarde van de optie suggereert in dit geval dat security geÔmplementeerd wordt (of kan worden) volgens het SSL protocol.

Samenvattend kunnen we hieruit afleiden dat SSL een protocol is dat een bepaalde wijze van encryptie en authenticatie voorschrijft. Deze secure datacommunication kan in een toepassing geÔmplementeerd worden door het gebruik van een socket implementatie waarvoor de optie SO_SECURE gedefinieerd is. De waarde van de optie bepaalt welk secure communication protocol toegepast zal worden. Aangezien deze optie op socket-level geinterpreteerd wordt, gebeurt de encryptie op het niveau van de applicatie ; de onderliggende protocollen (TCP ... ) krijgen gewoonweg een geŽncrypteerde datastroom te verwerken.

Daarentegen : SSL kent een Open Source tegenhanger, en die heet TLS : Transport Layer Security. De verwarring blijft.

Geraadpleegde Bronnen

Programmeren met Sockets


Koen Noens
december 2003