Like this:
------------------------------------------------------------------------------
// Source encoding: utf-8
// MinGW g++ build: “g++ main.cpp -std=c++17 -lws2_32 -lntdll”
#include <stdlib/all/basics.hpp> //
https://github.com/alf-p-steinbach/Wrapped-stdlib
using namespace stdlib::ext::hopefully_and_fail; // hopefully, fail
using namespace stdlib::ext::type_builders; // ptr_, ref_
#include <winsock2.h>
#include <ws2tcpip.h> // getaddrinfo, inet_ntop
// Goal: to wrap
//
// int WSAAPI getaddrinfo(
// ptr_<char const> pNodeName,
// ptr_<char const> pServiceName,
// ptr_<const addrinfo> pHints,
// ptr_<ptr<addrinfo>> ppResult
// );
#if defined( __GNUC__ ) and not defined( NO_INET_NTOP_FIX )
inline auto inet_ntop(
const int family,
const ptr_<const in6_addr> address,
const ptr_<char> buffer,
const socklen_t buffer_size
)
{
assert( family == AF_INET6 ); (void) family;
assert( buffer_size >= INET6_ADDRSTRLEN ); (void) buffer_size;
RtlIpv6AddressToStringA( address, buffer );
return buffer;
}
#endif
namespace cppx
{
template< class Derived >
class Sequential_iteration
{
auto self() -> Derived& { return *static_cast<ptr_<Derived>>(
this ); }
public:
// void advance();
// friend auto equal( ref_<const D> a, ref_<const D> b ) -> bool;
auto operator++()
-> Derived&
{
self().advance();
return self();
}
auto operator++( int )
-> Derived
{
Derived original = self();
self().advance();
return original;
}
friend auto operator==( ref_<const Derived> a, ref_<const
Derived> b )
-> bool
{ return equal( a, b ); }
friend auto operator!=( ref_<const Derived> a, ref_<const
Derived> b )
-> bool
{ return not equal( a, b ); }
};
} // namespace cppx
namespace winsock
{
using std::size;
using std::string;
using std::to_string;
auto fail_with_code( const int code, ref_<const string> msg )
-> bool
{ return fail( msg + ", code " + to_string( code ) ); }
namespace address
{
inline auto raw_info_list(
ref_<const string> host, // E.g. "
google.com",
decimal ip4, or hex ip6
ref_<const string> service // E.g. "http", or port
number
)
-> ptr_<addrinfo>
{
const addrinfo hints = {};
ptr_<addrinfo> p_first;
const int result = ::getaddrinfo(
host.c_str(),
service.c_str(),
&hints,
&p_first
);
hopefully( result == 0 ) or fail_with_code(
result, "winsock::address::raw_info_list - getaddrinfo
failed"
);
return p_first;
}
struct Family
{
enum Enum
{
unspecified = AF_UNSPEC, // 0 The address
Family is unspecified.
ip_v4 = AF_INET, // 2 Internet Protocol
version 4 (IPv4) address.
netbios = AF_NETBIOS, // 17 NetBIOS.
ip_v6 = AF_INET6, // 23 Internet Protocol
version 6 (IPv6).
irda = AF_IRDA, // 26 Infrared Data
Association (IrDA).
bluetooth = AF_BTH, // 32 Bluetooth.
};
};
class Info
{
::addrinfo* m_p_info;
public:
auto size() const
-> int
{ return m_p_info->ai_addrlen; }
auto family() const
-> Family::Enum
{ return static_cast<Family::Enum>( m_p_info->ai_family ); }
auto generic() const
-> ref_<const ::sockaddr>
{
assert( size() >= sizeof( ::sockaddr ) );
return *m_p_info->ai_addr;
}
auto is_ip_v4() const
-> bool
{
return
family() == Family::ip_v4
and size() >= sizeof( ::sockaddr_in );
}
auto unchecked_ip_v4_ptr() const
-> ptr_<const ::sockaddr_in>
{ return reinterpret_cast<ptr_<const ::sockaddr_in>>(
m_p_info->ai_addr ); }
auto ip_v4() const
-> ref_<const ::sockaddr_in>
{
assert( is_ip_v4() );
return *unchecked_ip_v4_ptr();
}
auto is_ip_v6() const
-> bool
{
return
family() == Family::ip_v6
and size() >= sizeof( ::sockaddr_in6 );
}
auto unchecked_ip_v6_ptr() const
-> ptr_<const ::sockaddr_in6>
{ return reinterpret_cast<ptr_<const ::sockaddr_in6>>(
m_p_info->ai_addr ); }
auto ip_v6() const
-> ref_<const sockaddr_in6>
{
assert( is_ip_v6() );
return *unchecked_ip_v6_ptr();
}
Info( const ptr_<::addrinfo> p_info )
: m_p_info{ p_info }
{}
};
class Info_list
{
ptr_<::addrinfo> m_p_first;
bool m_do_cleanup;
public:
class Iterator;
inline auto begin() const -> Iterator;
inline auto end() const -> Iterator;
~Info_list()
{
if( m_do_cleanup )
{
::freeaddrinfo( m_p_first );
}
}
explicit Info_list(
const ptr_<::addrinfo> p_first,
const bool do_cleanup = true
)
: m_p_first{ p_first }
, m_do_cleanup{ do_cleanup }
{}
Info_list(
ref_<const string> host,
ref_<const string> protocol = "http"
)
: Info_list{ raw_info_list( host, protocol ) }
{}
};
class Info_list::Iterator
: public cppx::Sequential_iteration<Iterator>
{
ptr_<addrinfo> m_p_current;
public:
void advance()
{
if( m_p_current )
{
m_p_current = m_p_current->ai_next;
}
}
friend auto equal( ref_<const Iterator> a, ref_<const
Iterator> b )
-> bool
{ return a.m_p_current == b.m_p_current; }
auto operator*() const
-> Info
{ return Info{ m_p_current }; }
Iterator( const ptr_<addrinfo> p_first = nullptr )
: m_p_current{ p_first }
{}
};
inline auto Info_list::begin() const
-> Iterator
{ return Iterator{ m_p_first }; }
inline auto Info_list::end() const
-> Iterator
{ return Iterator{ nullptr }; }
} // namespace address
inline auto string_from( ref_<const ::in_addr> address )
-> string
{ return ::inet_ntoa( address ); }
inline auto string_from( ref_<const ::in6_addr> address )
-> string
{
char buffer[INET6_ADDRSTRLEN];
return inet_ntop( address::Family::ip_v6, &address, buffer,
size( buffer ) );
}
class Library_envelope
{
WSAData m_info;
public:
auto major_version() const -> int { return
m_info.wHighVersion & 0xFF; }
auto minor_version() const -> int { return
m_info.wHighVersion >> 8; }
auto description() const -> string { return
m_info.szDescription; }
~Library_envelope() { WSACleanup(); }
Library_envelope()
{
const uint16_t major_version = 2;
const uint16_t minor_version = 2;
const uint16_t version = (minor_version << 8) | major_version;
const int result = WSAStartup( version, &m_info );
hopefully( result == 0 )
or winsock::fail_with_code(
result, "lib::Winsock::<init> - WSAStartup failed"
);
}
};
} // namespace winsock
namespace library
{
using Winsock = winsock::Library_envelope;
} // namespace library
namespace app
{
using namespace std;
auto operator<<( ref_<ostream> stream, ref_<const ::in_addr> address )
-> ref_<ostream>
{ return stream << winsock::string_from( address ); }
auto operator<<( ref_<ostream> stream, ref_<const ::in6_addr> address )
-> ref_<ostream>
{ return stream << winsock::string_from( address ); }
void run()
{
const library::Winsock ws;
cout << "Winsock " << ws.major_version() << "." <<
ws.minor_version() << "." << endl;
cout << "Its self-description: “" << ws.description() << "”."
<< endl;
cout << string( 72, '-' ) << endl;
auto const& hostname = "
google.com";
winsock::address::Info_list info_list{ hostname };
cout << "Address information for host “" << hostname << "”:" <<
endl;
int i = 0;
for( const winsock::address::Info& info : info_list )
{
cout << endl;
cout << ++i << ": " << info.size() << " bytes." << endl;
switch( info.family() )
{
case winsock::address::Family::ip_v4:
{
ref_<const sockaddr_in> a = info.ip_v4();
cout << "Socket IP v4: "
<< "family " << a.sin_family
<< ", port " << a.sin_port
<< ", addr " << a.sin_addr
<< endl;
break;
}
case winsock::address::Family::ip_v6:
{
ref_<const sockaddr_in6> a = info.ip_v6();
cout << "Socket IP v6: "
<< "family " << a.sin6_family
<< ", port " << a.sin6_port
<< ", flowinfo " << a.sin6_flowinfo
<< ", addr " << a.sin6_addr
<< ", scope_id " << a.sin6_scope_id
<< endl;
break;
}
default:
{
cout << "Socket in family " << info.family() << "."
<< endl;
break;
}
}
}
}
} // namespace app
auto main()
-> int
{
using namespace std;
try
{
app::run();
return EXIT_SUCCESS;
}
catch( exception const& x )
{
cerr << "!" << x.what() << endl;
}
return EXIT_FAILURE;
}
------------------------------------------------------------------------------
> I changed my mind to use class (see CSCall/AddrInfo.h). As always,
> nearly all the classes in the library expose what's wrapped,
> explicitly violating C++ data encapsulation principle.
>
> E.g. WyArray(std::vector), WyStr(std::string)
>
> The fact past have been showing 'good'(not bad) to me, I don't
> know why? Even we all admit data encapsulation is good!
Cheers!,
- Alf