C - Create a sockaddr struct - c

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.

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.

How to create sockaddr from sockaddr_in / sockaddr_in6 structure

Below is an extract of example code for IPv6 and IPv4 clinet code:
IPv6
int s;
struct sockaddr_in6 addr;
s = socket(AF_INET6, SOCK_STREAM, 0);
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(5000);
inet_pton(AF_INET6, "::1", &addr.sin6_addr);
connect(s, (struct sockaddr *)&addr, sizeof(addr));
IPv4
int s;
struct sockaddr_in addr;
s = socket(AF_INET, SOCK_STREAM, 0);
addr.sin_family = AF_INET;
addr.sin_port = htons(5000);
servaddr.sin_addr.s_addr = INADDR_ANY;
connect(s, (struct sockaddr *)&addr, sizeof(addr));
My aim is to write a user defined connect API for both IPv6 and IPv4 addresses. Since both of them convert structures to struct sockaddr structure - programtically how can we convert the sockaddr_in and sockaddr_in6 to sockaddr structure (populate its member variables) and then call connect with sockaddr sturcure?
programtically how can we convert the sockaddr_in and sockaddr_in6 to sockaddr structure (populate its member variables) and then call connect with sockaddr structure?
A typecast is not a conversion. You are not converting anything. Just as the examples you provided show, you create a sockaddr_in or sockaddr_in6 instance as needed and then pass it as-is to connect(). You are merely typecasting the pointer that points at the struct instance. Internally, connect() will look at its input addr's sa_family field and typecast the addr back to either sockaddr_in or sockaddr_in6 accordingly to access the data fields as needed.
My aim is to write a user defined connect API for both IPv6 and IPv4 addresses.
If you want to write protocol-agnostic code, you could do something like this:
int doConnect(int family, const char *ip, u_short port)
{
struct sockaddr_storage ss = {};
socklen_t addrlen;
switch (family)
{
case AF_INET:
{
struct sockaddr_in *addr = (struct sockaddr_in *) &ss;
addr->sin_family = AF_INET;
addr->sin_port = htons(port);
inet_pton(AF_INET, ip, &addr->sin_addr);
addrlen = sizeof(struct sockaddr_in);
break;
}
case AF_INET6:
{
struct sockaddr_in6 *addr = (struct sockaddr_in6 *) &ss;
addr->sin6_family = AF_INET6;
addr->sin6_port = htons(port);
inet_pton(AF_INET6, ip, &addr->sin6_addr);
addrlen = sizeof(struct sockaddr_in6);
break;
}
default:
return -1;
}
int s = socket(family, SOCK_STREAM_IPPROTO_TCP);
if (s != -1)
{
if (connect(s, (struct sockaddr *) &ss, addrlen) < 0)
{
close(s);
s = -1;
}
}
return s;
}
int s = doConnect(AF_INET, "127.0.0.1", 5000);
int s = doConnect(AF_INET6, "::1", 5000);
However, a better solution is to use getaddrinfo() instead and let it allocate the correct sockaddr data for you based on the input values it actually parses, eg:
int doConnect(const char *ip, u_short port)
{
struct addrinfo hints = {};
hints.ai_flags = AI_NUMERICHOST;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
struct addrinfo *addr;
char szPort[6];
sprintf(szPort, "%hu", port);
int s = -1;
int ret = getaddrinfo(ip, szPort, &hints, &addr);
if (ret == 0)
{
s = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (s != -1)
{
if (connect(s, addr->ai_addr, addr->ai_addrlen) == -1)
{
close(s);
s = -1;
}
}
freeaddrinfo(addr);
}
return s;
}
int s = doConnect("127.0.0.1", 5000);
int s = doConnect("::1", 5000);
The nice thing about getaddrinfo() is that you can also use it for servers, too. Simply use the AI_PASSIVE flag in the hints.ai_flags field, and then use the resulting addrinfo item(s) to call socket() and bind().
programtically how can we convert the sockaddr_in and sockaddr_in6 to sockaddr
You can't, as neither sockaddr_in nor sockaddr_in6 are guaranteed to fit into a sockaddr structure.
A POSIX-compliant system will provide a sockaddr_storage type that is guaranteed to be large enough to hold any type of sockaddr structure:
The <sys/socket.h> header shall define the sockaddr_storage
structure, which shall be:
Large enough to accommodate all supported protocol-specific address structures
Aligned at an appropriate boundary so that pointers to it can be cast as pointers to protocol-specific address structures and used to
access the fields of those structures without alignment problems
The sockaddr_storage structure shall include at least the following
members:
sa_family_t ss_family
When a pointer to a sockaddr_storage structure is cast as a pointer
to a sockaddr structure, the ss_family field of the
sockaddr_storage structure shall map onto the sa_family field of
the sockaddr structure. When a pointer to a sockaddr_storage
structure is cast as a pointer to a protocol-specific address
structure, the ss_family field shall map onto a field of that
structure that is of type sa_family_t and that identifies the
protocol's address family.

Can recvfrom function from Socket extract the sender IP address?

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.

Segfault when assigning AF_INET to the sin_family attribute of a sockaddr_in structure

Here's my problem:
I'm trying to fill a socket address struct with the appropriate information so that I can use it in a program the handles communication between a server and a client. This is part of the server code. The problem is that it segfaults. When I run gdb, it says that the seg fault occurs when I assign AF_INET to the sin_family attribute for the servaddr struct.
code:
servaddr->sin_family = (short)(AF_INET);
I can't seem to figure out why this occurs.
Here's the full code:
// Function Prototypes
struct sockaddr_in* getServerInfo(char[]);
int main()
{
char hostname[MAXHOSTNAMELEN];
struct sockaddr_in* servaddr = getServerInfo(hostname);
return 0;
} // End main
struct sockaddr_in* getServerInfo(char hostname[])
{
struct sockaddr_in* servaddr = malloc((size_t)sizeof(struct sockaddr_in));
gethostname(hostname, 32);
struct hostent *hostptr;
hostptr = gethostbyname(hostname);
memset((void *) &servaddr, 0, (size_t)sizeof(servaddr));
servaddr->sin_family = (short)(AF_INET);
memcpy((void *)& servaddr->sin_addr, (void *) hostptr->h_addr, hostptr->h_length);
servaddr->sin_port = htons((u_short)8000);
return servaddr;
}
Your bug is here:
memset((void *) &servaddr, 0, (size_t)sizeof(servaddr));
Do this instead:
memset((void *) servaddr, 0, (size_t)sizeof(*servaddr));
Otherwise you're zeroing the pointer for servaddr, (i.e. turning it into NULL). This then explodes when you try and use it.
Similarly you'll need to change your memcpy call.

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