Can recvfrom function from Socket extract the sender IP address? - c

We know the Recvfrom function has following synopses
SYNOPSIS
#include <sys/socket.h>
int recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen);
The from has the struct of sockaddr.
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)
};
But sockaddr doesn't seem to able to hold IP address.
Shouldn't recvfrom be using struct socaddr_in * from because
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];
};
And sin_addr will give the IP address. Is that a valid assumption?

The from parameter is defined as sockaddr* for historic reasons, to support legacy code that predates IPv6. sock_addr is fairly agnostic, but it also is not large enough to handle newer socket types. Any socket function that has a sockaddr* parameter actually expects whatever sockaddr-based struct is appropriate for the type of socket that is being used.
If you read from an IPv4 socket, it expects a sockaddr_in*, eg:
struct sockaddr_in from;
socklen_t len = sizeof(from);
recvfrom(s, ..., (struct sockaddr*)&from, &len);
// use from.sin_addr and from.sin_port as needed...
If you read from an IPv6 socket, it expects a sockaddr_in6* instead, eg:
struct sockaddr_in6 from;
socklen_t len = sizeof(from);
recvfrom(s, ..., (struct sockaddr*)&from, &len);
// use from.sin6_addr and from.sin6_port as needed...
If you want to write code that supports multiple protocols, use sockaddr_storage and type-cast as needed, eg:
struct sockaddr_storage from;
socklen_t len = sizeof(from);
recvfrom(s, ..., (struct sockaddr*)&from, &len);
switch (from.ss_family)
{
case AF_INET:
// use ((struct sockaddr_in*)&from) as needed...
break;
case AF_INET6:
// use ((struct sockaddr_in6*)&from) as needed...
break;
...
}
The same applies to other sockaddr-based functions, including connect(), bind(), accept() and sendto().

You can proceed as follows:
struct scokaddr_in A;
char buf[200];
int len;
recvfrom(fd, buf, 200, 0, (struct sockaddr*)&A, &len);
//from ip-address is stored in A.sin_addr...

If you wanted to save the IP address as a string, you could use the inet_ntop() function. Beej's guide provides some usefull info about how to use it, with IP-version agnostic examples.

Related

One Question Basic TCP/IP programming use bind() function

int serv_sock;
struct socckaddr_in serv_addr;
char *serv_port = "9190";
/*make server socket*/
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
/*serv_addr struct init*/
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_faimily = AF_INET;
serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
serv_addr.sin_port=htons(atoi(serv_port));
**bind(serv_sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr));**
i don't understand that why use (struct sockaddr *) ?
why can't wrote bind(serv_sock, &serv_addr, sizeof(serv_addr)); ?
why add (struct sockaddr *) type ? i don't understand
what's mean (struct sockaddr *) ?
So, i did one example
struct a {
int a;
int b;
};
int i = 10;
struct a b;
b.a = 10;
b.b = 20;
printf("%d \n", b.a); // A sentence
printf("%d \n", (struct a*)b.a); // B sentence
it returned 10, 10; i don't understand why use '(struct a*)'..
i don't understand a,b sentence difference
i want I would like to know the difference between the type with and without '*'.
The expression &serv_addr has type struct sockaddr_in *.
However, the function bind expects that the argument has type struct socckaddr *. This is a generic type for many address families.
The * character in this context indicates a pointer.
From the manual:
The actual structure passed for the addr argument will depend on the address family. The sockaddr structure is defined as something like:
struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
}
While the manual describes struct sockaddr_in as:
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 */
};
The reason the the generic type exists is that IPv4 is not the only address family that exists.
The manual describes the IPv6 version:
struct sockaddr_in6 {
sa_family_t sin6_family; /* AF_INET6 */
in_port_t sin6_port; /* port number */
uint32_t sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
uint32_t sin6_scope_id; /* Scope ID (new in 2.4) */
};
Note that the all start with a sa_family_t member, which describes which family is being represented. The struct sockaddr sa_data member is the "payload" of the struct.
Directly passing a struct sockaddr_in * or struct sockaddr_in6 * when struct sockaddr * is expected would cause a warning/error. The (struct sockaddr *) explicitly converts the pointer to the type struct sockaddr *, which bind expects.
The statement
printf("%d \n", (struct a*)b.a);
is not valid. You take an integer, convert it to a pointer to a struct, then tell printf to interpret that argument as an integer.

C - Create a sockaddr struct

