Code Comments
Programming Forum and web based access to our favorite programming groups.
Hi,
In the program below:
#include <stdio.h>
#include <errno.h>
#include <netdb.h>
#include <sys/socket.h>
int main(int argc, char **argv)
{
struct addrinfo hints, *res;
char ptr_ip[20];
int r;
memset(&hints, 0, sizeof(hints));
hints.ai_flags=AI_CANONNAME;
hints.ai_family=AF_INET;
if (r=getaddrinfo(argv[1], "domain", &hints, &res ))
{
printf("%s\n", gai_strerror(r));
exit(1);
}
inet_ntop(res->ai_family, res->ai_addr->sa_data, ptr_ip,
res->ai_addrlen);
printf("%s %s\n",res->ai_canonname, ptr_ip);
}
opmxopge@sgbe2 : /tmp > a.out sgbe2
sgbe2 0.53.10.95
opmxopge@sgbe2 : /tmp > ping -s sgbe2
PING sgbe2: 56 data bytes
64 bytes from sgbe2 (10.95.26.235): icmp_seq=0. time=0. ms
^C
----sgbe2 PING Statistics----
1 packets transmitted, 1 packets received, 0% packet loss
round-trip (ms) min/avg/max = 0/0/0
opmxopge@sgbe2 : /tmp >
Why is 'a.out' output '0.53.10.95' ?
Thanks in advance,
Jose Luis.
Post Follow-up to this messageHola Jose-Luis!
> In the program below:
[snip]
> Why is 'a.out' output '0.53.10.95' ?
inet_pton() expects as second parameter a pointer to the field sin_addr
of a sockaddr_in structure. However, the res->ai_addr is a pointer to a
generic socket address structure (a struct sockaddr).
Thus, in order to use inet_ntop() with res->ai_addr, you must first cast
the res->ai_addr pointer into a sockaddr_in struct pointer, and then
pass the address of the corresponding sin_addr field:
int
main()
{
struct sockaddr_in* sin;
// ...
// call getaddrinfo
sin = (struct sockaddr_in*) res->ai_addr;
inet_ntop(res->ai_addr->sa_family, &sin->sin_addr, ptr_ip, sizeof(ptr_ip));
printf("%s %s\n",res->ai_canonname, ptr_ip);
}
HTH,
Loic.
Post Follow-up to this messagejose_luis_fdez_diaz_news@yahoo.es wrote:
> struct addrinfo hints, *res;
...
> if (r=getaddrinfo(argv[1], "domain", &hints, &res ))
...
> inet_ntop(res->ai_family, res->ai_addr->sa_data, ptr_ip, res->ai_addrlen);[
/color]
...
>Why is 'a.out' output '0.53.10.95' ?
This is the *same* problem that you had previously! You *must*
provide the correct arguments to these functions, otherwise the
results are garbage. The man page for inet_ntop(3) states,
const char *inet_ntop(int af, const void *src,
char *dst, socklen_t cnt);
..
src points to a struct in_addr ...
but what you are providing is *not* a a pointer to struct
in_addr, and will not magically become one until *you* provide
the magic.
And a bit of magic is exactly what it needs, because indeed the
data is buried in that struct addrinfo, though figuring out
where it is and how to get it can be "fun" (spelled
"frustrating" the first time you go through this).
The man page for getaddrinfo(3) says a struct addrinfo looks
like this:
struct addrinfo {
..
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};
So res->ai_addr is a pointer to struct sockaddr. A quick
grep though the /usr/include headers comes up with a declaration
for struct sockaddr which looks like this:
struct sockaddr {
unsigned short int sa_prefix;
char sa_data[14]; /* Address data. */
};
Note that the above is a "reconstructed" declaration. The
actual declaration in the headers may (or may not) include
typedefs and #define'd macros. The above one example of what it
all boils down to.
There is no struct in_addr member. Instead there is a character
array that it says contains the address data... apparently as
raw data. You can grep for "struct in_addr" and what you'll
find is that it is a member of a struct sockaddr_in, and that a
struct sockaddr_in is purposely declared to be exactly the same
size as a struct sockaddr. That is done to allow casting a
pointer to the same data as either a struct sockaddr or a struct
sockaddr_in. The data can then be accessed in two different
ways, depending on which cast is used.
A struct sockaddr_in looks something like this (again, this is
"reconstructed", and the actual details will very likely be
different):
/* Structure describing an Internet socket address. */
struct sockaddr_in {
unsigned short int sa_prefix;
unsigned short int sin_port; /* Port number. */
struct in_addr sin_addr; /* Internet address. */
/* Pad to size of `struct sockaddr'. */
unsigned char sin_zero[sizeof (struct sockaddr) -
sizeof (unsigned short int) -
sizeof (unsigned short int) -
sizeof (struct in_addr)];
};
Comparing the two different structs gives you the answer to why
your results were so odd. You are two bytes off! You've
included the data for the port number as part of the IP address.
The trick is knowing just exactly how far into the
res->ai_addr->sa_data one has to offset in order to find the IP
address, and that is where the sockaddr_in structure is used.
(If you assumed it is always 2 bytes, as the above example
structs indicated, it will probably work just fine on almost
everything... and then you'll want to run it on a platform that
for whatever reason allocates 4 bytes for the port number, and
your the code would fail. So don't do that!)
Instead, have two pointers to the same data, with one being
a pointer to struct sockaddr and one to struct sockaddr_in.
You already have res->ai_addr, which is a pointer to a sockaddr
structure, so just do something like this,
struct sockaddr_in *si = (struct sockaddr_in) &res->ai_addr;
inet_ntop(res->ai_family, &si->sin_addr.s_addr, ptr_ip, res->ai_addrlen);
--
Floyd L. Davidson <http://web.newsguy.com/floyd_davidson>
Ukpeagvik (Barrow, Alaska) floyd@barrow.com
Post Follow-up to this messageIn article <877jizq0xl.fld@barrow.com>, floyd@barrow.com (Floyd L. Davidson) wrote: [snip lengthy explanation] > Instead, have two pointers to the same data, with one being > a pointer to struct sockaddr and one to struct sockaddr_in. > You already have res->ai_addr, which is a pointer to a sockaddr > structure, so just do something like this, > > struct sockaddr_in *si = (struct sockaddr_in) &res->ai_addr; > inet_ntop(res->ai_family, &si->sin_addr.s_addr, ptr_ip, res->ai_addrlen); Almost right. The 4th parameter is wrong; it should be the size of the destination buffer: inet_ntop(res->ai_family, &si->sin_addr.s_addr, ptr_ip, sizeof(ptr_ip)); (Note that the OP declared ptr_ip to be an array of char, not a pointer.)
Post Follow-up to this message"Wayne C. Morris" <wayne.morris@this.is.invalid> wrote: >In article <877jizq0xl.fld@barrow.com>, > floyd@barrow.com (Floyd L. Davidson) wrote: > >[snip lengthy explanation] > > >Almost right. The 4th parameter is wrong; it should be the size of the >destination buffer: > > inet_ntop(res->ai_family, &si->sin_addr.s_addr, ptr_ip, sizeof(ptr_ip)); > >(Note that the OP declared ptr_ip to be an array of char, not a pointer.) Technically that will be okay though, as long as sizeof(ptr_up) is larger than res->ai_addrlen, which it is in this case. -- Floyd L. Davidson <http://web.newsguy.com/floyd_davidson> Ukpeagvik (Barrow, Alaska) floyd@barrow.com
Post Follow-up to this message
Thanks for your long a clear explanation.
Regards,
Jose Luis.
PD. Alaska is a beautiful place. I think you are lucky to live there.
Floyd L. Davidson wrote:
> jose_luis_fdez_diaz_news@yahoo.es wrote:
> ...
> ...
>
res->ai_addrlen);
> ...
>
> This is the *same* problem that you had previously! You *must*
> provide the correct arguments to these functions, otherwise the
> results are garbage. The man page for inet_ntop(3) states,
>
> const char *inet_ntop(int af, const void *src,
> char *dst, socklen_t cnt);
>
> ...
>
> src points to a struct in_addr ...
>
> but what you are providing is *not* a a pointer to struct
> in_addr, and will not magically become one until *you* provide
> the magic.
>
> And a bit of magic is exactly what it needs, because indeed the
> data is buried in that struct addrinfo, though figuring out
> where it is and how to get it can be "fun" (spelled
> "frustrating" the first time you go through this).
>
> The man page for getaddrinfo(3) says a struct addrinfo looks
> like this:
>
> struct addrinfo {
> ...
> struct sockaddr *ai_addr;
> char *ai_canonname;
> struct addrinfo *ai_next;
> };
>
> So res->ai_addr is a pointer to struct sockaddr. A quick
> grep though the /usr/include headers comes up with a declaration
> for struct sockaddr which looks like this:
>
> struct sockaddr {
> unsigned short int sa_prefix;
> char sa_data[14]; /* Address data. */
> };
>
> Note that the above is a "reconstructed" declaration. The
> actual declaration in the headers may (or may not) include
> typedefs and #define'd macros. The above one example of what it
> all boils down to.
>
> There is no struct in_addr member. Instead there is a character
> array that it says contains the address data... apparently as
> raw data. You can grep for "struct in_addr" and what you'll
> find is that it is a member of a struct sockaddr_in, and that a
> struct sockaddr_in is purposely declared to be exactly the same
> size as a struct sockaddr. That is done to allow casting a
> pointer to the same data as either a struct sockaddr or a struct
> sockaddr_in. The data can then be accessed in two different
> ways, depending on which cast is used.
>
> A struct sockaddr_in looks something like this (again, this is
> "reconstructed", and the actual details will very likely be
> different):
>
> /* Structure describing an Internet socket address. */
> struct sockaddr_in {
> unsigned short int sa_prefix;
> unsigned short int sin_port; /* Port number. */
> struct in_addr sin_addr; /* Internet address. */
>
> /* Pad to size of `struct sockaddr'. */
> unsigned char sin_zero[sizeof (struct sockaddr) -
> sizeof (unsigned short int) -
> sizeof (unsigned short int) -
> sizeof (struct in_addr)];
> };
>
> Comparing the two different structs gives you the answer to why
> your results were so odd. You are two bytes off! You've
> included the data for the port number as part of the IP address.
>
> The trick is knowing just exactly how far into the
> res->ai_addr->sa_data one has to offset in order to find the IP
> address, and that is where the sockaddr_in structure is used.
> (If you assumed it is always 2 bytes, as the above example
> structs indicated, it will probably work just fine on almost
> everything... and then you'll want to run it on a platform that
> for whatever reason allocates 4 bytes for the port number, and
> your the code would fail. So don't do that!)
>
> Instead, have two pointers to the same data, with one being
> a pointer to struct sockaddr and one to struct sockaddr_in.
> You already have res->ai_addr, which is a pointer to a sockaddr
> structure, so just do something like this,
>
> struct sockaddr_in *si = (struct sockaddr_in) &res->ai_addr;
> inet_ntop(res->ai_family, &si->sin_addr.s_addr, ptr_ip,
res->ai_addrlen);
>
> --
> Floyd L. Davidson <http://web.newsguy.com/floyd_davidson>
> Ukpeagvik (Barrow, Alaska) floyd@barrow.com
Post Follow-up to this message
Show a Printable Version
Email This Page to Someone!
Receive updates to this thread
Powered by vBulletin
Copyright 2000-2006 Jelsoft Enterprises Limited.