Send GET Request in C to IPv6 web server on windows - c

I am trying to make a simple GET request to a webserver that has an IPv6 address, and it does not work. My code has to be available on Windows, so I use the Windows libraries, e.g.
#include <stdio.h>
#include <winsock2.h>
#pragma comment (lib, "Ws2_32.lib")
#include <windows.h>
#include <winuser.h>
#include <string.h>
Here is the part of the code that establishes the connection :
int portno = 825;
//yes, strange port number...
const char *adress = "[2a02:842a:86d1:d001:26dd:8d7a:8202:d9a3]";
WSADATA wsa;
SOCKET sockfd;
char message[4096] = "GET //page//index.php?data=somedata HTTP/1.1\r\nHost: [2a02:842a:86d1:d001:26dd:8d7a:8202:d9a3]\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n\r\n");
struct hostent *server;
struct sockaddr_in6 serv_addr;
int bytes, sent, received, total;
char response[4096];
int iResult = WSAStartup(MAKEWORD(2,2),&wsa);
sockfd = socket(AF_INET6 , SOCK_STREAM , 0 );
//while debugging i noticed that this line does not work
server = gethostbyname(adress);
memset(&serv_addr,0,sizeof(serv_addr));
printf("1\n");
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(portno);
memcpy(&serv_addr.sin_addr.s_addr,server->h_addr,server->h_length);
connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
If anybody knows how to change this code so that it will allow IPv6 connections, please tell me :)

Your call to gethostbyname() cannot possibly work, as this function only works with IPv4 addresses or hostnames, while you are providing an IPv6 address in URI notation ([addr]). Furthermore, gethostbyname() is obsolete and should not be used in new code.
Since you already have an IPv6 address, you don't need to perform a lookup. You could use getaddrinfo(), but you're better off just transforming that in binary form through the much simpler inet_pton(). Remember that you need to remove the enclosing [] brackets as they are only needed for URIs, not for literal addresses.
Here's an example:
const char *address = "2a02:842a:86d1:d001:26dd:8d7a:8202:d9a3";
if (inet_pton(AF_INET6, address, &serv_addr.sin6_addr.s6_addr) != 1) {
// invalid address, handle the error somehow
}
Also, note that the correct sockaddr_in6 has the following fields (see man 7 ipv6):
struct sockaddr_in6 {
sa_family_t sin6_family; /* AF_INET6 */
in_port_t sin6_port; /* port number */
uint32_t sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
uint32_t sin6_scope_id; /* Scope ID (new in 2.4) */
};
struct in6_addr {
unsigned char s6_addr[16]; /* IPv6 address */
};
While you are using sin_addr, sin_family, etc, as if it were a sockaddr_in structure. What you want instead is:
serv_addr.sin6_family = AF_INET6;
serv_addr.sin6_port = htons(portno);

Related

What is the socket address family defined by 30?

I am writing a simple FTP server program in C. I am mostly following Beej's Guide to Network Programming. In order to complete the pasv mode functionality, I have to create another socket and send its ip and port to the client.
Since gethostbyname() is deprecated, I am using getsockname() to get my current network IP address. My plan is that I separate two cases (ipv4 and ipv6) and send info to the client accordingly. But, whenever I try to get the address family it always 30. I am not sure if I am printing this correctly though.
I am doing something simple like this (which might include mistakes), and it prints 30.
struct sockaddr_storage my_addr;
int result;
socklen_t len = sizeof(my_addr);
result = getsockname(current_fd, (struct sockaddr *) &my_addr, &len);
if (my_addr.ss_family == AF_INET) { // ipv4
struct sockaddr_in * ipv4 = (struct sockaddr_in *) &my_addr;
addr = &(ipv4->sin_addr);
} else { // ipv6
struct sockaddr_in6 * ipv6 = (struct sockaddr_in6 *) &my_addr;
addr = &(ipv6->sin6_addr);
}
printf(" family: %u\n", (unsigned short) my_addr.ss_family);
When I searched this online, I got:
#define AF_ATM 30 /* ATM */
What is this exactly? Is this expected? If not, where am I doing something wrong?
Thank you.
Edit:
Apparently what it was printing was something different.
It is not defined as:
#define AF_ATM 30 /* ATM */
but it is defines as:
MT IPv6: Multi-Topology IP version 6 [RFC7307]

how to set string specific hostname in C?

