I would like to know how to get the public ip of my server with C programming language.I already know how to do this with libcurl but now i want to understand how to get this info with the socket programming (if it is possible).I've already tried with struct hostent *hp but i get only the local address 127.0.0.1
This is the code i've used:
int main(int argc, char *argv[]){
struct hostent *hp;
int i=0;
if((hp=gethostbyname(argv[1])) == NULL){
herror("gethostbyname()");
exit(1);
}
fprintf(stdout, "Hostname: %s\n", hp->h_name);
/* fprintf (stdout,"IP server: %s\n",inet_ntoa(*((struct in_addr *)hp->h_addr))); con questa printo solo 1 ip */
while (hp->h_addr_list[i] != NULL) { /* mentre così printo tutti gli eventuali ip */
printf("IP: %s\n", inet_ntoa(*(struct in_addr*)(hp->h_addr_list[i])));
i++;
}
return 0;
}
You can't do it on your own, there's no reliable way. Let's say you're behind a NAT box: you can never know the inside global address (i.e. the public address used by the NAT box). What's more, the NAT box might have multiple public addresses.
This happens because your public address is not your address. The public address is just a mirage, i.e. the way outside hosts perceive you. So it makes sense: you can only find it out by asking outside hosts.
TLDR
Do a request to an outside host:
[cnicutar#fresh ~]$ curl ifconfig.me
192.0.2.42
It is trivial to do it using libcurl or with your own sockets.
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'm trying to do a little sniffer using pcap in C like explained here
My problem is that pcap_loop absolutly catch no packets and/or does nothing, my callback function is never called. my guess was the time out value but even if i set it to 20 (ms), nothing changes. It hope it s only a simple error i can't see, but i'll let you guys try to figure it out, cause it's been messing my brain too much !!
Thanks
Nikko
Edit : i choose wlan0 as interface and it works with the program given at the link
My main :
int main(int argc, char* argv[]) {
// interface & err buff
char *dev, errbuff[PCAP_ERRBUF_SIZE];
int i = 0,inum= -1;
// it filters out only packet from this machin
char filter_exp[] = "ip host localhost";
struct bpf_program fp; /* compiled filter program (expression) */
/* typedef, no need for struct ... */
bpf_u_int32 mask;
bpf_u_int32 net;
int num_packets = 10;
// 1.0+ API pcap version
pcap_if_t * alldevs;
pcap_if_t * pdev;
pcap_t * handle;
// 1st argument interface
if(argc == 2) {
dev = argv[1];
printf("Chosen interface : %s\n",dev);
}
//+1.0 api version
if(pcap_findalldevs(&alldevs,errbuff)){
fprintf(stderr,"findalldev failed to retrieve interface\n %s",errbuff);
return(2);
}
if(alldevs == NULL){
fprintf(stderr,"Retrieved interface is null\n");
return(2);
}
// select all interfaces
for(pdev = alldevs; pdev != NULL;pdev = pdev->next) {
printf("Device %d : ",++i);
print_pcap_if_t(pdev);
//print_pcap_addr(pdev->addresses);
}
printf("Enter the interface number (1-%d):",i);
scanf("%d", &inum);
if(inum < 1 || inum > i){
fprintf(stderr,"Device %d not in list.\n",i);
return(2);
}
/* Jump to the selected adapter */
for(pdev=alldevs, i=0; i< inum - 1 ;pdev=pdev->next, i++);
printf("\n-------------------------------------------------\n");
//printf("Chosen device : %s",pdev->name);
//print_pcap_if_t(pdev);
/* activate device */
printf("activating\n");
handle = pcap_open_live(pdev->name,SNAP_LEN,1,1000,errbuff);
if(handle == NULL){
fprintf(stderr,"Could not open device for sniffing");
return(2);
}
/* compile filter */
if(pcap_compile(handle,&fp,filter_exp,0,net) == -1) {
fprintf(stderr,"Could not compile filtering rules");
return(EXIT_FAILURE);
}
/* apply filter */
if(pcap_setfilter(handle,&fp) == -1) {
fprintf(stderr,"Could not set filtering rules");
return(EXIT_FAILURE);
}
printf("Waiting for packets to come in your hands");
fflush(stdout);
pcap_loop(handle,num_packets,got_packet,NULL);
pcap_freecode(&fp);
pcap_close(handle);
pcap_freealldevs(alldevs);
return(0);
}
ip host localhost
"localhost" is the name for the IP address 127.0.0.1; it is not the IP address of your machine on the Internet, it's a special IP address used to send IPv4 packets from your machine to itself (e.g., "ftp localhost" if you want to test the FTP server on your machine by connecting to it from a command prompt on your machine).
Traffic to or from other hosts will not come from, or be sent, to, 127.0.0.1.
If your machine has the IP address 10.0.1.2, for example, try "ip host 10.0.1.2".
Ok i found the problem :
my filter "ip host localhost" aws the reason. I changed it to "ip" and there it was =)
I dont really understand though. What i do is i launch the program, and just refresh a web page after that. So my first request is a GET or stg, with source = localhost , no ? And the response will contains my address also in the destination field. According to man page :
host HOST True if either the IPv4/v6 source or destination of the packet is
HOST.
Would it be that it does no translation of "localhost" when setting the filter ?
Anyway , i hope it could help some others...
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.
Does anyone know how do I get my LAN IP and print it on the screen.
*I don't mean on shell, but in c programming.
**I will appreciate if you'll post me a sample code.
There's a few approaches; first, you could set up a connection to a known peer using connect(2) and then read the local socket 'name' with getsockname(2). This is a pretty poor mechanism, but it is easy.
But, getsockname(2) will only report one IP address, when a machine may have thousands of IP addresses, and which IP is returned will depend in part upon the peer that you chose! So, not very reliable.
A much better answer is to use rtnetlink(7) to read the IP addresses for the machine directly from the kernel: you would send RTM_GETADDR messages to the kernel for each interface on the machine and read the answers back. Your best bet for understanding how to use this is probably to read the source code for the ip program.
Another option is to use the SIOCGIFCONF ioctl(2) on an IP socket. This interface isn't as flexible as the rtnetlink(7) interface, so it might not always be correct, but it'll be a mid-way point. The ifconfig(8) utility uses this approach for displaying ifconfig -a output. Again, your best bet would be to read the source. (There is some slight documentation in ioctl_list(2).)
The getifaddrs() function from <ifaddrs.h> is the simplest way to get the current interfaces and corresponding addresses:
#include <stdio.h>
#include <ifaddrs.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main()
{
struct ifaddrs *iflist, *iface;
if (getifaddrs(&iflist) < 0) {
perror("getifaddrs");
return 1;
}
for (iface = iflist; iface; iface = iface->ifa_next) {
int af = iface->ifa_addr->sa_family;
const void *addr;
char addrp[INET6_ADDRSTRLEN];
switch (af) {
case AF_INET:
addr = &((struct sockaddr_in *)iface->ifa_addr)->sin_addr;
break;
case AF_INET6:
addr = &((struct sockaddr_in6 *)iface->ifa_addr)->sin6_addr;
break;
default:
addr = NULL;
}
if (addr) {
if (inet_ntop(af, addr, addrp, sizeof addrp) == NULL) {
perror("inet_ntop");
continue;
}
printf("Interface %s has address %s\n", iface->ifa_name, addrp);
}
}
freeifaddrs(iflist);
return 0;
}
gethostbyname should help you to do this. Example:
GetLocalAddress()
{
char ac[80];
// Get my host name
if (gethostname(ac, sizeof(ac)) != -1)
{
printf("Host name: %s\n", ac);
// Find IP addresses
struct hostent* p_he = gethostbyname(ac);
if (p_he != 0)
{
for (int i=0; p_he->h_addr_list[i] != 0; ++i)
{
struct in_addr addr;
memcpy(&addr, p_he->h_addr_list[i], sizeof(struct in_addr));
printf("IP address %d: %s\n", i, inet_ntoa(addr));
}
}
}
You might want to filter to remove 127.0.0.1 from the list.
Background:
I'm writing a daemon that makes outgoing TCP/IP connections. It will be running on machines with multiple (non-loopback) IP addresses. I'd like the users to be able to specify, in the daemon's config file, which IP address(es) to use for outgoing connections, or * to use all.
The addresses will be used in a rotation, each connection going out from the IP address used least recently. This behavior is important, as is * being a replacement for "all" so daemons running on multiple machines can point to the same config file on a fileshare, and have each use its own set of IP addresses.
Problem:
How do I get a list of all the IP addresses a machine can make outgoing (i.e. to any other computer) connections on? Given a list of all IP addresses, how would I filter out loopback addresses?
I'm in C, and if possible I'd like to use POSIX only, but the daemon will probably only ever run on Linux boxes, so I'd accept a Linux-centric answer.
Each IP address will be available on exactly one (possibly virtual) network device and vice versa, so a way to enumerate network devices and get associated IP addresses would also suffice, though I wouldn't really be happy about it. (Side questions: Is it even possible to associate multiple IP addresses with a single device? How 'bout the same IP under multiple devices? Not important.)
Insufficient Solutions:
gethostname()/gethostbyname() (as this question). Using that method, I only ever get 127.0.0.1 back (or .1.1 in Debian). I suspect this is because the hostname of the machine is in the hosts file, and that's as far as gethostbyname() checks. (I believe that's why in Debian I always get 127.0.1.1: Debian defaults to adding localhost as 127.0.0.1 and the machine's hostname as 127.0.1.1 to the hosts file, right?) I'd like a solution that ignores hosts and gives me everything actually there.
I've had no more luck with getaddrinfo() than gethostname()/gethostbyname(). It seems to be bound by the same problem. I tested this passing the machine's hostname and a NULL service (port) into it; the docs say passing a NULL hostname AND a NULL service is illegal, and this is backed up by testing. Not sure how else to ask it for everything on the machine, but I'm open to suggestions in this vein.
EDIT: this answer shows how to get the IP address from a device name, but doesn't show how to enumerate the device names. Any ideas?
FINAL EDIT: I've accepted caskey's answer to give him the credit for pointing me in the direction of how this needs to be done. I've posted my own answer listing the source code of how exactly to do it in case anyone else needs it.
Here's my proof of concept code using caskey's accepted answer, for posterity's sake:
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
static const char * flags(int sd, const char * name)
{
static char buf[1024];
static struct ifreq ifreq;
strcpy(ifreq.ifr_name, name);
int r = ioctl(sd, SIOCGIFFLAGS, (char *)&ifreq);
assert(r == 0);
int l = 0;
#define FLAG(b) if(ifreq.ifr_flags & b) l += snprintf(buf + l, sizeof(buf) - l, #b " ")
FLAG(IFF_UP);
FLAG(IFF_BROADCAST);
FLAG(IFF_DEBUG);
FLAG(IFF_LOOPBACK);
FLAG(IFF_POINTOPOINT);
FLAG(IFF_RUNNING);
FLAG(IFF_NOARP);
FLAG(IFF_PROMISC);
FLAG(IFF_NOTRAILERS);
FLAG(IFF_ALLMULTI);
FLAG(IFF_MASTER);
FLAG(IFF_SLAVE);
FLAG(IFF_MULTICAST);
FLAG(IFF_PORTSEL);
FLAG(IFF_AUTOMEDIA);
FLAG(IFF_DYNAMIC);
#undef FLAG
return buf;
}
int main(void)
{
static struct ifreq ifreqs[32];
struct ifconf ifconf;
memset(&ifconf, 0, sizeof(ifconf));
ifconf.ifc_req = ifreqs;
ifconf.ifc_len = sizeof(ifreqs);
int sd = socket(PF_INET, SOCK_STREAM, 0);
assert(sd >= 0);
int r = ioctl(sd, SIOCGIFCONF, (char *)&ifconf);
assert(r == 0);
for(int i = 0; i < ifconf.ifc_len/sizeof(struct ifreq); ++i)
{
printf("%s: %s\n", ifreqs[i].ifr_name, inet_ntoa(((struct sockaddr_in *)&ifreqs[i].ifr_addr)->sin_addr));
printf(" flags: %s\n", flags(sd, ifreqs[i].ifr_name));
}
close(sd);
return 0;
}
Works like a charm!
This can only be done in an operating system dependent fashion. You could try parsing the output of 'iptables', but the right answer for linux is to use ioctl.
SIOCGIFCONF takes a struct ifconf *. The ifc_buf field points to a
buffer of length ifc_len bytes, into which the kernel writes a list of
type struct ifreq [].
The struct ifreq is documented in linux/if.h:
struct ifreq
{
#define IFHWADDRLEN 6
union
{
char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */
} ifr_ifrn;
union {
struct sockaddr ifru_addr;
struct sockaddr ifru_dstaddr;
struct sockaddr ifru_broadaddr;
struct sockaddr ifru_netmask;
struct sockaddr ifru_hwaddr;
short ifru_flags;
int ifru_ivalue;
int ifru_mtu;
struct ifmap ifru_map;
char ifru_slave[IFNAMSIZ]; /* Just fits the size */
char ifru_newname[IFNAMSIZ];
void * ifru_data;
struct if_settings ifru_settings;
} ifr_ifru;
};
As you can see, it contains the address information you desire.
Some answers to side questions:
Adding multiple IPs to a device can be done with aliasing. Linux creates devices named like eth0:0 when you do this.
ifconfig eth0:0 10.0.0.1
Having the same IP under multiple devices can be done with channel bonding/link aggregation.
You can get the interface info required a couple of ways including calling ioctl() with the SIOCGIFCONF option and looping through the returned structures to get the interface address info.
Given a list of all IP addresses, how would I filter out loopback addresses?
See ifreq struct in caskey's answer. You can determine the loopback (properly) with:
if (ifru_flags & IFF_LOOPBACK)
Constants are in if.h
are you sure you are using gethostname()/gethostbyname() correctly? check out here, the only problem I see with doing this is that it's possible that a domain name has multiple ip addresses mapped to it. If that's the case then there's no way of knowing what the ip address belonging to the local machine is
How do I get a list of all the IP addresses a machine can make outgoing (i.e. to any other computer) connections on? Given a list of all IP addresses, how would I filter out loopback addresses?
Look at the source code of lsof and netstat. You'll see it involves traversing kernel memory structures, not just making system calls.