[Boost] SSL 소켓 기반 ECHO 서버

Alexandria·2024년 3월 4일
0

Boost

목록 보기
2/5
post-thumbnail

1. 서버

SSL 서버를 구동하기 위해서는 인증서가 필요하다.

자체 서명 인증서를 생성한다.

$ openssl genrsa -out private.key 2048
$ openssl req -x509 -new -nodes -key private.key -days 365 -out certificate.crt -subj "/C=KR/ST=Seoul/L=Seoul/O=test/OU=test"

OpenSSL 라이브러리를 이용하기 위해 패키지를 설치한다.

$ sudo apt -y install libssl-dev

boost의 asio를 이용하여 비동기적으로 데이터를 읽고 다시 ECHO 해주는 서버를 작성해본다.

#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>

typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket;

class session {
    public:
    session(boost::asio::io_service& io_service, boost::asio::ssl::context& context) : socket_(io_service, context) {}

    ssl_socket::lowest_layer_type& socket() {
        return socket_.lowest_layer();
    }

    void start() {
        socket_.async_handshake(boost::asio::ssl::stream_base::server, boost::bind(&session::handle_handshake, this, boost::asio::placeholders::error));
    }

    void handle_handshake(const boost::system::error_code& error) {
        if (!error) {
            socket_.async_read_some(boost::asio::buffer(data_, max_length), boost::bind(&session::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
        }
        else {
            delete this;
        }
    }

    void handle_read(const boost::system::error_code& error, size_t bytes_transferred) {
        if (!error) {
            boost::asio::async_write(socket_, boost::asio::buffer(data_, bytes_transferred), boost::bind(&session::handle_write, this, boost::asio::placeholders::error));
        }
        else
        {
            delete this;
        }
    }

    void handle_write(const boost::system::error_code& error) {
        if (!error) {
            socket_.async_read_some(boost::asio::buffer(data_, max_length), boost::bind(&session::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
        }
        else {
            delete this;
        }
    }

    private:
    ssl_socket socket_;
    enum { max_length = 1024 };
    char data_[max_length];
};

class server {
    public:
    server(boost::asio::io_service& io_service, unsigned short port) : io_service_(io_service), acceptor_(io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)), context_(boost::asio::ssl::context::sslv23) {
        context_.set_options(boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::single_dh_use);
        context_.use_certificate_chain_file("certificate.crt");
        context_.use_private_key_file("private.key", boost::asio::ssl::context::pem);
        start_accept();
    }

    void start_accept()
    {
        session* new_session = new session(io_service_, context_);
        acceptor_.async_accept(new_session->socket(), boost::bind(&server::handle_accept, this, new_session, boost::asio::placeholders::error));
    }

    void handle_accept(session* new_session, const boost::system::error_code& error) {
        if (!error) {
            new_session->start();
        }
        else {
            delete new_session;
        }
        start_accept();
    }

    private:
    boost::asio::io_service& io_service_;
    boost::asio::ip::tcp::acceptor acceptor_;
    boost::asio::ssl::context context_;
};

int main(int argc, char* argv[]) {
    try {
        boost::asio::io_service io_service;
        server s(io_service, 1234);
        io_service.run();
    }
    catch (std::exception& e) {
        std::cerr << "Exception: " << e.what() << "\n";
    }
    return 0;
}

컴파일 시 libssl.so, libcrypto.so, libpthread.so를 연결한다.

2. 클라이언트

주어진 호스트 정보를 통해 SSL 서버에 접속한다.

접속 시 인증서 정보는 자체 인증서이기 때문에 검증은 무시하도록 한다.

#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>

enum { max_length = 1024 };

class client {
    public:
    client(boost::asio::io_service& io_service, boost::asio::ssl::context& context, boost::asio::ip::tcp::resolver::iterator endpoint_iterator) : socket_(io_service, context) {
        socket_.set_verify_mode(boost::asio::ssl::verify_none);
        boost::asio::async_connect(socket_.lowest_layer(), endpoint_iterator, boost::bind(&client::handle_connect, this, boost::asio::placeholders::error));
    }

    void handle_connect(const boost::system::error_code& error) {
        if (!error) {
            socket_.async_handshake(boost::asio::ssl::stream_base::client, boost::bind(&client::handle_handshake, this, boost::asio::placeholders::error));
        }
        else {
            std::cout << "Connect failed: " << error.message() << "\n";
        }
    }

    void handle_handshake(const boost::system::error_code& error) {
        if (!error) {
            std::cout << "Enter message: ";
            std::cin.getline(request_, max_length);
            size_t request_length = strlen(request_);
            boost::asio::async_write(socket_, boost::asio::buffer(request_, request_length), boost::bind(&client::handle_write, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
        }
        else {
            std::cout << "Handshake failed: " << error.message() << "\n";
        }
    }

    void handle_write(const boost::system::error_code& error, size_t bytes_transferred) {
        if (!error) {
            boost::asio::async_read(socket_, boost::asio::buffer(reply_, bytes_transferred), boost::bind(&client::handle_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
        }
        else {
            std::cout << "Write failed: " << error.message() << "\n";
        }
    }

    void handle_read(const boost::system::error_code& error, size_t bytes_transferred) {
        if (!error) {
            std::cout << "Reply: ";
            std::cout.write(reply_, bytes_transferred);
            std::cout << "\n";
        }
        else {
            std::cout << "Read failed: " << error.message() << "\n";
        }
    }

    private:
    boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
    char request_[max_length];
    char reply_[max_length];
};

int main(int argc, char* argv[]) {
    try {
        boost::asio::io_service io_service;
        boost::asio::ip::tcp::resolver resolver(io_service);
        boost::asio::ip::tcp::resolver::query query("127.0.0.1", "1234");
        boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
        boost::asio::ssl::context ctx(boost::asio::ssl::context::sslv23);
        client c(io_service, ctx, iterator);
        io_service.run();
    }
    catch (std::exception& e) {
        std::cerr << "Exception: " << e.what() << "\n";
    }
    return 0;
}

컴파일 시 libssl.so, libcrypto.so, libpthread.so를 연결한다.

profile
IT 도서관

0개의 댓글

관련 채용 정보