Writing into h_addr_list - c

I am writing a wrapper for gethostbyname() function, which, before returning a pointer to the hostent structure, should allow for executing getaddrinfo() and eventually mapping the returned IPv6 structures to the IPv4 ones. However, I am having a problem with casting the returned in_addr structures properly in order to populate the h_addr_list of hostent addresses - in case the family identified equals AF_INET, of course.
I am basically doing the following:
strcpy(&s[0],name);
hp->h_name = strdup(s);
hp->h_addrtype = AF_INET;
hp->h_length = sizeof(struct in_addr);
struct sockaddr *sa= res->ai_addr;
// Segmentation fault:
memcpy(hp->h_addr_list[0], &(((struct sockaddr_in *)sa)->sin_addr.s_addr), hp->h_length);
Any hints? I haven't written any C code in a long time, so sorry if I am asking a stupid question. Thanks.

The s_addr member (in e.g. saddr->sin_addr.s_addr) is not a pointer. You have to use the address-of operator to make it a pointer.
And hp->h_addr_list[0] is a pointer, so when you use the address-of operator here, you get the address of that pointer, and will copy to the completely wrong address.

Ok, allocating blocks for the hostent and h_addr_list worked for me, some more context:
hp=(struct hostent *)calloc(1,sizeof(struct hostent));
hp->h_name = strdup(s);
hp->h_aliases = NULL;
hp->h_addrtype = AF_INET;
hp->h_length = sizeof(struct in_addr);
hp->h_addr_list = (char **)calloc(2,sizeof(char *));
hp->h_addr_list[0] = calloc(1,4);
struct sockaddr *sa = res->ai_addr;
memcpy(hp->h_addr_list[0], (char *)&(((struct sockaddr_in *)sa)->sin_addr.s_addr), hp->h_length);

Related

Casting between sockaddr and sockaddr_in6

I've been reading Beej's Guide to Network Programming and in one of his examples, he casts a pointer to struct sockaddr to a struct sockaddr_in6 pointer like shown below.
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";
}
How is this possible, since the sizes of the structs are different?
ai_family and ai_addr are fields of the addrinfo struct, so presumably the code you are quoting had called getaddrinfo() beforehand.
The result of getaddrinfo() is a NULL-terminated linked list of addrinfo structs, where the addrinfo::ai_addr field is a pointer to an allocated memory block that is of sufficient size to hold a socket address of the reported addrinfo::ai_family type. The size of the address is reported in the addrinfo::ai_addrlen field.
For AF_INET, the addrinfo::ai_addr field is pointing at a memory block containing a sockaddr_in struct.
For AF_INET6, the addrinfo::ai_addr field is pointing at a memory block containing a sockaddr_in6 struct.
That is why the type-casts work.
The addrinfo::ai_addr field is declared as struct sockaddr* so it can be passed as-is to the addr parameter of the bind() and connect() functions without type-casting. The addrinfo::ai_addrlen field can be passed as-is to their addrlen parameter.

Safely converting from struct sockaddr to struct sockaddr_storage

