Основная проблема заключается в том, что вы неправильно заполняете serv_addr
.
При вызове getadr()
ваш вызов memcpy()
для результата не копирует достаточное количество байтов для полного sockaddr_in6
(sizeof(sockaddr) is less than
sizeof (sockaddr_in6) ). Also, you are not asking
getaddrinfo () to output a port number, so the
sin6_port` результата не будет 80, как вы ожидаете.
Именно поэтому connect()
завершается неудачно, когда вы используете getaddrinfo()
вместоиз inet_pton()
.
Есть и другие проблемы с вашим кодом.
При использовании memset()
вам нужно использовать целое число 0
вместо символа '0'
. Это не одно и то же значение. Неиспользуемые sockaddr_in6
поля должны быть обнулены должным образом.
Ваша функция getadr()
приводит к утечке памяти, и в целом она неэффективна. Используйте параметр hints
getaddrinfo()
чтобы ограничить результаты, которые он выводит, чтобы вам не пришлось охотиться за ними. И вам нужно освободить вывод с помощью freeaddrinfo()
, когда вы закончите с его использованием.
isdigit()
не является правильным способомчтобы отличить числовой IP от имени хоста. И кроме того, вы на самом деле не нужноd сделать это различие вручную, так как getaddrinfo()
может анализировать числовую строку IP. Но если вы сделаете различие вручную, безоговорочно позвоните по номеру inet_pton()
, а если inet_pton()
потерпит неудачу, то наберите getaddrinfo()
.
С учетом вышесказанного попробуйте что-то более похожее на это:
#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: %s\n", 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: begin\n");
/*
r = inet_pton(AF_INET6, name, addr);
if (r == 1)
{
printf("getadr: end\n")
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: %s\n", inet_ntop(AF_INET6, addr, addrstr, sizeof(addrstr)));
printf("getadr: end\n");
}
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 resolve\n");
getadr(argv[1], &(serv_addr.sin6_addr));
printf("after address resolve\n");
r = connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
if (r < 0)
error("Connect Failed");
printf("after connect\n");
/* send request */
sprintf(buff, "GET %s HTTP/1.1\r\n", argv[2]);
write(sockfd, buff, strlen(buff));
sprintf(buff, "Host: %s\r\n", argv[1]);
write(sockfd, buff, strlen(buff));
sprintf(buff, "%s", "Connection: close\r\n\r\n");
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;
}
Тем не менее, getaddrinfo()
выводит связанный список IP-адресов. Для имени хоста можно разрешить несколько IP-адресов, не все из которых могут быть доступны с вашего компьютера. Вам следует перебирать весь список connect()
на каждом IP, пока один из них не будет успешным или список не будет исчерпан. Например:
#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: %s\n", es);
exit(1);
}
struct addrinfo* getadrs(const char* name, const char* port)
{
struct addrinfo hints, *p;
int r;
printf("getadr: begin\n");
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: end\n");
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 resolve\n");
serv_addrs = getadrs(argv[1], "80");
printf("after address resolve\n");
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: %s\n", 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 connect\n");
/* send request */
sprintf(buff, "GET %s HTTP/1.1\r\n", argv[2]);
write(sockfd, buff, strlen(buff));
sprintf(buff, "Host: %s\r\n", argv[1]);
write(sockfd, buff, strlen(buff));
sprintf(buff, "%s", "Connection: close\r\n\r\n");
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;
}