

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  This file is part of CMSDK version 2.1.1.260726 (26 July 2026)
//_/_/  
//_/_/  developed by
//_/_/  
//_/_/  Communicative Machines Ltd. 
//_/_/    http://cmlabs.com/
//_/_/  Copyright (c) 2026
//_/_/  
//_/_/  in collaboration with 
//_/_/  
//_/_/  Icelandic Institute for Intelligent Machines ses
//_/_/    Menntavegur 1
//_/_/    101 Reykjavik
//_/_/    ICELAND
//_/_/    http://www.iiim.is/
//_/_/
//_/_/  and is released under the following
//_/_/  
//_/_/  EXTENDED BSD License
//_/_/
//_/_/  Redistribution and use in source and binary forms, with or without
//_/_/  modification, is permitted provided that the following conditions 
//_/_/  are met:
//_/_/
//_/_/  - Redistributions of source code must retain the above copyright 
//_/_/    and collaboration notice, this list of conditions and the following 
//_/_/    disclaimer.
//_/_/
//_/_/  - Redistributions in binary form must reproduce the above copyright 
//_/_/    notice, this list of conditions and the following disclaimer in the 
//_/_/    documentation and/or other materials provided with the distribution.
//_/_/
//_/_/  - Neither the name of its copyright holders nor the names of its 
//_/_/    contributors may be used to endorse or promote products derived from 
//_/_/    this software without specific prior written permission.
//_/_/
//_/_/  - CADIA Clause: The license granted in and to the software under this 
//_/_/    agreement is a limited-use license. The software may not be used in 
//_/_/    furtherance of: (i) intentionally causing bodily injury or severe 
//_/_/    emotional harm to any person; (ii) invading the personal privacy or 
//_/_/    violating the human rights of any person; or (iii) committing or 
//_/_/    preparing for any act of war. 
//_/_/
//_/_/  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
//_/_/  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
//_/_/  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
//_/_/  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 
//_/_/  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
//_/_/  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
//_/_/  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
//_/_/  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
//_/_/  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
//_/_/  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
//_/_/  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//_/_/
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
/*! @file PsySSL.cpp
	@brief Implementation of the OpenSSL-based TLS server helpers (see PsySSL.h).

	Compiled to (almost) nothing unless @c _USE_SSL_ is defined. Contains the
	SSLServer select()/accept loop, certificate loading, and the TestSSL() smoke test.
*/
#include "PsySSL.h"

