IP as number without usual functions? - c

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

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.

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.

Extracting IP Address and Port Info from sockaddr_storage

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

Unsigned int appears as random negative integer after sending with UDP

I send a struct with one field (unsigned int) to a network emulator and it outputs the unsigned int but it is incorrect.
If I do NOT re-run the network emulator, if I resend the struct with the same number, it will be the same random integer. If I send that number +1, I get the random integer -1.
If i do re-run the network emulator and resend the struct with the same number, it shows a different random integer.
This is the stuct:
struct pkt_INIT {
unsigned int router_id;
};
This is the code to send that struct:
int sock;
struct sockaddr_in server_addr;
struct hostent *host;
char send_data[1024];
host= (struct hostent *) gethostbyname(emulator_host);
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
perror("socket");
exit(1);
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(emulator_port);
server_addr.sin_addr = *((struct in_addr *)host->h_addr);
bzero(&(server_addr.sin_zero),8);
struct pkt_INIT init;
init.router_id = router_id;
strcpy(send_data,(char *)&init);
sendto(sock, send_data, strlen(send_data), 0,
(struct sockaddr *)&server_addr, sizeof(struct sockaddr));
I don't have any of the code for the network emulator.
Don't use strcpy() to put a binary struct into a binary array. Likewise, do not use strlen() to get the length of a struct. Both functions are designed for strings, and will copy/read bytes until they encounter a null byte. Use memcpy() and sizeof() instead:
memcpy(send_data, &init, sizeof(init));
sendto(sock, send_data, sizeof(init), 0, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
In which case, just get rid of the array and pass the struct directly to sendto() instead:
sendto(sock, (char*)&init, sizeof(init), 0, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
Now, with that said, there is an issue with the router_id. Most network protocols require network byte ordering, so you may or may not need to use htonl(), depending on what the emulator is actually expecting:
init.router_id = htonl(router_id);
You are using: strcpy(send_data,(char *)&init);
Is this really what you intended to do? You are copying a struct variable to an array of chars. Assuming you want just to do byte-by-byte copy this way, there are still problems. init won't have NULL termination. So subsequent call to strlen() is dubious.
update:
Just store -1 in the char array and convert it as integer at the receiver side.
send_data[0]=255; //To store -1
send_data[1]='\0';
Receiver side:
int i;
i = recv_data[0];
'i' will have -1.

Resources