Internet - funkce

V této kapitole ukáži pár zajímavých funkcí, které se vám mohou hodit při práci po síti.

Struktura hostent

Struktura hostent obsahuje informace nalezené pomocí DNS dotazu. Tuto strukturu, resp. odkaz na ni, vrací například funkce gethostbyname()gethostbyaddr() (viz dále).

Definovaná je takto:

struct hostent {
    char    *h_name;        /* official name of host */
    char    **h_aliases;    /* alias list */
    int     h_addrtype;     /* host address type */
    int     h_length;       /* length of address */
    char    **h_addr_list;  /* list of addresses */
}
#define h_addr  h_addr_list[0]  /* for backward compatibility */

Položka h_addr je definovaná kvůli zpětné kompatibilitě. Dříve totiž bylo možné mít k jednomu DNS záznamu jen jednu IP adresu. Dnes už můžete mít více IP adres (v h_addr_list).

Co je ale zajímavější, a na první pohled to není patrné, tak h_addr_list obsahuje IP adresy jako čísla. Jenomže tyto čísla jsou uloženy jako posloupnost charů. Tato posloupnost je navíc v endianitě vlastní internetu, ne vašemu počítači.

Počet těchto „charů“, tedy bajtů, je dán položkou h_length. Pro IPv4 je to vždy 4. Pro IPv6 je to 16 (ne 6, opravdu 16).

Pro převod této posloupnosti na lidsky čitelný text jsem napsal funkci h_addr_to_string(). Pro výpis celé struktury hostent jsem napsal funkci printHostent(). Oboje funkce jsou deklarované v print-hostent.h:

  1. /*------------------------------------------------*/
  2. /* 43internetFunkce/print-hostent.h               */
  3. #include <netdb.h>
  4.  
  5. char *h_addr_to_string(char *addr, int len);
  6. void printHostent(struct hostent *server);
  7. /*------------------------------------------------*/

Nejdříve se podívejte na h_addr_to_string(). Převádí Ipv4 adresu předanou jako pole charů do textu jako "127.0.0.1" atp.

Díky tomu, že endianovitost pole charů odpovídá „lidskému“ zápisu, stačí mi každý prvek pole přetypovat na unsigned char a vytisknout v tom pořadí, v jakém je dostanu.

  1. /*------------------------------------------------*/
  2. /* 43internetFunkce/print-hostent.c               */
  3. #include <stdio.h>
  4. #include "print-hostent.h"
  5.  
  6. char *h_addr_to_string(char *addr, int len)
  7. {
  8.     static char ip4[16];
  9.     if (len != 4)
  10.         return "";
  11.  
  12.     sprintf(ip4, "%u.%u.%u.%u",
  13.         (unsigned char)addr[0],
  14.         (unsigned char)addr[1],
  15.         (unsigned char)addr[2], (unsigned char)addr[3]);
  16.     return ip4;
  17. }
  18.  

Funkce převede jen IPv4 adresy. Funkci pro převod IPv6 adres si můžete napsat za domácí úkol (použijte %x ;-);

Pole odkazů na aliasy a ip adresy jsou ve struktuře hostent ukončeny nulovým odkazem. Z toho vychází podmínky v cyklech while, viz níže.

U tisku adres (while (server->h_addr_list[i])) tisknu u první položky pole i hodnotu server->h_addr, abyste mi věřili, že je to opravdu to samé, co h_addr_list[0].

  1. void printHostent(struct hostent *server)
  2. {
  3.     size_t i;
  4.     printf("Oficiální jméno hosta:\n\t%s\n", server->h_name);
  5.     printf("Aliasy:\n");
  6.     i = 0;
  7.     while (server->h_aliases[i]) {
  8.         printf("\t%s\n", server->h_aliases[i]);
  9.         i++;
  10.     }
  11.     printf("Typ adresy IpV4: %s\n",
  12.            server->h_addrtype == AF_INET ? "yes" : "no");
  13.     printf("Délka adresy v bajtech: %i\n", server->h_length);
  14.  
  15.     printf("Seznam adres:\n");
  16.     i = 0;
  17.     while (server->h_addr_list[i]) {
  18.         printf("\t%s\n",
  19.                h_addr_to_string(server->h_addr_list[i],
  20.                     server->h_length));
  21.         if (i == 0) {
  22.             printf("\t (= %s)\n",
  23.                    h_addr_to_string(server->h_addr,
  24.                         server->h_length));
  25.         }
  26.         i++;
  27.     }
  28. }
  29. /*------------------------------------------------*/

