16 Ocak 2020 Perşembe

getaddrinfo metodu

Giriş
Linux'ta şu satırları dahil ederiz.
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
İmzası şöyle
int getaddrinfo(const char *node, 
                const char *service,
                const struct addrinfo *hints,
                struct addrinfo **res);
Verilen metini (isim veya IP adresi) binary formata çevirir. Gerekirse DNS sunucuna istekte bulunur. Döndürülen result listesi freeaddrinfo() ile silinmelidir, yoksa memory leak olur.

Kullanım
Örnek
Şöyle yaparız. Burada belirtilen bir adres için sorgulama yapılıyor.
struct addrinfo *ai;
struct addrinfo hints = ...; //1 Alanları doldur

int res = getaddrinfo(..., NULL, &hints, &ai); //2 Çağrıyı yap
if (res) { //Sonucu kontrol et
  fprintf(stderr, "%s: %s\n", argv[1], gai_strerror(res));
  return 1;
}
...
freeaddrinfo(ai); //Belleği sil
Örnek
Şöyle kullanırız.
#define PORT    "32001"
struct addrinfo hints, *res;


// Get the address info
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if (getaddrinfo(NULL, PORT, &hints, &res) != 0) {
  perror("getaddrinfo");
  return 1;
}
İşimiz bitince nesne şöyle silinir.
freeaddrinfo(res);
Hata Varsa
Şöyle yaparız.
int error = getaddrinfo(...,...,...,...);
if (error) {
  printf("Error %d: %s\n\n", error, gai_strerror(error));
  return;
}
1. node Parametresi
Aramak istediğimiz IP adresini veya sunucu ismini belirtir. Açıklaması şöyle.
node specifies either a numerical network address, or a network hostname, whose network addresses are looked up and resolved.
2. service Parametresi
Genellikle null verilir. Açıklaması şöyle. Yani string şeklinde "http" veya string şeklinde "80" gibi bir sayı olmalı.
service sets the port in each returned address structure.  If this argument is a service name (see services(5)), it is translated to the corresponding port number.  This argument can also be specified as a decimal number, which is simply converted to binary.  If service is NULL, then the port number of the returned socket addresses will be left uninitialized.

3. hints parametresi
7 tane alan vardır. Bunlardan genellikle ai_family ve ai_socktype alanları doldurulur.

3.1. hints.ai_family Alanı

3.2 hints.ai_socktype Alanı
- Stream ailesine ait değerler şunlar. IPPROTO_TCP, IPPROTO_SCTP. 
- Datagram ailesine ait değerler şunlar. IPPROTO_UDP, IPPROTO_UDPLITE.
- Ayrıca SOCK_SEQPACKET ve SOCK_DCCP aileleri de var.

Örnek
TCP için şöyle yaparız.
struct addrinfo hints; 
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;hints.ai_protocol = IPPROTO_TCP;
Örnek
UDP için şöyle yaparız.
struct addrinfo hints; 
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;

3.3 hints.ai_flags Alan
Şöyle yaparız.
struct addrinfo hints = {
    .ai_flags = AI_NUMERICHOST,
    .ai_family = AF_UNSPEC,
};

Sonuçları Dolaşmak
Şöyle yaparız.
struct addrinfo hints, *res, *p; 
...

int error = getaddrinfo(...,...,..., &res);
...
for (p = res; p != NULL; p = p->ai_next) {
  ...
}
Süzerek Dolaşmak
Sadece IPv6 adreslerini kullanmak için şöyle yaparız.
const std::string hostname = ...; const std::string port = ...;

struct addrinfo hints;
struct addrinfo* res {nullptr};
struct addrinfo* ptr {nullptr};

memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;

int rv;

if((rv = getaddrinfo(hostname.c_str(), port.c_str(), &hints, &res)) != 0) {
  return -1;
}

// Try to get the first available client connection.
for(ptr = res; ptr != nullptr; ptr = ptr->ai_next) {

  // Ignore undefined ip type.
  if(ptr->ai_family != AF_INET && ptr->ai_family != AF_INET6) {
    continue;
  }

  ...
}

freeaddrinfo(res);
Sonuç Listesini Kullanmak - ai_family + ai_socktype + ai_protocol
Elde edilen sonuç socket açmak için kullanılabilir.
Örnek
Şöyle yaparız
int sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
Sonuç Listesi Kullanmak - ai_addr + ai_addrlen
Elde edilen sonuç sunucu socket için kullanılabilir.

Örnek
Şöyle yaparız.
/* Bind to the address */
if (bind(sock, res->ai_addr, res->ai_addrlen) == -1) {
...error...
}
Örnek
Şöyle yaparız. Belirtilen IP adresi veya sunucuya ait IP numaraları bulunuyor ve ilki için isim sorgulaması yapılıyor.
struct addrinfo *ai;
struct addrinfo hints = ...

int res = getaddrinfo(argv[1], NULL, &hints, &ai);
if (res) {
    fprintf(stderr, "%s: %s\n", argv[1], gai_strerror(res));
    return 1;
}

char node[NI_MAXHOST];
res = getnameinfo(ai->ai_addr, ai->ai_addrlen, node, sizeof node, NULL, 0, NI_NAMEREQD);
freeaddrinfo(ai);
Sonuç Listesi Kullanmak ai_addr alanı
ai_addr alanı IPv4 veya IPv6 adresi içerir. bind için kullanılabilir.
bind(sock, res->ai_addr, res->ai_addrlen)
İstemci socket olarak connect için kullanılabilir.
if(connect(sock, (struct sockaddr*)res->ai_addr, ptr->ai_addrlen) < 0) {
  ...
}
Eğer ai_family AF_INET6 ise IPv6 adresini şöyle alabiliriz.
unsigned char buf[16];
struct sockaddr_in6 *in6 = (struct sockaddr_in6*)res->ai_addr;
memcpy(buf, in6->sin6_addr.s6_addr, 16);
IPv6 byte'larını şöyle görebiliriz.
for (int i = 0; i < 16; i++) {
    printf("%02X", buf[i]);
    if (i < 15) putchar(':');
}
ya da string olarak görebiliriz.
char str[64];
printf("%s\n", inet_ntop(AF_INET6, buf, str, sizeof buf));

Hiç yorum yok:

Yorum Gönder