I am trying to implement a DTLS server wrapper in C++.
What I intend to do is when I receive a message, if the client is unknown then assume it's a handshake and try to complete it. If the client is known, assume it's a regular session message.
The issue I have here is that my program expects the same message to be sent twice to process it correctly-ish (once for the 'is the peer known?' part and once for the SSL_accept/SSL_read part).
I assume I'm not setting my BIOs correctly but I have no idea what the correct way of doing this is.
Here is what my files look like at the moment:
/* server.hpp */
#ifndef _DTLS_SERVER_
#define _DTLS_SERVER_
#include <atomic>
#include <mutex>
#include <vector>
#include <arpa/inet.h>
#include <assert.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <map>
#include <algorithm>
class DTLSServer{
private:
public:
static int generate_cookie(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len); //placeholder
static int verify_cookie(SSL *ssl, const unsigned char *cookie, unsigned int cookie_len); //placeholder
struct client
{
BIO *bio;
SSL *ssl;
std::string onConnect(const uint16_t& p_sock, const std::string&, SSL_CTX *p_ctx);
std::string onMessage(const std::string&);
};
// fd to use
void init(const int &sock);
DTLSServer();
~DTLSServer();
//void stop(int p_signal);
// if unknown pair => adds new client => handshake else returns decyphered message
std::string autoConnect(const sockaddr_in &p_addr, const std::string p_string);
// remove client p_addr:p_port
void removeClient(const std::string &p_addr, const uint16_t &p_port);
// return added client p_addr:p_port
client &createClient(const std::string &p_addr, const uint16_t &p_port);
private:
SSL_CTX *i_ctx;
std::map<std::pair<std::string, uint16_t>, client>i_clients;
uint64_t i_securitySessionsLifeTime;
std::mutex i_dtlsMutex;
int i_sock;
public:
};
#endif
/* server.cpp */
#include "server.hpp"
#include <unistd.h>
#include <iostream>
#include <thread>
char cookie_str[] = "cookie";
DTLSServer::DTLSServer(){}
DTLSServer::~DTLSServer()
{
i_dtlsMutex.lock();
for(auto &l_cli : i_clients)
{
removeClient(l_cli.first.first, l_cli.first.second);
}
SSL_CTX_free(i_ctx);
i_dtlsMutex.unlock();
}
std::string DTLSServer::autoConnect(const sockaddr_in &p_addr, const std::string p_string){
char l_strAddress[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &p_addr.sin_addr.s_addr, l_strAddress, INET_ADDRSTRLEN);
std::string l_addr(l_strAddress);
uint16_t l_port = ntohs(p_addr.sin_port);
std::lock_guard<std::mutex>l_guard(i_dtlsMutex);
auto l_cli = std::find_if(i_clients.begin(),i_clients.end(), // looking for {addr, port} in clients
[l_addr, l_port](const std::pair<std::pair<std::string, uint16_t>, client> &el) // {(addr,port),client}
{
return l_addr == el.first.first
&& l_port == el.first.second;
});
if(l_cli!=i_clients.end()) // found a client
{
std::cout << "received message from " << l_addr << ":" << l_port << std::endl;
std::string retString = l_cli->second.onMessage(p_string); // decyphers message
if(retString == "TO REMOVE")
removeClient(l_addr, l_port);
return retString;
}
else // new client
{
std::cout << "adding " << l_addr << ":" << l_port << std::endl;
client &l_client = createClient(l_addr, l_port); // adding new client
auto l_newcli = std::find_if(i_clients.begin(),i_clients.end(),
[l_addr, l_port](const std::pair<std::pair<std::string, uint16_t>, client> &el)
{
return l_addr == el.first.first
&& l_port == el.first.second;
});
if(l_newcli==i_clients.end())
{
std::cout << "couldn't find added client" << std::endl;
return "";
}
else
std::cout << "valid" << std::endl;
return l_newcli->second.onConnect(i_sock, p_string, i_ctx); // creating SSL session for new client
}
}
DTLSServer::client &DTLSServer::createClient(const std::string &p_addr, const uint16_t &p_port)
{
std::pair<std::string, uint16_t> l_pair = {p_addr,p_port};
i_clients.insert(std::pair<std::pair<std::string, uint16_t>, client>(l_pair,client()));
return i_clients.at(l_pair);
}
void DTLSServer::removeClient(const std::string &p_addr, const uint16_t &p_port)
{
auto l_cli = std::find_if(i_clients.begin(),i_clients.end(), // looking for {addr, port} in clients
[&p_addr, &p_port](const std::pair<std::pair<std::string, uint16_t>, client> &el) // {(addr,port),client}
{
return p_addr == el.first.first
&& p_port == el.first.second;
});
if(l_cli!=i_clients.end()){
SSL_free(l_cli->second.ssl);
l_cli->second.ssl = nullptr;
i_clients.erase(l_cli);
}
else
{
std::cout << "could not delete" << std::endl;
}
}
void DTLSServer::init(const int &sock)
{
i_sock = sock;
SSL_load_error_strings();
SSL_library_init();
OpenSSL_add_all_ciphers();
memcpy(cookie_str, "cookie", 7);
i_ctx = SSL_CTX_new(DTLS_server_method());
/*if(SSL_CTX_set_cipher_list(i_ctx, "AES256-SHA256")!=1) //CLIENT PART
{
std::cout << "Could not set cipher" << std::endl;
}*/
SSL_CTX_set_min_proto_version(i_ctx, DTLS1_2_VERSION);
SSL_CTX_use_certificate_chain_file(i_ctx, "server-cert.pem");
SSL_CTX_use_PrivateKey_file(i_ctx, "server-key.pem", SSL_FILETYPE_PEM);
int l_ret = SSL_CTX_load_verify_locations(i_ctx, "root-ca.pem", NULL);
fprintf(stderr, "SSL_CTX_load_verify_locations -> %d\n", l_ret);
l_ret = SSL_CTX_set_default_verify_file(i_ctx);
fprintf(stderr, "SSL_CTX_set_default_verify_file -> %d\n", l_ret);
SSL_CTX_set_verify(i_ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
SSL_CTX_set_cookie_generate_cb(i_ctx, generate_cookie);
SSL_CTX_set_cookie_verify_cb(i_ctx, verify_cookie);
}
std::string DTLSServer::client::onConnect(const uint16_t & p_sock, const std::string& p_msg, SSL_CTX *p_ctx)
{
std::cout << "about to connect" << std::endl;
ssl = SSL_new(p_ctx);
if(nullptr==ssl)
std::cout << "couldn't create ssl session" << std::endl;
bio = BIO_new_dgram(p_sock, BIO_NOCLOSE);
if(nullptr==bio)
std::cout << "couldn't create ssl bio" << std::endl;
SSL_set_bio(ssl, bio, bio);
//write(p_sock, p_msg.c_str(), p_msg.size());
//BIO_write(bio, p_msg.c_str(), p_msg.size());
int needed = BIO_get_read_request(bio);
std::cout << "about to accept"<< std::endl;
int ret = SSL_accept(ssl);
//int ret = 0;
std::cout << "accepted"<< std::endl;
if (ret < 0)
{
std::cout << "failed to complete Handshake as " << strerror(errno) << std::endl;
}
return "";
}
std::string DTLSServer::client::onMessage(const std::string& p_msg)
{
int err = BIO_write(bio, &p_msg[0], p_msg.size());
int needed = BIO_get_read_request(bio);
char l_msg[1500] = {0};
/*if(needed > 0)
std::cout << "critical error" << std::endl;*/
int ret = SSL_read(ssl, &l_msg[0], 1500);
if(ret < 0)
{
std::cout << "unvalid message" << std::endl;
return "TO REMOVE";
}
else if(0 == ret)
{
std::cout << "client to remove" << std::endl;
return "TO REMOVE";
}
else
{
std::cout << "valid message" << std::endl;
std::string l_m{l_msg};
return "MESSAGE IS : " + l_m;
}
return "";
}
int DTLSServer::generate_cookie(SSL *ssl, unsigned char *cookie, unsigned int *cookie_len)
{
memmove(cookie, cookie_str, sizeof(cookie_str)-1);
*cookie_len = sizeof(cookie_str)-1;
return 1;
}
int DTLSServer::verify_cookie(SSL *ssl, const unsigned char *cookie, unsigned int cookie_len)
{
return sizeof(cookie_str)-1==cookie_len && memcmp(cookie, cookie_str, sizeof(cookie_str)-1)==0;
}
/* main.cpp */
#include "server.hpp"
#include <iostream>
int main()
{
DTLSServer l_dtls;
int l_socket;
if ((l_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
{
std::cout << "Could not create socket. " << strerror(errno) << std::endl;
return 1;
}
struct sockaddr_in l_sendingAddress;
l_sendingAddress.sin_family = AF_INET;
inet_pton(AF_INET,
"127.0.0.19",
&l_sendingAddress.sin_addr.s_addr);
l_sendingAddress.sin_port = htons(8888);
//Binding socket
if(bind(l_socket,reinterpret_cast<sockaddr *>(&l_sendingAddress),sizeof(l_sendingAddress)) < 0)
{
std::cout << "Could not bind. " << strerror(errno) << std::endl;
return 1;
}
l_dtls.init(l_socket);
char l_buffer[2000] = {0};
sockaddr_in l_client;
socklen_t l_clientLen = sizeof(l_client);
while(1)
{
ssize_t l_bytesReceived = recvfrom(l_socket, &l_buffer, 2000, MSG_DONTWAIT, reinterpret_cast<sockaddr *>(&l_client), &l_clientLen);
if (l_bytesReceived == -1)
{
continue;
}
std::string l_string{l_buffer, static_cast<size_t>(l_bytesReceived)};
std::cout << l_dtls.autoConnect(l_client, l_string) << std::endl;
memset(l_buffer, 0, 2000);
}
return 0;
}
My work is mainly based off the following source: https://github.com/stepheny/openssl-dtls-custom-bio
How can I set my BIOs to do what I want them to do?
Do I have to create custom BIOs?
Thanks in advance for your suggestions :)
Aucun commentaire:
Enregistrer un commentaire