getaddrinfo, bind, localhost and INADDR_ANY - c++

I'm learning socket programing and pretty new to this...
My question is, I want to write a UDP server that is able to receive packets from both remote server and local host via a OS-assigned socket.
I know I should somehow pass INADDR_ANY to bind() to let it accept packet from both. and I guess I need to pass char *service=NULL to getaddrinfo so that it will assign a port. However, I don't know if so what to do with the node parameter of getaddrinfo.
According to getaddrino man page, node and service cannot both be NULL, yet the INADDR_ANY will only be set in the returned socket address when (1) hints.ai_flags=AI_PASSIVE and (2) node is set to NULL. I'm confused with this confliction..
And since "have an OS-assigned port" is the assignment requirement, I can't change the service I guess.
I read that INADDR_ANY is basically 0.0.0.0, does that mean I can just pass this string as node?
Any comment is welcomed!

It is not getaddrinfo() that assigns a random available port. It is bind() that does so when port 0 is requested.
The getaddrinfo() man page is correct. The node and service parameters cannot both be NULL at the same time. You can set node to NULL to get INADDR_ANY - or - you can set service to NULL to get port 0. But, to get both, you will have to either:
set node to NULL, service to "0", and hints.ai_flags to AI_PASSIVE | AI_NUMERICSERV.
set node to "0.0.0.0" and service to NULL (AI_PASSIVE is ignored)
set node to "0.0.0.0", service to "0", and hints.ai_flags to AI_NUMERICSERV (AI_PASSIVE is ignored)
Any of those combinations will cause getaddrinfo() to return a pointer to a sockaddr_in that has its sin_addr set to INADDR_ANY and its sin_port set to 0.
You can then bind() using that sockaddr_in, and bind() will choose a random available port, which you can retrieve using getsockname() afterwards.
int server_fd = -1;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
struct addrinfo *result;
if( getaddrinfo(NULL, "0", &hints, &result) != 0 )
{
// error handling...
}
else if( (server_fd = socket(result->ai_family, result->ai_socktype, result->ai_protocol)) < 0 )
{
// error handling...
freeaddrinfo(result);
}
else if( bind(server_fd, result->ai_addr, result->ai_addrlen) < 0 )
{
// error handling...
close(server_fd);
freeaddrinfo(result);
}
else
{
freeaddrinfo(result);
// use server_fd as needed...
struct sockaddr_in bound_addr;
socklen_t addrlen = sizeof(bound_addr);
if( getsockname(server_fd, (struct sockaddr*)&bound_addr, &addrlen) < 0 )
{
// error handling...
}
else
{
// use ntohs(bound_addr.sin_port) as needed...
}
...
close(server_fd);
}
Or, since you know exactly what you want to bind() to, you can simply ignore getaddrinfo() and populate a sockaddr_in manually:
int server_fd;
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = 0;
if( (server_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0 )
{
// error handling...
}
else if( bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0 )
{
// error handling...
close(server_fd);
}
else
{
// use server_fd as needed...
struct sockaddr_in bound_addr;
socklen_t addrlen = sizeof(bound_addr);
if( getsockname(server_fd, (struct sockaddr*)&bound_addr, &addrlen) < 0 )
{
// error handling...
}
else
{
// use ntohs(bound_addr.sin_port) as needed...
}
...
close(server_fd);
}

Related

LIBSSH2 C++ with dual stack IPv4 and IPv6

I am working on a C++ project that needs to establish a connection via SSH to a remote server and execute some commands and transfer files via SFTP. However, I need this application to work in dual stack mode (e.g., with IPv6 and IPv4) mode.
In the snippet below, my program initially receives an host-name, IPv6 or IPv4 address. In the IPv4 input the connection is successful. However, I am having strange problems in the IPv6 mode that I am noticing that the connection is established via socket and the SSH session fails to start.
Currently, I believe it could be something related to the inet_ntop() method. Please notice that remoteHost variable is an char* type and the remotePort is uint16_t type.
// Initialize some important variables
uint32_t hostaddr = 0, hostaddr6 = 0;
struct sockaddr_in sin = {};
struct sockaddr_in6 sinV6 = {};
int rc = 0, sock = 0, i = 0, auth_pw = 0;
// Here we will initialize our base class with username and password
this->username = usrName;
this->password = usrPassword;
// Firstly, we need to translate the hostname into an IPv4 or IPv6 address
struct addrinfo hints={}, *sAdrInfo = {};
char addrstr[100]={};
void *ptr= nullptr;
char addrParsed[50]={};
memset (&hints, 0, sizeof (hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags |= AI_CANONNAME;
// Now we need to get some address info from the one supplied that remoteHost parameter
int errcode = getaddrinfo (remoteHost, nullptr, &hints, &sAdrInfo);
if (errcode != 0) {
SERVER_ERROR("[SSH] Error while getaddrinfo at SSHConnect() code %d", errno);
return -4;
}
inet_ntop(sAdrInfo->ai_family, sAdrInfo->ai_addr->sa_data, addrstr, 100);
// Here we need to determine if we are using IPv6 or IPv4
switch (sAdrInfo->ai_family) {
case AF_INET6:
ptr = &((struct sockaddr_in6 *) sAdrInfo->ai_addr)->sin6_addr;
break;
case AF_INET:
ptr = &((struct sockaddr_in *) sAdrInfo->ai_addr)->sin_addr;
break;
}
inet_ntop(sAdrInfo->ai_family, ptr, addrstr, 100);
sprintf(addrParsed, "%s", addrstr);
//This part is responsible for creating the socket and establishing the connection
// Now if we have an IPv4 based host
if (sAdrInfo->ai_family == AF_INET) {
this->hostaddr = inet_addr(addrParsed);
sock = socket(AF_INET, SOCK_STREAM, 0);
sin.sin_family = AF_INET;
// Now we need to set these (address and port to our sockaddr_in variable sin)
sin.sin_port = htons(remotePort);
memcpy(&sin.sin_addr, &hostaddr, sizeof(hostaddr));
}
// Now if we have an IPv6 based host
else if (sAdrInfo->ai_family == AF_INET6) {
this->hostaddr6 = inet_addr(addrParsed);
sock = socket(AF_INET6, SOCK_STREAM, 0);
sin.sin_family = AF_INET6;
// Now we need to set these (address and port to our sockaddr_in variable sin)
sinV6.sin6_port = htons(remotePort);
memcpy(&sinV6.sin6_addr.s6_addr32, &hostaddr6, sizeof(this->hostaddr6));
}
// Now we need to connect to our socket :D
if (sAdrInfo->ai_family == AF_INET) {
int resCon = connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in));
if (resCon != 0){
SERVER_ERROR("[SSH] failed to connect with error code: %d!\n", errno);
return -1;
}
}
else if (sAdrInfo->ai_family == AF_INET6) {
int resCon = connect(sock, (struct sockaddr*)(&sinV6), sizeof(struct sockaddr_in6));
if (resCon != 0){
SERVER_ERROR("[SSH] failed to connect with error code: %d!\n", errno);
return -1;
}
}
// Free our result variables
freeaddrinfo(sAdrInfo);
// Create a session instance
session = libssh2_session_init();
if(!session) {
return -2;
}
/* Now to start the session. Here will trade welcome banners, exchange keys and setup crypto, compression,
* and MAC layers */
rc = libssh2_session_handshake(session, sock);
if(rc) {
SERVER_ERROR("Failure establishing SSH session: %d\n", rc);
return -3;
}
What is wrong with the implementation that is generating the "Failure establishing SSH session" message with IPv6 stack?
Best regards,

winsock always returning -1 on socket

I'm trying to connect to my server on port 69 using winsock2.h. Everything appears to be compiling correctly but in a debugger maincommsock is always equal to -1. I can't seem to figure out why it isn't working, and need help. Not very experienced with socket programming, and not sure what to do now!. Any help will be greatly appreciated.
I'm not 100% sure what else to try. This is basically a function that establishes a connection on a socket, and sets connection to true, so that it can loop until it connects.
int maincommsock;
bool connection = false;
static void establishconn() {
maincommsock = socket(AF_INET, SOCK_STREAM, 0);
if (maincommsock == -1) {
connection = false;
}
//OutputDebugString((LPCSTR)commservers[1]);
/*
struct hostent *host;
if ((host = gethostbyname(SERV_ADDR)) == NULL) {
connection = FALSE;
}
*/
SOCKADDR_IN sockaddr;
sockaddr.sin_port = 69;
sockaddr.sin_family = AF_INET;
sockaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
//sockaddr.sin_addr.s_addr = *((unsigned long*)host->h_addr);
if (connect(maincommsock, (SOCKADDR *)(&sockaddr), sizeof(sockaddr)) != 1) {
connection = true;
}
}
EDIT
I used WSAStartup, and the error I'm currently getting is No connection could be made because the target machine actively refused it."
EDIT 2
used htons on the port. It says the operation completed successfully but no connections show up on the server
EDIT 3
I'm going to test it on linux, to make sure that it is actually a server side error and not a problem with my code. I'll keep you guys updated.
Your error handling is a bit lacking.
First off, when an error does occur, STOP what you are doing. Right now, you are not stopping, you just move on to the next API call as if no error occured.
Second, your error handling on connect() is wrong. connect() returns 0 on success and -1 on failure, but you are checking its return value for != 1 so you are going to treat successes and failures the same way.
Try this instead:
static SOCKET establishconn(const char *addr, u_short port)
{
SOCKET commsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (commsock == INVALID_SOCKET)
return INVALID_SOCKET;
//OutputDebugStringA(addr);
SOCKADDR_IN sockaddr = {};
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(port);
sockaddr.sin_addr.s_addr = inet_addr(addr);
if (sockaddr.sin_addr.s_addr == INADDR_NONE)
{
struct hostent *host = gethostbyname(addr);
if (!host)
{
closesocket(commsock);
return INVALID_SOCKET;
}
sockaddr.sin_addr.s_addr = *((u_long*) host->h_addr);
}
if (connect(commsock, (SOCKADDR &sockaddr, sizeof(sockaddr)) == SOCKET_ERROR)
{
closesocket(commsock);
return INVALID_SOCKET;
}
return commsock;
}
Then you can do this:
WSAStartup(...);
...
SOCKET maincommsock = establishconn("127.0.0.1", 69);
if (maincommsock != INVALID_SOCKET)
{
...
closesocket(maincommsock);
}
...
WSACleanup();
Berkeley sockets require the address and port information to be presented in network byte order. Winsock follows this API:
All of the data in the SOCKADDR_IN structure, except for the address family, must be specified in network-byte-order (big-endian).
Windows Dev Center
Use htons() to convert the port value from host to network byte order.
sockaddr.sin_port = htons(69);
inet_addr() already returns a value in network byte order.

How to receive a message from the server in a client? (UDP)

I'm trying to receive a message from the server in my client, and although I don't get any compiling errors, my buffer won't take what the server is sending. I've tried changing the parameters in recvfrom in the client to correlate to the parameters used in the client's sendto but the same thing happens, my memset buffer remains empty. I've also tried just sending a simple null terminated char array of size two to test it, and the same result occurs.
Server:
int sockfd;
struct addrinfo hints, *servinfo, *p;
int rv;
int numbytes;
struct sockaddr_storage their_addr;
char buf[MAXBUFLEN];
socklen_t addr_len;
char s[INET6_ADDRSTRLEN];
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // set to AF_INET to force IPv4
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if ((rv = getaddrinfo(NULL, MYPORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1)
perror("listener: socket");
continue;
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("listener: bind");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "listener: failed to bind socket\n");
return 2;
}
freeaddrinfo(servinfo);
while(1){
addr_len = sizeof their_addr;
if ((numbytes = recvfrom(sockfd, buf, MAXBUFLEN-1 , 0,
(struct sockaddr *)&their_addr, &addr_len)) == -1) {
perror("recvfrom");
exit(1);
}
buf[numbytes] = '\0';
string toRespond = theMove(buf, AG);
char * sendBack = new char[toRespond.size() + 1];
std::copy(toRespond.begin(), toRespond.end(), sendBack);
sendBack[toRespond.size()] = '\0';
sendto(sockfd, testing, strlen(testing), 0, (struct sockaddr *)&their_addr, addr_len);
}
Client:
int sockfd;
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage src_addr;
socklen_t src_addr_len = sizeof(src_addr);
int rv;
int numbytes;
if (argc != 3) {
fprintf(stderr,"usage: talker hostname message\n");
exit(1);
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
if ((rv = getaddrinfo(argv[1], SERVERPORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and make a socket
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("talker: socket");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "talker: failed to bind socket\n");
return 2;
}
char * chatBuff = (char*)malloc(sizeof(char)*512);
while(1){
scanf("%s", chatBuff);
if ((numbytes = sendto(sockfd, chatBuff, strlen(chatBuff), 0,
p->ai_addr, p->ai_addrlen)) == -1) {
perror("talker: sendto");
exit(1);
}
memset(chatBuff, '\0', sizeof(chatBuff));
if (recvfrom(sockfd, chatBuff, strlen(chatBuff), 0, (struct sockaddr*)&src_addr, &src_addr_len) == -1)
{
puts("throw computer out the stacks");
}
puts(chatBuff);
freeaddrinfo(servinfo);
printf("talker: sent %d bytes to %s\n", numbytes, argv[1]);
memset(chatBuff, '\0', sizeof(chatBuff));
}
memset(chatBuff, '\0', sizeof(chatBuff));
While not actually incorrect, this initializing of the entire buffer is cargo-cult nonsense when you intend to load it in the next line with a call that retuns the number of bytes loaded - that return would allow you to ensure a null-terminated string by setting one byte only. The only thing you must remember is that you must leave enough space for the null, either by oversizing the buffer or reducing the read length requested.
if (recvfrom(sockfd, chatBuff, strlen(chatBuff), 0, (struct sockaddr*)&src_addr, &src_addr_len) == -1)
In the (unnecessary and wasteful) line above, you use 'sizeof(chatBuff)' as the buffer size but then here, inexplicably, you shove in 'strlen(chatBuff)' - a RUNTIME CALL that returns the size of a null-terminated char array. Since you just set that array to all null, it returns zero, so your recvfrom() will always return with a 'buffer too small' error unless you receive an empty datagram.
So:
int bytesRec=recvfrom(sockfd, chatBuff, sizeof(chatBuff)-1, 0, (struct sockaddr*)&src_addr, &src_addr_len);
if(bytesRec<1) puts("throw computer out the stacks")
else chatBuff[bytesRec]=0;
In your server code, you are passing some unknown buffer named testing as the buffer to send, but you should be passing the sendBack buffer instead:
sendto(sockfd, sendBack, strlen(sendBack), 0, (struct sockaddr *)&their_addr, addr_len);
For that matter, you can eliminate sendBack and send the data from toRespond directly instead:
sendto(sockfd, toRespond.c_str(), toResponse.size(), 0, (struct sockaddr *)&their_addr, addr_len);
In both your server and client code, you are using AF_UNSPEC when calling getaddrinfo(). That is OK in a server, but generally not OK in a client. Imagine what happens if the server binds to an IPv6 address, but the client creates an IPv4 socket, or vice versa. Obvious mismatch, communication will not be possible (unless the server creates a dual-stack IPv6 socket that can accept both IPv4 and IPv6 packets). So in your client code, you should not use AF_UNSPEC. Use either AF_INET or AF_INET6, depending on what the server is actually bound to. If you must use AF_UNSPEC on the client side, then you need to call sendto() and recvfrom() for every possible server IP address until you receive a response from one of them.
Lastly, in your client code, your call to recvfrom() is assuming the response data will be no more than the same size as the data sent with sendto(). Is that actually the case? You did not show what theMove() does to the data the server receives, or how it generates a response. At the very least, you should replace strlen() with 512 when calling recvfrom(). Your client code is also assuming that the server's response will be null-terminated, but the server is not sending a null terminator in the data it echoes. So you need to terminate the buffer that you are passing to puts().

c++ winsock server-client remote connection

I am trying to create a simple app that exchanges messages between two remote computers.
With the "127.0.0.1" ip it works properly, waits for the client's confirm and connects to the client, but if I try a local "192.168.0.15" or my global ip with the same port, the server connects immediately to sth without waiting for the client.
Here's part of my server code:
char ip[20] = "127.0.0.1"; int port = 19385;
SOCKET sockListen, sockConnect;
SOCKADDR_IN address;
sockConnect = socket (AF_INET, SOCK_STREAM, NULL);
address.sin_addr.s_addr = inet_addr (ip);
address.sin_family = AF_INET;
address.sin_port = htons (port);
sockListen = socket (AF_INET, SOCK_STREAM, NULL);
bind (sockListen, (SOCKADDR*)&address, AddressSize);
listen (sockListen, SOMAXCONN);
sockConnect = accept (sockListen, (SOCKADDR*)&address, &AddressSize);
...
Please suggest how to fix the problem. thx
You are not supposed to initialize your sockConnect variable with socket() before calling accept(). accept() returns a new allocated socket handle that is already connected to a client. You are leaking the socket handle that socket() allocated.
You can only bind() to an IP that belongs to a NIC of the local machine that the app is running on. You cannot bind() to an external IP.
The code you have shown is not doing any error handling at all. You need to add that, eg:
int errCode;
SOCKET sockListen = socket (AF_INET, SOCK_STREAM, NULL);
if (sockListen == INVALID_SOCKET)
{
errCode = WSAGetLastError();
// ...
}
else
{
SOCKADDR_IN address = {0};
address.sin_addr.s_addr = inet_addr (ip);
address.sin_family = AF_INET;
address.sin_port = htons (port);
if (bind (sockListen, (SOCKADDR*)&address, sizeof(address)) == SOCKET_ERROR)
{
errCode = WSAGetLastError();
// ...
}
else
{
if (listen (sockListen, SOMAXCONN) == SOCKET_ERROR)
{
errCode = WSAGetLastError();
// ...
}
else
{
int addressSize;
SOCKET sockClient = accept (sockListen, (SOCKADDR*)&address, &addressSize);
if (sockClient == INVALID_SOCKET)
{
errCode = WSAGetLastError();
// ...
}
else
{
// ...
closesocket(sockClient);
}
}
}
closesocket(sockListen);
}

Finding Local IP via Socket Creation / getsockname

I need to get the IP address of a system within C++. I followed the logic and advice of another comment on here and created a socket and then utilized getsockname to determine the IP address which the socket is bound to.
However, this doesn't appear to work (code below). I'm receiving an invalid IP address (58.etc) when I should be receiving a 128.etc
Any ideas?
string Routes::systemIP(){
// basic setup
int sockfd;
char str[INET_ADDRSTRLEN];
sockaddr* sa;
socklen_t* sl;
struct addrinfo hints, *servinfo, *p;
int rv;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
if ((rv = getaddrinfo("4.2.2.1", "80", &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return "1";
}
// loop through all the results and make a socket
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("talker: socket");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "talker: failed to bind socket\n");
return "2";
}
// get information on the local IP from the socket we created
getsockname(sockfd, sa, sl);
// convert the sockaddr to a sockaddr_in via casting
struct sockaddr_in *sa_ipv4 = (struct sockaddr_in *)sa;
// get the IP from the sockaddr_in and print it
inet_ntop(AF_INET, &(sa_ipv4->sin_addr.s_addr), str, INET_ADDRSTRLEN);
printf("%s\n", str);
// return the IP
return str;
}
Your code already contains the hint: failed to bind socket. But you cut the part of the code that attempts connecting out (did you copy from Stevens UnP?). The socket is not connected to anything, so the network stack has not assigned the local address to it yet.
Once you connect the socket the kernel has to select the local address for it according to the routing table. At that point getsockname(2) will work as expected.
You do not need to allocate a socket to get the machine's available IP addresses. You can use the socket API gethostname() and gethostbyname() functions instead. Or, on Windows, you can alternatively use the Win32 API GetAdaptersInfo() or GetAdaptersAddresses() function instead.