How does this code from "Network programming" examples work? - c

I am reading Beej's "Guide to network programming".
In one of his intro examples he talks about getting the IP address for a hostname (like google.com or yahoo.com for instance).
Here is the code.
/*
** showip.c -- show IP addresses for a host given on the command line
*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
int main(int argc, char *argv[])
{
struct addrinfo hints, *res, *p;
int status;
char ipstr[INET6_ADDRSTRLEN];
if (argc != 2) {
fprintf(stderr,"usage: showip hostname\n");
return 1;
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
hints.ai_socktype = SOCK_STREAM;
if ((status = getaddrinfo(argv[1], NULL, &hints, &res)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
return 2;
}
printf("IP addresses for %s:\n\n", argv[1]);
for(p = res; p != NULL; p = p->ai_next) {
void *addr;
char *ipver;
// get the pointer to the address itself,
// different fields in IPv4 and IPv6:
if (p->ai_family == AF_INET) { // IPv4
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
ipver = "IPv4";
} else { // IPv6
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
ipver = "IPv6";
}
// convert the IP to a string and print it:
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
printf(" %s: %s\n", ipver, ipstr);
}
freeaddrinfo(res); // free the linked list
return 0;
}
The part that confuses me is the for loop.
for(p = res; p != NULL; p = p->ai_next) {
void *addr;
char *ipver;
// get the pointer to the address itself,
// different fields in IPv4 and IPv6:
if (p->ai_family == AF_INET) { // IPv4
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
ipver = "IPv4";
} else { // IPv6
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
ipver = "IPv6";
}
// convert the IP to a string and print it:
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
printf(" %s: %s\n", ipver, ipstr);
}
Would anyone mind going through psuedo-step-by-step at whats going on or what these things are? Is it iterating through a linked list?.. I have a general idea of what the struct addrinfo are but what the heck is struct *res and struct *p or void *addr and *char ipversion.

First thing's first, do you know what a linked list is? If you understand that, you'll recognise what that for loop is going. p is a pointer to a structure that also references (links) the next structure in the list. So you're looping through a list of those structures, which are addrinfo structs. 4
Now, the thing you need to know about network packets is that they're made up of a header. Specifically the Ethernet frame. This is the hardware to hardware protocol. It let's you get things around on a physical, bounded network but knows nothing about routing across physical network boundaries.
Next up comes tcp or possibly another transport layer protocol, which sits somewhere inbetween the two levels. TCP versus UDP versus X is about how you manage the packets - for example TCP requires packets be reassembled in order, whereas UDP is a "broadcast"-type protocol.
Finally, you have the internet protocol suite (IPv4, IPv6). These are higher level protocols that control the broader sense of routing, so they know about the internet at large, but less about the steps needed to get there.
A great explanation of this is the handy diagram on this page. To complete the picture, BGP is how routers know how to move stuff around.
tcp/udp fit into this picture by being a part of (enscapulated in) the protocol in question (IPv4 for example)
So ethernet frames contain other protocols most notably IPv4, which contain the information routers need to get it out across the internet (across multiple physical networks). The internet protocol specifies where you want to go, from where you are. So a typical's IPv4 body remains unchanged across its whole transit, but every time it traverses physical networks it gets wrapped up in a different ethernet packet.
Now, in the ethernet header there is a field for finding out what the "ethernet body" contains. This line:
if (p->ai_family == AF_INET) {
Does. AF_INET is a constant that matches the value tcp uses to identify the packet body as IPv4. So, if you're looking at an IPv4 header, this loop then goes on to read that information.
The else clause is technically wrong, because not being IPv4 doesn't automatically make it IPv6. You could change it to test for IPv6 like this:
else if (p->ai_family == AF_INET6) {
Which you might want to do, just in case you pick up something else.
Now it's worth explaining this bit of magic:
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
This basically takes the network, or raw, form of the data which appears as a sequence of bytes, and casts it (coverts it) into fields in a struct. Because you know how big the fields are going to be, this is a very quick and easy way to extract out what you need.
The last thing that needs explanation is this:
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
There are other ways of achieving this, specifically ntohs().
Basically network data is transmitted in big endian encoding, and in order to read it, you need (potentially) to convert the data to the encoding of your system. It could be big endian, or it could be little, it depends on your system for the most part. Have a read of the wikipedia article on endianness.
Summary: what you're looking at here is a combination of computer science structures, how networks work and C code.

Well, it's not that complicated. getaddrinfo returns a linked list of addrinfo structs (struct addrinfo **res in the manpage) where each of these structs contains information about one address available to the given interface (const char *node in the manpage).
Now, every struct is being inspected and information about the struct is being printed out. To print out either IPv4 or IPv6, the variable ipver is set accordingly. Before printing out the information, the address has to be converted from a binary form to a string. This is done by inet_ntop (*n*umber to *p*ointer).
The resulting string of inet_ntop (ipstr) and ipver are now printed out to console. Printing ipver, however, is not neccessary since you would recognize the address type from the ipstr: an IPv4 address (as we all know) gets written 192.168.1.10 whereas IPv6 addresses use colons to separate the address elements: 2001:0db8:85a3:0000:0000:8a2e:0370:7334.

Yes, res points to a linked list of addrinfo structures that represent the different IP addresses of a host. The MSDN documentation on the getaddrinfo function is pretty good. I don't know what platform you're running on, but it shouldn't be much different on other platforms.

Related

What is the socket address family defined by 30?

I am writing a simple FTP server program in C. I am mostly following Beej's Guide to Network Programming. In order to complete the pasv mode functionality, I have to create another socket and send its ip and port to the client.
Since gethostbyname() is deprecated, I am using getsockname() to get my current network IP address. My plan is that I separate two cases (ipv4 and ipv6) and send info to the client accordingly. But, whenever I try to get the address family it always 30. I am not sure if I am printing this correctly though.
I am doing something simple like this (which might include mistakes), and it prints 30.
struct sockaddr_storage my_addr;
int result;
socklen_t len = sizeof(my_addr);
result = getsockname(current_fd, (struct sockaddr *) &my_addr, &len);
if (my_addr.ss_family == AF_INET) { // ipv4
struct sockaddr_in * ipv4 = (struct sockaddr_in *) &my_addr;
addr = &(ipv4->sin_addr);
} else { // ipv6
struct sockaddr_in6 * ipv6 = (struct sockaddr_in6 *) &my_addr;
addr = &(ipv6->sin6_addr);
}
printf(" family: %u\n", (unsigned short) my_addr.ss_family);
When I searched this online, I got:
#define AF_ATM 30 /* ATM */
What is this exactly? Is this expected? If not, where am I doing something wrong?
Thank you.
Edit:
Apparently what it was printing was something different.
It is not defined as:
#define AF_ATM 30 /* ATM */
but it is defines as:
MT IPv6: Multi-Topology IP version 6 [RFC7307]

