I'm writing a UDP socket program which is provided an address from the command line.
To make sending and writing easier, I'm using getaddrinfo to convert the address to a sockaddr struct: either sockaddr_in or sockaddr_in6. Now I understand that I should use a union of sockaddrs:
typedef union address
{
struct sockaddr s;
struct sockaddr_in s4;
struct sockaddr_in6 s6;
struct sockaddr_storage ss;
} address_t;
As I understand they can't be pointers to avoid hiding strict aliasing problems. I'm having trouble seamlessly putting the information from getaddrinfo's addrinfo into this address_t:
struct addrinfo hint, *serv = NULL;
address_t addr;
hint.ai_family = AF_UNSPEC;
hint.ai_flags = 0;
hint.ai_socktype = SOCK_DGRAM;
hint.ai_protocol = IPPROTO_UDP;
ret = getaddrinfo(address_sr.c_str(), s_port.c_str(), &hint, &serv);
//address_sr and s_port are strings with the address and port
switch (serv->ai_addr) {
case AF_INET: {
addr->s4 = * (sockaddr_in*) serv->ai_addr;
//Here I want to fill the address_t struct with information
//This line causes a segfault
}
break;
case AF_INET6: {
addr->s6 = * (sockaddr_in6*) serv->ai_addr;
//Conversion here
}
break;
Also, copying the memory:
memcpy(&addr, serv->ai_addr, serv->ai_addrlen);
Causes a segfault too.
How exactly should I do this? I tried a dozen different ways and I just can't figure it out. How do I put an address from addrinfo to this union? Do I use sockaddr_storage or the sockaddr_ins?
EDIT: Editing for clarity and additional code information.
You need to de-reference the pointer.
addr->s4 = *(sockaddr_in*) serv->ai_addr;
I think you're not getting getaddrinfo right.
About the third argument, :
const struct addrinfo *hints
The hints argument points to an addrinfo structure that specifies
criteria for selecting the socket address structures returned in the
list pointed to by res. If hints is not NULL it points to an addrinfo
structure whose ai_family, ai_socktype, and ai_protocol specify
criteria that limit the set of socket addresses returned by
getaddrinfo()[...]
For example, you can ask for IPv4 address family only, and/or for datagram sockets only (which could be fine given your attempt to use UDP).
Basically, you provide an addrinfo instance, set the fields of interest, then pass a pointer to it to the function, as its third argument:
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */
ret = getaddrinfo(address_sr.c_str(), s_port.c_str(), &hint, &serv);
In this example, the function could return not just one, but a whole list of address structures:
The getaddrinfo() function allocates and initializes a linked list of
addrinfo structures, one for each network address that matches node
and service, subject to any restrictions imposed by hints, and returns
a pointer to the start of the list in res. The items in the linked
list are linked by the ai_next field.
So you have to loop through the function result this way:
for (rp = serv; rp != NULL; rp = rp->ai_next)
I strongly suggest to carefully read the documentation at the link I provided. There is also a long and detailed example which could alone solve your issues.
sockaddr_storage is large enough to hold any sockaddr_... type, thus making address_t just as large. So, I would just memcpy() the entire serv->ai_addr in one operation and get rid of the switch, eg:
struct addrinfo hints = {};
struct addrinfo *addrs, *serv;
address_t addr;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
...
ret = getaddrinfo(address_sr.c_str(), s_port.c_str(), &hint, &addrs);
if (ret == 0)
{
for (serv = addrs; serv != NULL; serv = serv->ai_next)
{
memcpy(&addr, serv->ai_addr, serv->ai_addrlen);
...
}
freeaddrinfo(addrs);
}
Related
I'm trying to write an IP Address class that both handles IPV6 and IPV4 addresses; to reach this purpose I'm using the sockadrr_storage data structure.
I've already written most of this class but now I've got some difficulties on creating an interface that uses in a correct and clever way the getaddrinfo() function.
I mean I want to create a constructor that could give and insert the right IP address in the sockadrr_storage data structure after having put a set of parameters.
Some examples:
ip_address ip_v4("127.0.0.1"); -> This will give a loopback address
ip_address ip_v6("::", 0 , AF_INET6 , SOCK_STREAM); -> This will give a V6 link local
ip_address host_adr("www.google.it"); -> This will perform a dns lookup
To do this, this function has to make a call to getaddrrinfo() with the right parameters.
I have to handle those cases:
loopback V4 and V6
wildcard V4 and V6
numerical string V4 and V6
hostname string V4 and V6
Here there is the code I have written until now.
bool ip_address::parse_addr(std::string addr_ , std::string port_ , int ai_family , int ai_socktype)
{
struct addrinfo hints , * host_info ;
memset(&hints , 0, sizeof(struct addrinfo));
switch(ai_family)
{
case AF_INET:
hints.ai_family = AF_INET;
hints.ai_socktype = ai_socktype;
break;
case AF_INET6:
hints.ai_family = AF_INET6;
hints.ai_socktype = ai_socktype;
break;
}
getaddrinfo(addr_.c_str(), port_.c_str(), &hints, &host_info);
if(host_info->ai_family == AF_INET )
{
struct sockaddr_in adr;
memset(&adr,0,sizeof(struct sockaddr_in));
adr.sin_family = AF_INET;
size_ = sizeof(struct sockaddr_in);
address_.ss_family = AF_INET;
((struct sockaddr_in *)&address_)->sin_addr = ((struct sockaddr_in *) host_info->ai_addr)->sin_addr;
}
else if(host_info->ai_family == AF_INET6 )
{
struct sockaddr_in6 adr;
memset(&adr,0,sizeof(struct sockaddr_in6));
adr.sin6_family = AF_INET6;
size_ = sizeof(struct sockaddr_in6);
address_.ss_family = AF_INET6;
((struct sockaddr_in6 *)&address_)->sin6_addr = ((struct sockaddr_in6 *) host_info->ai_addr)->sin6_addr;
}
freeaddrinfo(host_info);
return false;
parse_addr() is the method that is called from the constructor of ip_address class.
At the moment this code only works for the IPV4. In fact as I add this call ip_address ip_v6("::", 0 , AF_INET6 , SOCK_STREAM);to the main() it gives Segmentation fault.
Do you see anything wrong in this code?
Then I have a more generic question: could this be a clever solution to handle this problem? If you have any suggestions please let me know.
Thanks a lot.
in code :
if ((host = (struct hostent*) gethostbyname(address) ) == 0) // address is a string
I've got warning when cross compiling (generic arm architecture) on 4.5.x gcc :
(.text+0x1558): warning: gethostbyname is obsolescent, use getnameinfo() instead.
getnameinfo is:
int WSAAPI getnameinfo(
__in const struct sockaddr FAR *sa,
__in socklen_t salen,
__out char FAR *host,
__in DWORD hostlen,
__out char FAR *serv,
__in DWORD servlen,
__in int flags
);
And it got more parameters... And I'm confused with it, I just need it work as gethostbyname were working. What parameter to pass to keep it simple stupid as it was with gethostbyname?
Finally here is my try:
struct sockaddr_in servAddr;
struct hostent *host; /* Structure containing host information */
/* open socket */
if ((handle = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
return LILI_ERROR;
memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = inet_addr(address.ptr());
servAddr.sin_port = htons(port);
char servInfo[NI_MAXSERV];
if ( ( host = (hostent*) getnameinfo(
(struct sockaddr *) &servAddr
,sizeof (struct sockaddr)
,address.ptr(), address.size()
,servInfo, NI_MAXSERV
,NI_NUMERICHOST | NI_NUMERICSERV ) ) == 0)
return LILI_ERROR;
if (::connect(handle, (struct sockaddr *) &servAddr, sizeof(servAddr)) < 0)
return LILI_ERROR;
It compiles well and no segmentation fault on start up but I can't connect my server with it :(
gethostbyname() does a name→IP lookup. It should be replaced with getaddrinfo(), which can do the same.
This means the warning is completely wrong. getnameinfo() is the replacement of gethostbyaddr(), both for IP→name lookups. The reverse.
name→IP: gethostbyname(), getaddrinfo()
IP→name: gethostbyaddr(), getnameinfo()
The newer functions can do more: they handle IPv6 and can translate strings like 'http' to 80 (port). In the future they can also determine if e.g. TCP should be used for the service in question or SCTP. The interface is ready.
Beej's explains it pretty good. gethostbyname() does not works well with IPV6 and thus you should use getnameinfo() instead. All you have to do is to fill in the required informations, i.e.
getnameinfo(
&sa, // Pointer to your struct sockaddr
sizeof sa, // Size of this struct
host, // Pointer to hostname string
sizeof host, // Hostname string buffer length
service, // Pointer to service name string
sizeof service, // Service name string buffer length
0 // No flags given
);
Edit: After some research, I've found that
getnameinfo(&sa, sizeof(sa), hostname, size_hostname, NULL, 0, 0);
should be sufficient.
Edit #2 I've noticed you are trying to use the return value of getnameinfo as hostname. But that is not correct, the hostname is saved within the provided host pointer. The return value indicates whether the operation was sufficient. Also have a look at the man page.
I am currently working on an IPv6 class and use inet_pton to retrieve the actual binary representation of the IP from a string i.e.:
AdressV6::AdressV6(const String & _ip)
{
int result = inet_pton(AF_INET6, _ip.c_str(), &(m_nativeAdress));
if(result <= 0)
//throw...
//How can I retrieve the sope ID from that?
}
Is there a common way to do that? Do you just manually parse the string and look for the "%" that does not sound very bullet proof :(
Thank you!
I tried manual parsing for now which seems to work. Still, if there is a better way please let me know:
//retrieve scope ID
uint32 scopeId = 0;
size_t pos = _ip.find("%");
if(pos != String::npos)
{
String theId = _ip.substr(pos+1);
scopeId = atoi(theId.c_str());
}
m_scopeId = scopeId;
On BSD and BSD based systems (this includes MacOS X for example), the scope ID is embedded into the address itself for link local addresses as the second 16 bit word. Please refer to the FreeBSD Handbook and search for "8.1.1.3 Scope Index" (without the quotes).
So assuming that intf1 has scope ID 1 and intf2 has scope ID 2, inet_pton() will convert the strings as follows on these platforms:
"fe80::1234%intf1" -> fe80:1::1234
"fe80::1234%intf2" -> fe80:2::1234
"fe80::1234" -> fe80::1234
The last address is simply unscoped and thus cannot be really used for sending out data.
Please note that this is non-standard; inet_pton() does not work that way on Linux or Windows based systems. However, I think even on Linux and Windows based systems, inet_pton() allows a scope ID at the end, it will simply ignore it, though.
For non-link-local address, this trick doesn't work, of course, yet those addresses are usually not scoped. They can be scoped, but usually every interface has an own, unique interface IPv6 address, based on its interface identifier (even if you use DHCPv6, in which case it has a DHCP address assigned by the DHCP server, as well as the auto generated IPv6 interface address, unless this auto generation has been forbidden).
The struct sockaddr_in6 structure has a field for the scope ID but the RFC that defines this field (RFC 2553 - Section 3.3) does not really give much detail how this field is to be interpreted. It only says:
The mapping of sin6_scope_id to an interface or set of interfaces is
left to implementation and future specifications on the subject of
site identifiers.
So this field is entirely implementation specific.
If you want this field to be filled in correctly, and your code should be as cross-platform as possible, you should use getaddrinfo():
struct addrinfo hints;
struct addrinfo * result;
memset(&hints, 0, sizeof(hints));
// AI_NUMERICHOST prevents usage of DNS servers,
// it tells getaddrinfo that the input string is a numeric IP address.
hints.flags = AI_NUMERICHOST;
if (getaddrinfo("fe80::1234%intf1", NULL, &hints, &result) == 0) {
// result->ai_addr claims to be a pointer to struct sockaddr,
// in fact it will be a pointer to a struct sockaddr_in6 in our case.
struct sockaddr_in6 * so = (struct sockaddr_in6 *)result->ai_addr;
// It will be prefilled like this:
//
// so->sin6_family ==> AF_INET6;
// so->sin6_port ==> 0
// so->sin6_flowinfo ==> 0
// so->sin6_addr ==> fe80::1234
// so->sin6_scope_id ==> "intf1" as scope ID
// Do something with that sockaddr,
// e.g. set a port number and connect a socket to that address.
freeaddrinfo(result);
}
One extra tip: If you want to use the returned getaddrinfo() for a server socket (a socket that you want to bind locally and then call accept() on it), you should also set the passive flag:
hints.flags = AI_NUMERICHOST | AI_PASSIVE;
Not that it will play a role in most case but that is the correct way of using getaddrinfo().
inet_pton() does not support scope IDs. I don't know about other platforms, but on Windows you can use RtlIpv6StringToAddressEx() instead.
inet_pton() semi-supports scope identifiers, the scope is that it will not raise an error when parsing an address with one. The major limitation is that the parameter to the call is a struct in6_addr which does not contain a field for the scope identifier, the super structure struct sockaddr_in6 is required for that.
Easy way forward is to wrap getnameinfo() and getaddrinfo() with struct sockaddr parameters for convenience. For example,
socklen_t
sockaddr_len (
const struct sockaddr* sa
)
{
socklen_t sa_len;
switch (sa->sa_family) {
case AF_INET: sa_len = sizeof(struct sockaddr_in); break;
case AF_INET6: sa_len = sizeof(struct sockaddr_in6); break;
default: sa_len = 0; break;
}
return sa_len;
}
int
sockaddr_ntop (
const struct sockaddr* restrict sa,
char* restrict host,
size_t hostlen
)
{
return getnameinfo (sa, sockaddr_len (sa),
host, hostlen,
NULL, 0,
NI_NUMERICHOST);
}
int
sockaddr_pton (
const char* restrict src,
struct sockaddr* restrict dst /* will error on wrong size */
)
{
struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM, /* not really */
.ai_protocol = IPPROTO_TCP, /* not really */
.ai_flags = AI_NUMERICHOST
}, *result = NULL;
const int status = getaddrinfo (src, NULL, &hints, &result);
if (0 == status) {
memcpy (dst, result->ai_addr, result->ai_addrlen);
freeaddrinfo (result);
return 1;
}
return 0;
}
To answer the original premise but given a struct sockaddr, an additional API may be warranted, for example:
uint32_t
sockaddr_scope_id (
const struct sockaddr* sa
)
{
uint32_t scope_id;
if (AF_INET6 == sa->sa_family) {
struct sockaddr_in6 s6;
memcpy (&s6, sa, sizeof(s6));
scope_id = s6.sin6_scope_id;
} else
scope_id = 0;
return scope_id;
}
I don't normally go to stackoverflow for sigsegv errors, but I have done all I can with my debugger at the moment.
The segmentation fault error is thrown following the completion of the function. Any ideas what I'm overlooking? I suspect that it is due to the casting of the sockaddr to the sockaddr_in, but I am unable to find any mistakes there. (Removing that line gets rid of the seg fault -- but I know that may not be the root cause here).
// basic setup
int sockfd;
char str[INET_ADDRSTRLEN];
sockaddr* sa;
socklen_t* sl;
struct addrinfo hints, *servinfo, *p;
int rv;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
// return string
string foundIP;
// setup the struct for a connection with selected IP
if ((rv = getaddrinfo("4.2.2.1", NULL, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return "1";
}
// loop through all the results and make a socket
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("talker: socket");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "talker: failed to bind socket\n");
return "2";
}
// connect the UDP socket to something
connect(sockfd, p->ai_addr, p->ai_addrlen); // we need to connect to get the systems local IP
// get information on the local IP from the socket we created
getsockname(sockfd, sa, sl);
// convert the sockaddr to a sockaddr_in via casting
struct sockaddr_in *sa_ipv4 = (struct sockaddr_in *)sa;
// get the IP from the sockaddr_in and print it
inet_ntop(AF_INET, &(sa_ipv4->sin_addr), str, INET_ADDRSTRLEN);
printf("%s\n", str);
// return the IP
return foundIP;
}
It doesn't look like you ever initialize the pointer sa to point at a valid sockaddr (or sockaddr_in) object.
If you replace
sockaddr* sa;
with
sockaddr addr;
and replace all uses of sa with &addr you should be in better shape.
The same is also true of sl. At least according to the documentation for my getsockname the socklen_t* parameter needs to point at a valid socklen_t object initialized to the size in bytes of the address buffer.
E.g.
socklen_t slen = sizeof addr;
and use &slen instead of sl.
It looks to me like you don't ever set up the sa pointer to actually point at anything. Commenting out the line "struct sockaddr_in *sa_ipv4 = (struct sockaddr_in *)sa;" should cause a compile error, so I guess I can see how not being able to compile your program would also cause it to not segfault - hard for a non-existent binary to crash :)
just to add, valgrind is pretty useful in detecting uninitialized pointers or for that matter any pointer related errors.
You did not allocate sa before passing it to getsockname, so you effectively passed in some garbage pointer value there. It needs to be:
struct sockaddr_in sa;
socklen_t sl;
...
getsockname(sockfd, (struct sockaddr*)&sa, &sl);
...
I've been using getaddrinfo for looking up socket addresses for basic socket commands. Recently, though, the addresses it returns to me are for bogus IP addresses, which I have found using inet_ntop. I've tried my code, as well as that provided in Beej's Guide, and they both produce the same results. Here's the code:
struct addrinfo hints, *info;
int status;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if(status = getaddrinfo(address, port, &hints, &info)) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
}
char ip4[INET_ADDRSTRLEN];
inet_ntop(AF_INET, info->ai_addr, ip4, INET_ADDRSTRLEN);
std::cout<<ip4<<std::endl;
No matter what address I use, it always gives me an IP of the form
16.2.x.y
where 256*x + y is equal to the port number. Has anyone ever seen this happen, or can anyone guess why it's giving me this?
Shouldn't you be passing
((sockaddr_in const *)info->ai_addr)->sin_addr
to inet_ntop?