I am trying to create a void mksockaddr(int af, int proto, char addr[], struct sockaddr* dst) that creates a sockaddr structure, here's what I've done:
void sockaddr(int af, int port, char addr[], struct sockaddr* dst) {
if (af == AF_INET) {
struct sockaddr_in s;
s.sin_family = af;
s.sin_port = htons(port);
inet_pton(af, addr, &s.sin_addr);
memcpy(dst, &s, sizeof(s));
} else {
struct sockaddr_in6 s;
s.sin6_family = af;
s.sin6_port = htons(port);
s.sin6_flowinfo = 0;
inet_pton(af, addr, &s.sin6_addr);
memcpy(dst, &s, sizeof(s));
}
}
This seems to be no problem with AF_INET (IPv4), I can bind() without any problem, but when I try to use AF_INET6, bind() give me Invalid argument.
Here's the code I use to bind():
int sock_fd = socket(AF_INET6, SOCK_RAW, proto);
struct sockaddr sin;
sockaddr(AF_INET6, proto, src, &sin);
if(bind(sock_fd, &sin, sizeof(sin)) < 0) {
fprintf(stderr, "[ERR] can't bind socket: %s\n", strerror(errno));
exit(1);
} // got Invalid argument
However, I can bind() just fine if I construct a sockaddr_in6 myself:
struct sockaddr_in6 sin;
sin.sin6_port = htons(proto);
sin.sin6_family = AF_INET6;
inet_pton(AF_INET6, src, &sin.sin6_addr);
if(bind(sock_fd, (struct sockaddr*) &sin, sizeof(sin)) < 0) {
fprintf(stderr, "[ERR] can't bind socket.\n");
exit(1);
} // work just fine
So I cast the sockaddr created by the function back to sockaddr_in6, and I can see that all the fields are same except sin6_scope_id. To my understanding, sin6_scope_id does not matter unless I'm dealing with a link-local IPv6 address.
Am I missing anything here?
From a C perspective, for your code to be certain to work as intended, the caller must pass a valid pointer to the correct structure type in the dst argument. Your example does not do this. Instead, it declares a struct sockaddr, and passes a pointer to that. Type struct sockaddr itself is never meant to be used as the type of an actual object, and it is not large enough for all possible address types. In particular, it is not large enough for an IPv6 address.
On the other hand, POSIX plays a bit more fast and loose than standard C requires for conforming programs. This is especially evident with socket addresses. It defines a type struct sockaddr_storage to serve exactly your purpose: it is large enough and has appropriate alignment to hold the data of any supported socket address type. The docs specifically mention its use in generically supporting both IPv4 and IPv6. POSIX also sanctions casting among different socket address pointer types, although this leads to violations of C's struct aliasing rule.
Thus, I would rewrite your function to use struct sockaddr_storage explicitly, and I would furthermore simplify my code via appropriate casts. Moreover, I would have my function tell me the usable size of the address structure, which encompasses only that portion that is initialized:
void populate_sockaddr(int af, int port, char addr[],
struct sockaddr_storage *dst, socklent_t *addrlen) {
if (af == AF_INET) {
struct sockaddr_in *dst_in4 = (struct sockaddr_in *) dst;
*addrlen = sizeof(*dst_in4);
memset(dst_in4, 0, *addrlen);
dst_in4->sin_family = af;
dst_in4->sin_port = htons(port);
inet_pton(af, addr, &dst_in4->sin_addr);
} else if (af == AF_INET6) {
struct sockaddr_in6 *dst_in6 = (struct sockaddr_in6 *) dst;
*addrlen = sizeof(*dst_in6);
memset(dst_in6, 0, *addrlen);
dst_in6->sin6_family = af;
dst_in6->sin6_port = htons(port);
// unnecessary because of the memset(): dst_in6->sin6_flowinfo = 0;
inet_pton(af, addr, &dst_in6->sin6_addr);
} // else ...
}
You would then use it like so:
struct sockaddr_strorage addr;
socklen_t addrlen;
populate_sockaddr(af, port, src, &addr, &addrlen);
if (bind(sock_fd, (struct sockaddr *) &addr, addrlen) < 0) {
// ...
}
Note that the cast of &addr to type struct sockaddr * is utterly routine.

Determine if ipv4 or ipv6 data structure

