Překlad doménových jmen v MS Windows - Builder.cz - Informacni server o programovani

Odběr fotomagazínu

Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!

 

Zadejte Vaši e-mailovou adresu:

Kamarád fotí rád?

Přihlas ho k odběru fotomagazínu!

 

Zadejte e-mailovou adresu kamaráda:



C/C++

Překlad doménových jmen v MS Windows

cpp net

12. prosince 2002, 00.00 | Dnes si ukážeme, jak překládat doménová jména na IP adresy v operačním systému MS Windows. Ukážeme si také, jak používat v MS Windows knihovnu soketů. Na konci článku je malý ukázkový příklad ke stažení.

V minulém článku jsme si ukázali překlad doménových jmen na IP adresy v OS Linux. Dnes vytvoříme stejný program jako minule, ale pro OS MS Windows®. Nejprve zopakuji, proč vlastně překládat doménová jména. Ti, kteří četli minulý článek, mohou přeskočit následující odstavec.

V úvodním článku jsem si řekli, že IP adresa je číslo. Problém je v tom, že pamatovat si čísla je velice nepraktické. Například kdo by si pamatoval http://212.80.76.3. Proto byly zavedeny tak zvané doménové jména. Jedná se o textové řetězce, které se lépe pamatují. Například http://www.seznam.cz zná u nás snad každý. Málo kdo ale ví, že předchozí dvě adresy jsou stejné. Důvod, proč se zavedly doménové jména je čistě praktický.
Budeme-li chtít komunikovat pomocí soketů, budeme potřebovat znát IP adresy vzdálených počítačů. Nejčastěji ale budeme znát (od uživatelů) doménová jména. Dnes si ukážeme jak v MS Windows® překládat doménová jména na IP adresy. Také si ukážeme jak inicializovat knihovnu soketů v našem programu. V Unixových OS se žádná inicializace provádět nemusí.

Win Socket API

Rozhodneme-li se v našem programu v MS Windows® používat sokety, musíme inicializovat knihovnu soketů pomocí funkce WSAStartup. Nejprve si ale musíme povědět něco o struktuře WSADATA. Zkratka WSA umístěná před názvy některých funkcí a struktur znamená Win Socket Api. Všechny deklarace, které je potřeba vložit (include) do našich zdrojových textů jsou v hlavičkovém souboru winsock.h. Ten je ale vkládán do hlavičkového souboru windows.h. Patrně v každém Windowsovském programu je potřeba vkládat soubor windows.h, není proto potřeba se o deklarace soketových funkcí starat. Já budu ve svých příkladech vytvářet pro jednoduchost a názornost konzolové aplikace (o oknech tenhle seriál není), nemusel bych vkládat windows.h. Stačilo by winsock.h. Ale abych nikoho nemátl, budu používat jen windows.h. Další nutná věc je přilinkovat správnou knihovnu. V prostředí DEV-C++ je nutné přidat jako parametr pro linker -lwsock32. Používáte-li "čisté" MinGW32, je to parametr pro linker z příkazové řádky. U jiných vývojových prostředí se bude asi knihovna přilinkovávat trochu jinak.

Struktura WSADATA

Instance struktury WSADATA slouží k uchovávání informací o implementaci soketů (o knihovně soketů) na lokálním počítači. Tedy na počítači, na kterém běží program. Struktura obsahuje atributy:

  • WORD wVersion - číslo verze WinSock. Ve vyšším bytě je umístěno vedlejší číslo verze (číslo podverze). V nižším bytě je umístěno hlavním čísle verze. Zde je vhodné si pomoci makry HIBYTE a LOBYTE.
  • WORD wHighVersion - číslo maximální verze WinSock. Číslo verze udává maximální možnou verzi WinSock, která je použitelná pro dll soubor na lokálním počítači. Hlavní číslo verze a číslo podverze jsou umístěny stejně jako u předchozího atributu.
  • char szDescription[WSADESCRIPTION_LEN + 1] - textový popis knihovny soketů. Jedná se o klasický C řetězec zakončený znakem s ASCII 0. Identifikátor WSADESCRIPTION_LEN je makro. Udává maximální počet znaků, který může mít popis.
  • char szSystemStatus[WSASYS_STATUS_LEN + 1]; - textový popis stavu Soketů. Opět se jedná o klasický C řetězec. Identifikátor WSASYS_STATUS_LEN je makro udávající maximální počet znaků řetězce.
  • unsigned short iMaxSockets; - maximální počet soketů, které může aplikace (teoreticky) použít.
  • unsigned short iMaxUdpDg; - maximální velikost jednoho UDP datagramu. Údaj je v bytech. Je-li 0, potom není velikost datagramu implementací soketů nějak omezená. O UDP datagramech si povíme něco v dalších dílech.
  • char FAR *lpVendorInfo; - ukazatel na další informace o konfiguraci počítače.

Funkci WSAStartup předáme ukazatel na instanci této struktury. Funkce nám instanci naplní smysluplnými údaji.

Funkce WSAStartup

