I have a problem where I need to determine if the host exists prior to connecting to it. This host does not work with the function gethostbyaddr() because it is not PC-based and does not return host information. It is IP-based only. Whenever I try to call gethostbyaddr() on the IP address, WinSock returns 11004 (WSANODATA).
Is there a similar function (besides ping) to determine if an IP is valid before trying to connect?
If you have some kind of control over the destination host, one way you could periodically check if the host is present without using up ephemeral ports would be to send a UDP datagram, and wait for the ICMP response to tell you that the datagram was refused by the host.
You do this by creating a SOCK_DGRAM socket, binding to a local port, and calling sendto() to send to a known remote port which is not listening. You can then poll and call recvfrom() which should give an error if your host got the ICMP response back. If the host is not up then you will not get the response. You can reuse the same socket with the same port to send as many datagrams as are required periodically.
Sending ICMP echo request requires high privileges on most system, so is hard to do directly from your code.
Here is some sample code which does roughly what I describe:
struct sockaddr_in local_address;
struct sockaddr_in remote_address;
int sfd;
char * remote_host;
int s;
fd_set fds;
struct timeval timeout;
remote_host = argv[1];
sfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sfd < 0) {
perror("socket");
}
memset(&local_address, 0, sizeof(struct sockaddr_in));
local_address.sin_family = AF_INET;
local_address.sin_addr.s_addr = INADDR_ANY;
local_address.sin_port = htons(6799);
s = bind(sfd,
(struct sockaddr*)&local_address,
sizeof(local_address));
if (s != 0) {
perror("bind");
exit(1);
}
memset(&remote_address, 0, sizeof(struct sockaddr_in));
remote_address.sin_family = AF_INET;
remote_address.sin_addr.s_addr = inet_addr(remote_host);
remote_address.sin_port = htons(6799);
s = sendto(sfd,
"MSG",
3,
0,
(struct sockaddr*)&remote_address,
sizeof(remote_address));
if (s != 3) {
perror("sento");
exit(1);
}
FD_ZERO(&fds);
FD_SET(sfd, &fds);
timeout.tv_sec = 5;
timeout.tv_usec = 0;
s = select(sfd + 1, &fds, 0, 0, &timeout);
if (s == 1) {
char buf[512];
printf("Got data, host is up\n");
s = recvfrom(sfd, &buf[0], 512, 0, 0, 0);
perror("recvfrom");
} else {
printf("Timeout, host is down\n");
}
I solved the problem by using the built-in Windows API for PING. I changed the gethostbyname() to inet_addr.
shown here: ICMP.DLL Method
dllping.cpp
// Borland C++ 5.0: bcc32.cpp ping.cpp
// Visual C++ 5.0: cl ping.cpp wsock32.lib
//
// This sample program is hereby placed in the public domain.
#include <iostream.h>
#include <winsock.h>
#include <windowsx.h>
#include "icmpdefs.h"
int doit(int argc, char* argv[])
{
// Check for correct command-line args
if (argc < 2) {
cerr << "usage: ping <host>" << endl;
return 1;
}
// Load the ICMP.DLL
HINSTANCE hIcmp = LoadLibrary("ICMP.DLL");
if (hIcmp == 0) {
cerr << "Unable to locate ICMP.DLL!" << endl;
return 2;
}
// Look up an IP address for the given host name
struct hostent* phe;
if ((phe = gethostbyname(argv[1])) == 0) {
cerr << "Could not find IP address for " << argv[1] << endl;
return 3;
}
// Get handles to the functions inside ICMP.DLL that we'll need
typedef HANDLE (WINAPI* pfnHV)(VOID);
typedef BOOL (WINAPI* pfnBH)(HANDLE);
typedef DWORD (WINAPI* pfnDHDPWPipPDD)(HANDLE, DWORD, LPVOID, WORD,
PIP_OPTION_INFORMATION, LPVOID, DWORD, DWORD); // evil, no?
pfnHV pIcmpCreateFile;
pfnBH pIcmpCloseHandle;
pfnDHDPWPipPDD pIcmpSendEcho;
pIcmpCreateFile = (pfnHV)GetProcAddress(hIcmp,
"IcmpCreateFile");
pIcmpCloseHandle = (pfnBH)GetProcAddress(hIcmp,
"IcmpCloseHandle");
pIcmpSendEcho = (pfnDHDPWPipPDD)GetProcAddress(hIcmp,
"IcmpSendEcho");
if ((pIcmpCreateFile == 0) || (pIcmpCloseHandle == 0) ||
(pIcmpSendEcho == 0)) {
cerr << "Failed to get proc addr for function." << endl;
return 4;
}
// Open the ping service
HANDLE hIP = pIcmpCreateFile();
if (hIP == INVALID_HANDLE_VALUE) {
cerr << "Unable to open ping service." << endl;
return 5;
}
// Build ping packet
char acPingBuffer[64];
memset(acPingBuffer, '\xAA', sizeof(acPingBuffer));
PIP_ECHO_REPLY pIpe = (PIP_ECHO_REPLY)GlobalAlloc(
GMEM_FIXED | GMEM_ZEROINIT,
sizeof(IP_ECHO_REPLY) + sizeof(acPingBuffer));
if (pIpe == 0) {
cerr << "Failed to allocate global ping packet buffer." << endl;
return 6;
}
pIpe->Data = acPingBuffer;
pIpe->DataSize = sizeof(acPingBuffer);
// Send the ping packet
DWORD dwStatus = pIcmpSendEcho(hIP, *((DWORD*)phe->h_addr_list[0]),
acPingBuffer, sizeof(acPingBuffer), NULL, pIpe,
sizeof(IP_ECHO_REPLY) + sizeof(acPingBuffer), 5000);
if (dwStatus != 0) {
cout << "Addr: " <<
int(LOBYTE(LOWORD(pIpe->Address))) << "." <<
int(HIBYTE(LOWORD(pIpe->Address))) << "." <<
int(LOBYTE(HIWORD(pIpe->Address))) << "." <<
int(HIBYTE(HIWORD(pIpe->Address))) << ", " <<
"RTT: " << int(pIpe->RoundTripTime) << "ms, " <<
"TTL: " << int(pIpe->Options.Ttl) << endl;
}
else {
cerr << "Error obtaining info from ping packet." << endl;
}
// Shut down...
GlobalFree(pIpe);
FreeLibrary(hIcmp);
return 0;
}
int main(int argc, char* argv[])
{
WSAData wsaData;
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
return 255;
}
int retval = doit(argc, argv);
WSACleanup();
return retval;
}
icmpdefs.h
// Structures required to use functions in ICMP.DLL
typedef struct {
unsigned char Ttl; // Time To Live
unsigned char Tos; // Type Of Service
unsigned char Flags; // IP header flags
unsigned char OptionsSize; // Size in bytes of options data
unsigned char *OptionsData; // Pointer to options data
} IP_OPTION_INFORMATION, * PIP_OPTION_INFORMATION;
typedef struct {
DWORD Address; // Replying address
unsigned long Status; // Reply status
unsigned long RoundTripTime; // RTT in milliseconds
unsigned short DataSize; // Echo data size
unsigned short Reserved; // Reserved for system use
void *Data; // Pointer to the echo data
IP_OPTION_INFORMATION Options; // Reply options
} IP_ECHO_REPLY, * PIP_ECHO_REPLY;
Here you can find the source of a short DNS resolver in C++.
DNS queries are not going to help you to establish whether the box is up (which is what you seem to be trying to do).
If you can run a process on the target box, you could run a heartbeat service of some sort, which would accept a TCP connection from the monitoring app, and send an "I'm alive" message every 2.5 seconds. The inability to connect or the lack of heartbeats would tell your monitoring app that there's a problem.
Alternatively (and perhaps more straightforwardly), why not use ICMP ping?
If you're only allowed a certain number of ephemeral ports, stop using ephemeral ports. Bind the source socket to a known port number before using it to attempt to connect to the other machine.
Alternatively, you don't say why you want to avoid ping. If it's just about doing it in code, you can generate an ICMP packet yourself and use that.
Related
I made a server socket in C++ for Unix (TCP), that accepted only one client socket. Today, I attempted to make it accept multiple ones. For some reason, it ends up only accepting 1, and it's not receiving messages sent by the client that IS able to connect. I believe that the select function isn't working properly. FYI: I'm new to socket programming, so please understand if it's a stupid mistake.
Here's the code:
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <cstring>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/select.h>
#define PORT 8080
#define MAXCLIENTS 30
int main()
{
//create a socket
int listening = socket(AF_INET, SOCK_STREAM, 0); //(returns int) - makes socket (returns what socket it is in terms of an int)
if(listening < 0) //check if we were able to make a socket!
{
std::cerr << "Can't create socket!" << std::endl;
return -1;
}
//bind the socket to an IP/Port
sockaddr_in hint{}; // This creates a structure for the ipv4 info of the socket.
hint.sin_family = AF_INET; //set the family to ipv4
hint.sin_port = htons(PORT); //set the port to the macro set above (use host-to-network-short to conver the int to the port)
hint.sin_addr.s_addr = INADDR_ANY; //set the ip to any address
if(bind(listening, (sockaddr*)&hint, sizeof(hint)) < 0) //attempt to bind socket (inticated by what number the `listening` socket is) to ip/port
{
std::cerr << "Can't bind to IP/Port" << std::endl;
return -1;
}
//mark the socket for listening
if(listen(listening, MAXCLIENTS) < 0 /*SOMAXCONN = maximum amount of connections, defined by sys/socket.h*/) //attempt to listen on the socket number indicated by `listening`
{
std::cerr << "Can't listen on the socket!" << std::endl;
return -1;
}
//FD_CLR() = Remove 1 from set
//FD_SET() = Add to set
//FD_ZERO() = Remove everything from set
//FD_ISSET() = Check if something is part of a set
fd_set master; //define the set
int max_sd;
int client_socks[MAXCLIENTS]{};
while (true)
{
FD_ZERO(&master); //make sure it's cleared
FD_SET(listening, &master); //add the listening socket (server) to the set
max_sd = listening; //max socket descriptor set to the listening socket (need this for the select func)
for (int i = 0; i < MAXCLIENTS; i++) {
if (client_socks[i] > 0) //make sure the particular socket exists
{
FD_SET(client_socks[i], &master); //add it to the set
}
if (client_socks[i] > max_sd) //if the socket is greater than our current maximum socket descriptor
{
max_sd = client_socks[i];
}
}
//wait for some action on any socket within the master fd (this will set the master fd_set to be equal to whatever socket had some action on it)
int activity = select(max_sd + 1, &master, nullptr, nullptr, nullptr);
if (activity < 0) //error!
{
std::cerr << "Error while trying to select!" << std::endl;
}
int addrlen = sizeof(hint);
if (FD_ISSET(listening, &master)) //if the select got that there was action on the listening (server) socket - most likely, a client socket is trying to connect!
{
int client_socket;
client_socket = accept(listening, (sockaddr *) &hint, &addrlen); //accept the first client "waiting to get in"
if (client_socket < 0) {
std::cerr << "Something went wrong when trying to accept a client socket!" << std::endl;
break;
}
std::cout << "New connection: " << inet_ntoa(hint.sin_addr) << " on port " << ntohs(hint.sin_port) << std::endl;
if (send(client_socket, "Welcome to the socket party!", strlen("Welcome to the socket party!"), 0) != strlen("Welcome to the socket party!")) { //greetings!
std::cerr << "Error when sending welcome message." << std::endl; //something went wrong ;(
}
for (int i = 0; i < MAXCLIENTS; i++) //for each index, set client_sock to the address of the index of client_socks, so that we can set the value of it!
{
if(client_socks[i] == 0) //if this position is null (0)
{
client_socks[i] = client_socket;
}
}
}
//else, there was action on a client socket (most likely a message is being sent!
char buffer[2048]; //we need somewhere to store clients messages!
for (int &client_sock : client_socks) //loop through the client sockets
{
if (client_sock != 0)
{
if (FD_ISSET(client_sock, &master) == 0) //check if the select got action on the particular index in the client_socks array
{
memset(&buffer, 0, sizeof(buffer)); //make sure the buffer is clear!
if (read(client_sock, &buffer, 2048) == 0) //check if nothing was recieved from the client
{
getpeername(client_sock, (sockaddr*)&hint, (socklen_t*)&addrlen) < 0; //gets networking info, based off of which socket is passed (the if statement checks for errors). In addition, it sets the values of hint to the info from the passed socket
//print that the client disconnected
std::cout << "A client has disconnected! IP: " << inet_ntoa(hint.sin_addr) << " Port: " << ntohs(hint.sin_port) << std::endl;
close(client_sock); //close the socket
client_sock = 0; //set its value in the array to 0, so that we can reuse it!
}
//else, we got message from the client
for (int &socket : client_socks)
//if (socket != client_sock)
send(socket , buffer , strlen(buffer) , 0 );
}
}
}
return 0;
}
}
Your client_socks variable must be initialised, as Igor said - just append braces: int client_socks[MAXCLIENTS]{};.
Then your for (int &client_sock : client_socks) loop doesn't ignore 0 values (which you obviously want to be a sentinel given "if(client_socks[i] == 0) //if this position is null (0)"). Inside that loop, add "if (client_sock[i] == 0) continue;. Because you try to read` from descriptor 0, it will block waiting for keyboard input.
Further, if you wanted your code to be robust, you would make the listening socket non-blocking, as it's possible for that socket to select readable, but by the time your application goes to accept from it, the client connection attempt has already been dropped: then you'd block waiting to accept another client connection attempt that might never come, and not be servicing existing clients.
I'm working on a multithreaded UDP listener and I'm stuck in a problem that definitely surpasses me.
So, I'm required to receive huge amounts of UDP packets in several ports. Locally, the best solution for me was to call non blocking recvfrom in as much threads as ports I'm listening (select and poll were too slow for my requirements). I'm using a thread pool manager, it simply calls on threads and queues tasks. Here's the code:
void receiveFromSocket(void * arguments){
sockaddr_in client; // Local
socklen_t clientSize = sizeof(client);
memset(&client, 0, sizeof(client));
struct arg_struct_listenPort *args2 = (struct arg_struct_listenPort *)arguments;
int fd = args2->arg_fd;
int port = args2->arg_port;
for(;;) {
char buf[158];
memset(buf,0,158*sizeof(char));
int n = recvfrom(fd, (char * ) buf, 158, MSG_DONTWAIT, ( struct sockaddr *) &client, &clientSize);
if(n == -1){
//cerr << "Error while receiving from client: " << errno << endl;
continue;
}
if(n != 158){
cerr << "Discarded message since it's not 158 bytes." << endl;
continue;
}
struct arg_struct args;
args.arg_port = port;
memcpy(args.buf,buf,158);
thpool_add_work(globals.thpool, socketThread, (void*)(&args));
}
}
/// Runs the Socket listener
int network_accept_any()
{
vector<int>::iterator i;
for(i = globals.fds.begin(); i != globals.fds.end(); i++){
int port = distance(globals.fds.begin(),i);
struct arg_struct_listenPort args;
args.arg_fd = *i;
args.arg_port = globals.cmnSystemCatalogs[port].diag_port;
thpool_add_work(globals.thpool, receiveFromSocket, (void*)(&args));
}
cout << "Listening threads created..." << endl;
return 0;
}
This works perfectly fine locally. But when I compile it on a production environment, some ports listen the packets and other's simply don't! And the working ports change in each execution. I can , confirm that it is not a firewall problem. I also can clearly see the packets through Wireshark. I can receive packets on those ports through netcat. Netstat shows all ports open.
My local environment is an Ubuntu 18.04 VM, and the production environment is a Debian 9.8.
Here's how I call the sockets:
int lSocket(int port) {
//Crear Socket
int listening = socket(AF_INET, SOCK_DGRAM, 0);
if (listening == -1) {
cerr << "No se puede crear el socket";
exit(EXIT_FAILURE);
}
//Enlazar socket a un IP / puerto
struct sockaddr_in hint;
memset(&hint, 0, sizeof(hint));
hint.sin_family = AF_INET; //IPv4
hint.sin_port = htons(port); //Port
hint.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(listening, (struct sockaddr*)&hint, sizeof(hint)) == -1) { //Enlaza las opciones definidas al socket
cerr << "No se puede enlazar IP/puerto" << endl;
exit(EXIT_FAILURE);
}
return listening;
}
Any advise is greatly appreciated!
EDIT:
As suggested, I tried switching to blocking I/O, but the main issue remains. Still not receiving at all the opened ports.
What an amazing welcome!
#molbdnilo was absolutely right:
You're using pointers to objects whose lifetime has ended (&args).
This has undefined behaviour - it might appear to work, but it's a bug
that needs a-fixin'.
Here's the fixed code. Gotta be careful when feeding arguments to threads!
void receiveFromSocket(void * arguments){
sockaddr_in client; // Local
socklen_t clientSize = sizeof(client);
memset(&client, 0, sizeof(client));
struct arg_struct_listenPort *args2 = (struct arg_struct_listenPort *)arguments;
int fd = args2->arg_fd;
int port = args2->arg_port;
for(;;) {
char buf[158];
memset(buf,0,158*sizeof(char));
int n = recvfrom(fd, (char * ) buf, 158, MSG_WAITALL, ( struct sockaddr *) &client, &clientSize);
if(n == -1){
cerr << "Error while receiving from client: " << errno << endl;
continue;
}
if(n != 158){
cerr << "Discarded message since it's not 158 bytes." << endl;
continue;
}
arg_struct *args = new arg_struct;
args->arg_port = port;
memcpy(args->buf,buf,158);
thpool_add_work(globals.thpool, socketThread, (void*)(args));
}
}
/// Runs the Socket listener
int network_accept_any()
{
vector<int>::iterator i;
for(i = globals.fds.begin(); i != globals.fds.end(); i++){
int port = distance(globals.fds.begin(),i);
arg_struct_listenPort *args = new arg_struct_listenPort;
args->arg_fd = *i;
args->arg_port = globals.cmnSystemCatalogs[port].diag_port;
thpool_add_work(globals.thpool, receiveFromSocket, (void*)(args));
}
cout << "Listening threads created..." << endl;
return 0;
}
Also, I'll keep an eye on #John Bollinger 's and #Superlokkus comments.
Thank you all!
I have a code to get local ip address. This is the code I use.
typedef std::map<string,string> settings_t;
void loadLocalIp (settings_t &ipConfig)
{
struct ifaddrs * ifAddrStruct=NULL;
struct ifaddrs * ifa=NULL;
void * tmpAddrPtr=NULL;
getifaddrs(&ifAddrStruct);
for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa ->ifa_addr->sa_family==AF_INET) { // check it is IP4
// is a valid IP4 Address
tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
char addressBuffer[INET_ADDRSTRLEN];
inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
string key(ifa->ifa_name);
string value(addressBuffer);
cout<<key<<" =1 " <<value<<endl;
ipConfig.insert(std::pair<string,string>(key, value));
// printf("'%s': %s\n", ifa->ifa_name, addressBuffer);
}
}
if (ifAddrStruct!=NULL)
freeifaddrs(ifAddrStruct);//remember to free ifAddrStruct
}
int main()
{
settings_t ipConfig;
loadLocalIp(ipConfig);
cout<<ipConfig.at("enp2s0")<<endl;
return 0;
}
So My result, is
lo =1 127.0.0.1
enp2s0 =1 172.20.55.6
172.20.55.6
But In another computer, the interface name is different. They get result like bellow,
lo =1 127.0.0.1
ens32 =1 172.20.55.9
terminate called after throwing an instance of 'std::out_of_range'
what(): map::at
Aborted (core dumped)
I want to get my Ip address whatever the interface name is. How can I get my local ip address if the interface name varies from different computer. It should give the ip address whatever the interface name is. How can I do this?
My question is, Now I am getting my local IP from this method. But I should get IP whatever the Interface name is. One thing, I need to find that interface name and apply it in my above code (or) Is there any other option to find that IP without that interface?
I want to get my IP address whatever the interface name is.
It is difficult to reliably get the local ip address by looking at the network interface. As you have already discovered, the network interface name can be unique for each host you run on. To further complicate things, a computer may have multiple network interfaces and each of those may or may not be connected to the Internet.
You don't need to use the default interface. A more simplistic approach is to just let the OS routing table figure it out for you. You can do this by setting up a socket connection to some external server and then calling getsockname to get the local address. This example uses Google's DNS server at 8.8.8.8 to establish a socket connection but you can use whatever external server you'd like.
#include <iostream> ///< cout
#include <cstring> ///< memset
#include <errno.h> ///< errno
#include <sys/socket.h> ///< socket
#include <netinet/in.h> ///< sockaddr_in
#include <arpa/inet.h> ///< getsockname
#include <unistd.h> ///< close
int main()
{
const char* google_dns_server = "8.8.8.8";
int dns_port = 53;
struct sockaddr_in serv;
int sock = socket(AF_INET, SOCK_DGRAM, 0);
//Socket could not be created
if(sock < 0)
{
std::cout << "Socket error" << std::endl;
}
memset(&serv, 0, sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_addr.s_addr = inet_addr(google_dns_server);
serv.sin_port = htons(dns_port);
int err = connect(sock, (const struct sockaddr*)&serv, sizeof(serv));
if (err < 0)
{
std::cout << "Error number: " << errno
<< ". Error message: " << strerror(errno) << std::endl;
}
struct sockaddr_in name;
socklen_t namelen = sizeof(name);
err = getsockname(sock, (struct sockaddr*)&name, &namelen);
char buffer[80];
const char* p = inet_ntop(AF_INET, &name.sin_addr, buffer, 80);
if(p != NULL)
{
std::cout << "Local IP address is: " << buffer << std::endl;
}
else
{
std::cout << "Error number: " << errno
<< ". Error message: " << strerror(errno) << std::endl;
}
close(sock);
return 0;
}
None of these answers seemed good enough: either too much trouble walking through the interfaces or required connection to internet.
Here is a method based upon Justin Randall's answer. It's basically the same but it connects a UDP socket rather than a TCP. As per udp(7), using connect(3) on a unbound UDP socket:
will automatically assign a free local port [...] and bind the socket to INADDR_ANY
Moreover, conversely to a TCP socket, connect(3) on a UDP socket does not present any network overhead or communication, as it only changes the rules regarding which packet to drop and which to keep on the socket buffers.
Therefore, connecting to any IP address that is not INADDR_LOOPBACK is sufficient to retrieve a local address which has been chosen to bind the socket.
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <cstring>
#include <iostream>
int main(void) {
int sock = socket(PF_INET, SOCK_DGRAM, 0);
sockaddr_in loopback;
if (sock == -1) {
std::cerr << "Could not socket\n";
return 1;
}
std::memset(&loopback, 0, sizeof(loopback));
loopback.sin_family = AF_INET;
loopback.sin_addr.s_addr = 1337; // can be any IP address
loopback.sin_port = htons(9); // using debug port
if (connect(sock, reinterpret_cast<sockaddr*>(&loopback), sizeof(loopback)) == -1) {
close(sock);
std::cerr << "Could not connect\n";
return 1;
}
socklen_t addrlen = sizeof(loopback);
if (getsockname(sock, reinterpret_cast<sockaddr*>(&loopback), &addrlen) == -1) {
close(sock);
std::cerr << "Could not getsockname\n";
return 1;
}
close(sock);
char buf[INET_ADDRSTRLEN];
if (inet_ntop(AF_INET, &loopback.sin_addr, buf, INET_ADDRSTRLEN) == 0x0) {
std::cerr << "Could not inet_ntop\n";
return 1;
} else {
std::cout << "Local ip address: " << buf << "\n";
}
}
Thank you for your solution. It works fine. But When I search for solution, I came up with the following answer also. Please have a look at it. What is pros and cons of this answer.
FILE *f;
char line[100] , *p , *c;
f = fopen("/proc/net/route" , "r");
while(fgets(line , 100 , f))
{
p = strtok(line , " \t");
c = strtok(NULL , " \t");
if(p!=NULL && c!=NULL)
{
if(strcmp(c , "00000000") == 0)
{
printf("Default interface is : %s \n" , p);
break;
}
}
}
//which family do we require , AF_INET or AF_INET6
int fm = AF_INET;
struct ifaddrs *ifaddr, *ifa;
int family , s;
char host[NI_MAXHOST];
if (getifaddrs(&ifaddr) == -1)
{
perror("getifaddrs");
exit(EXIT_FAILURE);
}
//Walk through linked list, maintaining head pointer so we can free list later
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next)
{
if (ifa->ifa_addr == NULL)
{
continue;
}
family = ifa->ifa_addr->sa_family;
if(strcmp( ifa->ifa_name , p) == 0)
{
if (family == fm)
{
s = getnameinfo( ifa->ifa_addr, (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6) , host , NI_MAXHOST , NULL , 0 , NI_NUMERICHOST);
if (s != 0)
{
printf("getnameinfo() failed: %s\n", gai_strerror(s));
exit(EXIT_FAILURE);
}
printf("address: %s", host);
}
printf("\n");
}
}
freeifaddrs(ifaddr);
return 0;
Cool method of getting local ip is to execute the ipconfig command, save the output to a file read it, and parse the data so the output only shows your ipv4 address for example.
Can be done with something like this:
std::string GetParsedIPConfigData(std::string Columb)
{
//Set up command file path and command line command
std::string APPDATA = getenv("APPDATA");
std::string path = APPDATA + "\\localipdata.txt";
std::string cmd = "ipconfig > " + path;
//execute ipconfig command and save file to path
system(cmd.c_str());
//current line
std::string line;
//Line array : Here is all lines saved
std::string lineArray[500];
int arrayCount = 0;
std::ifstream file(path);
if (file.is_open())
{
//Get all lines
while (std::getline(file, line))
{
//Save each line into a element in an array
lineArray[arrayCount] = line;
arrayCount++;
}
for (int arrayindex = 0; arrayindex <= arrayCount; arrayindex++)
{
std::string s = Columb;
std::string s2 = ":";
//Search all lines and get pos
std::size_t i = lineArray[arrayindex].find(s);
std::size_t i2 = lineArray[arrayindex].find(s2);
//Found a match for Columb
if (lineArray[arrayindex].find(s) != std::string::npos)
{
//Validate
if (i != std::string::npos)
{
//Earse Columb name
lineArray[arrayindex].erase(i, s.length());
//Erase all blanks
lineArray[arrayindex].erase(remove_if(lineArray[arrayindex].begin(), lineArray[arrayindex].end(), isspace), lineArray[arrayindex].end());
//Found match for ':'
if (lineArray[arrayindex].find(s2) != std::string::npos)
{
//Validate
if (i2 != std::string::npos)
{
//Delete all characters prior to ':'
lineArray[arrayindex].erase(0, lineArray[arrayindex].find(":"));
lineArray[arrayindex].erase(std::remove(lineArray[arrayindex].begin(), lineArray[arrayindex].end(), ':'), lineArray[arrayindex].end());
}
}
//Return our data
return lineArray[arrayindex];
}
}
//Only go through all lines once
if (arrayindex == arrayCount)
break;
}
//Close file
file.close();
}
//Something went wrong
return "Invalid";
}
And the just call it like so:
cout << parser.GetParsedIPConfigData("IPv4 Address") << "\n\n";
(English is not my native tongue, don't worry if some sentences are strange ;) ).
I was developing a PONG game and by the way creating some classes to help me managing window, event ... and network because I added a LAN feature to the game but currently you have to enter the address of the one with who you want to play with. And a solution to that was a broadcast (scanning LAN for player). This was easy with ipv4, just use the address 255.255.255.255 but we are in 2017 and provide a feature that works only with ipv4 sucks...
Then I look for a way to broadcast with ipv6 and I learn about multi-cast but this part just get me lost. =(
I use standard libraries on Linux in C++, I found several example of multi-cast that didn't work with me. The best I have done at this time is sending a udp packet from one instance of the program to an other on the same computer.
How can I multi-cast with ipv6 udp socket on Linux in C/C++ ?
The best code found on Internet (I rearranged it) that almost work
(there is client and serv all in one, choice is made by adding 1 or 0 to argv) :
int main(int argc, char const *argv[]) {
struct sockaddr_in6 groupSock;
int sd = -1;
char databuf[10];
int datalen = sizeof databuf;
/* Create a datagram socket on which to send/receive. */
if((sd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
perror("Opening datagram socket error");
return 1;
} else {
cout << "Opening the datagram socket...OK." << endl;;
}
/* Enable SO_REUSEADDR to allow multiple instances of this */
/* application to receive copies of the multicast datagrams. */
int reuse = 1;
if(setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof reuse) < 0) {
perror("Setting SO_REUSEADDR error");
close(sd);
return 1;
} else {
cout << "Setting SO_REUSEADDR...OK." << endl;
}
/* Initialize the group sockaddr structure with a */
memset((char *) &groupSock, 0, sizeof groupSock);
groupSock.sin6_family = AF_INET6;
// address of the group
inet_pton(AF_INET6, "ff0e::/16", &groupSock.sin6_addr);
groupSock.sin6_port = htons(4321);
/* Set local interface for outbound multicast datagrams. */
/* The IP address specified must be associated with a local, */
/* multicast capable interface. */
int ifindex = if_nametoindex ("enp3s0");
cout << "ifindex is " << ifindex << endl;
if(setsockopt(sd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof ifindex)) {
perror("Setting local interface error");
return 1;
} else {
cout << "Setting the local interface...OK" << endl;
}
// choice is 0 for sending and 1 for receiving
int choice;
if (argc < 2) {
cout << "missing argv[1]" << endl;
return 1;
}
sscanf (argv[1], "%d", &choice);
// if sending
if (choice == 0) {
memset(databuf, 'a', datalen);
databuf[sizeof databuf - 1] = '\0';
if (sendto(sd, databuf, datalen, 0, (sockaddr*)&groupSock, sizeof groupSock) < 0) {
cout << "Error in send" << endl;
} else {
cout << "Send okay!" << endl;
}
}
// if receiving
else if (choice == 1) {
groupSock.sin6_addr = in6addr_any;
if(bind(sd, (sockaddr*)&groupSock, sizeof groupSock)) {
perror("Binding datagram socket error");
close(sd);
return 1;
} else {
cout << "Binding datagram socket...OK." << endl;
}
/* Join the multicast group ff0e::/16 on the local */
/* interface. Note that this IP_ADD_MEMBERSHIP option must be */
/* called for each local interface over which the multicast */
/* datagrams are to be received. */
struct ipv6_mreq group;
inet_pton (AF_INET6, "ff0e::", &group.ipv6mr_multiaddr.s6_addr);
group.ipv6mr_interface = ifindex;
if(setsockopt(sd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char *)&group, sizeof group) < 0) {
perror("Adding multicast group error");
close(sd);
return 1;
} else {
cout << "Adding multicast group...OK." << endl;
}
if (read(sd, databuf, datalen) < 0) {
perror("Error in read");
} else {
databuf[sizeof databuf - 1] = '\0';// just for safety
cout << "Read Okay" << endl;
cout << "Message is : " << databuf << endl;
}
}
return 0;
}
Here the address is ff0e:: but I have try with ff01:: and ff02::.
I need help, I have not found any simple documentation about that. Thanks in advance for any answer.
Edit :
Thanks Ron Maupin and Jeremy Friesner for these comments, it helps me.
Edit :
THANKS Jeremy ! Your advice to use ff12::blah:blah(...) instead of ff0e:: works ! Should I write answer to my question to close the thread ?
This code below is right:
The only thing wrong is the address used for the multicast.
Like Jeremy said it, ff0e:: is not correct, I used instead ff12::feed:a:dead:beef and it works.
It is possible to get the name and index of the available interface by using if_nameindex().
Update : I try to remove some code to see if it work without it and I manage to get this :
server :
// OPEN
int fd = socket(AF_INET6, SOCK_DGRAM, 0);
// BIND
struct sockaddr_in6 address = {AF_INET6, htons(4321)};
bind(fd, (struct sockaddr*)&address, sizeof address);
// JOIN MEMBERSHIP
struct ipv6_mreq group;
group.ipv6mr_interface = 0;
inet_pton(AF_INET6, "ff12::1234", &group.ipv6mr_multiaddr);
setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &group, sizeof group);
// READ
char buffer[128];
read(fd, buffer, sizeof buffer);
client :
// OPEN
int fd = socket(AF_INET6, SOCK_DGRAM, 0);
// ADDRESS
struct sockaddr_in6 address = {AF_INET6, htons(4321)};
inet_pton(AF_INET6, "ff12::1234", &address.sin6_addr);
// SEND TO
char buffer[128];
strcpy(buffer, "hello world!");
sendto(fd, buffer, sizeof buffer, 0, (struct sockaddr*)&address, sizeof address);
The following c++ program should convert each line to uppercase using socket datagram to communicate between two threads.
Example:
Hello World!<return>
HELLO WORLD!
123abc!<return>
123ABC!
<return>
<end program>
The program as written works for me, however if I comment the bugfix() function call in the main the program wait indefinitely after the first line of input.
Example:
Hello World!<return>
<the program wait indefinitely>
This happen on windows 7 with the last update as 10/04/2011 using the last MinGW32.
#include <iostream>
#include <cstdlib>
#include <cctype>
#include <sys/types.h>
#include <winsock.h>
#include <windows.h>
#include <process.h>
using namespace std;
#define CHECK(exp, cond) do { typeof(exp) _check_value_ = exp; check(_check_value_ cond, _check_value_, __LINE__, #exp #cond); } while(0)
template <class T>
void check(bool ok, T value, int line, const char* text) {
if (!ok) {
cerr << "ERROR(" << line << "):" << text << "\nReturned: " << value << endl;
cerr << "errno=" << errno << endl;
cerr << "WSAGetLastError()=" << WSAGetLastError() << endl;
exit(EXIT_FAILURE);
}
}
#define DATA_CAPACITY 1000
#define PORT 23584
#define TEST_IP "192.0.32.10"
#define MYSELF "127.0.0.1"
#define DST_IP MYSELF
sockaddr_in address(u_long ip, u_short port) {
sockaddr_in addr = { };
addr.sin_family = AF_INET;
addr.sin_port = port;
addr.sin_addr.s_addr = ip;
return addr;
}
void __cdecl client_thread(void* args) {
SOCKET s = *(SOCKET*)args;
sockaddr_in addr = address(inet_addr(DST_IP), htons(PORT));
char data[DATA_CAPACITY];
while (1) {
cin.getline(data, DATA_CAPACITY);
int data_len = strlen(data);
CHECK(sendto(s, data, data_len, 0, (sockaddr*)&addr, sizeof addr), >= 0);
CHECK(recvfrom(s, data, DATA_CAPACITY, 0, NULL, NULL), >= 0);
cout << data << endl;
if (data_len == 0)
break;
}
CHECK(closesocket(s), == 0);
}
void __cdecl server_thread(void* args) {
SOCKET s = *(SOCKET*)args;
sockaddr_in addr = address(INADDR_ANY, htons(PORT));
int addr_size = sizeof addr;
CHECK(bind(s, (sockaddr*)&addr, sizeof addr), != SOCKET_ERROR);
char data[DATA_CAPACITY];
while (1) {
int data_len = recvfrom(s, data, DATA_CAPACITY, 0, (sockaddr*)&addr, &addr_size);
CHECK(data_len, >= 0);
for (int i = 0; i < data_len; i++)
if (islower(data[i]))
data[i] = toupper(data[i]);
CHECK(sendto(s, data, data_len, 0, (sockaddr*)&addr, addr_size), >= 0);
if (data_len == 0)
break;
}
CHECK(closesocket(s), == 0);
}
// This function create a TCP connection with www.example.com and the close it
void bugfix() {
SOCKET s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
sockaddr_in addr = address(inet_addr(TEST_IP), htons(80));
connect(s, (sockaddr*)&addr, sizeof addr);
CHECK(closesocket(s), == 0);
}
int main()
{
cout << "Convert text to uppercase, an empty line terminate the program" << endl;
WSADATA wsaData;
CHECK(WSAStartup(MAKEWORD(2, 2), &wsaData), == 0);
SOCKET client = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
SOCKET server = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
CHECK(client, != INVALID_SOCKET);
CHECK(server, != INVALID_SOCKET);
// if this function is not called the program doesn't work
bugfix();
HANDLE hClient = (HANDLE)_beginthread(client_thread, 0, &client);
HANDLE hServer = (HANDLE)_beginthread(server_thread, 0, &server);
HANDLE h[] = { hClient, hServer };
WaitForMultipleObjects(sizeof h / sizeof *h, h, TRUE, INFINITE);
CHECK(WSACleanup(), == 0);
return EXIT_SUCCESS;
}
int data_len = strlen(data);
Tony Hoare called his definition of a NULL pointer his billion dollar mistake. Having strings zero-terminated must be Dennnis Ritchie's ten billion dollar mistake. Add one.
Your program is otherwise an elaborate way to discover that UDP is not a reliable protocol. The network stack is allowed to arbitrarily make UDP packets disappear or reorder them. Which is okay as long as there's another protocol on top of it that detects this, like TCP. You are flying without such bandaids, bugfix() is not actually a workaround.
Use TCP, send the packet length first so that the receiver will know how many bytes are following so you're immune to stream behavior. But more to the point, exchanging data between threads through a socket is a really expensive way to avoid using an array with a mutex. Threads have unfettered access to memory in the process, you don't need an interprocess communication mechanism to get them to exchange data.
I see several problems right off the bat.
I normally don't use IPPROTO_UDP flag to create the socket. Just pass 0 for the protocol parameter to the socket.
SOCKET client = socket(PF_INET, SOCK_DGRAM, 0);
SOCKET server = socket(PF_INET, SOCK_DGRAM, 0);
More important. You need to call "bind" on the client socket in the same way that you do the server socket. If you want the OS to pick a randomly available port for you, you can use 0 as the port value and IPADDR_ANY for the IP address. If you want to know what the OS picked as a local port for you, you can use getsockname. Something like the following:
void __cdecl client_thread(void* args) {
SOCKET s = *(SOCKET*)args;
sockaddr_in addr = address(inet_addr(DST_IP), htons(PORT));
sockaddr_in localAddrBind = address(INADDR_ANY, 0);
sockaddr_in localAddrActual = {};
int length = sizeof(localAddrActual);
int bindRet = bind(s, (sockaddr*)&localAddrBind, sizeof(localAddrBind));
getsockname(s, (sockaddr*)&localAddrActual, &length);
printf("Listening on port %d\n", ntohs(localAddrActual.sin_port));