So, I am trying to learn socket programming in C (and I am a noob in C), but how do we set string based hostname. For example:
memset(&echoServAddr, 0, sizeof(echoServAddr)); /* Zero out structure */
echoServAddr.sin_family = AF_INET; /* Internet address family */
echoServAddr.sin_addr.s_addr = inet_addr(servIP); /* Server IP address */
//echoServAddr.sin_addr.s_addr = servIP;
echoServAddr.sin_port = htons(echoServPort); /* Server port */
Over here:
echoServAddr.sin_addr.s_addr = inet_addr(servIP);
servIP must be of form 127.0.0.1 but I want to use strings like localhost?
You have to use getaddrinfo function. It's used like:
getaddrinfo(machine, port, &addrinfo, &addrinfo_result)
As example:
struct addrinfo hints; //This "helps" to search
struct addrinfo *result;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM; //Your socket's type
/* There are some more like protocol, flags.. */
error = getaddrinfo(argv[1], argv[2], &hints, &result);
In addition to getaddrinfo, you will want to look into gethostbyname and gethostbyaddr.
gethostbyname, particularly, does what you are looking to do. It asks the resolver library to take a string and perform a forward lookup to resolve it to a number:
#include <netdb.h>
extern int h_errno;
struct hostent *gethostbyname(const char *name);

How to detect your IP/name using gethostbyname()

is there a way to get the IP number from gethostname()?
We are randomly generating IP addresses for the computers in the lab we are in. We use gethostbyname(<random ip>) to get the IP of a computer.
What we want to do essentially is compare the ip that we get from gethostbyname() with what we get from gethostname().
We tried:
struct hostent* host;
char temp[MAX_LEN];
gethostname(temp, MAX_LEN);
host = gethostbyname(<random ip address>)
if(host->h_name == temp) printf("They are the same\n");
The problem is, is that host->h_name is '172.125.45.1' (i made that
up) and temp is 'u-my_comp'
so we cant compare the strings cause one gives us the name of the computer (u-my_comp), and the other gives the ip...
Is there anyway to make these functions return the same type of value?
we have tried doing something like
gethostname(temp, 24)
temp_host = gethostbyname(temp)
in hopes that now we could compare temp_host->h_name with host->h_name...but yeah, that didnt work either.
Any ideas?
thanks!
gethostbyname() is for converting a hostname into a socket address. If the "hostname" you supply is a dotted-quad IPv4 address, that will be all you get in the h_name parameter of the result.
To convert a socket address back into a name what you want is the companion function gethostbyaddr() - except that you don't, because both gethostbyname() and gethostbyaddr() are deprecated. Instead, you should be using getaddrinfo() and getnameinfo().
For example:
#include <stdio.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main()
{
struct sockaddr_in sa;
char host[1024];
int gni_err;
sa.sin_family = AF_INET;
sa.sin_port = 0;
sa.sin_addr.s_addr = inet_addr("127.0.0.1");
gni_err = getnameinfo((struct sockaddr *)&sa, sizeof sa, host, sizeof host, NULL, 0, NI_NAMEREQD | NI_NOFQDN);
if (gni_err == 0) {
printf("host is: %s\n", host);
} else {
fprintf(stderr, "Error looking up host: %s\n", gai_strerror(gni_err));
}
return 0;
}
If you call:
myhost = gethostbyname(temp);
(having allocated myhost) then you will have two hostent structures you will compare - you will have the IP address lists for both the target query host and the current host (not just the hostname for the current host).

getaddrinfo and IPv6