I have a function which takes in "struct sockaddr *" as a parameter (let's call this input_address), and then I need to operate on that address, which may be a sockaddr_in or sockaddr_in6, since I support both IPv4 and IPv6.
I'm getting some memory corruption and trying to track it down to it's source, and in the process found some code that seems suspect, so I would like to validate if this is the right way to do things.
struct sockaddr_storage *input_address_storage = (struct sockaddr_storage *) input_address;
struct sockaddr_storage result = [UtilityClass performSomeOperation: *input_address_storage];
At first I thought the cast in the first line was safe, but then in the second line I need to dereference that pointer, which seems like it may be wrong. The reason I am concerned is that it may end up copying memory that is beyond where the original structure is (since sockaddr_in is shorter than sockaddr_in6). I am not sure if this could cause a memory corruption (my guess is no), but nevertheless this code gives me a bad feeling.
I can't change the fact my function takes a "struct sockaddr *", so it seems like it would be difficult to work around this type of code, and yet I want to avoid copying from a memory location where I shouldn't be.
If anyone can validate whether what I am doing is wrong, and the best way to fix this, I'd appreciate it.
EDIT: An admin had changed my C tag for C# for some reason. The code I gave is primarily C, with one function call from objective C that doesn't really matter. That call could have been C.
The problem with your approach is that you are converting an existing struct sockaddr* into a struct sockaddr_storage*. Imagine what happens if the original was a ``struct sockaddr_in. Sincesizeof(struct sockaddr_in) < sizeof(struct sockaddr_storage)`, the memory-sanitizer complains of unbound memory reference.
struct sockaddr_storage is essentially a container to contain either your struct sockaddr_in or struct sockaddr_in6.
Hence, it is useful when you want to pass in a struct sockaddr* object but want to allocate enough memory for both sockaddr_in and sockaddr_in6.
A good example is the recvfrom(3) call:
ssize_t recvfrom(int socket, void *restrict buffer, size_t length,
int flags, struct sockaddr *restrict address,
socklen_t *restrict address_len);
Since address requires a struct sockaddr* object, we will construct a struct sockaddr_storage first, and pass it in:
struct sockaddr_storage address;
socklen_t address_length = sizeof(struct sockaddr_storage);
ssize_t ret = recvfrom(fd, buffer, buffer_length, 0, (struct sockaddr*)&address, &address_length);
if (address.ss_family == AF_INET) {
DoIpv4Work((struct sockaddr_in*)&address, ...);
} else if (address.ss_family == AF_INET6) {
DoIpv6Work((struct sockaddr_in6*)&address, ...);
}
The difference in your approach and mine is that I allocate a struct sockaddr_storage and then use it as struct sockaddr, but you do the REVERSE, and use a struct sockaddr and then use it as struct sockaddr_storage.

Why do we cast sockaddr_in to sockaddr when calling bind()?

The bind() function accepts a pointer to a sockaddr, but in all examples I've seen, a sockaddr_in structure is used instead, and is cast to sockaddr:
struct sockaddr_in name;
...
if (bind (sock, (struct sockaddr *) &name, sizeof (name)) < 0)
...
I can't wrap my head around why is a sockaddr_in struct used. Why not just prepare and pass a sockaddr?
Is it just convention?
No, it's not just convention.
sockaddr is a generic descriptor for any kind of socket operation, whereas sockaddr_in is a struct specific to IP-based communication (IIRC, "in" stands for "InterNet"). As far as I know, this is a kind of "polymorphism" : the bind() function pretends to take a struct sockaddr *, but in fact, it will assume that the appropriate type of structure is passed in; i. e. one that corresponds to the type of socket you give it as the first argument.
I don't know if its very much relevant for this question, but I would like to provide some extra info which may make the typecaste more understandable as many people who haven't spent much time with C get confused seeing such a typecaste.
I use macOS, so I am taking examples based on header files from my system.
struct sockaddr is defined as follows:
struct sockaddr {
__uint8_t sa_len; /* total length */
sa_family_t sa_family; /* [XSI] address family */
char sa_data[14]; /* [XSI] addr value (actually larger) */
};
struct sockaddr_in is defined as follows:
struct sockaddr_in {
__uint8_t sin_len;
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
Starting from the very basics, a pointer just contains an address. So struct sockaddr * and struct sockaddr_in * are pretty much the same. They both just store an address. Only relevant difference is how compiler treats their objects.
So when you say (struct sockaddr *) &name, you are just tricking the compiler and telling it that this address points to a struct sockaddr type.
So let's say the pointer is pointing to a location 1000. If the struct sockaddr * stores this address, it will consider memory from 1000 to sizeof(struct sockaddr) possessing the members as per the structure definition. If struct sockaddr_in * stores the same address it will consider memory from 1000 to sizeof(struct sockaddr_in).
When you typecasted that pointer, it will consider the same sequence of bytes upto sizeof(struct sockaddr).
struct sockaddr *a = &name; // consider &name = 1000
Now if I access a->sa_len, the compiler would access from location 1000 to sizeof(__uint8_t) which is same bytes size as in case of sockaddr_in. So this should access the same sequence of bytes.
Same pattern is for sa_family.
After that there is a 14 byte character array in struct sockaddr which stores data from in_port_t sin_port (typedef'd 16 bit unsigned integer = 2 bytes ) , struct in_addr sin_addr (simply a 32 bit ipv4 address = 4 bytes) and char sin_zero[8](8 bytes). These 3 add up to make 14 bytes.
Now these three are stored in this 14 bytes character array and we can access any of these three by accessing appropriate indices and typecasting them again.
user529758's answer already explains the reason to do this.
This is because bind can bind other types of sockets than IP sockets, for instance Unix domain sockets, which have sockaddr_un as their type. The address for an AF_INET socket has the host and port as their address, whereas an AF_UNIX socket has a filesystem path.

casting sockaddr_storage as sockaddr_in for inet_ntop

I am trying to cast a sockaddr_storage to a sockadd_in, so that i can print out the source ip address of a datagram packet,
i do not seem to be able to get the cast correct,
struct sockaddr_storage peer_addr;
getnameinfo((struct sockaddr *) &peer_add
peer_addrlen,
hostbuff, sizeof(hostbuff),
NULL, 0, NI_NAMEREQD);
inet_ntop(AF_INET, (((struct sockaddr_in *)peer_addr).sin_addr), ipbuff, INET_ADDRSTRLEN);
when i try to cast the structure to a sockaddr_in, i either get 'cannot convert to pointer', or when i remove the dereferance i get 'conversion to non scaler type requested'.
I have tried alot of combinations and simply don't understand where i am going wrong.
inet_ntop(peer_addr->ss_family, &(((struct sockaddr_in *)peer_addr)->sin_addr), ipbuff, INET_ADDRSTRLEN);
should work. But consider to use getnameinfo() instead, which is the more modern
interface:
char host[NI_MAXHOST];
getnameinfo((struct sockaddr *)peer_addr, peer_addr->ss_len, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
works for both IPv4 and IPv6 addresses.
Update according to the changed types in the question: This is a complete example
that should compile without warnings:
int socket = ...;
struct sockaddr_storage peer_addr;
socklen_t peer_addrlen;
char host[NI_MAXHOST];
ssize_t amount;
char buffer[1000];
amount = recvfrom(socket, buffer, sizeof(buffer), 0, (struct sockaddr *)&peer_addr, &peer_addrlen);
getnameinfo((struct sockaddr *)&peer_addr, peer_addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
Or, using inet_ntop():
char ipbuff[INET_ADDRSTRLEN];
inet_ntop(peer_addr.ss_family, &(((struct sockaddr_in *)&peer_addr)->sin_addr), ipbuff, INET_ADDRSTRLEN);
struct sockaddr_storage * peer_addr;
getnameinfo((struct sockaddr *) &peer_add
peer_addrlen,
hostbuff, sizeof(hostbuff),
NULL, 0, NI_NAMEREQD);
Here you are mixing up stuff.
getnameinfo() indeed takes a struct sockaddr* as its first parameter, but what you try to do here won't work: peer_addr is a struct sockaddr_storage *, you take its address - which is a struct sockaddr_storage **, and try to cast this. That won't work.
I don't know where your peer_addr comes from, but
either it should be a struct sockaddr_storage (I don't think there is a need to have a pointer to a struct sockaddr_storage somewhere)
or it is really a pointer, and you should pass (struct sockaddr *) peer_addr - without the & - to getnameinfo().
Another point: The second parameter for getnameinfo() is supposed to be the "real" size of the address struct you are inputting.

Problem while compiling socket connect code in C

struct hostent *lh = gethostbyname(hostname);
int socketDescriptor = socket(AF_INET,SOCK_STREAM, 0);
sockaddr_in socketInfo;
memset(&socketInfo, 0, sizeof(socketInfo));
socketInfo.sin_family = AF_INET;
socketInfo.sin_addr.s_addr = ((in_addr *)(lh->h_addr))->s_addr;
socketInfo.sin_port = htons(portNumber);
connect(socketDescriptor,&socketInfo,sizeof(socketInfo));
When trying to compile, I get the following error:
error: cannot convert ‘sockaddr_in*’ to ‘const sockaddr*’ for argument ‘2’ to ‘int connect(int, const sockaddr*, socklen_t)’
Things look "by the book", but I am missing something (obviously). What is it?
I think you are missing struct on sockaddr_in socketInfo. So it should be struct sockaddr_in socketInfo.
Also casting socketInfo to struct sockaddr * would be nice.
connect(socketDescriptor,&socketInfo,sizeof(socketInfo));
Should be
connect(socketDescriptor,(struct sockaddr *) &socketInfo,sizeof(socketInfo));
struct addrinfo *server;
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));/*set hints*/
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = 0;
hints.ai_protocol = 0; /* Any protocol */
getaddrinfo(srvname,port,&hints,&server); /*get server ip fit args*/
sid = socket(server->ai_family, server->ai_socktype,server->ai_protocol)
connect(sid,server->ai_addr, server->ai_addrlen)
Those are some codesnipets that will work and that you can start from.
What happens here is that I set up one struct with all the intel and than combine it with some more info to get one nice all pointers fit structure too pass to connect.
hope that helps
The socket interfaces, in addition to being very old, are deliberately broken in this way. The sockaddr_* structs implicitly start with the same members of sockaddr so it's safe to cast them to the "base" type. struct sockaddr also has an sa_family member so you can also decide at runtime given a struct sockaddr* which "derived" (though not really) type to cast it to.
So, the smallest thing you can do to change this is cast the struct sockaddr_in* to struct sockaddr*. Normally this would be very offensive. But don't worry; in this instance everyone is doing it. I might even prefer to cast to void* because it's fewer characters and you'll get the implict conversion to struct sockaddr*.
However... A few other unrelated points:
gethostbyname can fail. In fact it happens pretty often, say when the user types in a bogus address. Your program will crash when that happens. Check to see if it returned NULL to avoid this.
Actually, gethostbyname has been superseded by getaddrinfo. Use that. It will get you protocol-independence so that you're not tied to IPv4. It also returns you a struct sockaddr* so you don't have to do the ugly cast.
If it was just the const it would work, but obviously sockaddr and sockaddr_in are different types.

Resources