Checking the address family in socket programming - c

what will be the output of the following code :
char peer_ip[16];
inet_pton(AF_INET,"127.0.0.1",peer_ip);
now I have peer_ip in network form. How can I check what is the address family ??? I cannot use inet_ntop now. Is there any way ?? Will getaddrinfo work in this case ???

You can't—inet_pton gives you either a struct in_addr (for AF_INET) or a struct in6_addr (for AF_INET6), depending on what address family you pass in. If you consider these structures to be binary blobs of memory, there's no way you can recover the address family from them, you just have to keep track of what type of binary blob you have.
You should really be using a struct in_addr, not a char[16] as the value passed into inet_pton:
struct in_addr peer_ip;
inet_pton(AF_INET, "127.0.0.1", &peer_ip);

You have to go higher up and use getaddrinfo instead of inet_pton (which doesn't handle IPv6 scopes) and instead of opaque buffers use struct sockaddr_storage and struct sockaddr pointers then you can immediately determine the family with ss.ss_family or sa.sa_family as appropriate.

Related

why Network programs store IP addresses in the IP address structure

I'm a beginner in C, my textbooks cover some network programming in C, and states that Network programs store IP addresses in the IP address structure
/* Internet address structure */
struct in_addr {
unsigned int s_addr; /* Network byte order (big-endian) */
};
I'm confused, can't we just store a 32 bit integer?
in_addr represents an IPv4 address, which can indeed fit in a 32bit integer.
But, there are other types of socket addresses that cannot, such as IPv6 addresses.
Each type of socket address uses its own struct type:
in_addr for IPv4
in6_addr for IPv6
char[] for UNIX paths
etc
Usually wrapped inside a corresponding sockaddr struct:
sockaddr_in for IPv4
sockaddr_in6 for IPv6
sockaddr_un for UNIX
etc
Which is what you use with socket APIs like bind(), connect(), accept(), sendto(), recvfrom(), etc.
Very rarely would you ever need to use something like in_addr directly by itself. Typically you would use it in conjunction with an API that requires an IPv4 address to be passed via the in_addr struct.

How to know if sockaddr_storage usage at runtime

A runtime I'ld like to to know if an stack defined struct sockaddr_storage was assigned to any IP address (IPv4 or IPv6)
Any idea?
Check the ss_family field. It will be AF_INET for an IPv4 address, or AF_INET6 for an IPv6 address. You can then type-cast the sockaddr_storage accordingly to either sockaddr_in or sockaddr_in6 in order to access the sockaddr_in::sin_addr or sockaddr_in6::sin6_addr field, respectively, to make sure it is not set to zeros.

How do I get sin6_addr from an addrinfo?

My addrinfo pointer looks like this-
struct addrinfo hint, *res = NULL;
I then call get addrinfo.
hint.ai_family = AF_UNSPEC;
ret = getaddrinfo(curhost, NULL, &hint, &res);
curhost is a character array. Doing
saddrv6.sin6_addr=*(res->ai_addr).sin6_addr
is giving me an error that says
request for member 'sin6_addr' in something not a structure or union. saddrv6 is a sockaddr_in6 struct. What is a good way to fill sin6_addr from info that I already have in res? New to C programming here .
The specific error you're getting is because in:
*(res->ai_addr).sin6_addr
The . operator binds more tightly than *. You could change it to:
(*res->ai_addr).sin6_addr
which is probably what you meant, but the better way is to use the -> operator:
res->ai_addr->sin6_addr
However, that still doesn't work because ai_addr has the useless opaque type struct sockaddr *, not struct sockaddr_in6 *. To fix this you need to cast it to a pointer to the type it actually points to:
((struct sockaddr_in6 *)res->ai_addr)->sin6_addr
At this point your code should work. However, ultimately the ai_addr member of struct addrinfo is not really meant to be accessed directly but rather used abstractly and just passed to functions like connect, bind, sendto, recvfrom, etc. At this point we're talking about a matter of style and good programming practices rather than correctness per the language, though.
Note that if you just want to get the IPv6 address for the sake of printing it as a string, the getnameinfo function with the NI_NUMERICHOST flag lets you do this in an abstract way without having to poke through the opaque struct sockaddr *.

convert ip address found at s_addr member of in_addr to textual ip address

I have two programs that communicate to each other and one piece of data sent is the IP address stored as an unsigned long integer. It is retrieved in one program via the sockaddr_in struct when it accepts an outside connection. I then stored the IP address from the sin_addr member of the struct and the s_addr sub-member which returns the unsigned long version of the IP address.
I look here http://www.retran.com/beej/inet_ntoaman.html and it suggests that I can get the textual (human readable) version of the IP address by
using the inet_ntoa function, but it expects the in_addr struct as a parameter.
Is there a function I can use that returns the textual IP address using an unsigned long integer as the parameter?

Whats the addrlen field in recvfrom() used for?

I'm using recvfrom in my program to get DGRAM data from a server I specify in src_addr. However, I'm not sure why I need to initialize and pass in addrlen.
I read the man page and I didn't really understand what it's getting at.
If src_addr is not NULL, and the underlying protocol provides the source address, this source address is filled in. When
src_addr is NULL, nothing is filled in; in this case, addrlen is not
used, and should also be NULL. The argument addrlen is a value-result argument, which the caller should initialize before the
call to the size of the buffer associated with src_addr, and
modified on return to indicate the actual size of the source address. The returned address is truncated if the buffer provided is too small;
in this case, addrlen will return a value greater than was supplied to the call.
I'm guessing that it's got something to do with src_addr being ipv4 or ipv6. Is this correct?
Thanks!
Maybe there is a missinterpretation from your side. Talking about:
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
src_addr is not used to hand in the adress that you would like to listen to, but rather a storage location provided by you to get the actual source address handed out.
Thus if you set src_addr to NULL because youre not interested in the address at all, you don't have to care about addrlen as it won't get used anyway.
If on the other hand you want to be informed about the source address, you not only have to provide a storage location, but also tell how big the storage location you provided is.
Thats why you should initialize *addr_len to the buffer size you allocated.
After your call the value pointed to by addrlen will inform you about how much (if any) of the space you allocated to store the source address got actually filled with data.
About sizes
The whole hassle with struct sockaddr and passing sizes back and forth has to do with the fact that even thoug they're most heavily used in networking sockets were intended to be much more general concept.
Think about unix domain sockets as an an example as they are implemented via the filesystem they require an adressing scheme totaly different from that known from IP based networking. The type of sockaddr used here is:
struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
sun_path[UNIX_PATH_MAX]; /* pathname */
};
Compare this to the struct used in IP based networking:
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};
it should be clear both don't have too much in common.
sockets were designed to be able to fit both cases.
ssize_t recvfrom(int socket, void *buffer, size_t length, int flags,
struct sockaddr *address, socklen_t *address_len);`
The address_len argument specifies the length of the address structure i.e. the number of bytes to use from the start address indicated at address(start address of memory location + number of bytes from the start address that hold the value)
The structure is defined in /usr/include/bits/socket.h
/* Structure describing a generic socket address. */
struct sockaddr
{
__SOCKADDR_COMMON (sa_); /* Common data: address family and length. */
char sa_data[14]; /* Address data. */
};
Thus the sa_data field holds the address data (start address of the data) whose length is indicated by the address_len argument.
... whenever a function says it takes a struct sockaddr* you can cast your
struct sockaddr_in*, struct sockaddr_in6*, or struct sockadd_storage*
to that type with ease and safety.
Therefore, as indicated in the man page and #WhozCraig in the comment to your question, this field is updated with the actual size when the method returns.
More information
recvfrom
Beej's Guide to Network Programming - struct sockaddr and pals

Resources