Funkce WSAStartup inicializuje knihovnu soketů. Je nutné funkci zavolat VŽDY, chceme-li pracovat se sokety. Samozřejmě se volá jen 1x v programu. Není nutné ji volat před každou "soketovou" funkcí. Nesmíme na to ale zapomenout. Jinak žádná funkce ze soketového API nebude fungovat. Hlavička funkce:

  • int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData); - funkce inicializuje sokety. Prvním parametrem je číslo požadované verze. Dolní byte je hlavní číslo verze. Horní byte je číslo podverze. Budeme používat verzi 1.1. Pomůžeme si makrem MAKEWORD. Druhým parametrem je ukazatel na WSADATA. Zkratka LP znamená Pointer (jak jsme z programování ve Windows zvyklí). Ukazatel ukazuje na alokovanou ale neinicializovanou (má náhodné hodnoty) instanci struktury WSADATA. Po zavolání funkce bude tato struktura naplněná. V případě úspěchu se vrací číslo 0. Je-li vráceno číslo 0, můžeme používat sokety.
Funkce WSACleanup

Tato funkce by měla být zavolána po ukončení práce se sokety. Typicky na konci programu.

Samotný překlad

Tím jsme vyřešili ty nejpodstatnější rozdíly při práci se sokety v MS Windows® a v Unixových OS. Sice ještě na nějaké rozdíly narazíme, ale ty nebudou až tak podstatné. Nyní v podstatě můžeme použít velkou část zdrojového textu z minulého dílu. Nyní zopakuji v podstatě věci, které jsme probrali v minulém díle. Ještě než se začneme věnovat samotnému výkladu potřebných datových struktur a funkcí, musím upozornit na jednu věc. Jak jsem napsal v úvodním článku, sokety byly jako první na operačním systému BSD. V Unixových operačních systémech jsou API funkce v podstatě stejné. V MS Windows® se funkce trochu liší, navíc také záleží na verzi použité knihovny. MS začíná v nových knihovnách zavádět nové funkce, které se dají použít místo starých. Já jsem zvyklý (a hlavně se snažím) používat stejné funkce v obou prostředích. Tím pádem ale používám některé funkce, které mohou být v oficiální dokumentaci označeny jako "staré" a může tam být doporučena některá z nových funkcí. Zvyk je železná košile, proto já tady budu "propagovat" soketové API verze 1.1. Dnes se ale používá dll verze 2.*. Nemusíte se ale bát, zpětná kompatibilita existuje. Můj přístup má hlavně tu výhodu, že zdrojové texty pro MS Windows® i pro Linux budou téměř stejné. Nikomu ale tyto funkce nevnucuji. Bude-li existovat k nějaké mnou uváděné funkci novější alternativa, upozorním na ni.

Struktury

Nejprve se podívejme na důležité struktury hostent a in_addr. Pro jejich deklarace a deklarace všech dalších struktur nebo funkcí je nutné vložit hlavičkový soubor windows.h. Ten už ale máme vložený kvůli WSAStartup.

Struktura hostent

Instance struktury slouží k uchování charakteristických informací o vzdáleném počítači. Atributy má v podstatě stejné jako její "Unixová obdoba". Popsal jsem je v minulém článku, nebudu je zde opisovat. Můžete se podívat na popis struktury hostent v minulém díle. Nebo se můžete rovnou podívat na oficiální dokumentaci strutkury hostent. Struktura v MS Windows® se od té, která je v Linuxu, liší pouze typem atributů h_addr_type a h_length, které nejsou int ale short int. Není to ale nijak podstatný rozdíl.

Struktura in_addr

Instance struktury je IP adresa. Vnitřek struktury nemusíme příliš znát. Uvnitř struktury se nalézá unie, která umožní různým funkcím pracovat s různě formátovanou adresou. Musíme ale vědět, že tato struktura existuje. Někdy na ní budeme přetypovávat adresy ve tvaru 1 prvku pole h_addr_list struktury hostent.

Funkce pro práci s adresami a doménovými jmény

Pro překlad doménového jména na IP adresu (resp. pro získání struktury hostent z doménového jména) slouží funkce gethostbyname. Chceme-li získat strukturu hostent na základě známe IP adresy, slouží nám k tomu funkce gethostbyaddr. Chceme-li získat IP adresu v pro člověka čitelném tvaru, použijeme funkci inet_ntoa. Jak vidíte, funkce v MS Windows® se jmenují stejně jako funkce v Unixových OS, které jsme probrali v minulém článku. Mají i stejné typy a pořadí parametrů. Je-li vše v pořádku, tak se dokonce i stejně chovají. Rozdíl je pouze v případě, že něco selže. V MS Windows ® při selhání nějaké funkce týkající se soketového API zjistíme kód chyby pomocí funkce WSAGetLastError.

  • struct hostent *gethostbyname(const char *name); - parametrem funkce je doménové jméno. Například "www.seznam.cz". Funkce vrací ukazatel na strukturu hostnet v případě, že nedošlo k chybě. Jestliže došlo k chybě funkce vrací NULL. V případě chyby můžeme kód chyby získat funkcí WSAGetLastError. Budete-li vytvářet aplikaci pro knihovnu Win Socket verze 2, můžete místo funkce gethostbyname použít novější funkci getaddrinfo.
  • struct hostent *gethostbyaddr(const char *addr, int len, int type); - parametr addr je ukazatel na IP adresu ve tvaru posloupnosti bytů. Pro IP protokol verze 4 se bude jednat o posloupnost 4 bytů. Parametr len udává velikost adresy. Parametr type má vždy hodnotu makra AF_INET. Funkce vrací strukturu hostent pro danou adresu. V případě chyby vrací NULL a kód chyby lze získat funkcí WSAGetLastError. Budete-li vytvářet aplikaci pro knihovnu Win Socket verze 2, můžete místo funkce gethostbyaddr použít novější funkci getnameinfo.
  • char *inet_ntoa(struct in_addr in); - funkce slouží pro převod IP adresy ve formě posloupnosti bytů na řetězec ve "tečkovém" tvaru. Parametrem je IP adresa v binárním tvaru přetypovaná na typ in_addr.