Funkce gethostbyname() a gethostbyaddr()

V dalším příkladu uvidíte použití funkce gethostbyname(), která vrací na základě DNS jména informace o doméně. Dále funkci gethostbyaddr(), která vrací tytéž informace, ale na základě IP adresy.

Jak uvidíte, nevracejí funkce vždy stejné informace, ikdyž je použijete na doménu a její IP adresu.

struct hostent *gethostbyname(const char *name);
struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type);

Funkce gethostbyaddr() má jako první argument odkaz na strukturu struct in_addr (pro IPv4). Druhým argumentem je délka této struktury (sizeof(struct in_addr)) a posledním argumentem typ adresy, který je pro IPv4 AF_INET (pro IPv6 AF_INET6).

Pro připomenutí ještě ukáži definici struktury in_addr:

#typedef uint32_t in_addr_t
struct in_addr {
    in_addr_t s_addr;
}

Podívejte se, jak pomocí memcpy() přesouvám bajty z h_addr_list (z pole 4 bajtů) do s_addr (typu uint32_t). (Obě části paměti zabírají 32 bitů). Tímto způsobem si bajty zachovají správnou endianovitost.

Poslední nová funkce v příkladu je inet_ntoa().

char *inet_ntoa(struct in_addr in);

Tato funkce dělá v podstatě to samé, co moje funkce h_addr_to_string(), jen dostává jako argument strukturu struct in_addr.

  1. /*------------------------------------------------*/
  2. /* 43internetFunkce/hostname.c                    */
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <netdb.h>
  6. #include <netinet/in.h>
  7. #include <arpa/inet.h>
  8. #include "print-hostent.h"
  9.  
  10. static void usage()
  11. {
  12.     printf("USAGE: ./hostent server_name\n");
  13. }
  14.  
  15. int main(int argc, char *argv[])
  16. {
  17.     struct hostent *server;
  18.     struct in_addr addr;
  19.  
  20.     if (argc < 2) {
  21.         usage();
  22.         return 1;
  23.     }
  24.  
  25.     server = gethostbyname(argv[1]);
  26.     if (!server) {
  27.         printf("Server %s nebyl nalezen\n", argv[1]);
  28.         return 1;
  29.     }
  30.     printf("gethostbyname(%s):\n", argv[1]);
  31.     printf("****************************\n");
  32.     printHostent(server);
  33.     memset(&addr, 0, sizeof(addr));
  34.     memcpy(&(addr.s_addr), server->h_addr_list[0], sizeof(addr.s_addr));
  35.     server = gethostbyaddr(&addr, 4, AF_INET);
  36.  
  37.     if (!server) {
  38.         printf("Server %u nebyl nalezen\n", ntohl(addr.s_addr));
  39.         return 1;
  40.     }
  41.     printf("\ngethostbyaddr(%u,4, AF_INET):\n", ntohl(addr.s_addr));
  42.     printf("****************************\n");
  43.     printHostent(server);
  44.  
  45.     printf("\n****************************\n");
  46.     printf("inet_ntoa(%u) = %s\n", ntohl(addr.s_addr), inet_ntoa(addr));
  47.  
  48.     return 0;
  49. }
  50. /*------------------------------------------------*/

Všiměte si, že převádím před tiskem addr.s_add endianovitost pomocí ntohl().

Výstup volání ./hostname www.facebook.com vypadá takto:

gethostbyname(www.facebook.com):
****************************
Oficiální jméno hosta:
    star.c10r.facebook.com
Aliasy:
    www.facebook.com
Typ adresy IpV4: yes
Délka adresy v bajtech: 4
Seznam adres:
    31.13.91.3
     (= 31.13.91.3)

gethostbyaddr(520968996,4, AF_INET):
****************************
Oficiální jméno hosta:
    edge-star-shv-04-prn2.facebook.com
Aliasy:
Typ adresy IpV4: yes
Délka adresy v bajtech: 4
Seznam adres:
    31.13.91.3
     (= 31.13.91.3)

****************************
inet_ntoa(520968996) = 31.13.91.3

Výsledek volání gethostbyname() pro www.facebook.comgethostbyaddr() pro IP adresu 173.252.74.22 vracejí trochu rozdílné výsledky. Doména www.facebook.com je jen alias. V různých částech světa zřejmě dostanete jiné výsledky (jinou IP adresu, jiné oficiální DNS jméno). Facebook je příliš velký na to, aby běžel na jednom počítači …

Ještě ukáži pro srovnání výstup s www.google.com:

gethostbyname(www.google.com):
****************************
Oficiální jméno hosta:
    www.google.com