namespace	cmlabs{

#ifdef _USE_SSL_

SSLServer::SSLServer(const char *cFile, const char *kFile, int port) {

  PORT = port;
  CreateCTX();
  LoadCerts(cFile, kFile);

  // Get the server started.
  BindPort();
}

void SSLServer::CheckClients(int wait_t) {
  struct timeval tv;

  // Set how long to block.
  tv.tv_sec = wait_t;
  tv.tv_usec = 0;

  FD_ZERO(&fdset);
  FD_SET(master, &fdset);

  select(master+1, &fdset, NULL, NULL, (struct timeval *)&tv);

  // If master is set then someone is trying to connect
  if(FD_ISSET(master, &fdset)) {
    SSL *ssl;

    // Open up new connection
 //   struct sockaddr_in addr;
 //   int len = sizeof(addr);
	//int client = (int)accept(master, (struct sockaddr *)&addr, (int *)&len);
	int client = (int)accept(master, NULL, NULL);

	utils::SetSocketNonBlockingMode(client);


#ifdef DEBUG
    struct in_addr ip_address;
    memcpy(&ip_address, &addr.sin_addr.s_addr, 4);

    cout << "\n\n---------------------------------------------\n";
    cout << "Connection from: " << inet_ntoa(ip_address) << "  ("
         << ntohs(addr.sin_port) << ")\n";
#endif /* DEBUG */

    if(client == -1)
      perror("accept");

    ssl = SSL_new(ctx);
    SSL_set_fd(ssl, client);

#ifdef DEBUG
    cout << "Creating Thread\n";
#endif /* DEBUG */

    //pthread_t thread;
    //if(pthread_create(&thread, NULL, pthr_f, (void *)ssl) != 0) {
    //  fprintf(stderr, "pthread_create() has failed.\n");
    //  exit(1);
    //}
#ifdef WINDOWS
		DWORD dwThreadId;
		HANDLE thread = ::CreateThread( 
			NULL,							// no security attributes 
			0,								// use default stack size  
			(LPTHREAD_START_ROUTINE) pthr_f,	// thread function 
			(void *)ssl,							// argument to thread function 
			0,								// use default creation flags 
			&dwThreadId);					// returns the thread identifier 
		utils::Sleep(10);
#endif
  }
}

void SSLServer::BindPort(void) {
#ifdef WINDOWS
	WSADATA info;
	WSAStartup(MAKEWORD(1,1), &info);
#endif

	struct sockaddr_in addr;

//  master = socket(PF_INET, SOCK_STREAM, 0);
  master = (int)socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  memset(&addr, 0, sizeof(addr));

  addr.sin_family = AF_INET;
  addr.sin_port = htons(PORT);
  addr.sin_addr.s_addr = INADDR_ANY;

  // Open the socket
  if(::bind(master, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
    perror("bind");
    _exit(1);
  }

//	unsigned long parm = 1; // 1 = Non-blocking, 0 = Blocking
//	ioctlsocket(master, FIONBIO, &parm);

  // Set a limit on connection queue.
  if(listen(master, 5) != 0) {
    perror("listen");
    _exit(1);
  }

}

void SSLServer::CreateCTX(void) {
  // The method describes which SSL protocol we will be using.
  const SSL_METHOD *method;

	SSL_library_init();
  // Load algorithms and error strings.
  OpenSSL_add_all_algorithms();
  SSL_load_error_strings();

  // Compatible with SSLv2, SSLv3 and TLSv1
  method = SSLv23_server_method();

  // Create new context from method.
  ctx = SSL_CTX_new(method);
  if(ctx == NULL) {
    ERR_print_errors_fp(stderr);
    _exit(1);
  }
}

/* Load the certification files, ie the public and private keys. */
void SSLServer::LoadCerts(const char *cFile, const char *kFile) {
  if ( SSL_CTX_use_certificate_chain_file(ctx, cFile) <= 0) {
    ERR_print_errors_fp(stderr);
    _exit(1);
  }
  if ( SSL_CTX_use_PrivateKey_file(ctx, kFile, SSL_FILETYPE_PEM) <= 0) {
    ERR_print_errors_fp(stderr);
    _exit(1);
  }

  // Verify that the two keys goto together.
  if ( !SSL_CTX_check_private_key(ctx) ) {
    fprintf(stderr, "Private key is invalid.\n");
    _exit(1);
  }
}




#define REPLY "<html><body>Metalshell.com OpenSSL Server</body></html>"
#define MAX_PACKET_SIZE 1024

// Called when a new connection is made.
void *conn_thread(void *ssl) {
  int fd = SSL_get_fd((SSL *)ssl);

  bool success = true;
  int c = 0;
  while(SSL_accept((SSL *)ssl) == -1) {
	  if (++c > 100) {
		  success = false;
		  break;
	  }
	utils::Sleep(10);
  }


  if(!success) {
    ERR_print_errors_fp(stderr);
  } else {
    char cipdesc[128];
    const SSL_CIPHER *sslciph = SSL_get_current_cipher((SSL *)ssl);

    cout << "Encryption Description:\n";
    cout << SSL_CIPHER_description(sslciph, cipdesc, sizeof(cipdesc)) << endl;

	int bytes;
    char* buff = new char[MAX_PACKET_SIZE];
    // Wait for data to be sent.
	while ((bytes = SSL_read((SSL *)ssl, buff, sizeof(buff))) <= 0) {
		utils::Sleep(10);
	}
    buff[bytes] = '\0';

    // Show the browser request.
    cout << "Recieved: \n" << buff << endl;

    // Send the html reply.
    SSL_write((SSL *)ssl, REPLY, (int)strlen(REPLY));
	delete [] buff;
  }

  // Tell the client we are closing the connection.
  SSL_shutdown((SSL *)ssl);

  // We do not wait for a reply, just clear everything.
  SSL_free((SSL *)ssl);
  closesocket(fd);

  cout << "Connection Closed\n";
  cout << "---------------------------------------------\n";

  //pthread_exit(NULL);
  return NULL;
}

int TestSSL(int port) {
  SSLServer server("cert", "pkey", port);

  // Set the thread function.
  server.SetPthread_F(conn_thread);

  while(1) {
    /* Wait for 10 seconds, and if no one trys
     * to connect return back.  This allows us to do
     * other things while waiting.
     */
    server.CheckClients(10);
  }

  return 0;
}

#else // _USE_SSL_

int TestSSL(int port) {return 0;}

#endif //_USE_SSL_

}