In a kernel module, given a struct sockaddr with sa_family initialized as AF_UNSPEC, how can I reliably determine if it is a struct sockaddr_in or struct sockaddr_in6? On Linux 3.16.0-4-686-pae (x86).
struct sockaddr {
unsigned short sa_family; // AF_UNSPEC
char sa_data[14]; // ?
};
struct sockaddr_in {
unsigned short sin_family;
unsigned short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
struct sockaddr_in6 {
unsigned short sin6_family;
unsigned short sin6_port;
unsigned int sin6_flowinfo;
struct in6_addr sin6_addr;
unsigned int sin6_scope_id;
};
Usually, when something calls into the kernel and gives a struct sockaddr, it also has to give the length of the struct sockaddr. For example, see sendto():
ssize_t sendto (int sockfd, const void *buf, size_t buflen, int flags,
const struct sockaddr *addr, socklen_t addrlen);
Using the size of the buffer, you ought to be able to get a good guess as to what type of sockaddr you need to use:
if (addr.sa_family == AF_UNSPEC) {
switch (addrlen) {
case sizeof (struct sockaddr_in): {
addr.sa_family = AF_INET;
break;
}
case sizeof (struct sockaddr_in6): {
addr.sa_family = AF_INET6;
break;
}
default: {
// handle error
break;
}
}
}
In an ideal world, the sa_family would be set to seither AF_INET (IPv4) or AF_INET6 (IPv6) already, but unfortunately that doesn't appear to be the case here.

IP as number without usual functions?

Context
I would like to know if we can extract the ip information without having to use gethostinfo or getnameinfo etc...
Part of my code
struct sockaddr in_addr;
socklen_t in_len;
int infd;
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
in_len = sizeof in_addr;
infd = accept( fd , &in_addr , &in_len );
Question
I would like to get the client/incoming ip as a number without the usual formatting (eg 3232238637 instead of 192.168.12.45). Is it even possible ?
The source of those functions is obscure as one would wish, so I cannot figure out.
I read that in_addr (sockaddr) could have the information I need. There is no particular reason but discovering the "inners" of those functions and structures.
Thanks !
For IPv4:
struct sockaddr_in in_addr;
socklen_t in_len = sizeof in_addr;
int infd = accept(fd, (struct sockaddr*) &in_addr, &in_len);
// in_addr.sin_addr.s_addr contains the client's IP address
// as a 4-byte integer, in network byte order...
For IPv6:
struct sockaddr_in6 in_addr;
socklen_t in_len = sizeof in_addr;
int infd = accept(fd, (struct sockaddr*) &in_addr, &in_len);
// in_addr.sin6_addr.s6_addr contains the client's IP address
// as a 16-byte array...
If your question is how to "convert" from the binary representation of an IP address to a string "dotted-decimal" representation, rather than use one of the standard functions, then here is an example of how to do it for an IPV4 address ... if not, please clarify your question:
#include <stdio.h>
#include <netdb.h>
int main()
{
struct hostent *he;
long unsigned nbo;
he = gethostbyname("localhost");
nbo = htonl(*((int*)he->h_addr_list[0]));
printf("h_addr: %lx netbyteorder: %lx = %d.%d.%d.%d\n",
*((int*)he->h_addr_list[0]), nbo,
(nbo&0xFF000000)>>24, (nbo&0x00FF0000)>>16, (nbo&0x0000FF00)>>8,
(nbo&0x000000FF) );
}

Is it possible to cast struct to another?

Any one could describe how (struct sockaddr *)&server works here? Is it possible to cast bigger struct to smaller struct?
See these structs:
// IPv4 AF_INET sockets:
struct sockaddr_in {
short sin_family; // e.g. AF_INET, AF_INET6
unsigned short sin_port; // e.g. htons(3490)
struct in_addr sin_addr; // see struct in_addr, below
char sin_zero[8]; // zero this if you want to
};
struct in_addr {
unsigned long s_addr; // load with inet_pton()
};
struct sockaddr {
unsigned short sa_family; // address family, AF_xxx
char sa_data[14]; // 14 bytes of protocol address
};
This is the main program:
int main(int argc , char *argv[])
{
int socket_desc;
struct sockaddr_in server;
//Create socket
socket_desc = socket(AF_INET , SOCK_STREAM , 0);
if (socket_desc == -1)
{
printf("Could not create socket");
}
server.sin_addr.s_addr = inet_addr("74.125.235.20");
server.sin_family = AF_INET;
server.sin_port = htons( 80 );
//Connect to remote server
if (connect(socket_desc , (struct sockaddr *)&server , sizeof(server)) < 0)
{
puts("connect error");
return 1;
}
puts("Connected");
return 0;
}
This is refered as Type Punning. Here, both structures have the same size, so there is no question of struct size. Although you can cast almost anything to anything, doing it with structures is error-prone.
This is C's form of "inheritance" (notice the quotes). This works because C does not care about the underlying data in an address, just what you represent it as.
The function determines what structure it actually is by using the sa_family field, and casting it into the proper sockaddr_in inside the function.
You can cast sockaddr_in to sockaddr, but you cannot usually cast ANY struct to ANY other and assume that things will work properly.
In C, it's possible to cast anything to anything. You could even omit the cast to (struct sockaddr*), and probably just get a compiler warning.

Resources