UNP Chapter 11 - the name and address translation

1. Domain Name System

  The benefits of the program only uses the host name and service name, if the IP or port change, just change the mapping relationship, without recompiling the program.

1.1 Resource Record

  DNS resource record entry for useful items as follows:

A IPv4 address 
AAAA IPv6 address 
CNAME specification name 
    such as: 
    ftp.unpbook.com the CNAME is linux.unpbook.com 
    www.unpbook.com the CNAME is linux.unpbook.com

1.2 resolver and name server

  Program by calling parser library function call DNS service. Commonly used function gethostbyname, gethostbyaddr.

  Associated with the parser configuration files and libraries function as follows

2.1 gethostbyname

       struct hostent *gethostbyname(const char *name);

           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 */
           }

  This function can only return A resource records, which is IPv4.

  To consider IPv6, you should use getaddrinfo.

  h_name is CNAME (specification name), as the name will ftp.unpbook.com is linux.unpbook.com

  h_addrtype only to IPv4, only with

  h_addr_list points to an array, the array element is an IP address (network byte order, than decimal point).

  You can pass in dotted decimal, or domain name to call gethostbyname

  gethostbyname call error, does not set errno, but set h_errno, use hstrerror (h_errno) to get the string error description.

int
main(int argc, char **argv)
{
        char                    *ptr, **pptr;
        char                    str[INET_ADDRSTRLEN];
        struct hostent  *hptr;

        while (--argc > 0) { ptr = *++argv; if ( (hptr = gethostbyname(ptr)) == NULL) { err_msg("gethostbyname error for host: %s: %s", ptr, hstrerror(h_errno)); continue; } printf("official hostname: %s\n", hptr->h_name); for (pptr = hptr->h_aliases; *pptr != NULL; pptr++) printf("\talias: %s\n", *pptr); switch (hptr->h_addrtype) { case AF_INET: pptr = hptr->h_addr_list; for ( ; *pptr != NULL; pptr++) printf("\taddress: %s\n", Inet_ntop(hptr->h_addrtype, *pptr, str, sizeof(str))); break; default: err_ret("unknown address type"); break; } } exit(0); }

2.2 gethostbyaddr

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

  Enter IP address network byte order, find hosts CNAME.

  type is AF_INET

  Return hostent, usually we are interested only h_name.

Reentrant 2.3

  gethostbyname getbostbyaddr are not reentrant and, because the use of static hostent host.

  Non-reentrant usually cause problems in multi-threaded and signals.

  Such as

main()
{
     signal(SIGALRM, sig_alrm);
     hptr = gethostbyname(...);    
}

void 
sig_alrm(int sig)
{
     hptr=  gethostbyname(...);
}

  Reentrant can not reentrant Summary:

gethostbyname, gethostbyaddr, getservbyname, getservbyport are not reentrant because of the use of static variables. However, there is reentrant version 
inet_pton, inet_ntop non-reentrant 
inet_ntoa not reentrant, but there are reentrant version. 
getaddrinfo, getnameinfo reentrant

  For the treatment of errno, errno may be multi-threaded, signals an unexpected change, can be handled as follows

void
sig_alrm(int sig)
{
    int errno_save;

    errno_save  = errno;
    if (write(...) != nbytes)
         fprintf(stderr, "write error = %d\n", errno); errno = errno_save; }

 

 

3.1 getserbyname

       struct servent *getservbyname(const char *name, const char *proto);

           struct servent {
               char  *s_name;       /* official service name */
               char **s_aliases;    /* alias list */
               int    s_port;       /* port number */
               char  *s_proto;      /* protocol to use */
           }

  Enter the service name and protocol, query port number.

  If you specify proto, must ensure that the server uses the corresponding protocol, if not specified, because usually the server using the same port number to use different protocols, so it does not matter.

  s_port network byte order

  Configuration file / etc / services

  Typical call:

sptr = getservbyname("domain", "udp"); // DNS using UDP
sptr = getservbyname("ftp", "tcp");       // FTP using TCP