Aliasy:
Typ adresy IpV4: yes
Délka adresy v bajtech: 4
Seznam adres:
    173.194.116.242
     (= 173.194.116.242)
    173.194.116.243
    173.194.116.244
    173.194.116.240
    173.194.116.241

gethostbyaddr(2915202290,4, AF_INET):
****************************
Oficiální jméno hosta:
    prg02s11-in-f18.1e100.net
Aliasy:
Typ adresy IpV4: yes
Délka adresy v bajtech: 4
Seznam adres:
    173.194.116.242
     (= 173.194.116.242)

****************************
inet_ntoa(2915202290) = 173.194.116.242

K funkcím gethostbyname() a gethostbyaddr() existují i reentrantní, MT-Safe verze, které mají ovšem trochu jiné deklarace. Více se dočtete v manuálových stránkách.

K funkcím gethostbyname() a gethostbyaddr() existují novější alternativy – getaddrinfo() a getnameinfo(). Hezký příklad na tyto funkce najdete wikipedii.

Funkce getservbyname() a getservbyport()

Tyto funkce vracejí informace nalezené v souboru /etc/services ve struktuře struct servent:

struct servent {
    char  *s_name;       /* official service name */
    char **s_aliases;    /* alias list */
    int    s_port;       /* port number */
    char  *s_proto;      /* protocol to use */
}
struct servent *getservbyname(const char *name, const char *proto);
struct servent *getservbyport(int port, const char *proto);

Protokol může být "tcp" nebo "udp" (nebo i nějaký jiný, podívejte se do /etc/services), nebo NULL. V takovém případě se najde záznam bez ohledu na protokol.

Zvážení, nakolik jsou tyto funkce užitečné, nechám na vás. Informace získané ze souboru /etc/services jsou informace o defaultních nastaveních, realita může být jiná. Nalezená služba nemusí na vašem počítači vůbec běžet. A na jiném stroji může běžet pod jiným portem atd.

Příklad:

  1. /*------------------------------------------------*/
  2. /* 43internetFunkce/getserv.c                     */
  3. #include <netdb.h>
  4. #include <stdio.h>
  5.  
  6. int main(void)
  7. {
  8.     struct servent *srt;
  9.  
  10.     srt = getservbyname("mysql", NULL);
  11.     printf("Name = %s, port = %i, protocol = %s\n", srt->s_name,
  12.            ntohs(srt->s_port), srt->s_proto);
  13.     srt = getservbyport(htons(3306), NULL);
  14.     printf("Name = %s, port = %i, protocol = %s\n", srt->s_name,
  15.            ntohs(srt->s_port), srt->s_proto);
  16.     return 0;
  17. }
  18. /*------------------------------------------------*/

Výstup z programu:

Name = mysql, port = 3306, protocol = tcp
Name = mysql, port = 3306, protocol = tcp

Funkce gethostname()

Funkce gethostname() vrátí jméno vašeho počítače.

int gethostname(char *name, size_t len);

Příklad:

  1. /*------------------------------------------------*/
  2. /* 43internetFunkce/gethostname.c                 */
  3. #include <stdlib.h>
  4. #include <unistd.h>
  5. #include <limits.h>        /* HOST_NAME_MAX */
  6. #include <stdio.h>
  7. #include <errno.h>
  8.  
  9. int main(void)
  10. {
  11.     char name[HOST_NAME_MAX + 1];
  12.     if (gethostname(name, sizeof(name))) {
  13.         perror("gethostname");
  14.         exit(EXIT_FAILURE);
  15.     }
  16.     printf("hostname = %s\n", name);
  17.     return 0;
  18. }
  19. /*------------------------------------------------*/

Výstup z programu:

hostname = linux-xigc.site

Jak vidíte, jméno mého počítače není nijak nápadité. Nechal jsem takové, jaké mi bylo nabídnuto během instalace.

Pokud si projdete manuálové stránky ke zde probraným funkcím, najdete odkazy na další a další zajímavé (i nezajímavé) funkce.

Komentář Hlášení chyby
Vytvořeno: 6.10.2014
Naposledy upraveno: 19.11.2015
Tato stánka používá ke svému běhu cookies, díky kterým je možné monitorovat, co tu provádíte (ne že bych to bez cookies nezvládl). Také vás tu bude špehovat google analytics. Jestli si myslíte, že je to problém, vypněte si cookies ve vašem prohlížeči, nebo odejděte a už se nevracejte :-). Prohlížením tohoto webu souhlasíte s používáním cookies. Dozvědět se více..