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]
Related
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.
I've got a sockaddr_storage containing the ipv4 address and port of a remote host. I haven't seen these structs before though and I'm not sure how to cast it into a struct where I can directly retrieve IP address and port number. I've tried googling the struct but haven't found anything. Any suggestions on how to do this?
Thanks
You can cast the pointer to struct sockaddr_in * or struct sockaddr_in6 * and access the members directly, but that's going to open a can of worms about aliasing violations and miscompilation issues.
A better approach would be to pass the pointer to getnameinfo with the NI_NUMERICHOST and NI_NUMERICSERV flags to get a string representation of the address and port. This has the advantage that it supports both IPv4 and IPv6 with no additional code, and in theory supports all future address types too. You might have to cast the pointer to void * (or struct sockaddr * explicitly, if you're using C++) to pass it to getnameinfo, but this should not cause problems.
To extend an answer above and provide a code that uses getnameinfo function, check this snippet:
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);
The result is that a hoststr contains an IP address from struct sockaddr_storage and a portstr contains a port respectively.
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.
I am writing my first sockets program on Linux and am trying to print the IP address and port of the peer I have connected to. I use getpeername() along with inet_ntop() and ntohs() to get the data out of the sockaddr_in struct. When I look at the results, I get an IP address that does not go to any server that I know of (ping fails) and says that I am listening to a port that netstat says is not being used.
What am I doing wrong? I should be getting 130.215.28.181:39000, but instead I am getting 209.94.72.137:18825 every time I run the program. Looking at netstat shows that I am indeed listening on port 39000.
Here is a snippet from my client program:
connect(sockfd,&serv_addr,sizeof(serv_addr))
// print welcome message
char ipstr[INET6_ADDRSTRLEN];
bzero(ipstr, 50);
struct sockaddr_in *address;
socklen_t address_len = sizeof(*address);
getpeername(sockfd, (struct sockaddr *) address, &address_len);
inet_ntop(AF_INET, &address->sin_addr, ipstr, sizeof(ipstr));
printf("Connection established successfully with %s:%i!\n", ipstr, ntohs(address->sin_port));
You're not allocating any memory for your sockaddr_in structure, you's just passing a pointer to some random memory location. Instead, allocate the address structure on the stack:
struct sockaddr_in addr;
socklen_t addr_len = sizeof(addr);
int err = getpeername(sockfd, (struct sockaddr *) &addr, &addr_len);
if (err != 0) {
// error
}
You should also be checking the return value of every function that is documented to return an error code. In particular, both connect and getpeername return error codes that you should be checking.
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));