Server socket: get own IP direction after accept [duplicate]

I want to get the IP address of the computer my program is launched on, to be able then to send it to a client, but I always get 0.0.0.1 instead of the real IP address (like 127.0.0.1 for instance).
I'm currently able to get the port, but not the IP address.
How can I get it?
The best solution would be to be able to get it with a sockaddr_in. Here's what I'm currently doing:
int open_connection(char* ip, int* port)
{
int sock;
struct sockaddr_in sin;
socklen_t len;
int i;
i = 0;
len = sizeof(sin);
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
return (-1);
bzero(&sin, sizeof(struct sockaddr_in));
sin.sin_family = AF_INET;
if (bind(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0)
perror("Error on bind");
if (getsockname(sock, (struct sockaddr *)&sin, &len) != 0)
perror("Error on getsockname");
strcpy(ip, inet_ntoa(sin.sin_addr)); // IP = 0.0.0.0
*port = sin.sin_port;
return (sock);
}
EDIT: I understand I was going on the wrong way with my way of thinking. So my question is: What's the best way to get your own IP address?
When you bind() a socket to 0.0.0.0, that is the only IP the socket has available when calling getsockname(). It means the socket is bound to all local interfaces. In order to get a specific IP from a socket, it has to be bound to a specific IP.
Using the socket API to get the machine's local IP(s) is the wrong approach anyway. A common mistake is to use gethostname() with gethostbyname() or getaddrinfo() to get the local IP list. Usually that works, but it has some hidden gotchas that can cause false information, but people tend to ignore that fact, or don't even know about it in the first place (I didn't know about it for years, but then I learned better).
Instead, you really should use platform-specific APIs for enumerating the local networking interfaces. That will provide more reliable information. Windows has GetAdaptersInfo() and GetAdaptersAddresses(). Other platforms have getifaddrs(). Those will tell you what local IPs are available. You can then bind() a socket to 0.0.0.0 in order to accept clients on any of those IPs, or bind() to a specific IP to accept clients only on that IP.
The sockets API allows you to enumerate the IP addresses assigned to your network interfaces, but it will not tell you what you "real IP" is if you are connecting to the Internet from behind a router.
The only way to know it is by asking someone outside. Thats how servers like FileZilla FTP Server do that. They instruct you to configure the URL to a "ip.php" script like this one in the server's settings so it can ask the Internet whats its public IP address, to use in Passive Mode.
You can also consider using STUN, a protocol widely used in VoIP to discover public IP.
You could call ioctl(sock, SIOCGIFADDR, adr)
see netdevice(7)
Following #Remy Lebeau's answer I wrote a function that return current machine's address. I have only tested this on macOS High Sierra.
interfaec can be anything among lo0, en0, etc.
ipVersion can be AF_INET or AF_INET6.
long int getInternalAddress(char* interface, sa_family_t ipVersion)
{
struct ifaddrs *ifaddrHead, *ifaddr;
/* int_8 */
sa_family_t family;
int n;
char *interfaceName;
if (getifaddrs(&ifaddrHead) != 0)
{
fprintf(stderr, "ifaddrs error");
}
/* iterate through address list */
for (ifaddr = ifaddrHead, n = 0; ifaddr != NULL; ifaddr = ifaddr->ifa_next, n++)
{
family = ifaddr->ifa_addr->sa_family;
interfaceName = ifaddr->ifa_name;
if (!family || family != ipVersion || strcmp(interfaceName, interface)) continue;
struct sockaddr *addr = ifaddr->ifa_addr;
struct sockaddr_in* addr_in = (struct sockaddr_in*) addr;
long int address = addr_in->sin_addr.s_addr;
freeifaddrs(ifaddrHead);
return address;
}
freeifaddrs(ifaddrHead);
return 0;
}
To use it,
int main()
{
long int address = getInternalAddress((char*) &"en0", AF_INET);
printf("%li\n", address);
return 0;
}
I'm still a beginner in C, if there is anything wrong please tell me.

How to get its own IP address with a socket address?

I want to get the IP address of the computer my program is launched on, to be able then to send it to a client, but I always get 0.0.0.1 instead of the real IP address (like 127.0.0.1 for instance).
I'm currently able to get the port, but not the IP address.
How can I get it?
The best solution would be to be able to get it with a sockaddr_in. Here's what I'm currently doing:
int open_connection(char* ip, int* port)
{
int sock;
struct sockaddr_in sin;
socklen_t len;
int i;
i = 0;
len = sizeof(sin);
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1)
return (-1);
bzero(&sin, sizeof(struct sockaddr_in));
sin.sin_family = AF_INET;
if (bind(sock, (struct sockaddr *) &sin, sizeof(sin)) != 0)
perror("Error on bind");
if (getsockname(sock, (struct sockaddr *)&sin, &len) != 0)
perror("Error on getsockname");
strcpy(ip, inet_ntoa(sin.sin_addr)); // IP = 0.0.0.0
*port = sin.sin_port;
return (sock);
}
EDIT: I understand I was going on the wrong way with my way of thinking. So my question is: What's the best way to get your own IP address?
When you bind() a socket to 0.0.0.0, that is the only IP the socket has available when calling getsockname(). It means the socket is bound to all local interfaces. In order to get a specific IP from a socket, it has to be bound to a specific IP.
Using the socket API to get the machine's local IP(s) is the wrong approach anyway. A common mistake is to use gethostname() with gethostbyname() or getaddrinfo() to get the local IP list. Usually that works, but it has some hidden gotchas that can cause false information, but people tend to ignore that fact, or don't even know about it in the first place (I didn't know about it for years, but then I learned better).
Instead, you really should use platform-specific APIs for enumerating the local networking interfaces. That will provide more reliable information. Windows has GetAdaptersInfo() and GetAdaptersAddresses(). Other platforms have getifaddrs(). Those will tell you what local IPs are available. You can then bind() a socket to 0.0.0.0 in order to accept clients on any of those IPs, or bind() to a specific IP to accept clients only on that IP.
The sockets API allows you to enumerate the IP addresses assigned to your network interfaces, but it will not tell you what you "real IP" is if you are connecting to the Internet from behind a router.
The only way to know it is by asking someone outside. Thats how servers like FileZilla FTP Server do that. They instruct you to configure the URL to a "ip.php" script like this one in the server's settings so it can ask the Internet whats its public IP address, to use in Passive Mode.
You can also consider using STUN, a protocol widely used in VoIP to discover public IP.
You could call ioctl(sock, SIOCGIFADDR, adr)
see netdevice(7)
Following #Remy Lebeau's answer I wrote a function that return current machine's address. I have only tested this on macOS High Sierra.
interfaec can be anything among lo0, en0, etc.
ipVersion can be AF_INET or AF_INET6.
long int getInternalAddress(char* interface, sa_family_t ipVersion)
{
struct ifaddrs *ifaddrHead, *ifaddr;
/* int_8 */
sa_family_t family;
int n;
char *interfaceName;
if (getifaddrs(&ifaddrHead) != 0)
{
fprintf(stderr, "ifaddrs error");
}
/* iterate through address list */
for (ifaddr = ifaddrHead, n = 0; ifaddr != NULL; ifaddr = ifaddr->ifa_next, n++)
{
family = ifaddr->ifa_addr->sa_family;
interfaceName = ifaddr->ifa_name;
if (!family || family != ipVersion || strcmp(interfaceName, interface)) continue;
struct sockaddr *addr = ifaddr->ifa_addr;
struct sockaddr_in* addr_in = (struct sockaddr_in*) addr;
long int address = addr_in->sin_addr.s_addr;
freeifaddrs(ifaddrHead);
return address;
}
freeifaddrs(ifaddrHead);
return 0;
}
To use it,
int main()
{
long int address = getInternalAddress((char*) &"en0", AF_INET);
printf("%li\n", address);
return 0;
}
I'm still a beginner in C, if there is anything wrong please tell me.

Cannot open FTP connection when server address is FQDN

My FTP program written in C only works when the server address is an IP address. But the connection fails when the server address is a Fully Qualified Domain Name (FQDN).
When ftp_host is FQDN, the ftp connection fails to open. Please help.
I'd strongly recommend using the getaddrinfo() function, as gethostbyname() is obsolete. This also has the advantage that it's easy to convert to IPv6, either now or later.
I'm assuming you only care about IPv4 addresses, so here's an example of a function which will take a hostname as its parameter and fill in a struct sockaddr_in for you:
int get_ftp_addr(const char *hostname, struct sockaddr_in *addr)
{
char host_buffer[256];
struct addrinfo hints;
struct addrinfo *result;
struct sockaddr_in *res_addr;
int error = -1;
char *colon;
snprintf(host_buffer, sizeof(host_buffer), "%s", hostname);
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
colon = strchr(host_buffer, ':');
if (colon) {
*colon = '\0';
error = getaddrinfo(host_buffer, colon + 1, &hints, &result);
} else {
error = getaddrinfo(host_buffer, "ftp", &hints, &result);
}
if (error != 0 || !result) {
return error;
}
res_addr = (struct sockaddr_in*)(result->ai_addr);
memcpy(addr, res_addr, sizeof(struct sockaddr_in));
freeaddrinfo(result);
return 0;
}
Note that I only take a copy of the string to avoid modifying the version in the caller - don't feel obliged to keep that part if you don't want to, but personally I think it makes a cleaner interface.
This function will accept both IP addresses in dotted quad notation and fully-qualified hostnames, as getaddrinfo() accepts both of those. If a port is specified with a colon it will be used, otherwise the default FTP port will be used.
A zero return value indicates success. A positive return can be passed into gai_strerror() to get a string error code, or you can check the getaddrinfo() man page for the possible error codes. A return of -1 indicates a successful result from getaddrinfo() but no result structure - I don't think this can happen, but I don't like leaving any possible return code unhandled.
There are several caveats here, the two most important are:
This code only support IPv4 as it stands, although getaddrinfo() makes it really easy to support IPv6 as well. If you want to support both then change the AF_INET in the hints structure to AF_UNSPEC and you'll get all address families. You'll need to iterate over addresses, though, and filter out only the IPv4 and IPv6 ones (see my next point).
A DNS lookup can potentially look up to multiple IP addresses - this is quite common with large sites such as Google as they use this feature for load-balancing across hosts, and also redundancy. Ideally you code should iterate across all the returned addresses and try connecting to each one until one works. For an FTP client this is perhaps overkill, but I think it's important to be aware of it.
If you want to support IPv6, or support multi-A-records (i.e. multiple addresses back from the DNS query), then you need to follow the ai_next pointer in the struct addrinfo structure - something like this:
struct addrinfo *res;
/* Assume result is initialised as above via getaddrinfo() */
for (res = result; res != NULL; res = res->ai_next) {
...
}

How to get ip address from sock structure in c?

I'm writing simple server/client and trying to get client IP address and save it on server side to decide which client should get into critical section. I googled it several times but couldn't find proper way to get IP address from sock structure.
I believe this is a way to get IP from sock struct after server accept request from client. More specifically in c after server execute
csock = accept(ssock, (struct sockaddr *)&client_addr, &clen)
Thanks
OK assuming you are using IPV4 then do the following:
struct sockaddr_in* pV4Addr = (struct sockaddr_in*)&client_addr;
struct in_addr ipAddr = pV4Addr->sin_addr;
If you then want the ip address as a string then do the following:
char str[INET_ADDRSTRLEN];
inet_ntop( AF_INET, &ipAddr, str, INET_ADDRSTRLEN );
IPV6 is pretty easy as well ...
struct sockaddr_in6* pV6Addr = (struct sockaddr_in6*)&client_addr;
struct in6_addr ipAddr = pV6Addr->sin6_addr;
and getting a string is almost identical to IPV4
char str[INET6_ADDRSTRLEN];
inet_ntop( AF_INET6, &ipAddr, str, INET6_ADDRSTRLEN );
The easier and correct way for extracting IP address and port number would be:
printf("IP address is: %s\n", inet_ntoa(client_addr.sin_addr));
printf("port is: %d\n", (int) ntohs(client_addr.sin_port));
The SoapBox's accepted answer won't be correct for all architectures. See Big and Little Endian.
Assuming client_addr is a struct sockaddr_in (which it usually is). You can get the IP address (as a 32-bit unsigned integer) from client_addr.sin_addr.s_addr.
You can convert it to a string this way:
printf("%d.%d.%d.%d\n",
int(client.sin_addr.s_addr&0xFF),
int((client.sin_addr.s_addr&0xFF00)>>8),
int((client.sin_addr.s_addr&0xFF0000)>>16),
int((client.sin_addr.s_addr&0xFF000000)>>24));

Resources