Skip to content
Advertisement

IPv6 example program fails on connect()

IPv6 example program fails on connect()

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <ctype.h>

void error(const char * es)
{
  fprintf(stderr, "Error: %sn", es);
  exit(1);
}

struct sockaddr * getadr(char * name)
{
  struct addrinfo * p;
  int r;
  struct sockaddr_in6 * sap;
  unsigned long long addrl, addrh;

  printf("getadr: beginn");
  r = getaddrinfo(name, NULL, NULL, & p);
  if (r) error(gai_strerror(r));
  sap = NULL;
  while (p && !sap) {

    /* traverse the available addresses */
    if (p - > ai_family == AF_INET6 && p - > ai_socktype == SOCK_STREAM) {

      /* get the IPv6 address */
      sap = (struct sockaddr_in6 * ) p - > ai_addr;

    }
    p = p - > ai_next;

  }
  if (!sap) error("No address found");
  addrh = (unsigned long long) ntohl(sap - > sin6_addr.__in6_u.__u6_addr32[0]) << 32 |
    (unsigned long long) ntohl(sap - > sin6_addr.__in6_u.__u6_addr32[1]);
  addrl = (unsigned long long) ntohl(sap - > sin6_addr.__in6_u.__u6_addr32[2]) << 32 |
    (unsigned long long) ntohl(sap - > sin6_addr.__in6_u.__u6_addr32[3]);
  printf("Address: %llx:%llx:%llx:%llx:%llx:%llx:%llx:%llxn",
    addrh >> 48 & 0xffff, addrh >> 32 & 0xffff, addrh >> 16 & 0xffff, addrh & 0xffff,
    addrl >> 48 & 0xffff, addrl >> 32 & 0xffff, addrl >> 16 & 0xffff, addrl & 0xffff);
  printf("getadr: endn");

  return ((struct sockaddr * ) sap);

}

