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

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.

Related

Getting SSL error if somebody tryies to connect to my Websocket server

I wrote a Websocket server in c++ using the openssl#3 library and the build in Socket librarys.
Everytime i try to connect from Frontend i get this error:
00B6571701000000:error:0A00009C:SSL routines:ssl3_get_record:http request:ssl/record/ssl3_record.c:345:
I can't really figure out what this means and Im very new to Openssl so i do not know how to fixs this problem.
This is my code: (I put it in a Class because Im writing a project and i don't want the whole code in my main.cpp)
Imports:
#include <openssl/ssl.h>
#include <openssl/err.h>
#include "netdb.h"
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <string>
#include <vector>
Connection struct:
typedef struct {
int socket;
SSL* ssl;
SSL_CTX* ctx;
} Connection;
And this is the Header for the class:
class WebSocketTCPServer{
public:
WebSocketTCPServer();
~WebSocketTCPServer();
void startServer(int port);
void handleConnection(Connection* connection);
void closeConnection(Connection* connection);
void closeServer();
void send(Connection* connection, const std::string& message);
static std::string receive(Connection* connection);
private:
int serverSocket;
std::vector<Connection*> connections;
}
And here the definition:
#include "../Websocket.h"
#include <thread>
#include <iostream>
WebSocketTCPServer::WebSocketTCPServer() {
serverSocket = 0;
}
WebSocketTCPServer::~WebSocketTCPServer() {
closeServer();
}
void WebSocketTCPServer::startServer(int port) {
//command to create a self signed certificate: openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 3650
SSL_library_init();
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
const SSL_METHOD* method = TLSv1_2_server_method();
SSL_CTX* ctx = SSL_CTX_new(method);
if (ctx == NULL) {
ERR_print_errors_fp(stderr);
abort();
}
if (SSL_CTX_use_certificate_file(ctx, "cert.pem", SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
abort();
}
if (SSL_CTX_use_PrivateKey_file(ctx, "key.pem", SSL_FILETYPE_PEM) <= 0 ) {
ERR_print_errors_fp(stderr);
abort();
}
serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket < 0) {
perror("ERROR opening socket");
exit(1);
}
int optval = 1;
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval , sizeof(int));
struct sockaddr_in serveraddr{};
bzero((char *) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons((unsigned short)port);
if (bind(serverSocket, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0) {
perror("ERROR on binding");
exit(1);
}
if (listen(serverSocket, 5) < 0) {
perror("ERROR on listen");
exit(1);
}
std::cout << "ws://localhost:" << port << std::endl;
while (true) {
struct sockaddr_in clientaddr{};
socklen_t clientlen = sizeof(clientaddr);
int clientSocket = accept(serverSocket, (struct sockaddr *) &clientaddr, &clientlen);
if (clientSocket < 0) {
perror("ERROR on accept");
exit(1);
}
auto* connection = new Connection();
connection->socket = clientSocket;
connection->ctx = ctx;
connection->ssl = SSL_new(ctx);
SSL_set_fd(connection->ssl, clientSocket);
if (SSL_accept(connection->ssl) <= 0) {
ERR_print_errors_fp(stderr);
} else {
connections.push_back(connection);
std::thread t(&WebSocketTCPServer::handleConnection, this, connection);
t.detach();
}
}
SSL_CTX_free(ctx);
}
void WebSocketTCPServer::handleConnection(Connection *connection) {
//read msg and print it
std::string msg = receive(connection);
printf("%s", msg.c_str());
}
void WebSocketTCPServer::closeConnection(Connection *connection) {
close(connection->socket);
connections.erase(std::remove(connections.begin(), connections.end(), connection), connections.end());
delete connection;
}
void WebSocketTCPServer::closeServer() {
for(Connection* connection : connections) {
closeConnection(connection);
}
close(serverSocket);
}
void WebSocketTCPServer::send(Connection *connection, const std::string& message) {
::send(connection->socket, message.c_str(), message.length(), 0);
}
std::string WebSocketTCPServer::receive(Connection *connection) {
char buffer[1024] = {0};
int valread = recv(connection->socket, buffer, 1024, 0);
return {buffer, static_cast<size_t>(valread)};
}
Thanks for you help
... SSL routines:ssl3_get_record:http request:ssl/record/ssl3_record.c:345:
This means that your server gets a plain HTTP request (or ws://), not a HTTPS request (or wss://). Thus this specific problem is not in your server, but that the frontend accesses the server in the wrong way.

Reason for connection error in socket programming (on client side)?

I'm having issues with my connect() method on the client side of my socket programming. I'm not sure if the issue is with my code or my method of running it. I'm running it in two seperate terminal windows - one for the server (which I'm running first) with the command './server 8080' and one for the client with the command './client 4 8080 hello'. When I run my code, the server program stops in the while loop just after the printf("this prints\n") line. I presume this means that it is waiting for a client to connect to it. The client program fails on the connect() call, and prints out my error message "Connection Failed". My code is posted below.
Server Code:
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <sys/wait.h>
#define bufsize 1024
void eatZombies(int n){
wait3(NULL,WNOHANG,NULL); // Nom Nom
}
int main(int argc, char *argv[]){
int sock, length, msgsock, status;
struct sockaddr_in server;
pid_t id;
signal(SIGCHLD, &eatZombies);
int server_fd = socket(AF_INET, SOCK_STREAM, 0);
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(atoi(argv[1])); // this time 1st arg is port#
if(bind(server_fd, (struct sockaddr *)&server, sizeof(server)) < 0){
printf("Error binding the socket\n");
exit(0);
}
if(listen(server_fd, SOMAXCONN) < 0){
printf("Error listening for connections\n");
exit(0);
}
char buffer[1024] = {0};
char *hello = "Hello from server";
int addrlen = sizeof(server);
while(1){
printf("this prints\n");
int client_fd = accept(server_fd, (struct sockaddr *)&server, (socklen_t*)&addrlen);
printf("this doesnt\n");
if(client_fd < 0){
printf("Error accepting connection\n");
exit(0);
}
// the next call makes a new child process that will actually handle the client.
id = fork();
// when id == 0, this is the child and needs to do the work for the server.
// when if > 0, this is the parent, and it should just loop around,
// when id < 0, we had an error.
if(id > 0){
continue;
}
else if(id < 0){
printf("Error\n");
exit(0);
}
read(client_fd, buffer, 1024);
printf("%s\n", buffer);
write(client_fd, hello, strlen(hello), 0);
printf("Hello message sent\n");
exit(0);
}
return 0;
}
Client Code:
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define bufsize 1024
int main(argc, argv) int argc; char *argv[];{
int sock, rval;
struct hostent *host;
struct sockaddr_in server; // not a pointer
char buf[bufsize];
printf("%d\n", argc);
if(argc != 4){
printf("usage:\ntcpclient hostname port string\n\n");
return(-1);
}
// look up hostname (server) using DNS
if ((host = gethostbyname(argv[1])) == 0) {
fprintf(stderr, "%s: unknown host\n", argv[1]);
return(-1);
}
// Set up fields for socket to point to host and port
bcopy(host->h_addr, &server.sin_addr, host->h_length);
server.sin_family = AF_INET;
server.sin_port = htons(atoi(argv[2]));
// Create socket
sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock < 0){
printf("Socket Creation Failed\n");
exit(0);
}
// connect (3-way handshake)
if(connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0){
printf("Connection Failed\n");
exit(0);
}
// Copy the arg into buf so we can send it to the server
strncpy(buf, argv[3], bufsize);
// Send sentence to server
send(sock, buf, strlen(buf), 0);
printf("Message sent\n");
// read response from server
rval = read(sock, buf, bufsize);
// print result to window
fprintf(stdout,"%s\n", buf);
close(sock);
}
When running ./client 4 8080 hello, 4 is the host name. You meant to call ./client localhost 8080 hello.
So it was just a mistake in calling the application, not in the code.

openssl query client certificate

My server is unable to get peer certificate
This works when SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,0); is removed
Adding it causes the client and server to not communicate
querying client certificate from server always gives NULL
Below is the following code:-
Header file socket.h, used in server and client sample
#ifndef SOCKET_H
#define SOCKET_H
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <set>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <thread>
int g_start=[]()->int{
system("../Servercert.sh"); /*create the certificates*/
SSL_load_error_strings(); /* load all error messages */
SSL_library_init(); /* load & register all cryptos, etc for TLS 1.2. */
return 0;
}(); //initialize this for all who use TLS 1.2
/*---------------------------------------------------------------------*/
/*--- LoadCertificates - load from files. ---*/
/*---------------------------------------------------------------------*/
void LoadCertificates(SSL_CTX* ctx, char* CertFile, char* KeyFile)
{
/* set the local certificate from CertFile */
if ( SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM) <= 0 )
{
ERR_print_errors_fp(stderr);
abort();
}
/* set the private key from KeyFile (may be the same as CertFile) */
if ( SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0 )
{
ERR_print_errors_fp(stderr);
abort();
}
/* verify private key */
if ( !SSL_CTX_check_private_key(ctx) )
{
fprintf(stderr, "Private key does not match the public certificate\n");
abort();
}
}
SSL_CTX* InitCTX(void)
{ const SSL_METHOD *method;
SSL_CTX *ctx;
method = SSLv23_method(); /* Create new client-method instance */
ctx = SSL_CTX_new(method); /* Create new context */
if ( ctx == NULL )
{
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}
#endif // SOCKET_H
my server source file
#include <iostream>
using namespace std;
#include "socket.h"
SSL *socketsInListenMode(SSL_CTX * &ctx,int portno)
{
ctx = InitCTX(); /* initialize SSL */
LoadCertificates(ctx, "server.cert","server.key");
SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,0);
sockaddr_in serv_addr={},cli_addr={};
int sockListenfd = socket(AF_INET, SOCK_STREAM, 0);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
int bindret=bind(sockListenfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
cout<<bindret<<endl;
if(bindret < 0) return 0;
listen(sockListenfd,10);
socklen_t s=0;
int newsockfd = accept(sockListenfd, (struct sockaddr *)&cli_addr, &s);
SSL *ssl = SSL_new(ctx); /* get new SSL state with context */
SSL_set_fd(ssl, newsockfd); /* set connection socket to SSL state */
SSL_accept(ssl);
close( sockListenfd );
sockListenfd = 0; //this will cause Accept to return
return ssl;
}
#include<string>
int main()
{
SSL_CTX *ctx=0;
SSL *ssl = socketsInListenMode(ctx,5000);
string s="asif";
SSL_write(ssl,s.c_str(),s.length()); //may no write the full string (but thats okay for our sample)
close(SSL_get_fd(ssl));
SSL_free(ssl);
SSL_CTX_free(ctx);
return 0;
}
my client source file
#include <iostream>
#include "../server/socket.h"
using namespace std;
int SocketConnect(int portno, string strIP)
{
struct sockaddr_in serv_addr={};
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(strIP.c_str());
serv_addr.sin_port = htons(portno);
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
int ret=connect(sockfd,(struct sockaddr *) &serv_addr, sizeof(serv_addr));
if(-1 == ret)
{
return -1;
}
return sockfd;
}
SSL *secureConnect(SSL_CTX * &ctx,int portno, string strIP)
{
ctx = InitCTX();
LoadCertificates(ctx, "server.cert","server.key");
SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,0);
SSL *ssl = SSL_new(ctx); /* create new SSL connection state */
int sfd=SocketConnect(portno,strIP); //socketConnect
if(-1 == sfd)
return NULL;
SSL_set_fd(ssl, sfd);
SSL_connect(ssl); //SSL connect
return ssl;
}
int main()
{
SSL_CTX *ctx=0;
SSL *ssl = secureConnect(ctx,5000,"127.0.0.1");
char buff[100]={};
SSL_read(ssl,buff,sizeof(buff));
cout<<buff<<endl<<flush;
close(SSL_get_fd(ssl));
SSL_free(ssl);
SSL_CTX_free(ctx);
return 0;
}
In fact is should not work without SSL_CTX_set_verify also as you have not specified where to find the CA certificates when peer certificate is verified during handshake, may be it works if you have used self-signed certificate
You should use SSL_CTX_load_verify_locations to set the CA certificates

