getaddrinfo for IPv6 always fails with error code 11268096 - c++

Edit: Upon Checking on IPv6 Test I Found out My ISP does not provide IPv6... otherwise this code is good
getaddrinfo() always fails for IPv6 with the error code 11268096, but it is successful for IPv4.
Setting Hint.ai_family = AF_INET6; is what triggers the error, but I do not know why.
Also, how do I get the sin_port/sin6_port in numbers? I am always getting port 0.(As #Remy Lebeau pointed out I am only asking for IP of the Domain so it won't output port...)
void GetAddrInfoFromHostNameIPV6(const char* DomainName, addrinfo* Result, bool& IsSuccessful)
{
IsSuccessful = false;
addrinfo Hint;
addrinfo* Return = nullptr;
int ErrorCode;
memset(&Hint, 0, sizeof(Hint));
Hint.ai_family = AF_INET6;
Hint.ai_socktype = SOCK_STREAM;
//Hint.ai_socktype = SOCK_DGRAM;
ErrorCode = getaddrinfo(DomainName, NULL, &Hint, &Return) << '\n';
if (ErrorCode != 0)
{
std::cout << "\n Error GetAddrInfoFromHostNameIPV6() Failed with Error Code: " << ErrorCode << " in GetAddrInfoFromHostName In: NW_P!";
}
else
{
*Result = *Return;
char IpAddress[INET6_ADDRSTRLEN];
uint16_t Port;
inet_ntop(AF_INET6, &((sockaddr_in6*)((Result)->ai_addr))->sin6_addr, IpAddress, INET6_ADDRSTRLEN);
Port = *(&((sockaddr_in6*)(Result->ai_addr))->sin6_port);
std::cout << "\n IPV6 Address of Domain '" << DomainName << "' Is " << IpAddress << " With Port: " << Port;
IsSuccessful = true;
}
if (!IsSuccessful)// For the safe of readability
{
std::cout << "\n Error GetAddrInfoFromHostName() Failed in NW_P!\n";
}
}

You are bit-shifting the error code 10 bits to the left before assigning it to ErrorCode.
Decimal 11268096 is binary 101010111111000000000000. Notice all those extra zeros on the right?
You need to get rid of << '\n' after getaddrinfo() returns, it doesn't belong there, as you are not outputting the error code to std::cout on that line of code.
Removing the bit shifting, the real error code is 11004 (binary 10101011111100) which is WSANO_DATA:
Valid name, no data record of requested type.The requested name is valid and was found in the database, but it does not have the correct associated data being resolved for. The usual example for this is a host name-to-address translation attempt (using gethostbyname or WSAAsyncGetHostByName) which uses the DNS (Domain Name Server). An MX record is returned but no A record—indicating the host itself exists, but is not directly reachable.
You can pass the error code to gai_strerror() to get a human-readable string for your error message output, eg:
std::cout << "\n Error GetAddrInfoFromHostNameIPV6() Failed with Error Code: " << ErrorCode << " (" << gai_strerror(ErrorCode) << ") in GetAddrInfoFromHostName In: NW_P!";
As for the port number being 0, you are not asking getaddrinfo() to parse any service name/port string as input (the pServiceName parameter is NULL), you are only asking for translating a domain name into an IP, so it is not going to output any port number. Port numbers are not used by Domains themselves. Port numbers are used by services (HTTP, etc) that are running on servers where domains/IPs point to.
On a side note, you are leaking the addrinfo list that getaddrinfo() outputs. You need to call freeaddrinfo() when you are done using the list.

Related

GetAddrInfo (C++) on Windows not handling IPv6 correctly, returning error code 11004 for domains that resolve fine through other means

Note: I looked extensively at a similar (but not duplicate) question here: GetAddrInfo cannot resolve ipv6.google.com (but nslookup can). This question is 9 years old, the OP is not experiencing the exact same behavior as I am, and the already-accepted answer does not solve my problem—in fact, I am specifically following the advice of that answer to no avail in the sample code below, and I'm getting a different error than on that question. Furthermore, the likelihood that I would succeed in getting anyone to engage on a question that old by merely commenting on it is nil. It is not the same problem as the problem I am having today, so I am created a new, more-detailed question.
I have a Windows 10 machine running Visual Studio 2017 and a Windows Server 2016 machine running Visual Studio 2019. Both of the machines have IPv4 and IPv6 addresses configured. On both machines, nslookup.exe returns the expected results for three different domains:
> nslookup www.google.com
...
Addresses: 2607:f8b0:4002:80a::2004
172.217.3.228
...
> nslookup ipv4.google.com
...
Addresses: 74.125.136.102
...
> nslookup ipv6.google.com
...
Addresses: 2607:f8b0:4002:812::200e
...
Now I'm trying to write an example program that uses GetAddrInfo to lookup these same three domains:
#include <iostream>
#include <sstream>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib,"WS2_32.Lib")
int main(int argc, char* argv[])
{
WORD wVersionRequested = MAKEWORD(2, 2);
WSADATA wsaData;
int err( WSAStartup(wVersionRequested, &wsaData) );
if (err != 0)
{
std::cout << "WSAStartup failed with error " << err << std::endl << std::flush;
return 1;
}
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM; // so that we get each IP address once, instead of once per socket type
hints.ai_protocol = 0; // we don't care about the protocol
hints.ai_canonname = NULL; // should be NULL for hints
hints.ai_addr = NULL; // should be NULL for hints
hints.ai_next = NULL; // should be NULL for hints
hints.ai_flags = 0;
hints.ai_family = AF_UNSPEC; // I vary this and the hostname below
struct addrinfo* results;
int error = getaddrinfo("ipv6.google.com", NULL, &hints, &results);
if (error != 0)
{
std::ostringstream msg;
std::cout << "Could not resolve hostname due to ";
std::cout << "the following error in getaddrinfo: " << error << ": ";
if (error == -11)
{
std::cout << "EAI_SYSTEM";
}
else
{
std::cout << gai_strerrorA(error);
}
std::cout << std::endl << std::flush;
return 1;
}
for (struct addrinfo* result = results; result != NULL; result = result->ai_next)
{
if (result->ai_family == AF_INET6)
{
char buffer[INET6_ADDRSTRLEN];
std::string ipv6Str(
inet_ntop(
AF_INET6,
&((struct sockaddr_in6*)result->ai_addr)->sin6_addr,
buffer,
sizeof(buffer)));
std::cout << ipv6Str << std::endl << std::flush;
}
else
{
char buffer[INET_ADDRSTRLEN];
std::string ipv4Str(
inet_ntop(
AF_INET,
&((struct sockaddr_in*)result->ai_addr)->sin_addr,
buffer,
sizeof(buffer)));
std::cout << ipv4Str << std::endl << std::flush;
}
}
freeaddrinfo(results);
return 0;
}
For simplicity, I'm just changing the hints.ai_family and hard-coded hostname, then re-compiling and re-running (with Visual Studio). These are the results I get:
Hostname
hints.ai_family
Result
Expected?
www.google.com
AF_UNSPEC
One IPv4 and one IPv6 address
✅
www.google.com
AF_INET
One IPv4 address only
✅
www.google.com
AF_INET6
Err 11004: Name valid, no data
⛔️
ipv4.google.com
AF_UNSPEC
One IPv4 address only
✅
ipv4.google.com
AF_INET
One IPv4 address only
✅
ipv4.google.com
AF_INET6
Err 11004: Name valid, no data
✅
ipv6.google.com
AF_UNSPEC
Err 11004: Name valid, no data
⛔️
ipv6.google.com
AF_INET
Err 11004: Name valid, no data
✅
ipv6.google.com
AF_INET6
Err 11004: Name valid, no data
⛔️
I've hammered at this for a full day now and I can't get getaddrinfo to return the expected results. Why isn't this working? It appears that nslookup.exe using something other than getaddrinfo (perhaps a more low-level query of root and glue name servers), because it works fine there. Also note that this code (with different includes and without the WSA startup procedure) is working just fine in RedHat, CentOS, Ubuntu, and macOS High Sierra.
Some things I've tried other than varying the ai_family, all of which yield the same results as in the table below:
ai_flags = AI_NUMERICSERV
ai_flags = AI_ALL
ai_flags = AI_ALL | AI_NUMERICSERV
ai_socktype = SOCK_DGRAM
ai_socktype = 0
ai_protocol = IPPROTO_TCP
Also, it turns out that ipv6.google.com is actually a CNAME for ipv6.l.google.com, so I tried using that domain, instead, to no avail.

Mutations and C++ lambdas

Trying to do functional programming in C++ and getting bit by some kind of mutation rule.
I have this code:
bpf_u_int32 netp, maskp;
struct in_addr addr;
// ...code
auto set_address_string = [&](auto &do_assign) {
if ((do_assign = inet_ntoa(addr)) == NULL)
cerr << "error - inet_ntoa() failed" << endl;
else
cout << "Some IP: " << do_assign << endl;
};
char *net, *mask;
addr.s_addr = netp;
set_address_string(net);
addr.s_addr = maskp;
set_address_string(mask);
cout << string(net) << endl;
Which is printing the value of mask when I expected the contents of net to be printed, say that net is supposed to be "10.0.0.0" and mask is supposed to be "255.255.255.0".
I assume this has to do with the & I used for the variable do_assign?
EDIT:
Actual output is:
Some IP: 10.0.0.0
Some IP: 255.255.255.0
255.255.255.0
but expected was:
Some IP: 10.0.0.0
Some IP: 255.255.255.0
10.0.0.0
From the manual:
The inet_ntoa() function converts the Internet host address in, given
in network byte order, to a string in IPv4 dotted-decimal notation.
The string is returned in a statically allocated buffer, which
subsequent calls will overwrite.
Both net and mask will point to the same buffer, and, of course, the buffer will contain the result of the last call.

How to use miniupnpc with Boost.Asio UDP when binding a socket to a random port

I'm astonished by the lack of documentation on miniupnp, I believe there's a lot of people using it, but almost no documentation at all, I found a piece of code in the source of RakNet to guide me.
Now I'm having a conceptual issue...
I'm developing an app that connects to a server via UDP (the server should be accessible, the server UDP port is a specific one which is open, and I can test this using any open port checker), then the server puts two or more clients talking to each other (p2p), so I need to circumvent NAT in the clients for that to work.
I already have NAT punch through working, and that already solves lots of cases.
Now, I want to add the UPNP functionality, to attack the NAT issue with this too.
I'm using miniupnpc, and I handle the connections with Boost.Asio.
struct UPNPDev * devlist = 0;
devlist = upnpDiscover(2000, 0, 0, 0, 0, 0);
if (devlist) {
std::cout << "\nList of UPNP devices found on the network :\n";
struct UPNPDev * device;
for(device = devlist; device; device = device->pNext) {
std::cout << "\ndesc: " << device->descURL << "\n st: " << device->st << "\n";
}
char lanaddr[64]; /* my ip address on the LAN */
struct UPNPUrls urls;
struct IGDdatas data;
if (UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)) == 1) {
string port = lexical_cast<string>(socket->local_endpoint().port());
int r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype,
port.c_str(), port.c_str(), lanaddr, 0, "UDP", 0, "0");
if (r != UPNPCOMMAND_SUCCESS) {
std::cout << "\nupnp fail";
}
char intPort[6];
char intClient[16];
char desc[128];
char enabled[128];
char leaseDuration[128];
r = UPNP_GetSpecificPortMappingEntry(urls.controlURL,
data.first.servicetype,
port.c_str(), "UDP", 0,
intClient, intPort,
desc, enabled, leaseDuration);
if (r != UPNPCOMMAND_SUCCESS) {
std::cout << "\nupnp fail";
}else {
std::cout << "\nupnp success on port " << port;
}
}
}
As you can see, I execute the UPNP after having bound the socket (I bind it without an specific port, like this:)
asio::udp::socket socket(*this->ioService, asio::udp::endpoint());
My question is, does this UPNP execution makes any sense? Will this socket actually use the UPNP port map if I execute the UPNP on the random port bound to the socket AFTER I bind it?
Thanks guys!