3.2 getservbyport

       struct servent *getservbyport(int port, const char *proto);

  Enter the port number and protocol, service name query

  port must network byte order

  Typical call:

sptr = getservbyport(hton(53), "udp");    // DNS using UDP

 

4. Use gethostbyname and getserbyname

int
main(int argc, char **argv)
{
        int                                     sockfd, n;
        char                            recvline[MAXLINE + 1];
        struct sockaddr_in      servaddr;
        struct in_addr          **pptr; struct in_addr *inetaddrp[2]; struct in_addr inetaddr; struct hostent *hp; struct servent *sp; if (argc != 3) err_quit("usage: daytimetcpcli1 <hostname> <service>"); if ( (hp = gethostbyname(argv[1])) == NULL) { if (inet_aton(argv[1], &inetaddr) == 0) { err_quit("hostname error for %s: %s", argv[1], hstrerror(h_errno)); } else { inetaddrp[0] = &inetaddr; inetaddrp[1] = NULL; pptr = inetaddrp; } } else { pptr = (struct in_addr **) hp->h_addr_list; } if ( (sp = getservbyname(argv[2], "tcp")) == NULL) err_quit("getservbyname error for %s", argv[2]); for ( ; *pptr != NULL; pptr++) { sockfd = Socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = sp->s_port; memcpy(&servaddr.sin_addr, *pptr, sizeof(struct in_addr)); printf("trying %s\n", Sock_ntop((SA *) &servaddr, sizeof(servaddr))); if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) == 0) break; /* success */ err_ret("connect error"); close(sockfd); } if (*pptr == NULL) err_quit("unable to connect"); while ( (n = Read(sockfd, recvline, MAXLINE)) > 0) { recvline[n] = 0; /* null terminate */ Fputs(recvline, stdout); } exit(0); }

 

5.1 getaddrinfo

       int getaddrinfo(const char *hostname, const char *service,
                       const struct addrinfo *hints,
                       struct addrinfo **res);

           struct addrinfo {
               int              ai_flags;
               int ai_family; int ai_socktype; int ai_protocol; size_t ai_addrlen; struct sockaddr *ai_addr; char *ai_canonname; struct addrinfo *ai_next; };

  getaddrinfo supports IPv4, IPv6, and provide the name to address two conversion service to the port, the return of a sockaddr structure instead of an address list, it can be used directly in the socket library functions.

  hostname is a host name or address strings (dotted decimal or hexadecimal string)

  service is a service name or port number in decimal string

  hints can be empty, or may be a pointer to a struct addrinfo, it represents a desired return information implied.

    You can set hints members

               int              ai_flags;
               int              ai_family;
               int              ai_socktype;
               int              ai_protocol;

    ai_flags flag values ​​and meanings are available:

