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 *.
Related
I am trying to understand the following casting from this code
char out_packet_buffer[4500] ;
struct ip6_hdr *iphdr ;
iphdr = (struct ip6_hdr *) &out_packet_buffer[0];
Is my understanding correct that the member variables of the struct iphdr are stored in char array out_packet_buffer? Later in the code, out_packet_buffer is never used. Instead, iphdr is memcpyied to an uint8_t memory location (ether_frame). But iphdr is not uint8_t.
I'd appreciate any guidance for me to understand what is happening here.
Thanks
Is my understanding correct that the member variables of the struct iphdr are stored in char array out_packet_buffer?
Kind of. What happens in this casting is that we start "looking" at the memory chunk that starts from &out_packet_buffer[0] (or just out_packet_buffer) as a struct ip6_hdr instead of a char[].
Any later usage of iphdr is using the same memory, but splits it into struct ip6_hdr members instead of into char
As #Christian Gibbons said, I also think this violates strict aliasing which is UB
It looks like the code is preparing a packet for transmission over a network. The packet will consist of a header and a payload. The whole packet is, presumably, stored in out_packet_buffer. The ip6_header structure is the first few bytes of this, the data payload follows after. Using a structure for the header makes the code more readable but there’ll probably be a "structure order to network order" function just before it’s sent to a socket.
In any case, the data packet is just a sequence of bytes, so casting it to any 8-bit type is feasible
I am reading Beej's guide, and he talks about the different structers programmers created.
He says we can pass sockaddr_storage/in6/in to addrinfo, and it will be converted to sockaddr,
but how exactly is it possible? sockaddr is 16 bytes, while sockaddr_in6 is 28 bytes.
I read a little part of RFC 3493:
Notice that the sockaddr_in6 structure will normally be larger than
the generic sockaddr structure. On many existing implementations the
sizeof(struct sockaddr_in) equals sizeof(struct sockaddr), with both
being 16 bytes. Any existing code that makes this assumption needs
to be examined carefully when converting to IPv6.
But it doesn't explain what happens when sockaddr_in6 is casted to sockaddr.
Remember that all functions that take a struct sockaddr pointer, also takes the size of the structure. Together with the meta-data on the actual socket, it's easy for the system to know what kind of structure you're passing.
Also note that it's always pointers to the address structures being passed around, not actual structures which would not work. So you never to e.g.
(struct sockaddr) a_in6_sockaddr
you do
(struct sockaddr *) &a_in6_sockaddr
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.
I am writing a network program where, in the server part, I want to accept connections from multiple clients using a listening socket. So I declare an array of address structs like this:
struct sockaddr_in* client;
which I create using malloc and later on, to accept connections I type:
newsock = accept(fd_skt, (struct sockaddr *)&client[i], &(sizeof(client[i])));
and there I get "lvalue required as unary '&' operand" from the compiler. Can anyone figure out what I have done wrong?
Yes, you can't take the address of something that isn't an lvalue, that is an object with an address. The result of the sizeof operator is just a value, it isn't an object with an address.
You need to create a local variable so that you can take its address.
E.g.
socklen_t addrlen = sizeof client[i];
newsock = accept(fd_skt, (struct sockaddr *)&client[i], &addrlen));
As an aside, struct sockaddr_in* client; declares a pointer, not an array. To use client as an array you need to assign it to a dynamically allocated array at some point before the call to accept. I assume that this is what you are doing when you say "I create using malloc".
Alternatively you could actually declare client as an array.
struct sockaddr_in client[MAX_CLIENTS];
Charles' answer is correct, but one way to get around this kind of obnoxious function interface that requires a pointer to a value you plan to just throw away is to use compound literals:
newsock = accept(fd_skt, (struct sockaddr *)&client[i], (socklen_t[]){sizeof client[0]});
I'm learning socket programming in C & downloaded a simple tcp server source file. I understand every line except the 2nd parameters in these functions:
accept(socket_fd, (struct sockaddr *)&client, &length);
bind(socket_fd, (struct sockaddr *)&server, length);
The accept + bind functions are the typical functions in "sys/types.h" & "sys/socket.h", and the man page describes it as a pointer to a struct, but I still can't understand what's really going on here.
Can someone please explain what is going on in the second parameter? The brackets, pointer and address symbols are confusing me in the same expression.
Thanks in advance!
The & symbol essentially means "get the address of the value/object". The (struct sockaddr *) is a cast. It tells the compiler that you want to treat the address as a pointer to a sockaddr structure. So together, it is telling the compiler that client can be treated as a sockaddr structure and to pass the address of it to the function. In the case of the accept function, the address of the connecting socket will be stored in the given structure.
What's happening is that accept and bind function are expecting struct sockaddr pointers, and your client and server variables are probably declared as (struct sockaddr *). So, in order to avoid a warning in C or a compiler error in C++, you need an explicit cast which you do by putting the expression:
(struct sockaddr *)
Before your parameter.
And you need the ampersand, because client and server are not pointers. They were probably declared like:
struct sockaddr_in client, server;
It's also worth mentioning that the structures are closely related. Take a look at the picture from Stevens UnP.