Issue connecting to IRC Server

Been working on creating an IRC bot recently, and while it seems to connect & work with most IRC servers, it seems to have issues with the Twitch IRC server (irc.twitch.tv).
When connecting to another server, data is received & sent without any issues, however with the twitch connection, I can't send or receive data.
Initialize:
Connection::Connection(char* server, int port, std::string nick, std::string pass)
{
fServer = server;
fPort = port;
fNick = nick;
fPass = pass;
fChanCount = 0;
clear_buffer();
fSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
cout << "Attempting to resolve host: " << fServer << endl;
fHost = resolve_host(fServer);
fSaddr.sin_addr.S_un.S_addr = fHost;
fSaddr.sin_port = htons(fPort);
fSaddr.sin_family = AF_INET;
cout << "Resolved to: " << fHost << endl;
}
Open Connection:
int Connection::Connect()
{
if (connect(fSock, (sockaddr*)&fSaddr, sizeof(fSaddr)) == SOCKET_ERROR)
{
cout << "Can't connect to " << fServer << endl;
return 0;
}
recv(fSock, fBuffer, 1024 * 8, 0);
cout << fBuffer << endl << endl << flush;
send_message("PASS " + fPass);
send_message("NICK " + fNick);
send_message("USER " + fNick + " 0 * :" + fNick);
return 1;
}
Clear Buffer:
void Connection::clear_buffer()
{
memset(fBuffer, 0, sizeof(fBuffer));
}
Receive:
string Connection::receive_message()
{
clear_buffer();
recv(fSock, fBuffer, 1024 * 8, 0);
return fBuffer;
}
Completely stumped on what could be causing this, can provide more detail if needed.
Your USER message does not look like what the RFC2812 spec is asking for.
3.1.3 User message
Command: USER
Parameters: <user> <mode> <unused> <realname>
The USER command is used at the beginning of connection to specify
the username, hostname and realname of a new user.
The <mode> parameter should be a numeric, and can be used to
automatically set user modes when registering with the server. This
parameter is a bitmask, with only 2 bits having any signification: if
the bit 2 is set, the user mode 'w' will be set and if the bit 3 is
set, the user mode 'i' will be set. (See Section 3.1.5 "User
Modes").
The <realname> may contain space characters.
Numeric Replies:
ERR_NEEDMOREPARAMS ERR_ALREADYREGISTRED
Example:
USER guest 0 * :Ronnie Reagan ; User registering themselves with a
username of "guest" and real name
"Ronnie Reagan".
USER guest 8 * :Ronnie Reagan ; User registering themselves with a
username of "guest" and real name
"Ronnie Reagan", and asking to be set
invisible.
Perhaps that's what is causing the problem?
Otherwise I have found some servers require a password in the PASS message even if the server is not configured to require one. In those cases I send:
PASS none

C++ HTTP Winsock: "Banned URL" at school, even websites that are permitted

I'm fairly new to working with sockets and am working on one of my first projects; the first of which I actually fully intend to complete without any libraries. I'm on Windows 7, using only WinAPI.
I'm working on it partially at school,and at my school they have a web filter, FortiGuard I believe. Even when I try to GET a page at a domain that's allowed when I try to open it via a web browser, for example google.com, I'm returned the following message:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style type="text/css">html,body{height:100%;padding:0;margin:0;}.oc{display:table;width:100%;height:100%;}.ic{display:table-cell;vertical-align:middle;height:100%;}div.msg{display:block;border:1px solid#30c;padding:0;width:500px;font-family:helvetica,sans-serif;margin:10px auto;}h1{font-weight:bold;color:#fff;font-size:14px;margin:0;padding:2px;text-align:center;background: #30c;}p{font-size:12px;margin:15px auto;width:75%;font-family:helvetica,sans-serif;text-align:left;}</style>
<title>The URL you requested has been blocked</title>
</head>
<body><div class="oc"><div class="ic"><div class="msg"><h1>The URL you requested has been blocked</h1><p>The page you have requested has been blocked, because the URL is banned.<br /><br />URL = invalid<br /></p></div></div></div></body>
</html>
(I tried to break that up a bit).
As you can see it says the URL is banned, and I think that this is the web filter as this only happens when I'm at school
At least I believe I'm using a correct standard HTTP GET request to a website that works normally on a web browser, yet I'm receiving this message. Am I doing anything wrong with the socket request?
Here's my socket code:
I have a basic "socket_t" structure to pass to all of my functions. Here's the definition for that:
//custom socket structure
typedef struct
{
//windows-specific
struct sockaddr win_addr;
u_long mode;
SOCKET socket;//acutal SOCKET structure
// General
bool listening;//set to true if actively listening
bool thread_terminate;//when boolean is set to true, listening thread terminates
void (*error_callback) (int);
http_response_t * response;
} socket_t;
for the connection:
//see socket_t definition in socket.h
//returns 0 on success, SOCKET_ERROR on WinSock failure, positive error code on DNS failure
int socket_connect(socket_t * sock, char * addr, int port)
{
//bear in mind sock is the custom socket_t structure. sock->socket is the actual SOCKET structure.
//pardon the nomenclature. rookie code.
//TODO: IPv6 support?
//DNS lookup structures
struct addrinfo * res = NULL;// Result of the getaddrinfo() call
struct sockaddr * sockaddr_v4 = NULL;// IPv4 sockaddr structure
// So-called "hints" structure detailed in the getaddrinfo() MSDN page.
// I guess it contains information for the DNS lookup.
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
//Perform DNS lookup
DWORD getaddrinfo_res = getaddrinfo(addr, "80\0", &hints, &res);//hardcode port number, for now
if(getaddrinfo_res != 0) return getaddrinfo_res;
//for each
for(struct addrinfo * ptr = res; ptr != NULL; ptr = ptr->ai_next)
{
switch(ptr->ai_family)
{
case AF_INET:
sockaddr_v4 = (struct sockaddr *) ptr->ai_addr;//set current address
sock->win_addr = * sockaddr_v4;//set socket address
}
}
//initialize actual SOCKET
sock->socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);// TCP/IP, stream-oriented, and TCP rather than UDP; respectively
if(sock->socket == INVALID_SOCKET) return SOCKET_ERROR;
//actual connection
int connect_res = connect(sock->socket, &(sock->win_addr), sizeof(sock->win_addr));
if(connect_res == SOCKET_ERROR) return SOCKET_ERROR;
return 0;
}
And the HTTP request bit:
// Execute an HTTP request.
// Param 1: socket_t object
// Param 2: resource locator not including site name.
int socket_httprequest(socket_t * sock, char * rl)
{
std::string str_req = std::string("GET /");
str_req.append(rl);
str_req.append(" HTTP/1.0\r\n");
/*
//user agent header
str_req.append("User-Agent: conley-client/");
str_req.append(VERSION);
str_req.append("\r\n");
*/
//final newline
str_req.append("\r\n");
const char * z_req = str_req.c_str();
return send(sock->socket, z_req, (int) strlen(z_req), 0);
}
They are all called in the main function like so:
// Initialization
conley::init_sockets();
conley::init_http();
conley::socket_t * sock = conley::socket_alloc(err_callback);
int connect_res = conley::socket_connect(sock, "google.com\0", 80);
if(connect_res > 0) std::cout << "DNS ERR " << connect_res << std::endl;
if(connect_res < 0) std::cout << "CONNECT ERR " << WSAGetLastError() << std::endl;
if(connect_res == 0) std::cout << "Connected" << std::endl;
int httpreq_res = conley::socket_httprequest(sock, "\0");
if(httpreq_res != -1) std::cout << "HTTP request success: " << httpreq_res << " bytes sent" << std::endl;
else std::cout << "HTTP request failure: Error " << WSAGetLastError() << std::endl;
conley::socket_listen(sock);
Thank you for your time!
Probably the error is not caused by your program, but because the filter detects that the request is not send by a browser. Browsers send headers like user-agent to identify themselves to the server that they sent the request to. My advise is to use a tool like Wireshark to capture the traffic generated by a browser and try to mimic it from you application.