AI_PASSIVE socket for passive open 
AI_CANONAME getaddrinfo returned to the host name specification 
AF_NUMERICHOST prevent any type of name mapping, hostname parameter address must be an address string 
AF_NUMERICSERV name to prevent any type of port mapping, service parameters must is a port number 
AI_V4MAPPED if the specified ai_famliy simultaneously the AF_INET6, then if there is no available AAAA record, returns the IPv6 address a record corresponding to the IPv4-mapped 
AI_ALL if specified AI_V4MAPPED simultaneously, then in addition to return AAAA records IPv6 address, but also returns a record corresponding to the IPv6 address is IPv4-mapped. 
AI_ADDRCONFIG select the type of the return address according to the configuration where the host, that is, only to find the same IP address version configured network interfaces other than the interface address where the host and feedback.

  If the hints parameter is a null pointer, then assume this function ai_flag, ai_socktype, ai_protocol are null values, as AF_UNSPEC ai_family

  If the function returns success (0), result parameter returns the list addrinfo structure.

  For example, in the absence of any information suggesting that premise, request Finding domain service on a host two IP addresses, then it will return four addrinfo structure,

    A first combination of IP address SOCK_STREAM socket type

    A first combination of IP address type SOCK_DGRAM socket

    The second combination of IP address SOCK_STREAM socket type

    The second combination of IP address type SOCK_DGRAM socket

  And these structures are uncertain return order.

  Addrinfo returned socket functions other structures may be used,

    socket calls using ai_family, ai_socktype, 

    connect, bind calls use ai_addr, and ai_addrlen

  Return res variable points addrinfo space is dynamically allocated, freeaddrinfo release required.

 

  Getaddrinfo common use is as follows:

  (1) TCP or UDP client process, specify the hostname and service, upon return, TCP client processes one by one using the return address to call socket, connect, until a call to address successfully. UDP client calls sendto or connect, if the address does not work (as you receive an error message, or a timeout), then test different address until it succeeds.

  (2) Service process generally designated service only without specifying the hostname, while AI_PASSIVE flag. TCP server process then calls socket, bind, listen. If you want to get accept customer address, then use malloc to ai_addrlen address structure. UDP service call socket, bind, recvfrom, if aware of their server handles only one type of socket, then you should make hints of ai_socktype set to SOCK_STREAM or SOCK_DGRAM.

  (3) So far, our server (UDP, TCP) only to create a listening socket or a datagram socket. While the other is designed with select or poll the server to handle multiple sockets, in this case server to traverse the entire list addrinfo structure and create a socket for each structure, then use select or poll.

      The problem with this technique is that one of the reasons getaddrinfo returns multiple structures is that the service can at the same time by the IPv4, IPv6 processing, but the two protocols are not completely independent, if we create a port for a given a IPv6 listening sockets , then there is no need to create a socket to a port with IPv4, because the connection from the IPv4 and IPv6 protocol stacks will be listening socket automatically.

 

5.2 gai_strerror

       const char *gai_strerror(int errcode);

  getaddrinfo misinterpretation by the gai_strerror.

 

5.3 freeaddrinfo

      void freeaddrinfo(struct addrinfo *res);

  res directed getaddrinfo returned list is allocated dynamically, released by freeaddrinfo.

  Also note that the shallow copy problem res.

 

5.4 getnameinfo

       int getnameinfo(const struct sockaddr *sa, socklen_t salen,
                       char *host, size_t hostlen,
                       char *serv, size_t servlen, int flags);

  Input socket address, host name, and return the service name.

  Sock_ntop and getnameinfo difference is that the former does not involve DNS, only to return an IP address and a port number to display the version, the latter usually try to get the host name and service.

 

5.5 pairs package getaddrinfo

  getaddrinfo is recommended, but the call was too much trouble, so common operations package

struct addrinfo *
host_serv(const char *host, const char *serv, int family, int socktype)
{
        int                             n;
        struct addrinfo hints, *res;

        bzero(&hints, sizeof(struct addrinfo));
        hints.ai_flags = AI_CANONNAME;  /* always return canonical name */ hints.ai_family = family; /* AF_UNSPEC, AF_INET, AF_INET6, etc. */ hints.ai_socktype = socktype; /* 0, SOCK_STREAM, SOCK_DGRAM, etc. */ if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0) return(NULL); return(res); /* return pointer to first on linked list */

 

int
tcp_connect(const char *host, const char *serv)
{
        int                             sockfd, n;
        struct addrinfo hints, *res, *ressave;

        bzero(&hints, sizeof(struct addrinfo));
        hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0) err_quit("tcp_connect error for %s, %s: %s", host, serv, gai_strerror(n)); ressave = res; do { sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sockfd < 0) continue; /* ignore this one */ if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0) break; /* success */ Close(sockfd); /* ignore this one */ } while ( (res = res->ai_next) != NULL); if (res == NULL) /* errno set from final connect() */ err_sys("tcp_connect error for %s, %s", host, serv); freeaddrinfo(ressave); return(sockfd); }

 