Příklad

Jako ukázkový příklad těchto funkcí si ukážeme program pro překlad doménových jmen. Program bude jednoduchá konzolová aplikace, která jako svůj parametr získá doménové jméno a vrátí informace o zadaném počítači.

#include <iostream>
#include <windows.h>

int main(int argc, char *argv[])
{
    WORD wVersionRequested = MAKEWORD(1,1);
    WSADATA data;
    if (argc != 2)
    {
        std::cerr << "Syntaxe:\n\t" << argv[0] << " " << "adresa" << std::endl;
        return -1;
    }
    if (WSAStartup(wVersionRequested, &data) != 0)
    {
        std::cout << "Nepodařilo se inicializovat sokety" << std::endl;
        // Podle všeho, zde se WSACleanup volat nemusí.
        return -1;
    }
    std::cout << "Informace o konfiguraci soketů na tomto počítači"<<std::endl;
    std::cout << "\tVerze: " << (int)LOBYTE(data.wVersion) << "." << 
      (int)HIBYTE(data.wVersion) << std::endl;
    std::cout << "\tNejvyšší verze: " << (int)LOBYTE(data.wHighVersion) << "." 
       << (int)HIBYTE(data.wHighVersion) << std::endl;
    std::cout << "\tPopis: " << data.szDescription << std::endl;
    std::cout << "\tStav: " << data.szSystemStatus << std::endl;
    std::cout << "\tMaximální poèet otevřených soketů: " << data.iMaxSockets
      << std::endl;
    std::cout << "\tMaximální velikost UDP datagramů: ";
    if (data.iMaxUdpDg == 0)
    {
        std::cout << "neomezena" << std::endl;
    }
    else
    {
        std::cout << data.iMaxUdpDg << " byte" << std::endl;
    }    
    std::cout << "Informace o " << argv[1] << ":" << std::endl;
    hostent *H = gethostbyname(argv[1]);
    if (H == NULL)
    {
        std::cerr << "Nepodařilo se zjistit adresu" << std::endl;
        WSACleanup();
        return -1;
    }
    std::cout << "Oficiální jméno: " << H->h_name << std::endl;
    std::cout << "Alternativy: " << std::endl;
    /* Budeme procházet pole H->h_aliases. Pole je ukončené NULL.*/
    char **alternativy = H->h_aliases;
    while(*alternativy != NULL)
    {
        cout << "\t" << *(alternativy++) << std::endl;
    }
    /* Budeme procházet pole H->h_addr_list. Pole je ukončené NULL. 
       Zvolíme jiný postup než v předchozím případě.*/
    std::cout << "Adresy: " << std::endl;
    register int index = 0;
    while (	H->h_addr_list[index]!= NULL)
    {
        /* H->h_addr_list je binární forma IP adresy. Pro převod na
           tečkovou konvenci použijeme inet_ntoa.*/
        std::cout << "\t" << inet_ntoa(*(in_addr *)H->h_addr_list[index++])
             << std::endl;
    }
    std::cout << "Počet adres: " << index << std::endl;
    std::cout << "Velikost adres: " << H->h_length << std::endl;
    WSACleanup();
    return 0;
}

Ukázkový příklad je ke stažení zde. Jedná se o projekt nástroje DEV-C++. Obsahuje také soubor Makefile pro překladač MinGW. Můžete program spustit a experimentovat. Chcete-li si zkusit adresu s alternativními názvy, můžete zkusit dát jako parametr našemu programu například adresu www.root.cz. Chcete-li vidět počítač s více IP adresami, můžete zkusit dát třeba www.post.cz

Příště

Příště vytvoříme TCP klienta v Linuxu. Navážeme taky první TCP spojení. V dalším článku vytvoříme TCP klienta pro MS Windows®


Obsah seriálu (více o seriálu):

Tématické zařazení:

 » Rubriky  » C/C++  

 

 

 

Nejčtenější články
Nejlépe hodnocené články

 

Přihlášení k mému účtu

Uživatelské jméno:

Heslo: