SSL_read(ssl, buf, sizeof(buf)) buf has an Additional beginning bytes - c++

I working with windows x64, openssl library in c++ 20 with CMake. I have a websocket server and trying to connect it with this code:
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <winsock2.h>
#include <cstdio>
#include <string>
#include <iostream>
#pragma comment(lib, "ws2_32.lib")
using namespace std;
int main() {
WSADATA wsaData;
SSL_CTX *ssl_ctx;
SSL *ssl;
SOCKET sock;
struct sockaddr_in server_addr;
struct hostent *host;
int ret;
char buf[1024];
string request;
string response = "";
//Initialize Windows Socket
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
printf("Failed to initialize.\n");
return -1;
}
//Initialize SSL
SSL_library_init();
//Create SSL context and set the TLS version
ssl_ctx = SSL_CTX_new(TLSv1_2_client_method());
//Create socket
sock = socket(AF_INET, SOCK_STREAM, 0);
//Connect to server
host = gethostbyname("localhost");
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080);
server_addr.sin_addr.s_addr = *(unsigned long *) (host->h_addr);
ret = connect(sock, (struct sockaddr *) &server_addr, sizeof(server_addr));
if (ret == SOCKET_ERROR) {
printf("Failed to connect.\n");
return -1;
}
//Create SSL object
ssl = SSL_new(ssl_ctx);
//Connect the SSL object with the socket
SSL_set_fd(ssl, sock);
//Establish TLS connection
SSL_connect(ssl);
//Construct and send a WebSocket request
request = "GET / HTTP/1.1\r\n";
request += "Host: localhost:8080\r\n";
request += "Upgrade: websocket\r\n";
request += "Connection: Upgrade\r\n";
request += "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n";
request += "Sec-WebSocket-Version: 13\r\n\r\n";
SSL_write(ssl, request.c_str(), request.length());
//Receive a response
while (true) {
memset(buf, 0, sizeof(buf));
int len = SSL_read(ssl, buf, sizeof(buf));
response = string(buf, len);
cout << response << endl;
}
//Clean up
SSL_free(ssl);
closesocket(sock);
SSL_CTX_free(ssl_ctx);
WSACleanup();
return 0;
}
I know the server is sending the correct json object because I can hit the server with a Postman or Curl script and I get the correct json object.
the first response is this:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
�{"type":"pong"}
but SSL_read() set the buf to "�\xf{"type":"pong"}" and all the messages that recived has that Additional beginning bytes.
I want to get this "{"type":"pong"}".
I try another server and get different byte. how to get response without that bytes?

This is Websocket pong message (Upgrade: websocket). 2 first bytes �\xf are 0x00 0x0f, that is 15, that is the length of the JSON.
{"type":"pong"}
cURL decoded the pong message in the dump.

Related

Why does this minimal HTTP test server fail half of the time?

I'm trying to write a minimal HTTP server on Windows and see the response in Chrome with http://127.0.0.1/5000. The following code works sometimes ("Hello World"), but the request fails half of the time with ERR_CONNECTION_ABORTED in Chrome (even after I restart the server). Why?
Error:
#include <winsock2.h>
#include <iostream>
#pragma comment(lib, "ws2_32.lib")
int main()
{
WSADATA WSAData;
SOCKET sock, csock;
SOCKADDR_IN sin, csin;
WSAStartup(MAKEWORD(2,0), &WSAData);
sock = socket(AF_INET, SOCK_STREAM, 0);
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_family = AF_INET;
sin.sin_port = htons(5000);
bind(sock, (SOCKADDR *) &sin, sizeof(sin));
listen(sock, 0);
while(1)
{
int sinsize = sizeof(csin);
if((csock = accept(sock, (SOCKADDR *) &csin, &sinsize)) != INVALID_SOCKET)
{
std::string response = "HTTP/1.1 200 OK\nConnection: close\nContent-Length: 11\n\nHello World";
send(csock, response.c_str(), response.size(), 0);
std::cout << "done";
closesocket(csock);
}
}
return 0;
}
You fail to read the client's request before closing the connection. This usually results in the server sending a RST back to the client, which can cause the ERR_CONNECTION_ABORTED when it is processed before the response itself was processed.
As observed by another (deleted) answer, this can be "mitigated" by adding some short delay before the connection is closed, so that the response is processed by the client.
The right fix is to read the request from the client, though.
Apart from that, your response is not valid HTTP since you use \n instead of \r\n as line end and header end.
Working solution:
#include <winsock2.h>
#include <iostream>
#define DEFAULT_BUFLEN 8192
#pragma comment(lib, "ws2_32.lib")
int main()
{
char recvbuf[DEFAULT_BUFLEN];
WSADATA WSAData;
SOCKET sock, csock;
SOCKADDR_IN sin, csin;
WSAStartup(MAKEWORD(2,0), &WSAData);
sock = socket(AF_INET, SOCK_STREAM, 0);
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_family = AF_INET;
sin.sin_port = htons(5000);
bind(sock, (SOCKADDR *) &sin, sizeof(sin));
listen(sock, 0);
while (1)
{
int sinsize = sizeof(csin);
if ((csock = accept(sock, (SOCKADDR *) &csin, &sinsize)) != INVALID_SOCKET)
{
recv(csock, recvbuf, DEFAULT_BUFLEN, 0);
std::string response = "HTTP/1.0 200 OK\r\nConnection: close\r\nContent-Length: 11\r\n\r\nHello World";
send(csock, response.c_str(), response.size(), 0);
std::cout << "done";
closesocket(csock);
}
}
return 0;
}