int
tcp_listen(const char *host, const char *serv, socklen_t *addrlenp)
{
        int                             listenfd, n;
        const int               on = 1;
        struct addrinfo hints, *res, *ressave;

        bzero(&hints, sizeof(struct addrinfo)); hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0) err_quit("tcp_listen error for %s, %s: %s", host, serv, gai_strerror(n)); ressave = res; do { listenfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (listenfd < 0) continue; /* error, try next one */ Setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0) break; /* success */ Close(listenfd); /* bind error, close and try next one */ } while ( (res = res->ai_next) != NULL); if (res == NULL) /* errno from final socket() or bind() */ err_sys("tcp_listen error for %s, %s", host, serv); Listen(listenfd, LISTENQ); if (addrlenp) *addrlenp = res->ai_addrlen; /* return size of protocol address */ freeaddrinfo(ressave); return(listenfd); } /* end tcp_listen */

  The above function has a problem that the specified address family is AF_UNSPEC, that is likely to return to a non-anticipated address family socket, IPv4 as expected, return IPv4 and IPv6.

  Solution:

    We can enforce protocol address, such as inet_pton

inet_pton(AF_INET,"0.0.0.0", &foo);        //succeeds
inet_pton(AF_INET, "0::0", &foo);           // fails
inet_pton(AF_INET6, "0.0.0.0", &foo);     // fails
inet_pton(AF_INET6, "0::0", &foo);          //succeeds

 

Connectionless UDP

int
udp_client(const char *host, const char *serv, SA **saptr, socklen_t *lenp)
{
        int                             sockfd, n;
        struct addrinfo hints, *res, *ressave;

        bzero(&hints, sizeof(struct addrinfo));
        hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0) err_quit("udp_client error for %s, %s: %s", host, serv, gai_strerror(n)); ressave = res; do { sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sockfd >= 0) break; /* success */ } while ( (res = res->ai_next) != NULL); if (res == NULL) /* errno set from final socket() */ err_sys("udp_client error for %s, %s", host, serv); *saptr = Malloc(res->ai_addrlen); memcpy(*saptr, res->ai_addr, res->ai_addrlen); *lenp = res->ai_addrlen; freeaddrinfo(ressave); return(sockfd); } /* end udp_client */

UDP connection

int
udp_connect(const char *host, const char *serv)
{
        int                             sockfd, n;
        struct addrinfo hints, *res, *ressave;

        bzero(&hints, sizeof(struct addrinfo));
        hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0) err_quit("udp_connect error for %s, %s: %s", host, serv, gai_strerror(n)); ressave = res; do { sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sockfd < 0) continue; /* ignore this one */ if (connect(sockfd, res->ai_addr, res->ai_addrlen) == 0) break; /* success */ Close(sockfd); /* ignore this one */ } while ( (res = res->ai_next) != NULL); if (res == NULL) /* errno set from final connect() */ err_sys("udp_connect error for %s, %s", host, serv); freeaddrinfo(ressave); return(sockfd); } /* end udp_connect */

 

int
udp_server(const char *host, const char *serv, socklen_t *addrlenp)
{
        int                             sockfd, n;
        struct addrinfo hints, *res, *ressave;

        bzero(&hints, sizeof(struct addrinfo));
        hints.ai_flags = AI_PASSIVE; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; if ( (n = getaddrinfo(host, serv, &hints, &res)) != 0) err_quit("udp_server error for %s, %s: %s", host, serv, gai_strerror(n)); ressave = res; do { sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sockfd < 0) continue; /* error - try next one */ if (bind(sockfd, res->ai_addr, res->ai_addrlen) == 0) break; /* success */ Close(sockfd); /* bind error - close and try next one */ } while ( (res = res->ai_next) != NULL); if (res == NULL) /* errno from final socket() or bind() */ err_sys("udp_server error for %s, %s", host, serv); if (addrlenp) *addrlenp = res->ai_addrlen; /* return size of protocol address */ freeaddrinfo(ressave); return(sockfd); } /* end udp_server */

  UDP socket is no need to provide SO_REUSEADDR, because UDP is not similar WAIT_TIME state thereof.

 

Guess you like

Origin www.cnblogs.com/yangxinrui/p/12437321.html