I'm trying to understand what the getaddrinfo function returns :
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netdb.h>
int main (int argc, char *argv[])
{
struct addrinfo *res = 0 ;
getaddrinfo("localhost", NULL ,NULL,&res);
printf("ai_flags -> %i\n", res->ai_flags) ;
printf("ai_family -> %i\n", res->ai_family) ;
printf("ai_socktype -> %i\n", res->ai_socktype) ;
printf("ai_protocol -> %i\n", res->ai_protocol) ;
printf("ai_addrlen -> %i\n", res->ai_addrlen) ;
struct sockaddr_in* saddr = (struct sockaddr_in*)res->ai_addr;
printf("ai_addr hostname -> %s\n", inet_ntoa(saddr->sin_addr));
freeaddrinfo(res);
return 0 ;
}
results :
ai_flags -> 40
ai_family -> 2
ai_socktype -> 1
ai_protocol -> 6
ai_addrlen -> 16
ai_addr hostname -> 127.0.0.1
In /etc/hosts, I 've got :
127.0.0.1 localhost
::1 localhost
Getaddrinfo returns only 127.0.0.1 and not ::1 ? I don't understand why ?
The second question is where can I find the meaning of those ints (40,2,1,6 etc) ? I've read the man but there is nothing about that.
I also wanted to know if it's possible to give a IPv6 adress (for example ::1) and the function returns the name : localhost ?
Thanks a lot !!
#jwodder and #onteria_ covered the IPv6 portion well, so I'll just tackle the numbers portion:
ai_flags -> 40
Probably this is going to be the sum of the following two in /usr/include/netdb.h:
# define AI_V4MAPPED 0x0008 /* IPv4 mapped addresses are acceptable. */
# define AI_ADDRCONFIG 0x0020 /* Use configuration of this host to choose
This is the protocol family, inet, inet6, apx, unix, etc.:
ai_family -> 2
bits/socket.h:78:#define PF_INET 2 /* IP protocol family. */
bits/socket.h:119:#define AF_INET PF_INET
This is the socket type, stream, dgram, packet, rdm, seqpacket:
ai_socktype -> 1
bits/socket.h:42: SOCK_STREAM = 1, /* Sequenced, reliable, connection-based
The higher-level protocol, TCP, UDP, TCP6, UDP6, UDPlite, ospf, icmp, etc:
ai_protocol -> 6
Funny enough, in /etc/protocols:
tcp 6 TCP # transmission control protocol
The size of the struct sockaddr. (Differs based on the address family! Ugh.)
ai_addrlen -> 16
This is because you're getting back a struct sockaddr_in, see linux/in.h:
#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */
struct sockaddr_in {
sa_family_t sin_family; /* Address family */
__be16 sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
/* Pad to size of `struct sockaddr'. */
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
sizeof(unsigned short int) - sizeof(struct in_addr)];
};
And the last one, from /etc/hosts :)
ai_addr hostname -> 127.0.0.1
res also contains a field struct addrinfo *ai_next;, which is a pointer to additional entries found by getaddrinfo, or NULL if there were no other entries. If you examine res->ai_next, you should find the IPv6 entry.
As for the integer fields in a struct addrinfo, they correspond to predefined constants with implementation-defined values, and the integer values themselves are not of general interest. If you want to know what a given field means, compare it against the constants that can be assigned to that field (SOCK_STREAM, SOCK_DGRAM, etc. for ai_socktype; IPPROTO_TCP, IPPROTO_UDP, etc. for ai_protocol; and so forth) or, for ai_flags, test each bit corresponding to a predefined constant (e.g., if (res->ai_flags & AI_NUMERICHOST) {printf("ai_flags has AI_NUMERICHOST\n"); }).
extern struct sockaddr_in6 create_socket6(int port, const char * address) {
struct addrinfo hints, *res, *resalloc;
struct sockaddr_in6 input_socket6;
int errcode;
/* 0 out our structs to be on the safe side */
memset (&hints, 0, sizeof (hints));
memset (&input_socket6, 0, sizeof(struct sockaddr_in6));
/* We only care about IPV6 results */
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_DEFAULT;
errcode = getaddrinfo (address, NULL, &hints, &res);
if (errcode != 0)
{
perror ("[ERROR] getaddrinfo ");
return input_socket6;
}
resalloc = res;
while (res)
{
/* Check to make sure we have a valid AF_INET6 address */
if(res->ai_family == AF_INET6) {
/* Use memcpy since we're going to free the res variable later */
memcpy (&input_socket6, res->ai_addr, res->ai_addrlen);
/* Here we convert the port to network byte order */
input_socket6.sin6_port = htons (port);
input_socket6.sin6_family = AF_INET6;
break;
}
res = res->ai_next;
}
freeaddrinfo(resalloc);
return input_socket6;
}
Here is some code that explains it. Basically unless you give getaddrinfo some hints to tell it to only work with IPV6, it will also give IPV4 results. That is why you have to loop through the results as shown.
Other answers have been given to most parts, but to answer this final part:
I also wanted to know if it's possible to give a IPv6 adress (for example ::1) and the function returns the name : localhost ?
The function you want there is getnameinfo(); given a socket address it returns a string name.

Why can't a bind linux service to the loop-back only?

I am writing a server application that will provide a service on an ephemeral port that I only want accessible on the loopback interface. In order to do this, I am writing code like the following:
struct sockaddr_in bind_addr;
memset(&bind_addr,0,sizeof(bind_addr));
bind_addr.sin_family = AF_INET;
bind_addr.sin_port = 0;
bind_addr.sin_addr.s_addr = htonl(inet_addr("127.0.0.1"));
rcd = ::bind(
socket_handle,
reinterpret_cast<struct sockaddr *>(&bind_addr),
sizeof(bind_addr));
The return value for this call to bind() is -1 and the value of errno is 99 (Cannot assign requested address). Is this failing because inet_addr() already returns its result in network order or is there some other reason?
inet_addr should be avoided, for there is a much saner method of constructing struct sockaddrs (which means it also obsoletes gethostby*):
#include <netdb.h>
/* Error checking omitted for brevity */
struct addrinfo hints = {.ai_flags = AI_PASSIVE};
struct addrinfo *res;
getaddrinfo("::1", NULL, &hints, &res); /* or 127.0.0.1 if you are 60+ */
bind(fd, res->ai_addr, res->ai_addrlen);
freeaddrinfo(res);
Is this failing because inet_addr() already returns its result in network order
Yes.
So remove the htonl call.

Resources