how to protect against TLS GOLDENDOODLE with c++ and openssl?

have a simple web server made with C++ and openssl, and when running SSL Lab's SSL Server Test on it, it informs me that the server is vulnerable to GOLDENDOODLE and Sleeping POODLE (among other things, screenshot here),
i'm running libopenssl 1.1.1c, which is the latest openssl release as of writing, so i don't think it's a case of using an old outdated vulnerable TLS library, instead i'm probably just using it wrong,
hence the question: how do you protect against GOLDENDOODLE with openssl? here is the entire (vulnerable?) server source code:
#include <iostream>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <thread>
#include <chrono>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
const uint16_t port=443;
int create_socket(const uint16_t port)
{
int s;
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
s = socket(AF_INET, SOCK_STREAM, 0);
if (s < 0)
{
perror("Unable to create socket");
exit(EXIT_FAILURE);
}
if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0)
{
perror("Unable to bind");
exit(EXIT_FAILURE);
}
if (listen(s, 1) < 0)
{
perror("Unable to listen");
exit(EXIT_FAILURE);
}
return s;
}
void init_openssl()
{
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
}
void cleanup_openssl()
{
EVP_cleanup();
}
SSL_CTX *create_context()
{
const SSL_METHOD *method;
SSL_CTX *ctx;
method = SSLv23_server_method();
ctx = SSL_CTX_new(method);
if (!ctx)
{
perror("Unable to create SSL context");
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
return ctx;
}
void configure_context(SSL_CTX *ctx)
{
// - Congratulations! Your certificate and chain have been saved at:
// /etc/letsencrypt/live/fuviewer.ml/fullchain.pem
// Your key file has been saved at:
// /etc/letsencrypt/live/fuviewer.ml/privkey.pem
(void)ctx;
SSL_CTX_set_ecdh_auto(ctx, 1);
/* Set the key and cert */
// SSL_FILETYPE_PEM
if (SSL_CTX_use_certificate_chain_file(ctx, "fullchain.pem") <= 0)
{
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
if (SSL_CTX_use_PrivateKey_file(ctx, "privkey.pem", SSL_FILETYPE_PEM) <= 0 )
{
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
}
int main(int argc, char **argv)
{
(void)argc;
(void)argv;
int sock;
SSL_CTX *ctx;
init_openssl();
ctx = create_context();
configure_context(ctx);
sock = create_socket(port);
std::cout << "server running!" << std::flush;
/* Handle connections */
while(1)
{
struct sockaddr_in addr;
socklen_t len = sizeof(addr);
SSL *ssl;
const char reply[] =
"HTTP/1.0 200 OK\r\n"
"Test-header: Yep\r\n"
"Content-Length: 3\r\n"
"\r\n"
"abc";
const auto reply_size=sizeof(reply)-1;
int client = accept(sock, (struct sockaddr*)&addr, &len);
if (client < 0)
{
perror("Unable to accept");
exit(EXIT_FAILURE);
}
ssl = SSL_new(ctx);
SSL_set_fd(ssl, client);
if (SSL_accept(ssl) <= 0)
{
ERR_print_errors_fp(stderr);
}
else
{
if(SSL_write(ssl, reply, reply_size)!=reply_size)
{
throw std::runtime_error("FAILED TO SEND ALL BYTES");
};
{
// openssl gets cranky if we don't try to read at least 1 byte, even tho we don't really want to..
uint8_t unused;
SSL_read(ssl,&unused,sizeof(unused));
}
SSL_shutdown(ssl);
//std::this_thread::sleep_for(std::chrono::seconds(1));
}
SSL_free(ssl);
close(client);
}
close(sock);
SSL_CTX_free(ctx);
cleanup_openssl();
}
While there are only few information about affected TLS stacks it looks like that OpenSSL should not be vulnerable, even when using CBC ciphers.
I think what you see instead is a false positive, which is triggered by your server not matching the expectations of the test. While I don't know how the GOLDENDOODLE detection by SSLLabs works I've looked at the original detection program from Tripwire.
And it looks like that this script expects a proper HTTP server at the other end, i.e. one which first reads the request and then sends the response. Only, your server does it the other way: first sending the response and reading (and ignoring) a bit of the request only after the response was sent. Such non-HTTP behavior is not taken into account (why should it) and confuses the detection which results in falsely reporting a non-existing problem.

UDP client stucks in recvfrom only in specific situation

I'm writing a C++ code that implements an UDP server and client.
The code works fine when I write two codes, one for the server and another for the client, as in this example : https://www.geeksforgeeks.org/udp-server-client-implementation-c/ .
What I'm trying to do is to write a client function and a server function in the same code. The ideia is that I select how the program is going to work with the command lines argument.
The problem is that, implementing this way and testing in two terminals running the same code, with different command line arguments, one for server and another for client, the client stucks in the recvfrom, when receiving the server response.
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#define MAXLINE 1024
#define PORT 32000
int send(){
int sockfd;
char buffer[MAXLINE];
char *hello = "Hello from server";
struct sockaddr_in servaddr, cliaddr;
// Creating socket file descriptor
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
memset(&cliaddr, 0, sizeof(cliaddr));
// Filling server information
servaddr.sin_family = AF_INET; // IPv4
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
servaddr.sin_port = htons(PORT);
// Bind the socket with the server address
if ( bind(sockfd, (const struct sockaddr *)&servaddr,
sizeof(servaddr)) < 0 )
{
perror("bind failed");
exit(EXIT_FAILURE);
}
socklen_t len;
int n;
n = recvfrom(sockfd, (char *)buffer, MAXLINE,
MSG_WAITALL, ( struct sockaddr *) &cliaddr,
&len);
buffer[n] = '\0';
printf("Client : %s\n", buffer);
sendto(sockfd, (const char *)hello, strlen(hello),
MSG_CONFIRM, (const struct sockaddr *) &cliaddr,
len);
printf("Hello message sent.\n");
return 0;
}
int receive(){
int sockfd;
char buffer[MAXLINE];
char *hello = "Hello from client";
struct sockaddr_in servaddr;
// Creating socket file descriptor
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
perror("socket creation failed");
exit(EXIT_FAILURE);
}
memset(&servaddr, 0, sizeof(servaddr));
// Filling server information
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int n;
socklen_t len;
sendto(sockfd, (const char *)hello, strlen(hello),
MSG_CONFIRM, (const struct sockaddr *) &servaddr,
sizeof(servaddr));
printf("Hello message sent.\n");
n = recvfrom(sockfd, (char *)buffer, MAXLINE,
MSG_WAITALL, (struct sockaddr *) &servaddr,
&len);
buffer[n] = '\0';
printf("Server : %s\n", buffer);
return 0;
}
int main(int argc, char const *argv[]) {
int command = atoi(argv[1]);
if(command == 0){
send();
}
if(command == 1){
receive();
}
return 0;
}
The expected results is something like this, that i get when running the client and the server on separated codes:
Server side:
Hello from client
Hello message sent
Client side:
Hello message sent
Hello from server
But what I get when running the code above is
Server side:
Hello from client
Hello message sent
Client side:
Hello message sent
---gets stucked here---
What am i doing wrong?
In your send() function, you are not initializing len to the length of the buffer where recvfrom can store the client address.
According to the man page for recvfrom:
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
If src_addr is not NULL, and the underlying protocol provides the
source address of the message, that source address is placed in the
buffer pointed to by src_addr. In this case, addrlen is a value-result
argument. Before the call, it should be initialized to the size of the
buffer associated with src_addr. Upon return, addrlen is updated to
contain the actual size of the source address.
It's not working because the client address isn't being properly received so the response message is being sent to the wrong address. To resolve your problem, you just need to initialize len before the call to recvfrom:
socklen_t len = sizeof(cliaddr); // The size of the buffer you're passing to store the client address

Sockets downloading too little or too much of a webpage

Why does my code only download half a webpage?? Sometimes it downloads 4x the webpage's size :S
I cannot find what is wrong which is why I'm asking. Basically, I connect to the socket, send my Request and read the response to a buffer. I tried saving it to a file and printing it to the screen but it prints and saves incomplete data or too much data. I'm not sure if its a buffer-overflow or not or what I'm doing wrong.
Any ideas?
#define _WIN32_WINNT 0x501
#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <fstream>
using namespace std;
void Get(string WebPage)
{
WSADATA wsaData;
string Address;
struct addrinfo *result;
struct sockaddr_in *sockaddr_ipv4;
char Buffer[50000] = {0};
string Header = "GET / HTTP/1.1\r\n";
Header += "Host: " + WebPage + "\r\n";
Header += "Connection: close\r\n";
Header += "\r\n";
if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) return;
SOCKET Socket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
getaddrinfo(WebPage.c_str(), NULL, NULL, &result);
if (result->ai_family == AF_INET)
{
sockaddr_ipv4 = (struct sockaddr_in *) result->ai_addr;
Address = inet_ntoa(sockaddr_ipv4->sin_addr);
}
freeaddrinfo(result);
SOCKADDR_IN SockAddr;
memset(&SockAddr, 0, sizeof(SockAddr));
SockAddr.sin_port = htons(80);
SockAddr.sin_family = AF_INET;
SockAddr.sin_addr.s_addr = inet_addr(Address.c_str());
if(connect(Socket,(SOCKADDR*)(&SockAddr),sizeof(SockAddr)) == SOCKET_ERROR) return;
if (send(Socket, Header.c_str(), Header.size(), 0) == SOCKET_ERROR) return;
shutdown(Socket, SD_SEND);
std::string Response;
while(true)
{
int Val = recv(Socket, Buffer, sizeof(Buffer), 0);
if (Val == 0)
break;
else if (Val == SOCKET_ERROR)
{
cout<<"Error!";
}
else
{
Response += Buffer;
}
}
closesocket(Socket);
WSACleanup();
ofstream File;
File.open("C:/Saved.html");
File<<Response;
File.close();
}
int main()
{
Get("villavu.com");
}
Edit: recv isn't null terminating the data for you - you need to write how much data you received, not just += it.
Is there any binary data in your response? If so, the
Response += Buffer;
will stop at the first null character. I would use a vector to store the data from the recv as such:
vector<char> recvBuffer(50000);
int bytesReceived = recv(socket, recvBuffer.data(), recvBuffer.size(), 0);
//error checking
recvBuffer.resize(bytesReceived);
and again store your received data in another vector, copying it back in.
vector<char> pageContents;
pageContents.insert(pageContents.end(), recvBuffer.begin(), recvBuffer.end());
That wouldn't explain your 4x data though.
Another issue I see is that you aren't zeroing out your buffer after it is used.
IOW: You need to write how much data you received, not just += the array.

Winsock problems sending data C++

I'm trying to code a simple FTP client with Winsock.
I have the following code:
using namespace std;
#include <iostream>
#include <cstring>
#include <cstdio>
#include <winsock.h>
#include <windows.h>
int main() {
const int MAX_TRIES = 10;
char * host = "localhost";
int port = 21;
char * userName = "b8_8780454";
char * pass = "test";
char * testFileSource = "C:\\Windows\\notepad.exe";
WSADATA WSAData;
SOCKADDR_IN server;
SOCKET sock;
WSAStartup(MAKEWORD(2,2), &WSAData);
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET) {
cout<<"fail";
return -1;
}
server.sin_family = PF_INET;
server.sin_port = htons(port);
server.sin_addr = *((struct in_addr *)gethostbyname(host)->h_addr);
memset(server.sin_zero,0,8);
int errorCode = connect(sock, (LPSOCKADDR)&server, sizeof(struct sockaddr));
int tries = 0;
while (errorCode == SOCKET_ERROR) {
if (tries >= MAX_TRIES) {
cout<<"fail 2";
return -1;
}
errorCode = connect(sock, (LPSOCKADDR)&server, sizeof(struct sockaddr));
tries++;
}
char serverMsg[2048];
Sleep(1000);
cout<<"Waiting for server response..."<<endl;
int r = recv(sock,serverMsg,2048,0);
serverMsg[r] = '\0';
cout<<endl<<endl<<"Server said: "<<endl<<serverMsg<<endl<<endl;
char userB[1024] = "USER ";
strcat(userB,userName);
cout<<"Sending... "<<userB<<endl;
cout<<"sended: "<<send(sock, userB, strlen(userB), 0)<<" bytes"<<endl;
Sleep(1000);
cout<<"Waiting for server response..."<<endl;
serverMsg[0] = '\0';
recv(sock,serverMsg,2048,0); // <-- program keeps lock here
cout<<endl<<endl<<"Server said: "<<endl<<serverMsg<<endl<<endl;
getchar();
return 0;
}
I think the send is not working properly, nevertheless it is returning >0 but on the server side i can't see this client sending any data. I think i maybe a problem with the conection setup, but i have been checking some sites and I am not able to catch the error
This is what the program prints:
Waiting for server response...
Server said:
220-FileZilla Server version 0.9.31 beta
220-written by Tim Kosse (Tim.Kosse#gmx.de)
220 Please visit http://sourceforge.net/projects/filezilla/
Sending... USER b8_8780454
sended: 15 bytes
Waiting for server response...
Server said:
421 Login time exceeded. Closing control connection.
by Tim Kosse (Tim.Kosse#gmx.de)
220 Please visit http://sourceforge.net/projects/filezilla/
In my FTP server I can't see this client sending any data to the server. Any clue?
You need a new line (\n) after your username.
strcat(userB,userName);
strcat(userB,"\n");