#include <WinSock2.h>
#include <WS2tcpip.h>
#include "stdio.h"
int main(int argc, wchar_t *argv[]) {
//init Windows Sockets
//IResult
int IResult = NULL;
WSADATA wsaDataStruct;
IResult = WSAStartup(MAKEWORD(2, 2), &wsaDataStruct);
if (IResult != 0) // Error
{
//printf u are fucked;
printf("Error: Winsock2 Init, %d", IResult);
return 1;
}
//Result is output of getaddrinfo, hints is a helper struct
struct addrinfo*
result = NULL, //Linked list of addresses
* ptr = NULL,
hints = {};
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
//Hostname
char hostname[64] = {};
gethostname(hostname, 64);
IResult = getaddrinfo(hostname, "4500", &hints, &result);
if (IResult != 0) { //Smth bad happened get Error
printf("Error: Winsock2 addrinfo, %d", IResult);
WSACleanup();
return 2;
}
//Loop addresses
addrinfo* addr = result;
while (addr!=nullptr)
{
char ip[16];
inet_ntop(addr->ai_family, addr->ai_addr, ip, 16);
printf("Address found: %s", ip);
addr = addr->ai_next;
}
return 0;
}
I am looping through the linked list of addrinfo structs and so far it returns one struct only which has an address of 2.0.17.148, while my machine has a local address of 192.168.2.1
I have only 1 network interface, and addrinfo returns a valid struct.
inet_ntop's argument should be a pointer to an in_addr (IPv4) or an in6_addr (IPv6).
You are passing it to a sockaddr, which will be a sockaddr_in for IPv4 or a sockaddr_in6 for IPv6. (You won't get any IPv6 addresses because you only asked for IPv4 addresses using AF_INET). The sockaddr contains the family code, the port, and the address.
To get the in_addr you need to access it within the sockaddr_in structure: First cast addr->ai_addr to sockaddr_in*, then access the sin_addr member, then get a pointer to it.
inet_ntop(addr->ai_family, &((sockaddr_in*)addr->ai_addr)->sin_addr, ip, 16);
Note: this only works for IPv4 (AF_INET family). To support IPv6 as well, you need to check whether the family is AF_INET or AF_INET6, and use sockaddr_in6 for AF_INET6, and allocate more space for ip because the addresses are longer.
Related
1I'm reading Beej's programming network guide. I read his code and did an important change to check ipv6 but it's not working right. Can't get an ip Address. How to use this on a Linux system?
https://i.imgur.com/USVzBw1.png
#include <stdio.h>
#include <string.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 if (p->ai_family==AF_INET6) { // 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;
}```
This looks suspicious:
You check to make sure argc is 2 to validate you have at least one argument. Makes sense
if (argc != 2) {
But then later:
if ((status = getaddrinfo(argv[2], NULL, &hints, &res)) != 0) {
You reference argv[2], which is undefined when the only valid indicies in argv are 0 and 1.
Pretty sure you meant to use argv[1] as follows:
if ((status = getaddrinfo(argv[1], NULL, &hints, &res)) != 0) {
Also, this is a bug:
// convert the IP to a string and print it:
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
printf(" %s: %s\n", ipver, ipstr);
Nothing wrong with those statements, but when p->ai_family is neither AF_INET nor AF_INET6, then those statements get executed anyway. addr and ipver are undefined. Hence, undefined behavior. And yes, on Linux, there's plenty of address types that are not IP based.
You are welcome to reference my ResolveHostname function on Github.
I found the following code to open a connection in C:
int OpenConnection(const char *hostname, int port)
{
int sd;
struct hostent *host;
struct sockaddr_in addr = {0};
if ((host = gethostbyname(hostname)) == NULL)
{
perror(hostname);
abort();
}
sd = socket(PF_INET, SOCK_STREAM, 0);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = *(long *)(host->h_addr_list[0]);
if (connect(sd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
{
close(sd);
perror(hostname);
abort();
}
return sd;
}
As I was rewriting this code in C++, I found out that I should be using getaddrinfo instead of gethostbyname, according to a comment in this post: C - What does *(long *)(host->h_addr); do?.
I was looking at Beej's guide to socket programming, and found the following example:
int status;
struct addrinfo hints;
struct addrinfo *servinfo; // will point to the results
memset(&hints, 0, sizeof hints); // make sure the struct is empty
hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
hints.ai_flags = AI_PASSIVE; // fill in my IP for me
if ((status = getaddrinfo(NULL, "3490", &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
exit(1);
}
// servinfo now points to a linked list of 1 or more struct addrinfos
// ... do everything until you don't need servinfo anymore ....
freeaddrinfo(servinfo); // free the linked-list
However, this example applies to creating a server, which makes me wonder
A] is getaddrinfosupposed to be used for clients
and
B] How would I modify the hints struct and the function parameters in order to make this code suitable for creating a client?
Currently, I have the following code but I
struct addrinfo hints = {0};
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
struct addrinfo *client_info;
const int status = getaddrinfo(hostname, port, &hints, &client_info); //don't know if its correct
Update:
For anyone who may find it useful, here is my final code after reading the accepted answer:
int OpenConnection(const char *hostname, const char *port)
{
struct hostent *host;
if ((host = gethostbyname(hostname)) == nullptr)
{
//More descriptive error message?
perror(hostname);
exit(EXIT_FAILURE);
}
struct addrinfo hints = {0}, *addrs;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
const int status = getaddrinfo(hostname, port, &hints, &addrs);
if (status != 0)
{
fprintf(stderr, "%s: %s\n", hostname, gai_strerror(status));
exit(EXIT_FAILURE);
}
int sfd, err;
for (struct addrinfo *addr = addrs; addr != nullptr; addr = addr->ai_next)
{
sfd = socket(addrs->ai_family, addrs->ai_socktype, addrs->ai_protocol);
if (sfd == ERROR_STATUS)
{
err = errno;
continue;
}
if (connect(sfd, addr->ai_addr, addr->ai_addrlen) == 0)
{
break;
}
err = errno;
sfd = ERROR_STATUS;
close(sfd);
}
freeaddrinfo(addrs);
if (sfd == ERROR_STATUS)
{
fprintf(stderr, "%s: %s\n", hostname, strerror(err));
exit(EXIT_FAILURE);
}
return sfd;
}
The gethostbyname() and gethostbyaddr() functions are deprecated on most platforms, and they don't implement support for IPv6. IPv4 has reached its limits, the world has been moving to IPv6 for awhile now. Use getaddrinfo() and getnameinfo() instead, respectively.
To answer your questions:
A. getaddrinfo() and getnameinfo() can be used for clients and servers alike, just as gethostbyname() and gethostbyaddr() can be. They are just host/address resolution functions, how the resolved values get used is up to the calling app to decide.
B. client code using getaddrinfo() would look something like this:
int OpenConnection(const char *hostname, int port)
{
int sd, err;
struct addrinfo hints = {}, *addrs;
char port_str[16] = {};
hints.ai_family = AF_INET; // Since your original code was using sockaddr_in and
// PF_INET, I'm using AF_INET here to match. Use
// AF_UNSPEC instead if you want to allow getaddrinfo()
// to find both IPv4 and IPv6 addresses for the hostname.
// Just make sure the rest of your code is equally family-
// agnostic when dealing with the IP addresses associated
// with this connection. For instance, make sure any uses
// of sockaddr_in are changed to sockaddr_storage,
// and pay attention to its ss_family field, etc...
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
sprintf(port_str, "%d", port);
err = getaddrinfo(hostname, port_str, &hints, &addrs);
if (err != 0)
{
fprintf(stderr, "%s: %s\n", hostname, gai_strerror(err));
abort();
}
for(struct addrinfo *addr = addrs; addr != NULL; addr = addr->ai_next)
{
sd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (sd == -1)
{
err = errno;
break; // if using AF_UNSPEC above instead of AF_INET/6 specifically,
// replace this 'break' with 'continue' instead, as the 'ai_family'
// may be different on the next iteration...
}
if (connect(sd, addr->ai_addr, addr->ai_addrlen) == 0)
break;
err = errno;
close(sd);
sd = -1;
}
freeaddrinfo(addrs);
if (sd == -1)
{
fprintf(stderr, "%s: %s\n", hostname, strerror(err));
abort();
}
return sd;
}
I've always used gethostbyname() since "forever". It's always worked, it continues to work, and it's "simpler".
getaddrinfo() is the newer function:
http://man7.org/linux/man-pages/man3/getaddrinfo.3.html
The getaddrinfo() function combines the
functionality provided by the gethostbyname(3) and getservbyname(3)
functions into a single interface, but unlike the latter functions,
getaddrinfo() is reentrant and allows programs to eliminate
IPv4-versus-IPv6 dependencies.
I understand that getaddrinfo() ismore robust, more efficient, and more secure: You shouldn't be using gethostbyname() anyway
ADDENDUM:
In reply to your specific questions:
A] getaddrinfo() is preferred over gethostbyname() to lookup the IP address of a hostname; either "client" or "server".
B] Q: How would I modify the hints struct and the function parameters?
A: The "hints" look OK, but I would probably modify the port to NULL.
Here's a complete example:
https://www.kutukupret.com/2009/09/28/gethostbyname-vs-getaddrinfo/
#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: %s hostname\n", argv[0]);
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;
}
for(p = res;p != NULL; p = p->ai_next) {
void *addr;
if (p->ai_family == AF_INET) {
return 1;
} else {
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
/* convert the IP to a string and print it: */
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
printf("Hostname: %s\n", argv[1]);
printf("IP Address: %s\n", ipstr);
}
}
freeaddrinfo(res); // free the linked list
return 0;
}
I have written a UDP broadcaster program to broadcast packets on the subnet 192.168.1.255, port 19000 - let's say it runs on Machine A 192.168.1.26. This is based on http://beej.us/guide/bgnet/html/single/bgnet.html#broadcast. With error checking removed, it looks like:
struct hostent* he = gethostbyname("192.168.1.255");
int fd = socket(AF_INET, SOCK_DGRAM, 0);
int broadcast = 1;
int errors = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(int));
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr = *((struct in_addr*) he->h_addr);
memset(addr.sin_zero, '\n', sizeof(addr.sin_zero));
const char* message = "Hi there";
int len = strlen(message);
int bytes_sent = sendto(fd, data, len, 0, (struct sockaddr*) &addr, sizeof(addr));
I have written a corresponding receiver application to be distributed on machines on the same network as the broadcaster. This is based on http://beej.us/guide/bgnet/html/single/bgnet.html#datagram. Again, with error checking removed, this program looks like this:
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE;
struct addrinfo* servinfo;
int rv = getaddrinfo(NULL, "19000", &hints, &servinfo);
int fd = -1;
struct addrinfo* p;
for (p = servinfo; p != NULL; p = p->ai_next)
{
fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
int errors = bind(fd, p->ai_addr, p->ai_addrlen);
int broadcast = 1;
errors |= setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(int));
if (errors == 0)
break;
}
freeaddrinfo(servinfo);
struct sockaddr their_addr;
socklen_t addr_len = sizeof(their_addr);
int bufsize = 4096;
char buf[bufsize];
int bytes_received = recvfrom(fd, &buf[0], bufsize, 0, (struct sockaddr*) &their_addr, &addr_len);
When I run the broadcaster, in Wireshark I can see packets delivered to other machines on the subnet with the expected source address 192.168.1.26 and destination address 192.168.1.255. The payload is correct. However, my receiver application does not pick up the packets.
If I change the broadcaster to send only to a single receiver machine, e.g. 192.168.1.58, the receiver application on that machine receives the packets correctly but obviously no other machine does.
I have disabled the firewalls on both machines.
Can anyone suggest what's wrong?
I have a ubuntu linux server which is running a c++ socket program which should listen on port 6555 for incoming connections and send "Welcome to the server!" to them. It works when I telnet from localhost, but when I telnet from outside the network it doesn't work.
The local network administrator has told me he has forwarded the port 6555 to the server's local IP, as he did previously for 80.
I also have a web server running on the 80 port, and I can telnet into that from anywhere, so I don't know what the problem is.
I tried disabling the iptables (ufw) firewall on my server temporarily and trying to connect, but there was no difference.
Open Port Checker says the port is closed. Does this mean the forwarding has not worked?
Here is the code, compiled in g++:
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <cstring>
#include <netinet/in.h>
#include <unistd.h>
#include "../include/netutils.hh"
int main() {
sockaddr_storage clt_addr;
socklen_t sin_size;
int cltSock = -1;
const std::string port = "6555";
int servSock = -1;
try {
servSock = getSrvSocket(port, 10);
while (true) {
sin_size = sizeof clt_addr;
cltSock = accept(servSock, (sockaddr*)&clt_addr, &sin_size);
if (cltSock == -1) continue;
if (!fork()) {
close(servSock);
send(cltSock, "Welcome to the server!\n", 23, 0);
while(true);
}
close(cltSock);
}
}
catch(std::string excpt)
{
std::cerr << excpt << '\n';
return -1;
}
return 0;
}
with getSrvSocket() in netutils.cc:
int getSrvSocket(std::string port, int BACKLOG) {
int yes = 1;
addrinfo *p;
addrinfo hints;
addrinfo *servinfo;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; //Can use either IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; //Automatically set IP address
int sock = -1;
if (getaddrinfo(NULL, port.c_str(), &hints, &servinfo) !=0) {
throw std::string("Error: Could not resolve address info");
}
for (p = servinfo; p != NULL; p = p->ai_next) {
sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (sock == -1) continue;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes,
sizeof(int)) == -1) {
throw std::string("Error: setsockopt() failure");
}
if (bind(sock, p->ai_addr, p->ai_addrlen) == -1) {
close(sock);
continue;
}
break;
}
freeaddrinfo(servinfo);
if (NULL == p) throw std::string("Error: Server failed to bind");
if (listen(sock, BACKLOG) == -1) {
throw std::string("Error: Failed to to start listen()");
}
struct sigaction sa;
sa.sa_handler = sigchld_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
throw std::string("Error: sigaction() failed");
}
return sock;
}
getSrvSocket() is specifying AF_UNSPEC when calling getaddrinfo(), which means it can output both IPv4 and IPv6 addresses. But you are only binding to one address and then stopping. So it is possible that you are actually binding to an IPv6 address, and then your client is trying to connect to it using IPv4 instead (or vice versa), which will obviously not work.
If you want to support both IPv4 and IPv6, you should bind a separate listening socket to each address that getaddrinfo() returns. You should also log the addresses that you successfully bind to, so you know what clients can connect to (and what you need to tell the network admin to forward to).
Otherwise, don't use AF_UNSPEC with getaddrinfo(). Use AF_INET (IPv4) or AF_INET6 (IPv6) instead.
I'm trying to make a server to listen on both IPv4 and IPv6 in dual stack mode.
I want the same port number for both IPv4 and IPv6 servers, and I want it to be on a random selection of port (using port "0")
when I bind, each server get different port, and I want it to be the same.
so I thought it should be done by the getaddrinfo function.
But when I give it the "0" port it stays "0" in the addrinfo results, what cause each bind to give me different port number.
My question: Is there a way to tell the getaddrinfo to select a single free port which is free on all interfaces, then bind the given address to all interfaces?
if there isn't, is there other way to find a free port number? (without binding and stop when fail)
please refer to the following code:
EDIT: now the code can fully compiled on VS 10.
#ifdef WIN32
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#define closesocket close
#endif
#include <iostream>
#include <vector>
#include <stdio.h>
#include <string>
int GetAddressFamily()
{
return AF_UNSPEC;
}
std::string ipaddress(addrinfo* info)
{
std::string retval;
char temp[260];
socklen_t addrlen = (socklen_t)info->ai_addrlen;
int res = ::getnameinfo(info->ai_addr, addrlen, temp, 256, NULL, 0, NI_NUMERICHOST);
if(res){
std::cout<<gai_strerrorA(res)<<std::endl;
}else{
retval = temp;
}
return retval;
}
int getport(addrinfo* info)
{
int retval=0;
if (info->ai_family == AF_INET) {
retval = htons(((struct sockaddr_in*)(info->ai_addr))->sin_port);
}else{
retval = htons(((struct sockaddr_in6*)(info->ai_addr))->sin6_port);
}
return retval;
}
int main()
{
char *hostName = NULL; //GetHostName();
int portNum = 0;
#ifdef WIN32
WSADATA w;
if(0 != WSAStartup(MAKEWORD(2, 2), &w))
{
std::cerr<<" WSAStartup() failed \n";
return -1;
}
#endif
addrinfo hints,*results,*tmp;
memset(&hints,0,sizeof(hints));
hints.ai_family = GetAddressFamily();
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_NUMERICSERV;
if(hostName){
hints.ai_flags |= AI_CANONNAME;
//AI_CANONNAME - fills ai_cannonname in address.
}else{
hints.ai_flags |= AI_PASSIVE;
//AI_PASSIVE - give ADDR_ANY and IN6ADDR_ANY_INIT when hostName is NULL
}
char portbuff[40];
sprintf(portbuff,"%u",portNum);
int res = ::getaddrinfo(hostName, portbuff,&hints, &results);
if(res){
std::cerr<<gai_strerrorA(res)<<std::endl;
}else{
std::vector<int> sockets;
for(tmp = results; tmp ; tmp=tmp->ai_next){
std::cout<<ipaddress(tmp).c_str()<<" : "<<port(tmp)<<std::endl;
int s = socket(tmp->ai_family,tmp->ai_socktype,tmp->ai_protocol);
if(s != -1){
res = bind(s, tmp->ai_addr, (int)tmp->ai_addrlen);
if(res != -1){
sockaddr_storage addr;
socklen_t len =sizeof(addr);
int res = getsockname(s, (struct sockaddr *)&addr, &len);
std::cout<<"Bound to port: ";
if(addr.ss_family == AF_INET){
std::cout<<htons(((sockaddr_in*)&addr)->sin_port)<<std::endl;
}else{
std::cout<<htons(((sockaddr_in6*)&addr)->sin6_port)<<std::endl;
}
sockets.push_back(s);
}
}
}
for(int i=0;i<sockets.size();i++){
closesocket(sockets[i]);
}
}
::freeaddrinfo(results);
return 0;
}
EDIT2: My solution for now:
I added the following function to be called after first successful bind, and will set the given port to addrinfo list:
void setport(addrinfo* info,int port)
{
for(addrinfo* tmp = info; tmp ; tmp=tmp->ai_next){
if (tmp->ai_family == AF_INET) {
((struct sockaddr_in*)(tmp->ai_addr))->sin_port = htons(port);
}else{
((struct sockaddr_in6*)(tmp->ai_addr))->sin6_port = htons(port);
}
}
It should be called after successful bind:
port = getport(result)
//...after bind:
if(port == 0) {
port = printed value after succesful bind
setport(result, port)
}
I think your best option is to use an IPv6 socket with IPV6_V6ONLY disabled. Then that single socket will acccept both IPv6 and IPv4 connections. IPv4 clients will have the address set to the mapped address ::ffff:a.b.c.d.
There's not really any reason to use getaddrinfo here, because you're not looking anything up. Something like this should do it (untested):
int s = socket(AF_INET6, SOCK_STREAM, 0);
if (s < 0)
throw std::system_error(errno, generic_category());
const int off = 0;
if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off)) {
close(s);
throw std::system_error(errno, generic_category());
}
struct sockaddr_in6 addr{};
socklen_t alen = sizeof(addr);
if (bind(s, static_cast<struct sockaddr*>(&addr), alen)) {
close(s);
throw std::system_error(errno, generic_category());
}
getsockname(s, static_cast<struct sockaddr*>(&addr), &alen);
int port = ntohs(addr.sin6_port);
PS. It's a good idea to always set the IPV6_V6ONLY option to whichever value you wish, because default varies between OS.