Extracting IP Address and Port Info from sockaddr_storage - c

I'm currently working on a UDP server that receives a request from a client. The datagram I receive is a byte (char) array 5 elements long, with the final two elements being a port number.
Eventually this server will have to return both the IP address and the port number in a datagram of its own.
I already know how to use inet_ntop and the sockaddr struct I've connected with and received from to print out the ip, but it returns a string that's not in the format I want. For instance:
string1 = inet_ntop(their_addr.ss_family,get_in_addr(
(struct sockaddr *)&their_addr),s, sizeof s);
returns:
127.0.0.1
or:
[1][2][7][.][0][.][0][.][1]
when I need something like:
[127][0][0][1]
Should I be using some sort of character and array manipulation to make my 4-element byte array? Or does a sockaddr have this information in a way that I can leave it in this hex form and return it?

Assuming for IPv4.
After taking the address of your sockaddr_storage or sockaddr structure and casting it to the IPv4 version sockaddr_in, you can then access the individual bytes of the IPv4 address.
struct sockaddr_in *sin = (struct sockaddr_in *)&their_addr;
Then you can take address of the s_addr member which is a 32 bit value (in_addr_t) that holds the 4 bytes of the ip address (in network byte order) and cast it to a pointer to an unsigned char which then lets you access the individual bytes of the value.
unsigned char *ip = (unsigned char *)&sin->sin_addr.s_addr;
printf("%d %d %d %d\n", ip[0], ip[1], ip[2], ip[3]);

You want to probably use getnameinfo() function:
int getnameinfo(const struct sockaddr *sa, socklen_t salen,
char *host, size_t hostlen,
char *serv, size_t servlen, int flags);
E.g.:
struct sockaddr_storage client_addr;
socklen_t client_len = sizeof(struct sockaddr_storage);
/* Accept client request */
int client_socket = accept(server_socket,
(struct sockaddr *)&client_addr, &client_len);
char hoststr[NI_MAXHOST];
char portstr[NI_MAXSERV];
int rc = getnameinfo((struct sockaddr *)&client_addr,
client_len, hoststr, sizeof(hoststr), portstr, sizeof(portstr),
NI_NUMERICHOST | NI_NUMERICSERV);
if (rc == 0)
printf("New connection from %s %s", hoststr, portstr);

Here is a simple immutable class I used for the same purpose you mentioned in your question:
class address_t {
private:
uint16_t m_Port = 0;
std::string m_Ip = "";
public:
address_t(const sockaddr_in & address) {
m_Ip = inet_ntoa(address.sin_addr);
m_Port = ntohs(address.sin_port);
}
uint16_t GetPort() const { return m_Port; }
std::string GetIp() const { return m_Ip; }
std::string ToString() const {
return "IP: " + m_Ip + ", Port: " + std::to_string(m_Port);
}
};

Related

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.

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) );
}

error in ip address received

