Getting the source address of an incoming socket connection - c

I have a server with a incoming socket from a client.
I need the get the IP address of the remote client.
Tried searching google for in_addr but it's a bit troublesome.
Any suggestions?

You need the getpeername function:
// assume s is a connected socket
socklen_t len;
struct sockaddr_storage addr;
char ipstr[INET6_ADDRSTRLEN];
int port;
len = sizeof addr;
getpeername(s, (struct sockaddr*)&addr, &len);
// deal with both IPv4 and IPv6:
if (addr.ss_family == AF_INET) {
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
port = ntohs(s->sin_port);
inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr);
} else { // AF_INET6
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr;
port = ntohs(s->sin6_port);
inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr);
}
printf("Peer IP address: %s\n", ipstr);

Assuming you're using accept() to accept incoming socket connections, getpeername() isn't needed. The address information is available via the 2nd and 3rd parameters of the accept() call.
Here is Eli's answer modified to do it without getpeername():
int client_socket_fd;
socklen_t len;
struct sockaddr_storage addr;
char ipstr[INET6_ADDRSTRLEN];
int port;
len = sizeof addr;
client_socket_fd = accept(server_socket_fd, (struct sockaddr*)&addr, &len);
// deal with both IPv4 and IPv6:
if (addr.ss_family == AF_INET) {
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
port = ntohs(s->sin_port);
inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr);
} else { // AF_INET6
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr;
port = ntohs(s->sin6_port);
inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr);
}
printf("Peer IP address: %s\n", ipstr);

Since you say it is an incoming connection from a client, as an alternative to getpeername you can just save the address that was returned by the accept() call, in the second and third parameters.

Related

Strange behaviour while sending broadcast message

I have two programs: client and server. They're trying to find themselves in local network using broadcast.
Client sends simple packet on broadcast with SERVER_PORT (known before) and server prints info about connection, but when i tried this solution I found some strange behavaiour, when I uncomment last two lines of server.c server prints (one custom struct)
Connection from: 0.0.0.0 on port: 0
after commenting those lines everything works properly, am I missing something?
server.c
int broadcast_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in broadcast_addr;
broadcast_addr.sin_addr.s_addr = INADDR_ANY;
broadcast_addr.sin_port = htons(SERVER_PORT);
broadcast_addr.sin_family = AF_INET;
if (bind(broadcast_socket, (struct sockaddr *)&broadcast_addr,
sizeof(broadcast_addr))) {
perror("bind");
}
struct sockaddr_in recv_addr;
char buf[MAX_PACKET_SIZE];
socklen_t len;
if (recvfrom(broadcast_socket, buf, MAX_PACKET_SIZE, 0,
(struct sockaddr *)&recv_addr, &len) < 0) {
perror("recvfrom");
}
printf("Connection from: %s on port: %d\nMessage: %s\n",
inet_ntoa(recv_addr.sin_addr), ntohs(recv_addr.sin_port), buf);
/* struct network_packet packet; */
/* struct sockaddr_in my_addr; */
client.c
int find_server(struct sockaddr_in *out) {
struct sockaddr_in broadcast;
struct network_packet packet;
int yes = 1;
socklen_t len;
broadcast.sin_addr.s_addr = INADDR_ANY;
broadcast.sin_port = htons(CLIENT_PORT);
broadcast.sin_family = AF_INET;
int socket_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (bind(socket_fd, (struct sockaddr *)&broadcast, sizeof(broadcast))) {
perror("bind");
}
if (get_broadcast_addr(&broadcast.sin_addr)) {
return -1;
}
printf("Target address: %s\n", inet_ntoa(broadcast.sin_addr));
broadcast.sin_port = htons(SERVER_PORT);
broadcast.sin_family = AF_INET;
setsockopt(socket_fd, SOL_SOCKET, SO_BROADCAST, &yes, sizeof(yes));
char buf[10] = "test";
sendto(socket_fd, buf, strlen(buf), 0, (struct sockaddr *)&broadcast,
sizeof(broadcast));
if (recvfrom(socket_fd, &packet, sizeof(packet), 0,
(struct sockaddr *)&broadcast, &len) < 0) {
perror("recvfrom");
}
struct sockaddr_in *sa = (struct sockaddr_in *)packet.data;
memcpy(out, sa, packet.header.packet_length);
return 0;
}
struct network_packet_header {
enum network_packet_type type;
int packet_length;
};
struct network_packet {
struct network_packet_header header;
unsigned char data[MAX_DATA_LENGTH];
};
You have to initialize the variable you pass as recvfrom's addrlen to the size of the address struct.

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.

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.

Binding a socket: SIGSEGV