int main(int argc, char * argv[]) {
  int sockfd = 0, n = 0;
  char buff[1024];
  struct sockaddr_in6 serv_addr;
  int r;
  struct sockaddr * sap;

  if (argc != 3) {

    printf("Usage: socket <server> <page>n");
    exit(1);

  }

  if ((sockfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0)
    error("Could not create socket");

  memset( & serv_addr, '0', sizeof(serv_addr));

  serv_addr.sin6_family = AF_INET6;
  serv_addr.sin6_port = htons(80);

  printf("before address resolven");
  if (isdigit(argv[1][0])) {

    r = inet_pton(AF_INET6, argv[1], & serv_addr.sin6_addr);
    if (r <= 0) error("inet_pton error occured");

  } else {

    sap = getadr(argv[1]);
    memcpy( & serv_addr, sap, sizeof(struct sockaddr));

  }
  printf("after address resolven");

  r = connect(sockfd, (struct sockaddr * ) & serv_addr, sizeof(serv_addr));
  if (r < 0) error("Connect Failed");
  printf("after connectn");

  /* send request */
  sprintf(buff, "GET %s HTTP/1.1rn", argv[2]);
  write(sockfd, buff, strlen(buff));

  sprintf(buff, "Host: %srnrn", "www.example.com" /*argv[1]*/ );
  write(sockfd, buff, strlen(buff));
  do {

    r = read(sockfd, buff, sizeof(buff));
    if (r > 0) {

      buff[r] = 0;
      printf("%s", buff);

    }

  } while (r);

  return 0;

}

I arranged the server argument to be evaluated by inet_pton() if numeric, otherwise, it goes through getaddrinfo(). inet_pton() sets up the address and it works. getaddrinfo() does not, apparently, it dies in connect (hangs up). The example program is a simple web page fetch and print (not https). I used the www.example.com server to test it.

Note in the following example run that I use the same address getaddrinfo()gives me in a numeric example, then that works fine.

What am I doing wrong here?

The compile is simply gcc socket.c -o socket.

$ socket6 www.example.com /
before address resolve
getadr: begin
Address: 2606:2800:220:1:248:1893:25c8:1946
getadr: end
after address resolve
^C
(hangs up in connect, CTL-C out of it)
$ socket6 2606:2800:220:1:248:1893:25c8:1946 /
before address resolve
after address resolve
after connect
HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: max-age=604800
Content-Type: text/html; charset=UTF-8
...
(prints the rest of the page)

I did find a similar post where they recommend adding the interface name to the server name, like www.example.com%enp2s0, but that was just rejected as invalid by getaddrinfo().

Advertisement

Answer

The main problem is that you are not populating serv_addr correctly.

When calling getadr(), your call to memcpy() for the result does not copy enough bytes for a complete sockaddr_in6 (sizeof(sockaddr) is less thansizeof(sockaddr_in6)). Also, you are not askinggetaddrinfo()to output a port number, so thesin6_port` of the result will not be 80 as you are expecting.

That is why connect() fails when you use getaddrinfo() instead of inet_pton().

There are other problems with your code, too.

When using memset(), you need to use an integer 0 rather than a character '0'. They are not the same value. Unused sockaddr_in6 fields need to be zeroed out properly.

Your getadr() function leaks memory, and it is inefficient in general. Use the hints parameter of getaddrinfo() to limit the results it outputs so you don’t have to go hunting for them. And you need to free the output with freeaddrinfo() when you are done using it.

isdigit() is not the correct way to differentiate a numeric IP from a hostname. And besides, you don’t really need to do this differentiation manually, as getaddrinfo() can parse a numeric IP string. But if you do differentiate manually, call inet_pton() unconditionally and then call getaddrinfo() if inet_pton() fails.

With that said, try something more like this instead:

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <ctype.h>

void error(const char* es)
{
    fprintf(stderr, "Error: %sn", es);
    exit(1);
}

void getadr(const char* name, struct in6_addr *addr)
{
    struct addrinfo hints, *p;
    int r;
    struct sockaddr_in6 *sap;
    char addrstr[INET6_ADDRSTRLEN];

    printf("getadr: beginn");

    /*
    r = inet_pton(AF_INET6, name, addr);
    if (r == 1)
    {
        printf("getadr: endn")
        return;
    }
    */

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

    r = getaddrinfo(name, NULL, &hints, &p);
    if (r != 0)
        error(gai_strerror(r));

    sap = (struct sockaddr_in6*)p->ai_addr;

    memcpy(addr, &(sap->sin6_addr), sizeof(*addr));
    freeaddrinfo(p);

    printf("Address: %sn", inet_ntop(AF_INET6, addr, addrstr, sizeof(addrstr)));

    printf("getadr: endn");
}

int main(int argc, char *argv[])
{
    int sockfd, r;
    char buff[1024];
    struct sockaddr_in6 serv_addr;

    if (argc != 3)
    {
        printf("Usage: socket <server> <page>n");
        exit(1);
    }

    sockfd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
    if (sockfd < 0)
        error("Could not create socket");

    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin6_family = AF_INET6;
    serv_addr.sin6_port = htons(80);

    printf("before address resolven");

    getadr(argv[1], &(serv_addr.sin6_addr));

    printf("after address resolven");

    r = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    if (r < 0)
        error("Connect Failed");

    printf("after connectn");

    /* send request */

    sprintf(buff, "GET %s HTTP/1.1rn", argv[2]);
    write(sockfd, buff, strlen(buff));
    sprintf(buff, "Host: %srn", argv[1]);
    write(sockfd, buff, strlen(buff));
    sprintf(buff, "%s", "Connection: closernrn");
    write(sockfd, buff, strlen(buff));

    do
    {
        r = read(sockfd, buff, sizeof(buff));
        if (r <= 0) break;
        printf("%.*s", r, buff);
    }
    while (true);

    close(sockfd);
    return 0;
}

That said, getaddrinfo() outputs a linked list of IP addresses. It is possible for a hostname to resolve to multiple IPs, not all of which may be reachable from your machine. You should loop through the entire list connect()‘ing to each IP until one of them succeeds or the list is exhausted. For example:

#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
#include <ctype.h>

void error(const char* es)
{
    fprintf(stderr, "Error: %sn", es);
    exit(1);
}

struct addrinfo* getadrs(const char* name, const char* port)
{
    struct addrinfo hints, *p;
    int r;

    printf("getadr: beginn");

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET6 /*AF_UNSPEC*/;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    r = getaddrinfo(name, port, &hints, &p);
    if (r != 0)
        error(gai_strerror(r));

    printf("getadr: endn");
    return p;
}

void* adrptr(struct sockaddr* addr)
{
    switch (addr->sa_family)
    {
        case AF_INET:
            return &(((struct sockaddr_in*)addr)->sin_addr);

        case AF_INET6:
            return &(((struct sockaddr_in6*)addr)->sin6_addr);
    }

    return NULL;
}

int main(int argc, char *argv[])
{
    int sockfd = -1, r;
    char buff[1024], addrstr[INET6_ADDRSTRLEN];
    struct addrinfo *serv_addrs, *addr;

    if (argc != 3)
    {
        printf("Usage: socket <server> <page>n");
        exit(1);
    }

    printf("before address resolven");

    serv_addrs = getadrs(argv[1], "80");

    printf("after address resolven");

    for(addr = serv_addrs; addr != NULL; addr = addr->ai_next)
    {
        sockfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
        if (sockfd < 0)
            error("Could not create socket");

        printf("Address: %sn", inet_ntop(addr->ai_family, adrptr(addr->ai_addr), addrstr, sizeof(addrstr)));

        r = connect(sockfd, addr->ai_addr, addr->ai_addrlen);
        if (r == 0) break;

        close(sockfd);
        sockfd = -1;
    }

    if (sockfd < 0)
        error("Connect Failed");

    printf("after connectn");

    /* send request */

    sprintf(buff, "GET %s HTTP/1.1rn", argv[2]);
    write(sockfd, buff, strlen(buff));
    sprintf(buff, "Host: %srn", argv[1]);
    write(sockfd, buff, strlen(buff));
    sprintf(buff, "%s", "Connection: closernrn");
    write(sockfd, buff, strlen(buff));

    do
    {
        r = read(sockfd, buff, sizeof(buff));
        if (r <= 0) break;
        printf("%.*s", r, buff);
    }
    while (true);

    close(sockfd);
    return 0;
}
User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement