2009.10_Programowanie przy użyciu gniazd sieciowych_[Programowanie].pdf

(745 KB) Pobierz
439033680 UNPDF
Programowanie
Programowanie przy użyciu gniazd sieciowych
gniazd sieciowych
Rafał Kułaga
Podstawową umiejętnością, którą musi opanować każdy programista chcący pisać aplikacje sieciowe,
jest wykorzystanie mechanizmu gniazd sieciowych (ang. network sockets ). Pozwala on na wygodne
przesyłanie i odbieranie danych, niezależnie od wykorzystywanego sprzętu sieciowego. Podstawową
ideą gniazd sieciowych jest bowiem zapewnienie warstwy abstrakcji dla niskopoziomowych funkcji
sieciowych. Jeżeli chcesz dowiedzieć się, w jaki sposób nowoczesne systemy operacyjne realizują
komunikację sieciową, jakie są rodzaje gniazd sieciowych oraz w jaki sposób możesz wykorzystać je
w swoich aplikacjach, to jest to artykuł dla Ciebie. Zapraszam do lektury!
konywać co do znaczenia funkcji sieciowych
we współczesnych aplikacjach. Śmiało można
stwierdzić, że zdecydowana większość dostęp-
nych na rynku programów (w tym również gier kompu-
terowych), w taki czy inny sposób wykorzystuje połącze-
nia sieciowe komputera. Dotyczy to nie tylko baz danych,
aplikacji biznesowych i wspomagających zarządzanie, w
których naturalne jest zastosowanie architektury klient-
serwer, lecz również całej gamy aplikacji działających w
architekturze równy z równym (ang. P2P – Peer To Peer ),
pozwalających na wymianę plików.
Kiedy mówimy o programowaniu sieciowym, z
pewnością przychodzą nam na myśl języki programo-
wania, takie jak PHP, ASP, J2EE. Rozwiązania budo-
wane przy ich użyciu niemal zawsze korzystają z funk-
cji sieciowych. Ich cechą charakterystyczną jest wyko-
rzystanie strony internetowej jako interfejsu użytkow-
nika oraz przesyłanie poleceń i efektów ich wykona-
nia za pomocą protokołu HTTP. W tym artykule skupi-
my się jednak na innym aspekcie programowania przy
wykorzystaniu sieci komputerowych – będziemy mie-
li pełną kontrolę nad reprezentacją danych wysyłanych
poprzez sieć.
Jako programista aplikacji sieciowych korzystających
jedynie z mechanizmu gniazd, udostępnianego przez sys-
tem operacyjny, będziesz odpowiedzialny za zdeiniowa-
nie protokołu transmisji danych pomiędzy dwoma proce-
sami, działającymi na oddalonych maszynach. Może się
to wydawać dość skomplikowane, szczególnie jeżeli nie
posiadasz zbyt dużej wiedzy o budowie typowych proto-
kołów sieciowych warstwy aplikacji, jednak w rzeczywi-
stości sprowadza się do odpowiedniego przemyślenia roz-
kładu danych w części pakietu przetwarzanej przez apli-
kację.
Tworzenie bezpiecznych aplikacji w języku C/C++
korzystających z gniazd sieciowych wymaga od nas jed-
nak dużej ostrożności w manipulowaniu otrzymany-
mi danymi. Pamiętajmy, że błędy w tym aspekcie mo-
gą spowodować podatność naszego programu na ataki
wykorzystujące przepełnienie bufora (ang. buffer over-
low ). Na szczególne niebezpieczeństwo zostaje narażo-
ny nasz system w przypadku, gdy program uruchamia-
ny jest z uprawnieniami użytkownika root, co często jest
64
październik 2009
Programowanie
J estem przekonany, że nikogo nie trzeba prze-
439033680.041.png 439033680.042.png 439033680.043.png 439033680.044.png 439033680.001.png 439033680.002.png
Programowanie
Programowanie przy użyciu gniazd sieciowych
konieczne w celu wykorzystania niższych nu-
merów portów.
W artykule zostanie opisany sposób dzia-
łania gniazd sieciowych, ich typy oraz zasto-
sowanie. W trakcie lektury artykułu nauczysz
się, jak tworzyć proste aplikacje posiadające
możliwość bezpiecznej wymiany danych za
pomocą sieci komputerowej. Przyjrzymy się
również narzędziom, pozwalającym na testo-
wanie aplikacji korzystających z mechanizmu
gniazd. Wspomnimy również o dwóch bar-
dzo przydatnych bibliotekach – libnet i libp-
cap, pozwalających na niskopoziomowy do-
stęp do sieci.
Listing 1. Podstawowe struktury
struct addrinfo
{
int ai_lags ; // lagi sterujące
int ai_family ; // protokół: AF_INET (IPv4), AF_INET6
(IPv6), AF_UNSPEC (dowolny)
int ai_socktype ; // typ gniazda: SOCK_STREAM (TCP),
SOCK_DGRAM (UDP)
int ai_protocol ; // protokół
size_t ai_addrlen ; // rozmiar struktury ai_addr
struct sockaddr * ai_addr ; // wskaźnik na strukturę sockaddr_in
char * ai_canonname ; // nazwa hosta
struct addrinfo * ai_next ; // następny element listy
};
System operacyjny
a komunikacja sieciowa
Zanim przejdziemy do praktycznej realiza-
cji komunikacji sieciowej za pomocą mecha-
nizmu gniazd, warto poznać sposób, w ja-
ki system operacyjny (a konkretnie część ją-
dra systemu, zwana stosem TCP/IP) obsługu-
je te funkcje.
Gdy nasz komputer odbiera sygnały po-
chodzące z sieci, sprzęt, a konkretnie – inter-
fejs sieciowy, usuwa nagłówki protokołów
warstw znajdujących się poniżej warstwy sie-
ciowej. Od tej chwili, za obróbkę odebranych
danych odpowiada jedynie system operacyjny
oraz aplikacje (Rysunek 1).
W czasie przetwarzania przez część stosu
TCP/IP odpowiedzialną za obsługę protoko-
łów warstwy sieciowej, obcięty zostaje nagłó-
wek protokołu IP. Otrzymany segment (jed-
nostka danych protokołów warstwy transpor-
tu) zostaje przekazany w górę stosu.
Na poziomie warstwy transportu mamy
do czynienia z numerami portów, stanowią-
cych identyikatory, pozwalające na przeka-
zanie danych wydobytych z segmentu do od-
powiedniego procesu. W większości syste-
mów operacyjnych, obsługiwane są dwa ty-
py portów, odpowiadające dwóm najpopu-
larniejszym protokołom warstwy transportu:
TCP i UDP. Dla każdego z protokołów przy-
dzielony jest zakres portów – zwróćmy jed-
nak uwagę, że różne procesy mogą korzy-
stać z tego samego numeru portu pod warun-
kiem, że używają różnych protokołów war-
stwy transportu.
Każdy z portów posiada unikalny numer
identyikujący z zakresu 0 – 65535 (port iden-
tyikowany jest za pomocą 16-bitowej liczby
naturalnej). Porty o numerach 0 – 1023 okre-
ślane są jako ogólnie znane i przypisane do
najpopularniejszych usług (takich jak telnet,
WWW, e-mail). W celu utworzenia gniaz-
da i przypisania go do portu z tego zakresu,
konieczne są uprawnienia użytkownika root. Rysunek 1. Enkapsulacja danych w modelu TCP/IP
struct sockaddr
{
unsigned short sa_family ; //wersja adresu: AF_INET (Ipv4),
AF_INET6 (Ipv6)
char sa_data [ 14 ]; //tablica przechowująca adres
};
struct sockaddr_in
{
short int sin_family ; //rodzina adresów: AF_INET
unsigned short int sin_port ; //numer portu
struct in_addr sin_addr ; //struktura przechowująca adres IP
unsigned char sin_zero [ 8 ]; //wypełnić zerami
};
struct in_addr
{
uint32_t s_addr ; //adres IP
};
������������
��������������
��������
���
��������
��������
��
�������
��������
�����
����������
������
�����
www.lpmagazine.org
65
439033680.003.png 439033680.004.png 439033680.005.png 439033680.006.png 439033680.007.png 439033680.008.png 439033680.009.png 439033680.010.png 439033680.011.png 439033680.012.png 439033680.013.png 439033680.014.png 439033680.015.png 439033680.016.png 439033680.017.png 439033680.018.png
 
Programowanie
Programowanie przy użyciu gniazd sieciowych
Porty o wyższych numerach możemy dowol-
nie przypisywać tworzonym przez nas apli-
kacjom.
Jednak w jaki sposób system operacyjny
wie , jaki port przypisany jest do danej aplika-
cji, oraz w jaki sposób możemy dokonać takie-
go przypisania? Właśnie w tym celu stosuje się
gniazda sieciowe.
tor, przekazywany w wywołaniach systemo-
wych, informujący jądro, na jakim obiekcie
ma zostać wykonana dana operacja. Gniazda
sieciowe są kolejnym mechanizmem, do któ-
rego dostęp odbywa się za pomocą deskryp-
torów plików.
ciowych znajdziesz na stronach wymienio-
nych w tabelce W Sieci ).
W obrębie gniazd internetowych wyróż-
niamy trzy najważniejsze typy:
Gniazda połączeniowe TCP – transmisja
danych realizowana przy użyciu gniazd
tego typu odbywa się z wykorzystaniem
protokołu TCP w warstwie transportu.
Gwarantuje on dostarczenie danych, za-
pobiega odebraniu pakietów w nieodpo-
wiedniej kolejności, kosztem prędkości
przesyłania danych;
Gniazda bezpołączeniowe UDP – trans-
misja danych realizowana przy użyciu
gniazd tego typu odbywa się z wyko-
rzystaniem protokołu UDP w warstwie
transportu. Protokół UDP jest protoko-
łem bezpołączeniowym – oznacza to,
że nie jest gwarantowane dostarczenie
pakietów, ani ich odpowiednia kolej-
ność. Protokół UDP znajduje zastoso-
wanie tam, gdzie ważna jest duża szyb-
kość przesyłania danych, a utrata czę-
ści pakietów nie stanowi dużego pro-
blemu (media strumieniowe, gry kom-
puterowe);
• Gniazda r aw (ang. raw sockets )
– gniazda sieciowe, które dają aplika-
cjom bezpośredni dostęp do nagłów-
ków pakietu. Gdy korzystamy z gniazd
raw, jesteśmy odpowiedzialni za odpo-
wiednie przypisanie wartości wszyst-
kim polom. Podczas gdy w codzien-
nym zastosowaniu byłoby to co naj-
mniej niewygodne, to w pewnych
przypadkach (takich jak np. testowa-
nie irewalli oraz oprogramowania sie-
ciowego pod kątem bezpieczeństwa,
szczególnie pod względem odporności
na ataki Denial of Service ) jest to nie-
zwykle przydatna możliwość;
Typy gniazd sieciowych
Istnieje wiele różnych standardów siecio-
wych, jak również wiele protokołów warstwy
transportu. Z tego względu mamy do czynie-
nia z wieloma typami gniazd. W artykule opi-
sane zostały jedynie standardowe gniazda in-
ternetowe, służące do komunikacji za pomo-
cą sieci lokalnych oraz internetu (więcej in-
formacji na temat innych typów gniazd sie-
Gniazda sieciowe
Z pewnością wiesz, że w systemach unikso-
wych dostęp do urządzeń, plików, katalogów
oraz kolejek FIFO odbywa się za pomocą de-
skryptorów plików. Są one liczbami całkowi-
tymi, zapisanymi w postaci typu int języka
C/C++. Deskryptor pliku stanowi identyika-
Listing 2. Sposób użycia funkcji getaddrinfo()
#include <sys/types.h> //deinicje typów danych
#include <sys/socket.h> //obsługa gniazd
#include <netdb.h> //operacje na sieciowej bazie danych
int getaddrinfo ( const char * node , // adres IP lub nazwa domenowa
const char * service , // port lub nazwa usługi
const struct addrinfo * hints , // struktura służąca jako wzór
struct addrinfo ** res ); // wskaźnik na początek listy wyników
// w kodzie programu
int errn ; // kod błędu
struct addrinfo hints ; // wzór dla wywołania getaddrinfo()
struct addrinfo * nodeinf ; // informacje o interesującym nas adresie
memset ( & hints , 0 , sizeof ( hints )); // czyścimy strukturę
hints . ai_family = AF_INET ; // interesuje nas jedynie protokół Ipv4
hints . ai_socktype = SOCK_STREAM ; // będziemy używać protokołu TCP
hints . ai_lags = AI_PASSIVE ; // tylko, jeżeli chcemy pobrać nasz adres!
if (( errn = getaddrinfo ( NULL , ”5000”, &hints, &nodeinf)) == -1)
{
fprintf ( stderr , getaddrinfo error: % s \ n ", gai_strerror(errn));
exit ( 1 );
W niniejszym artykule opiszemy zastosowa-
nie dwóch pierwszych typów gniazd siecio-
wych. Jeżeli jesteś zainteresowany bliższym
poznaniem tematyki związanej z gniazdami
raw , to polecam zapoznanie się z biblioteka-
mi libpcap i libnet, o których powiemy w dal-
szej części artykułu.
Rysunek 2. Interfejs programu Wireshark
Działanie gniazd sieciowych
W celu utworzenia nowego gniazda, korzysta-
my z wywołania systemowego socket() , po-
dając typ gniazda jako argument. Zwraca ono
wartość typu int , będącą deskryptorem pli-
ku. Po utworzeniu, gniazdo nie jest przypisa-
ne do żadnego portu – jeżeli chcemy tego do-
konać, korzystamy z wywołania systemowego
66
październik 2009
439033680.019.png 439033680.020.png 439033680.021.png 439033680.022.png
 
Programowanie
Programowanie przy użyciu gniazd sieciowych
bind() , jako argument podając deskryptor pli-
ku gniazda oraz żądany port.
Od tej chwili możemy na danym porcie
nasłuchiwać połączeń (za pomocą wywołania
systemowego listen() ) oraz akceptować je
przy użyciu funkcji accept() . Zaakceptowa-
nie połączenia powoduje utworzenie nowego
deskryptora pliku, pozwalającego na oddziel-
ną obsługę komunikacji z każdym z łączących
się komputerów.
Wysyłanie i odbieranie danych odbywa
się przy pomocy funkcji send() i recv() . Na-
leży tu pamiętać o istnieniu maksymalnej jed-
nostki transmisyjnej (MTU – ang. Maximum
Transmission Unit ), określającej maksymal-
ny rozmiar datagramu. Zawsze należy spraw-
dzać, czy rzeczywisty rozmiar przesłanych da-
nych (zwracany przez funkcję send() ) pokry-
wa się z rozmiarem żądanym – jeżeli jest ina-
czej, musimy wywołać funkcję send() jesz-
cze raz, tym razem odpowiednio modyikując
wskaźnik początku obszaru w pamięci.
Znacznie prościej wygląda obsługa komu-
nikacji przy pomocy gniazd bezpołączenio-
wych UDP – przesyłać i odbierać dane mo-
żemy bezpośrednio po otrzymaniu deskrypto-
ra pliku gniazda. Wykorzystujemy w tym celu
dwie funkcje: sendto() i recvfrom() . War-
to również wspomnieć o możliwości połącze-
nia gniazd UDP – w takim przypadku możemy
korzystać ze standardowych funkcji send() i
recv() . Pamiętaj jednak, że dane nadal prze-
syłane będą przy użyciu protokołu UDP – ich
dotarcie do celu nie będzie gwarantowane.
Po zakończeniu przesyłania danych, na-
leży zamknąć deskryptor pliku gniazda przy
użyciu wywołania systemowego close() . Je-
żeli chcemy poprawnie zakończyć połączenia
dla gniazd TCP, to powinniśmy przed tym wy-
wołać funkcję shutdown() .
O aktualnie otwartych gniazdach oraz sta-
nie, w jakim się znajdują, możemy dowiedzieć
się przy użyciu programu netstat. Dokładne
informacje na temat jego użycia znajdziesz w
dokumentacji ( man netstat ).
(szczególnie w przypadku większych aplika-
cji), jest utworzenie odpowiednich obiektów,
reprezentujących wykorzystywane gniazda i
ukrywające przed nami szczegóły działania
konkretnych wywołań systemowych. Roz-
wiązanie takie jest szczególnie warte polece-
nia, jeżeli swój kod zamierzasz wykorzysty-
wać wielokrotnie, w różnych aplikacjach.
Powiedzieliśmy już, że wykorzystując
mechanizm gniazd sieciowych, jesteśmy od-
powiedzialni za zdeiniowanie zasad, na któ-
rych mają porozumiewać się ze sobą progra-
my. W tym przypadku, wykorzystanie snif-
ferów w celu rozwiązywania problemów
okazuje się wręcz niezbędne – pozwala bo-
wiem na wizualizację przesyłanych danych
oraz szybkie wykrywanie błędów w ich re-
prezentacji.
Testowanie programów
wykorzystujących gniazda
Wykorzystując w praktyce informacje zawar-
te w tym artykule, z pewnością nie raz natra-
isz na problemy i trudne do wykrycia błędy w
kodzie, uniemożliwiające poprawną wymia-
nę danych. W takim przypadku, oprócz stan-
dardowego debuggera, warto mieć również
pod ręką programy, które pozwolą nam prze-
konać się, jakie dane w rzeczywistości wysy-
łamy w sieć .
Tcpdump i tcplow
Większość Czytelników z pewnością miała
już kiedyś doświadczenia z tymi narzędziami
– pozwalają one na monitorowanie danych od-
bieranych i wysyłanych przez nasz komputer
za pośrednictwem sieci.
Tcpdump jest standardowo dostępny w
każdej dystrybucji Linuksa. W celu przechwy-
tywania danych korzysta z niskopoziomowych
Listing 3. Sposób użycia wywołania socket()
#include <sys/types.h>
#include <sys/socket.h>
int socket ( int domain , // wersja protokołu IP: PF_INET (v4) lub
PF_INET6 (v6)
int type , // typ gniazda: SOCK_STREAM, SOCK_DGRAM
int protocol ); // 0, jeżeli chcemy by protokół został wybrany
za nas
int sock ;
// tu wywołujemy funkcję getaddrinfo() jak w Listingu 2
sock = socket ( nodeinf -> ai_family , nodeinf -> ai_socktype , nodeinf -> ai_
protocol );
Listing 4. Sposób użycia funkcji bind()
#include <sys/types.h>
#include <sys/socket.h>
int bind ( int sockfd , // deskryptor pliku gniazda
struct sockaddr * my_addr , // adres gniazda (IP i port) naszego komputera
int addrlen ); // wielkość struktury my_addr
Wymagane
biblioteki i pliki nagłówkowe
Do rozpoczęcia programowania przy użyciu
gniazd sieciowych wystarczy nam dowolna
dystrybucja Linuksa z zainstalowanymi pa-
kietami klasy Development. W artykule nie
wykorzystujemy żadnych dodatkowych bi-
bliotek, jedynie standardowe wywołania sys-
temowe.
Do kompilacji polecam zastosować kom-
pilator gcc w przypadku gdy programy pisane
są w języku C i g++ dla języka C++. Zwróć
uwagę, że bardzo wygodnym rozwiązaniem
// wywołujemy funkcje getaddrinfo() i socket() tak jak w Listingu 2 i
Listingu 3
if ( bind ( sockfd , nodeinf -> ai_addr , nodeinf -> ai_addrlen ) == - 1 )
{
perror ( NULL );
exit ( 1 );
}
Listing 5. Sposób użycia funkcji listen()
#include <sys/socket.h>
int listen ( int sockfd , // deskryptor pliku gniazda
int backlog ); // dozwolona liczba połączeń w kolejce
www.lpmagazine.org
67
439033680.023.png 439033680.024.png 439033680.025.png 439033680.026.png 439033680.027.png 439033680.028.png 439033680.029.png 439033680.030.png 439033680.031.png 439033680.032.png 439033680.033.png 439033680.034.png 439033680.035.png
 
Programowanie
Programowanie przy użyciu gniazd sieciowych
mechanizmów sieciowych systemu, do których
dostęp wymaga uprawnień użytkownika root.
Aby rozpocząć przechwytywanie na interesują-
cym nas interfejsie, z zapisem do pliku, należy
wydać polecenie:
Wireshark
Przyznam, że opisując powyższe programy,
nie mogłem się doczekać, kiedy przejdziemy
do aplikacji Wireshark (Rysunek 2). Jest to
bowiem zdecydowanie najlepszy program do
analizy przechwyconego ruchu sieciowego,
oferujący wiele bardzo przydatnych i łatwych
w obsłudze funkcji.
Od razu chciałbym Cię jednak prze-
strzec przed uruchamianiem programu Wi-
reshark z uprawnieniami użytkownika ro-
ot. Może to być bardzo niebezpieczne,
szczególnie jeżeli korzystasz z dodatko-
wych parserów protokołów. Zdecydowanie
lepszym rozwiązaniem jest przechwycenie
ruchu do pliku (przy użyciu programu tcp-
dump) a następnie jego analiza w pakiecie
Wireshark.
Najnowszą wersję programu Wi-
reshark znajdziesz na stronie http:
//www.wireshark.org/ oraz w repozytoriach
większości dystrybucji. Tą drugą opcję po-
lecam szczególnie tym, którzy chcieliby
uniknąć dość czasochłonnej kompilacji i
rozwiązywania zależności.
Dokładne informacje na temat wykorzy-
stania programu Wireshark do analizy pakie-
tów znajdziesz w cyklu artykułów Analiza pa-
kietów sieciowych , opublikowanym w nume-
rach: kwietniowym, czerwcowym oraz wa-
kacyjnym Linux+. Jeżeli nie czytałeś jeszcze
tych artykułów, to gorąco Cię do tego zachę-
cam – nauczą Cię one nie tylko obsługi apli-
kacji Wireshark, lecz również zwiększą Twoją
wiedzę na temat działania sieci w ogóle, co po-
zwoli na szybsze rozwiązywanie problemów z
tworzonymi aplikacjami.
Podstawowe struktury
Wiesz już, jak działa mechanizm gniazd
sieciowych w Linuksie – przyszedł czas
na praktyczne wykorzystanie go w tworzo-
nych aplikacjach. Zanim jednak zajmiemy
się opisem poszczególnych wywołań sys-
temowych, opiszemy podstawowe struktu-
ry, których poznanie jest niezbędne w ce-
lu efektywnego wykorzystania informacji
zawartych w dalszej części artykułu. Oma-
wiane struktury przedstawione zostały na
Listingu 1.
tcpdump -i interfejs -w nazwa_
pliku.dmp iltry
Filtry to wyrażenia, informujące program tcp-
dump, jakie pakiety są dla nas interesujące z
punktu widzenia dalszej analizy. Dokładny
opis działania wszystkich opcji programu oraz
iltrów znajdziesz w dokumentacji aplikacji
( man tcpdump ).
Program tcplow różni się od programu tcp-
dump przeznaczeniem – za jego pomocą może-
my przekonać się o postaci danych przesyłanych
za pośrednictwem strumieni TCP, przez co może
okazać się wygodniejszym narzędziem do testo-
wania tworzonych programów. Aplikacja ta nie
jest jednak standardowo dostępna w większości
dystrybucji – możesz ją pobrać ze strony http:
//www.circlemud.org/~jelson/software/tcplow/ .
Znajdują się tam również pakiety binarne dla
najpopularniejszych dystrybucji.
Wykorzystanie aplikacji tcplow wygląda
bardzo podobnie jak w przypadku tcpdump.
Aby rozpocząć zapisywanie strumieni do od-
powiadających im plików, należy wydać po-
lecenie:
addrinfo – informacje o adresach
Podstawową strukturą jest addrinfo , słu-
żąca do przechowywania informacji o adre-
sach. Zawiera informacje o wersji protoko-
łu IP, typie gniazda, protokole, adres gniazda
(struktura sockaddr ) Najważniejszą funkcją,
operującą na strukturze addrinfo , jest ge-
taddrinfo() . Dzięki niej uzyskujemy listę
wszystkich adresów danego hosta, spośród
których możemy wybrać najbardziej nam od-
powiadający.
sockaddr, sockaddr_in, in_addr – adresy
Teraz sytuacja trochę się skomplikuje. Mamy
bowiem trzy struktury (jeżeli korzystamy je-
dynie z wersji czwartej protokołu IP), które
służą do przechowywania adresów. Skąd bie-
rze się taka różnorodność i jak sobie z nią po-
radzić?
Struktura sockaddr jest najogólniejszą
strukturą przechowującą adresy gniazd – nie
jest ona ograniczona w żaden sposób do kon-
kretnej rodziny protokołów (którą deiniuje-
my przypisując odpowiednią wartość skła-
dowej sa_family ). W praktyce nie będziesz
jednak korzystał z tej struktury – ręczne wpi-
sywanie danych do tablicy sa_data mija się
z celem.
Dla protokołu IPv4 odpowiednie jest wy-
korzystanie struktury sockaddr_in (istnieje
jej odpowiednik dla protokołu IPv6 – soc-
kaddr_in6 ). Zapisane są w niej pełne infor-
macje o adresie sieciowym: adres IP (w po-
staci struktury in_addr ) oraz numer portu
docelowego (zmienna składowa sin_port ).
Zwróć uwagę na zastosowanie tablicy obiek-
tów typu unsigned char – wypełniamy ją
zerami w celu zapewnienia odpowiednie-
go rozmiaru struktury sockaddr_in , takie-
go samego jak struktury sockaddr . Dzięki
temu, pomimo że większość wywołań sys-
temowych wymaga podania adresu w posta-
ci struktury sockaddr , możemy dokonać rzu-
towania.
Sam 32-bitowy adres IP zapisany jest w
strukturze in_addr w postaci zmiennej ty-
tcplow -i interfejs iltry
Tcplow, podobnie jak tcpdump, korzysta z bi-
blioteki libpcap, oferując przez to taką samą
składnię wyrażeń iltrujących.
Listing 6. Użycie funkcji accept()
#include <sys/types.h>
#include <sys/socket.h>
int accept ( int sockfd , // deskryptor pliku gniazda
struct sockaddr * addr , // adres gniazda
socklen_t * addrlen ); // wielkość struktury addr
// wywołujemy funkcje getaddrinfo(), socket(), bind() i listen() tak jak
na wcześniejszych listingach
char * port = ”5000” ; // wykorzystywany port
const int backlog = 10 ; // maksymalna liczba połączeń
struct sockaddr remote_addr ; // adres komputera inicjalizującego połączenie
socklen_t addr_size ; // rozmiar struktury sockaddr
int newsockfd ; // deskryptor nowego gniazda
addr_size = sizeof ( remote_addr );
newsockfd = accept ( sockfd , & remote_addr , & addr_size );
// możemy rozpocząć przesyłanie danych za pomocą gniazda newsockfd
68
październik 2009
439033680.036.png 439033680.037.png 439033680.038.png 439033680.039.png 439033680.040.png
 
Zgłoś jeśli naruszono regulamin