I've declared a pointer to the following struct in my main function
struct sockaddr_in* server;
I'm using this struct to a function that returns a socket descriptor bound to this struct.
int openSocket(char* ip_addr, int port, struct sockaddr_in* server){
int sockfd, len;
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if(sockfd < 0){
perror("Failed to open socket: ");
exit(-1);
}
len = sizeof(server);
bzero(&server, len);
server->sin_family= AF_INET;
inet_aton(ip_addr, &server->sin_addr);
server->sin_port= htons(port);
if((bind(sockfd, (struct sockaddr*)&server, len)) < 0){
perror("Bind failed: ");
exit(-1);
}
return sockfd;
}
However when I try to read the individual fields in the struct that was passed into the function, I get a seg fault. That is when I run the following code in my main
sockfd = openSocket(vector->ip_addr, vector->port, server);
printf("%s %d\n", inet_ntoa(server->sin_addr), htons(server->sin_port) );
The values being passed into the openSocket function are correct.
I get a segmentation fault. Any help appreciated.
After I looked through your codes again, I found there are three mistakes.
In main(), you just declared one pointer to struct sockaddr_in, but you didn't assign it, you can define struct sockaddr_in server and pass &server to openSocket().
In openSocket, "server" is declared to pointer, but your statements len = sizeof(server); bzero(&server, len); are not correct, this is where your segmentation violation occurs.
In openSocket(), bind(sockfd, (struct sockaddr*)&server, len) is not correct, you don't need to use &server, just use server.
So you should change your code as below:
len = sizeof(server); ----> len = sizeof(struct sockaddr_in)
bzero(&server, len); ----> bzero(server, len);
if((bind(sockfd, (struct sockaddr*)&server, len)) < 0){ ----->
if((bind(sockfd, (struct sockaddr*)server, len)) < 0){
struct sockaddr_in* server;
sockfd = openSocket(vector->ip_addr, vector->port, server);
printf("%s %d\n", inet_ntoa(server->sin_addr), htons(server->sin_port) );
---->
struct sockaddr_in server;
sockfd = openSocket(vector->ip_addr, vector->port, &server);
printf("%s %d\n", inet_ntoa(server.sin_addr), htons(server.sin_port) );
You just declared "server" as a pointer to struct sockaddr_in, but you didn't assign it. You can do it like below:
struct sockaddr_in server;
sockfd = openSocket(vector->ip_addr, vector->port, &server);
printf("%s %d\n", inet_ntoa(server.sin_addr), htons(server.sin_port) );
bind(sockfd, (struct sockaddr*)&server, len)
The problem is here. server is already a pointer. You should not take its address.
bind(sockfd, (struct sockaddr*)server, len)
And, as pointed out by #nos, you need to initialize it somewhere. There's no reason for the static variable to be a pointer. It can just be a struct sockaddr_in.

getnameinfo() function on a dualstack socket returning incorrect IP address?

I am using TCP accept() function on a dualstack socket. Next I am trying to print IPV4 mapped Ipv6 client address using getnameinfo(). For the first connection to accept(), getnameinfo() is correctly returning IPv4-Ipv6 client source address. But for second clients on wards it is returning the same IPv4 mapped Ipv6 address as first. Can any one help me in this ?
Pseudo Code:
struct sockaddr_storage client_ip;
socklen_t sock_len = sizeof(client_ip);
char buffer[INET6_ADDRSTRLEN];
struct sockaddr_in sa4;
static char *pclient_ipv4_addr = NULL;
while(1)
{
fd = accept(master_fd, (struct sockaddr*)&client_ip, &sock_len);
if (!getnameinfo((struct sockaddr*)&client_ip, sock_len, buffer,
sizeof(buffer),0,0,NI_NUMERICHOST))
{
pclient_ipv4_addr = buffer;
if (client_ip.ss_family==AF_INET6)
{
struct sockaddr_in6 *sa6=(struct sockaddr_in6*)&client_ip;
if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr))
{
memset(&sa4,0,sizeof(sa4));
sa4.sin_family=AF_INET;
sa4.sin_port=sa6->sin6_port;
memcpy(&sa4.sin_addr.s_addr, sa6->sin6_addr.s6_addr+12, 4);
memcpy(&client_ip, &sa4,sizeof(sa4));
sock_len=sizeof(sa4);
pclient_ipv4_addr = inet_ntoa(sa4.sin_addr);
printf("IPV4 add is %s\n", pclient_ipv4_addr );
}
}
}
memset(buffer, 0 , INET6_ADDRSTRLEN);
}
For eg: For First client pclient_ipv4_addr is "192.168.208.13", then for next client also
pclient_ipv4_addr is printing the same address. Is non reentrant of getnameinfo(0 any issue? How
can solve this?
Try something more like this:
struct sockaddr_storage client_ip;
socklen_t sock_len;
while(1)
{
sock_len = sizeof(client_ip);
fd = accept(master_fd, (struct sockaddr*)&client_ip, &sock_len);
if (fd == -1)
break;
if (client_ip.ss_family==AF_INET)
{
struct sockaddr_in *sa4 = (struct sockaddr_in*) &client_ip;
printf("IPv4 add is %s\n", inet_ntoa(sa4->sin_addr) );
}
else if (client_ip.ss_family==AF_INET6)
{
struct sockaddr_in6 *sa6 = (struct sockaddr_in6*) &client_ip;
if (IN6_IS_ADDR_V4MAPPED(&sa6->sin6_addr))
{
struct in_addr a4;
memcpy(&a4, sa6->sin6_addr.s6_addr+12, 4);
printf("IPv4 add is %s\n", inet_ntoa(a4) );
}
else
{
char buffer[INET6_ADDRSTRLEN];
if (getnameinfo((struct sockaddr*)&client_ip, sock_len, buffer, sizeof(buffer), 0, 0, NI_NUMERICHOST) == 0)
printf("IPv6 add is %s\n", buffer );
}
}
}

Resources