I am implementing raw sockets in C. I have two programs (say server and client). Client is sending info to server and then server is sending ACK. Client has sent info and server has successfully received it. Now server is sending back ACK. Now, Client has parsed ethernet header successfully. I have problem in receiving the IP address.
1. What should be the type of arguments of the function CreateIPHeader()?
2. How to print IP addresses in ParseIPHeader()?
3. What should be the data type of value returned by ParseIPHeader, if I want just the IP addresses?
struct iphdr *CreateIPHeader(char *src_ip,char *dst_ip)
{
struct iphdr *ip_header;
ip_header=malloc(sizeof(struct iphdr));
//OTHER FIELDS OF IP HEADER
ip_header->saddr = inet_ntoa(*((struct in_addr *)(src_ip)));
ip_header->daddr = inet_ntoa(*((struct in_addr *)(dst_ip)));
ip_header->check=ComputeChecksum((unsigned char *)ip_header,ip_header->ihl*4);
printf("\nip to be sent = %s",ip_header->saddr); //printing correct IP
printf("\nip to be rcvd = %s",ip_header->daddr);
return(ip_header);
}
char* ParseIPHeader(unsigned char *packet,int len)
{
struct iphdr *ip_header,*ret_ip;
unsigned char *out;
struct ethhdr *ethernet_header;
out=malloc(2048);
memset(out, 0, 2048);
ethernet_header=(struct ethhdr *) out;
ret_ip=(struct iphdr *) (out + sizeof(struct ethhdr));
if(ntohs(ethernet_header->h_proto)==ETH_P_IP)
{
if(len>=(sizeof(struct ethhdr)+sizeof(struct iphdr)))
{
ip_header=(struct iphdr*)(packet+sizeof(struct ethhdr));
ret_ip->saddr = ip_header->daddr;
ret_ip->daddr = ip_header->saddr;
printf("daddr SENT = %s",ret_ip->daddr); //how to print them?
printf("saddr SENT = %s",ret_ip->saddr);
}
else
printf("IP packet does not have full header\n");
}
else
{
//not an IP packet
}
return out;
}
int main()
{
unsigned char in[2048];
int len;
char *rcv_ip;
Struct iphdr *ip_header;
memset(in,0,2048);
len=recvfrom(raw,in,2048,0,(struct sockaddr *)&packet_info,&packet_info_size);
rcv_ip=ParseIPHeader(in,len); /*I want this function to return me the ip addresses which I would use in the next line.*/
ip_header =CreateIPHeader(rcv_ip+5,rcv_ip);
memset(in,0,2048);
memcpy(in+sizeof(struct ethhdr),ip_header,ip_header->ihl*4);
sendrawpacket(raw,in,pkt_len);
free(ip_header);
return 0;
}
Any help would be appreciated.
Thanks :)
Instead of:
printf("\nDest Addr %s \n",inet_ntoa(*((struct in_addr *)&((ip_header->daddr)))));
printf("\nSource Addr %s \n",inet_ntoa(*((struct in_addr *)&(ip_header->saddr))));
Perhaps:
printf("\nDest Addr %s \n",inet_ntoa(ip_header->daddr));
printf("\nSource Addr %s \n",inet_ntoa(ip_header->saddr));
If indeed the printf is causing you to segfault and not something else, then perhaps:
struct in_addr dest;
dest.s_addr = ip_header->daddr;
printf("\nDest Addr %s \n", inet_ntoa(dest));
This is because (assuming you are using struct iphdr *ip_header) ip_header->daddr has a type of __u32 and inet_ntoa takes a struct in_addr.
There are a couple things wrong.
CreateIPHeader
You are confusing inet_ntoa() with inet_aton(). When creating the IP header, you want to set ip_header->saddr using something like
inet_aton(src_ip, (struct in_addr *) &ip_header->saddr)
Remember, the s_addr and d_addr in struct iphdr are of type __u32, not char arrays. Thus, when you make this change, the printf statements in CreateIPHeader will break.
main
The reason why your printf's are working is due to Problem #1. On receive, you call CreateIPHeader to create an incorrect struct iphdr (see above), which now incorrectly has C-strings assigned to saddr and daddr. Later, abc points to this incorrect header; your printf's magically work because abc->saddr and abc->daddr actually are C strings.
You are only overwriting the iphdr portion of packet_buffer (your memcpy statement). You must also change the h_dest and h_src values in struct ethhdr.
You really don't need to convert the IP addresses into a C string and then convert them back into IP addresses.
Also, you aren't freeing ip_header.
In general, you can do something like this:
int main()
{
unsigned char in[2048];
unsigned char out[2048];
int len;
memset(in, 0, 2048);
memset(out, 0, 2048);
len = recvfrom(raw, in, 2048, 0,
(struct sockaddr *) &packet_info, &packet_info_size);
struct ethhdr *in_eth = (struct ethhdr *) in;
if (ntohs(ethernet_header->h_proto) == ETH_P_IP &&
len >= sizeof(struct ethhdr) + sizeof(struct iphdr) {
struct iphdr *in_ip = (struct iphdr*) (in + sizeof(struct ethhdr));
/* create outbound packet, starting with eth header */
struct ethhdr *out_eth = (struct ethhdr *) out;
/* ... set h_dest and h_src */
struct iphdr *out_ip = (struct iphdr *) (out + sizeof(struct ethhdr));
out_ip->saddr = in_ip->daddr;
out_ip->daddr = in_ip->saddr;
/* calculate the IPv4 checksum, packet len etc */
sendrawpacket(raw, out, pkt_len);
}
return 0;
};
No guarantee that's bug-free. Just wrote it in the browser.

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.

How should I print server address

int server_sockfd, client_sockfd; //server and client filedescriptors
socklen_t server_len, client_len;
struct sockaddr_in server_address; //server address
struct sockaddr_in client_address; //client address
int server_port = 10000;
char *def_server_address ="127.0.0.1";
server_len = sizeof(server_address);
memset(&server_address, 0, server_len );
server_address.sin_family = AF_INET;
if (x == 1) {
server_address.sin_addr.s_addr = INADDR_ANY;}
else {
server_address.sin_addr.s_addr = inet_addr(def_server_address);
}
server_address.sin_port = htons(server_port);
How should I print the address of the server from server_address? Using printf.
Use inet_ntop() to convert it to a string
This function converts the network address structure src in the af
address family into a character string. The resulting string is
copied to the buffer pointed to by dst, which must be a non-null
pointer. The caller specifies the number of bytes available in this
buffer in the argument size.
inet_ntop() extends the inet_ntoa(3) function to support multiple
address families, inet_ntoa(3) is now considered to be deprecated in
favor of inet_ntop().
That worked for me:
struct sockaddr_in sa;
char buffer[INET_ADDRSTRLEN];
inet_ntop( AF_INET, &sa.sin_addr, buffer, sizeof( buffer ));
printf( "address:%s\n", buffer );
AF_INET is used to denote that the sin_addr points to an IPv4 network address. The resulting string is copied to the buffer variable. You should specify the number of bytes available in the buffer in the last argument of inet_ntop().
I used an example from Beej's Networking:
https://beej.us/guide/bgnet/html/#ip-addresses-part-deux
This works:
struct sockaddr_in sa;
struct sockaddr_in6 sa6;
int e1 = inet_pton(AF_INET, "10.12.110.57", &(sa.sin_addr)); // IP4
int e2 = inet_pton(AF_INET6, "2001:db8:63b3:1::3490", &(sa6.sin6_addr)); // IP6
// Example for IPv4 string
char ip4[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(sa.sin_addr), ip4, INET_ADDRSTRLEN);
printf("The IPv4 address is %s\n", ip4);
// Example for IPv6 string
char ip6[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &(sa6.sin6_addr), ip6, INET6_ADDRSTRLEN);
printf("The Ipv6 address is %s\n", ip6);
Output:
The IPv4 address is 10.12.110.57
The Ipv6 address is 2001:db8:63b3:1::3490
Try this:
printf("%s\n", inet_ntoa(server_address->sin_addr));

Resources