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.
Related
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);
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]
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);
I am reading Beej's "Guide to network programming".
In one of his intro examples he talks about getting the IP address for a hostname (like google.com or yahoo.com for instance).
Here is the code.
/*
** showip.c -- show IP addresses for a host given on the command line
*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
struct addrinfo hints, *res, *p;
int status;
char ipstr[INET6_ADDRSTRLEN];
if (argc != 2) {
fprintf(stderr,"usage: showip hostname\n");
return 1;
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
hints.ai_socktype = SOCK_STREAM;
if ((status = getaddrinfo(argv[1], NULL, &hints, &res)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
return 2;
}
printf("IP addresses for %s:\n\n", argv[1]);
for(p = res; p != NULL; p = p->ai_next) {
void *addr;
char *ipver;
// get the pointer to the address itself,
// different fields in IPv4 and IPv6:
if (p->ai_family == AF_INET) { // IPv4
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
ipver = "IPv4";
} else { // IPv6
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
ipver = "IPv6";
}
// convert the IP to a string and print it:
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
printf(" %s: %s\n", ipver, ipstr);
}
freeaddrinfo(res); // free the linked list
return 0;
}
The part that confuses me is the for loop.
for(p = res; p != NULL; p = p->ai_next) {
void *addr;
char *ipver;
// get the pointer to the address itself,
// different fields in IPv4 and IPv6:
if (p->ai_family == AF_INET) { // IPv4
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
ipver = "IPv4";
} else { // IPv6
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
ipver = "IPv6";
}
// convert the IP to a string and print it:
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
printf(" %s: %s\n", ipver, ipstr);
}
Would anyone mind going through psuedo-step-by-step at whats going on or what these things are? Is it iterating through a linked list?.. I have a general idea of what the struct addrinfo are but what the heck is struct *res and struct *p or void *addr and *char ipversion.
First thing's first, do you know what a linked list is? If you understand that, you'll recognise what that for loop is going. p is a pointer to a structure that also references (links) the next structure in the list. So you're looping through a list of those structures, which are addrinfo structs. 4
Now, the thing you need to know about network packets is that they're made up of a header. Specifically the Ethernet frame. This is the hardware to hardware protocol. It let's you get things around on a physical, bounded network but knows nothing about routing across physical network boundaries.
Next up comes tcp or possibly another transport layer protocol, which sits somewhere inbetween the two levels. TCP versus UDP versus X is about how you manage the packets - for example TCP requires packets be reassembled in order, whereas UDP is a "broadcast"-type protocol.
Finally, you have the internet protocol suite (IPv4, IPv6). These are higher level protocols that control the broader sense of routing, so they know about the internet at large, but less about the steps needed to get there.
A great explanation of this is the handy diagram on this page. To complete the picture, BGP is how routers know how to move stuff around.
tcp/udp fit into this picture by being a part of (enscapulated in) the protocol in question (IPv4 for example)
So ethernet frames contain other protocols most notably IPv4, which contain the information routers need to get it out across the internet (across multiple physical networks). The internet protocol specifies where you want to go, from where you are. So a typical's IPv4 body remains unchanged across its whole transit, but every time it traverses physical networks it gets wrapped up in a different ethernet packet.
Now, in the ethernet header there is a field for finding out what the "ethernet body" contains. This line:
if (p->ai_family == AF_INET) {
Does. AF_INET is a constant that matches the value tcp uses to identify the packet body as IPv4. So, if you're looking at an IPv4 header, this loop then goes on to read that information.
The else clause is technically wrong, because not being IPv4 doesn't automatically make it IPv6. You could change it to test for IPv6 like this:
else if (p->ai_family == AF_INET6) {
Which you might want to do, just in case you pick up something else.
Now it's worth explaining this bit of magic:
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
This basically takes the network, or raw, form of the data which appears as a sequence of bytes, and casts it (coverts it) into fields in a struct. Because you know how big the fields are going to be, this is a very quick and easy way to extract out what you need.
The last thing that needs explanation is this:
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
There are other ways of achieving this, specifically ntohs().
Basically network data is transmitted in big endian encoding, and in order to read it, you need (potentially) to convert the data to the encoding of your system. It could be big endian, or it could be little, it depends on your system for the most part. Have a read of the wikipedia article on endianness.
Summary: what you're looking at here is a combination of computer science structures, how networks work and C code.
Well, it's not that complicated. getaddrinfo returns a linked list of addrinfo structs (struct addrinfo **res in the manpage) where each of these structs contains information about one address available to the given interface (const char *node in the manpage).
Now, every struct is being inspected and information about the struct is being printed out. To print out either IPv4 or IPv6, the variable ipver is set accordingly. Before printing out the information, the address has to be converted from a binary form to a string. This is done by inet_ntop (*n*umber to *p*ointer).
The resulting string of inet_ntop (ipstr) and ipver are now printed out to console. Printing ipver, however, is not neccessary since you would recognize the address type from the ipstr: an IPv4 address (as we all know) gets written 192.168.1.10 whereas IPv6 addresses use colons to separate the address elements: 2001:0db8:85a3:0000:0000:8a2e:0370:7334.
Yes, res points to a linked list of addrinfo structures that represent the different IP addresses of a host. The MSDN documentation on the getaddrinfo function is pretty good. I don't know what platform you're running on, but it shouldn't be much different on other platforms.
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.