UDP recvfrom not working

I have UDP Server program which receives data from port 7888. The server code is below.
//UDPIPV4Server.h
#pragma once
#include <iostream>
#include <string>
#include <winsock2.h>
#include <windows.h>
#include <Ws2tcpip.h>
using std::string;
using std::cout;
using std::endl;
using std::cerr;
class UDPIPV4Server
{
public:
UDPIPV4Server();
~UDPIPV4Server(void);
UINT type;
string mac_address;
UINT port;
int socket_var;
struct sockaddr_in si_server, si_client;
int Config(void);
int RecvData(char* recv_buffer, int buf_size,string *ip_addr);
};
//UDPIPV4Server.cpp
int UDPIPV4Server::Config(void) {
WSADATA wsadata;
int error = WSAStartup(0X0202, &wsadata);
if(error) {
cerr<<"UdpIPV4Server.cpp:- WSAStartup failed"<<endl;
return -1;
}
if ((socket_var = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
cerr<<"UdpIPV4Server.cpp:- socket function failed"<<endl;
return -1;
}
memset((char *) &si_server, 0, sizeof(si_server));
si_server.sin_family = AF_INET;
si_server.sin_port = htons(7888);
char host[NI_MAXHOST] = "10.8.0.2";
if(inet_pton(AF_INET, host, &si_server.sin_addr) != 1) {
cerr<<"UdpIPV4Server.cpp: inet_pton() failed\n";
return -1;
}
if(bind(socket_var,(struct sockaddr *)&si_server,sizeof(si_server)) == -1) {
cerr<<"UdpIPV4Server.cpp:- bind failed: "<<endl;
return -1;
}
return 0;
}
//recv data from the UDP client
//recv_buffer - [out] receive buffer
//buf_size - [in] size of receive buffer in bytes
//ip_addr - [out] ip address of the remote client
int UDPIPV4Server::RecvData(char* recv_buffer, int buf_size, string *ip_addr) {
int recv_len;
cout<<"waiting for data\n";
memset((char*)&si_client, 0, sizeof(struct sockaddr_in));
int si_client_len = sizeof(si_client);
if((recv_len = recvfrom(socket_var, recv_buffer, buf_size, 0, (struct sockaddr *)&si_client, &si_client_len)) == -1) {
cerr<<"udpipv4server.cpp:- recvfrom failed"<<endl;
return recv_len;
}
char client_addr[INET_ADDRSTRLEN];
cout<<"Received packets from "<<inet_ntoa(si_client.sin_addr)<<":"<<ntohs(si_client.sin_port)<<endl;
*ip_addr = inet_ntoa(si_client.sin_addr);
return recv_len;
}
//main.cpp
#include "UDPIPV4Server.h"
int main() {
UDPIPV4Server udp_server;
udp_server.Config();
char recv_frame_buffer[65534] = "";
memset(recv_frame_buffer,0,sizeof(recv_frame_buffer));
int retval;
string ip_addr;
if((retval = udp_server.RecvData(recv_frame_buffer,sizeof(recv_frame_buffer),&ip_addr)) == -1) {
cerr<<"ReceiverCommModule:- Error in receving data"<<endl;
continue;
}
}
The above program receives data on 10.8.0.2:7888. But this code is not working when data is received. I have checked with wireshark, the data is being received at 10.8.0.2:7888. But socket is unable to read the data from the port.The UDPIPV4Server.config() function passed successfully. But the UDPIPV4Server.RecvData() is not returning. The UDP recvfrom is waiting as such there is no data received. Is it anything wrong with the code? Thank you.
I had the same issue. in UDP, recvfrom behaves as if no data were available dispite wireshark confirmed that a valid UDP packet arrived on the correct port. It happens on my compagny's computer but it works fine on my personnal one. I guess this is linked to windows fire wall or antivir that filters incoming packets if the associated port is not allowed for your software. I noticed that recvfrom works once, just after having sent packet through the same port. May be this is linked to my computer's config.

C Server - "The connection was reset"

I modified beej's guide to networking example (as seen below) to pass back a html response to a browser. I am getting "The connection was reset" every few refreshes, and can't seem to figure out why? Its as if it is closing the connection before it sends out the html response.
Any ideas, or suggestions to debug?
Edit: It does sometimes pass the correct data to the browser.
Here is the code:
/*
** server.c -- a stream socket server demo
*/
#include <iostream>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#define PORT "8080" // the port users will be connecting to
#define BACKLOG 10000 // how many pending connections queue will hold
using namespace std;
void sigchld_handler(int s)
{
while(waitpid(-1, NULL, WNOHANG) > 0);
}
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void)
{
int sockfd, new_fd; // listen on sock_fd, new connection on new_fd
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr; // connector's address information
socklen_t sin_size;
struct sigaction sa;
int yes=1;
char s[INET6_ADDRSTRLEN];
int rv;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("server: socket");
continue;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("server: bind");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "server: failed to bind\n");
return 2;
}
freeaddrinfo(servinfo); // all done with this structure
if (listen(sockfd, BACKLOG) == -1) {
perror("listen");
exit(1);
}
sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
perror("sigaction");
exit(1);
}
printf("server: waiting for connections...\n");
while(1) { // main accept() loop
sin_size = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
if (new_fd == -1) {
cout << "accept fail" << endl;
continue;
}
inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *)&their_addr),
s, sizeof s);
printf("server: got connection from %s\n", s);
if (!fork()) { // this is the child process
close(sockfd); // child doesn't need the listener
string response = "HTTP/1.0 200 OK\r\n\r\n<html><head><title>Test</title></head><body>ok!</body></html>";
if (send(new_fd, response.c_str(), response.length(), 0) == -1)
cout << "error" << endl;
close(new_fd);
cout << "sent." << endl;
exit(0);
}
close(new_fd); // parent doesn't need this
}
return 0;
}
So there's 2 errors here (3 really, the last one being that if you want to talk to a browser you really have to implement HTTP properly which is non-trivial)
You're sending the response immediately when you accept a client. A browser being a bit slow might then receive a response even before it's finished sending a request - which'll confuse the browser.
You're not reading the request. That means when you close the socket, there'll be unread data, this'll lead to a TCP RST (which causes "connection reset .... " errors) being sent when you close the socket. In some cases the browser would have read the response before that happens, in other cases it might not, (and in some cases, I'd guess it'll be confused as you have no Content-Length: header, so the browser doesn't know if it ws supposed to receive more data when it encounters a TCP RST). This particular case is described better here