I'm teaching a class where I'd like to show students how open a socket in C++ and read both http: and https: webpages under both Linux and Windows. The Linux version version was a breeze with OpenSSL. But under Windows using Microsoft's WSA socket library, I've only been able to read non-ssl pages. I cannot figure out how to get their WSASetSocketSecurity() function to work.
With the WSASetSocketSecurity call in the following code fragment commented out, I'm able to read http: (port 80) pages. I'm able to connect() to https: (port 443) hosts but attempts to send an HTML GET request and then recv() the page fail as expected with either nothing returned or a 400 bad request page from some servers because I haven't negotiated encryption.
Uncommenting the WSASetSocketSecurity call to guarantee encryption, the connect call always fails with WSAGetLastError = 10060 (Connection timed out) on both http: and https: pages.
Calling WSASetSocketSecurity but specifying allow insecure connections allows me to read http: pages but fails with https: pages in the same way as if WSASetSocketSecurity had not been called at all.
Fundamentally, I'm not able to turn on encryption and then connect and I don't know why.
I have tried experiments replacing the socket, connect and other calls with WSAxxx() versions, imagining there might be distinction similar to the way you have to do an SSL_connect call after the connect in Linux but that makes no difference. The only thing I can think of that I have not yet tried is authenticating the host using WSASetSocketPeerTargetName(), but it doesn't seem to me I should need to do that if all I want is an SSL link.
What am I missing here? Has anyone made this work?
// Initialize the socket library.
// wVersionRequested = 2.2 (current latest)
WSADATA wsaData;
int wsaStartupResult = WSAStartup( MAKEWORD( 2, 2 ), &wsaData );
assert( wsaStartupResult == 0 );
// Get the host address.
ADDRINFOA *addressInfo;
int addrInfoResult = getaddrinfo( url.Host, url.Service,
nullptr, &addressInfo );
assert( addrInfoResult == 0 );
sockaddr *socketAddress = addressInfo->ai_addr;
size_t socketAddressLength = addressInfo->ai_addrlen;
PrintAddress( socketAddress, socketAddressLength );
// Create a TCP/IP socket.
SOCKET s = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
assert( s != INVALID_SOCKET );
// Turn on SSL.
SOCKET_SECURITY_SETTINGS security =
{
SOCKET_SECURITY_PROTOCOL_DEFAULT,
SOCKET_SETTINGS_GUARANTEE_ENCRYPTION
// SOCKET_SETTINGS_ALLOW_INSECURE
};
/*
int setSecurityResult = WSASetSocketSecurity( s,
&security, sizeof( security ), nullptr, nullptr );
assert( setSecurityResult == 0 );
*/
// Connect to the host.
int connectResult = connect( s, socketAddress, socketAddressLength );
if ( connectResult != 0 )
cerr << "Connect failed, WSAGetLastError = " << WSAGetLastError( ) << endl;
assert( connectResult == 0 );
// Send a GET message for the desired page.
string getMessage = "GET ";
getMessage += url.CompleteUrl;
getMessage += " HTTP/1.1\r\nHost: ";
getMessage += url.Host;
getMessage += "\r\nConnection: close\r\n\r\n";
cout << getMessage << endl;
send( s, getMessage.c_str( ), getMessage.length( ), 0 );
// Read from the socket until there's no more data.
HANDLE Stdout = GetStdHandle( STD_OUTPUT_HANDLE );
char buffer[ 10240 ];
int bytes;
DWORD length;
while ( ( bytes = recv( s, buffer, sizeof( buffer ), 0 ) ) > 0 )
WriteFile( Stdout, buffer, bytes, &length, nullptr );
freeaddrinfo( addressInfo );
closesocket( s );
WSACleanup( );
Your assumption that socket security turns on SSL / TLS is wrong. It is actually for enforcing use of IPsec protocol. See Winsock Secure Socket Extensions. If you need SSL/TLS then you should use Secure Channel (built-in Windows SSL/TLS library), OpenSSL or other dedicated library working on top of sockets.