From 3bbfef613aa7aaeb522b68421b9c20f1694c15a8 Mon Sep 17 00:00:00 2001 From: marcelb Date: Tue, 23 May 2023 22:16:13 +0200 Subject: [PATCH] Implement SSL, enabled timeout --- .vscode/settings.json | 46 +++++++++++++- example/cert.pem | 21 ++++++ example/privkey.pem | 28 ++++++++ lib/tcp_socket.hpp | 20 +++++- src/tcp_socket.cpp | 141 +++++++++++++++++++++++++++++++++++++++-- test/client.cpp | 6 +- test/client.o | Bin 30544 -> 39984 bytes test/compile-client.sh | 2 +- test/compile-server.sh | 2 +- test/server.cpp | 32 ++++++---- test/server.o | Bin 30592 -> 39952 bytes 11 files changed, 272 insertions(+), 26 deletions(-) create mode 100644 example/cert.pem create mode 100644 example/privkey.pem diff --git a/.vscode/settings.json b/.vscode/settings.json index 1507357..adb4a79 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,49 @@ { "files.associations": { - "functional": "cpp" + "functional": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "*.tcc": "cpp", + "cctype": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "compare": "cpp", + "concepts": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "deque": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "random": "cpp", + "string_view": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "initializer_list": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "limits": "cpp", + "new": "cpp", + "numbers": "cpp", + "ostream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "cinttypes": "cpp", + "typeinfo": "cpp" } } \ No newline at end of file diff --git a/example/cert.pem b/example/cert.pem new file mode 100644 index 0000000..360eaa3 --- /dev/null +++ b/example/cert.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDazCCAlOgAwIBAgIUcmzC65mt2XxhXwskSJT7io+uesIwDQYJKoZIhvcNAQEL +BQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM +GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMzA1MjMxNjQzMDNaFw0yNDA1 +MjIxNjQzMDNaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEw +HwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQCykD9egLZJuJ+4vWScFZJye4MGkAI0JEBjirLd5wmU +lMVp5gNdQy5OlyEu0YY5HfYj15fXu3bdjBIdMvR5b64SvNpomej5G2BctvB4iFfi +9r440MARGzckKoUve7M3q9lKQDYIGT4uuF3YVrXocNXPPViTsQDQPkmGzSY2O1Ay +gbBcvBDR034K+Yu2dS8AQ84/yBUIQnbpg1myAAXp5PQxHW2fQrQbGY/vy+orx1hE +eHE62+0h4dgpmQFUFXT8tTwolvEm6kCkOmj9LoHSkXAcJwBnybN43vInyhyRuteM +PfaYLBE8gov+CKQiAcpUSnLzj/j5H47hfifRgudUuLORAgMBAAGjUzBRMB0GA1Ud +DgQWBBRCJrbSSAB7K4nKtLZ5hFLiPjdLQTAfBgNVHSMEGDAWgBRCJrbSSAB7K4nK +tLZ5hFLiPjdLQTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQAi +CBwLMSqlB5uTlyjJMSbWw5qY1GKJtCGUAzHXlr6LnmqFgNl+BX4IDu8YaNilPqHZ +F4NhFwMZN9+FJZcyCG8lJ7sqIFge+KVG8jmP2SsA8qY/HyvzmDH0ZTDYxRGymzEH +ryVm8QXLR6ndqb75yK3CnN4tafuEz0ThObdzN1wIKRSUb+CjeuY2DFRqvrdmqnhg +tv3ZCW720yNOpaHTIzdObEM4BlqRBEXVXLJVWmY9vHWnZZMoiRnliv+y28Bh0pNH +JcMUvZfkJGPWBb50HucDziZLeGnEQjBMUW97OCSu6hx0nCDhK44ClmU6Eit+1xmu +MRoDLJeh59UFEvWylyXW +-----END CERTIFICATE----- diff --git a/example/privkey.pem b/example/privkey.pem new file mode 100644 index 0000000..01f76a0 --- /dev/null +++ b/example/privkey.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCykD9egLZJuJ+4 +vWScFZJye4MGkAI0JEBjirLd5wmUlMVp5gNdQy5OlyEu0YY5HfYj15fXu3bdjBId +MvR5b64SvNpomej5G2BctvB4iFfi9r440MARGzckKoUve7M3q9lKQDYIGT4uuF3Y +VrXocNXPPViTsQDQPkmGzSY2O1AygbBcvBDR034K+Yu2dS8AQ84/yBUIQnbpg1my +AAXp5PQxHW2fQrQbGY/vy+orx1hEeHE62+0h4dgpmQFUFXT8tTwolvEm6kCkOmj9 +LoHSkXAcJwBnybN43vInyhyRuteMPfaYLBE8gov+CKQiAcpUSnLzj/j5H47hfifR +gudUuLORAgMBAAECggEAS8QpH1A+CMnt+9Vg+1mr6ie+UOqKBNXc/F+9xapixkjZ ++zU9+DSpUW+PBO6ipVmxN0QjicZhiWAFz53vXhelJQ49HmUwFYMoQC7fmkVABW3t +CO6KkLebjhz8pkk2okioyni0dHtBh1FzgHhKd3ZpQqYlD4XrcnD7gddKf3hrbwIg +5pnr/QkPxeyli7hMUJJv2qWvaBtvqfhMqhjRH7T484CV6hEtURzuVAMgUB2hM4XP +Opjlp5pZAw/4eS2fXK7R4DypaiklOWo7K7u+twSvWTxNDqYRvmcKZ37zWuoKKAD0 +/rTl5VjtF3yRRv43cxu/6wxF/KGjbkJ5hszeQAMQgQKBgQC3mkGqh0tW7zEf8s5n +iotLznjIpY6VpEJ4ehTiVz1IJc7URxyn2H7/cRok19rbg904uQJhrPCOyLWnmMxb +tdKTNaR/LczMe/rgTlBRyN577VxriEXHuU1fSzig5wvZIo3ZCbIJ13T5EvPbcVIi +/VEIk4zyEroU9OSNKT4m/Stj8wKBgQD4+VHekVWo4qpEW/mh9w7SKA2DDXdFFlcP +cn3jtyDRFa1e6so29HFTpy/2pF2+4Kb6o6cq04GhfDPQqY3Opn8/pChEP6Vp0qyj +SegbidCmIpsYKPyzJUTmGN+Y40Tk6XA3e9OK1eR0uAcm7Tq3FpObNvONJtd7i6AD +ckyop/afawKBgCWF/zms4Pbpd67B3vFGXWWm0wSe4V8v7O3WdYI0ti+SmozD49Vh +58KpCODSxMXsU0AOf7AauidUWdvg3JM5u8meQvpDEAISrJk/KCcu2FmXjzXi6fAB +rRB2vSIVVlSQPVFIK/za7eshtnj41gKUpwULstiefy1dR4CaFzu8riuFAoGAIua4 +Xk3bRzB3E9wc02KRtk4dDsj9djbaf3TaRuit9gFm605YiHmdxU9Dfvytk4tkfPAi +B/PcUSnbDZ4nHdfjMKWva1nMs1fwEXfTzMB1+kQRn8JnIinQTb0g4wrA5qH+tBhs +oCK5ws7lWcSZnX7RtElwvNG8FTqOdM06B8572P8CgYBEZpdWLPKlS7cHnw506CaT +bZMgsI0RnXTgqlrlQR029/9zS3ocrmcOov+tOYDGLQnZjRJ1LxWUIIqPaJ5RvUua +pZu8tbvYNnyuI+Ugby6pUQIzFr6flQ2sJWVwoGEAX1f5LiffcXrkZ2F37c9ORCnA +g9V/U/i/RkSgH0cbouMPvg== +-----END PRIVATE KEY----- diff --git a/lib/tcp_socket.hpp b/lib/tcp_socket.hpp index b702bf6..f2d25af 100644 --- a/lib/tcp_socket.hpp +++ b/lib/tcp_socket.hpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include "ip.hpp" @@ -20,16 +22,25 @@ class server { server (const ushort port, const uint limit = 1000); ~server (); - }; +class secure { + public: + SSL_CTX* fds; + + secure(); // client + secure(const string cert, const string priv); //server + ~secure(); + +}; class client { public: int sock; struct sockaddr_in addr; + SSL* ssl = NULL; - client (const string address, const ushort port); + client (const string address, const ushort port, const uint timeout = 500, SSL_CTX* securefds = NULL); ~client (); bool tell (const string msg); string obey (size_t byte_limit = 1024); @@ -39,15 +50,18 @@ class client { class comming { public: const server *srv; + struct sockaddr_in addr; int conn; string ipv4; string ipv6; + SSL* ssl = NULL; - comming(const server *_srv, const uint timeout); + comming(const server *_srv, const uint timeout = 100, SSL_CTX* securefds = NULL); ~comming(); bool tell (const string msg); string obey (size_t byte_limit = 1024); + }; diff --git a/src/tcp_socket.cpp b/src/tcp_socket.cpp index b6606b4..7f6ae7e 100644 --- a/src/tcp_socket.cpp +++ b/src/tcp_socket.cpp @@ -40,7 +40,47 @@ server::~server () { } -client::client(const string address, const ushort port) { +secure::secure() { + fds = SSL_CTX_new(SSLv23_client_method()); + if (!fds) { + //throw "[ERROR] Creating SSL context "; + cout << endl << "[ERROR] Creating SSL context "; + + } +} + +secure::secure(const string cert, const string priv) { + + SSL_library_init(); + SSL_load_error_strings(); + OpenSSL_add_all_algorithms(); + + // Create an SSL context + fds = SSL_CTX_new(SSLv23_server_method()); + if (!fds) { + // throw "[ERROR] Creating SSL context "; + cout << endl << "[ERROR] Creating SSL context "; + } + + // Load the server's certificate and private key files + if (SSL_CTX_use_certificate_file(fds, cert.c_str(), SSL_FILETYPE_PEM) <= 0) { + // throw "[ERROR] Loading certificate file."; + cout << endl << "[ERROR] Loading certificate file."; + } + + if (SSL_CTX_use_PrivateKey_file(fds, priv.c_str(), SSL_FILETYPE_PEM) <= 0) { + //throw "[ERROR] Loading private key file."; + cout << endl << "[ERROR] Loading private key file."; + } + +} + +secure::~secure () { + SSL_CTX_free(fds); +} + + +client::client(const string address, const ushort port, const uint timeout, SSL_CTX* securefds) { sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { @@ -57,11 +97,41 @@ client::client(const string address, const ushort port) { printf("[EROR] Ne mogu se povezati s poslužiteljem!"); } + struct timeval tv; + tv.tv_sec = 0; // za sad 2 sekunde timeout, harkodirano + tv.tv_usec = timeout*1000; + + if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval))) { + printf ("[ERROR] 503 Ne mogu postaviti timeout!"); + sock = -1; + } + + if (securefds) { + ssl = SSL_new(securefds); + if (!ssl) { + //throw "[ERROR] Creating SSL object."; + cout << endl << "[ERROR] Creating SSL object."; + } + SSL_set_fd(ssl, sock); + + } + // Perform the SSL handshake + if (SSL_connect(ssl) <= 0) { + SSL_free(ssl); + // throw "[ERROR] Performing SSL handshake."; + cout << endl << "[ERROR] Performing SSL handshake."; + } + } client::~client () { + if (ssl) { + SSL_shutdown(ssl); + SSL_free(ssl); + } + if (sock <= 0) { printf ("[ERROR] Soket destruktor: već zatvoren soket!"); } @@ -74,19 +144,32 @@ client::~client () { bool client::tell (const string msg) { - ssize_t sended = send(sock, msg.c_str(), msg.length(),0); + size_t sended = 0; + if (ssl) { + sended = SSL_write(ssl, msg.c_str(), msg.length()); + } + else { + sended = write(sock, msg.c_str(), msg.length()); + } return sended == msg.length(); } string client::obey (size_t byte_limit) { - char res[byte_limit] = {0}; - ssize_t n = read ( sock , res, byte_limit); + char res[byte_limit] = {0}; + + if (ssl) { + SSL_read(ssl, res, byte_limit); + } + else { + read(sock , res, byte_limit); + } + return (string) res; } -comming::comming(const server *_srv, const uint timeout) { +comming::comming(const server *_srv, const uint timeout, SSL_CTX* securefds) { srv = _srv; socklen_t len = sizeof(struct sockaddr_in); @@ -94,6 +177,31 @@ comming::comming(const server *_srv, const uint timeout) { printf("[ERROR] Ne mogu preuzeti vezu klijenta!"); } + struct timeval tv; + tv.tv_sec = 1; // za sad 2 sekunde timeout, harkodirano + tv.tv_usec = 0; + + if (setsockopt(conn, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval))) { + printf("[ERROR] 503 Ne mogu postaviti timeout!"); + conn = -1; + } + + + if (securefds) { + ssl = SSL_new(securefds); + if (!ssl) { + // throw "[ERROR] Creating SSL object."; + cout << endl << "[ERROR] Creating SSL object."; + } + SSL_set_fd(ssl, conn); + // Perform SSL handshake + if (SSL_accept(ssl) <= 0) { + SSL_free(ssl); + // throw "[ERROR] Performing SSL handshake."; + cout << endl << "[ERROR] Performing SSL handshake."; + } + } + char ipv4_buff[INET_ADDRSTRLEN]; char ipv6_buff[INET6_ADDRSTRLEN]; @@ -106,6 +214,12 @@ comming::comming(const server *_srv, const uint timeout) { } comming::~comming() { + + if (ssl) { + SSL_shutdown(ssl); + SSL_free(ssl); + } + if (conn <= 0) { printf ("[ERROR] Comming destruktor: već zatvoren soket!"); } @@ -116,12 +230,25 @@ comming::~comming() { } bool comming::tell (const string msg) { - ssize_t sended = send(conn, msg.c_str(), msg.length(),0); + ssize_t sended = 0; + if (ssl) { + sended = SSL_write(ssl, msg.c_str(), msg.length()); + } + else { + sended = write(conn, msg.c_str(), msg.length()); + } return sended == msg.length(); } string comming::obey (size_t byte_limit) { char res[byte_limit] = {0}; - ssize_t n = read ( conn , res, byte_limit); + + if (ssl) { + SSL_read(ssl, res, byte_limit); + } + else { + read(conn , res, byte_limit); + } + return (string) res; } diff --git a/test/client.cpp b/test/client.cpp index 083c994..ca4a890 100644 --- a/test/client.cpp +++ b/test/client.cpp @@ -6,10 +6,12 @@ using namespace std; int main() { - client myserver("localhost", 5000); - string sends = "Hello wld!"; + secure crypto; + client myserver("localhost", 5000, 500, crypto.fds); + string sends = "Hello world!"; cout << myserver.tell(sends) << " " << sends.length() << endl; cout << myserver.obey(); + return 0; } \ No newline at end of file diff --git a/test/client.o b/test/client.o index 9254249c6572c3d042fabc5c8e29b8f2eb8b17ff..012ee4cc0852f39ad5b145a440d445484f7ec268 100755 GIT binary patch literal 39984 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
a98X=iB}nNk0T|5-l;|EOr3a*b^a?Pg$pSF_ zn>-@PVr=Fuf&nuoPx~-GP@;VZB`J=5R-o*F{1{Nt%jHL;N^hDEDM>ZW`so#0N{SCp zez<*L=Onx`!twp`BShSi=D5-mQy<4Km-%=Xg5$~$Li(TYSH&T-ODypUO1Xs964p!D zE@791dnDX1VV{Kk5)MjuOu}Ia*(Ho8kAz+cOC&6puv)@;3EL&?l5mfN`z7p?uwTMK z36DuQETIba;<;4l;gzsN!g2|#C9IdQUBWI2_ei*3!afQ6B^;FSn1sU;@GRi7rub4 z4ho#~qg01K7e=+3e>f38x8slMRr98z@5Y6TU1-i{ivC?(WYhm4&OepiV5|2j&`JJx zX9)>>pMuMGBwfwZYW^&M!XGDpjf4E{C06<8LJrld=6@yM_yqYi4)UM7+A6DyV^q zf7O6-o=yH84)o_iztp0MKvE9;^PxaC`2hzyIve;stm+FwF5Ls5bc+N30}k|`a=M4T zu~3wc2*5<#KM%W*AJ!Rbmh=j2T&Ui!NIIRXD6Ip1f_sV?$3jpU+sOG9mVtdz&cQN4 zQ~l&g&@X`;Prab4{l%*ea(vh@kUkAke>#^@s^D~St^eLiboT^vU#IfgL8o@Fy+TlC z$c%lG?w9@iLP>wxLH_Fw^!eCeWozFm2Rg+k$;SVX1APV@Mf#_t{`_7IbZrGan;Zk< zg8Zi9T=*exY}{YRi@ zvzH#RlRozO=PYcfiGQ2)PpeRe%>_N1e*zBleGc^Z9Oxcw+|PIONl4Zxz=cG&Py{l| zfnEtZjW7FrxY~jL+nhdjTs%Z_uzx787xk$)en&tj{}j#^^k)QM<9R=khz7n4I?d0l zUL)eNiPNXDwLvR=lY^X}aJq+$$Oji{9vG4Idu;TQ^Ms!3<#^=x_)t|B=-K>vmjnHK z4)p&3-2?yF{ak>GvdN#t>0|r*IEbqW(WG$9O&P4pufZE9`?Obp%mQ@q*R8bm;7_#6$0n?#&KB( zx>t}azm1@0(|?--|6T|F?{NB9eTE$P7f;AuZ;;b5|11(}()`F%O(>S|6-N;+EZ7j? z;Riv8Cpwy%ibE_DFw;o18VE(!7LJ3nF=}jVi8fFaHatp;8^MlF7K*mEw}cbnMuZ2; z%3&ZZTEqy(V!^Ejo>|4VvZfefml=&6t*sREOv4OtCM>L>mI%rehuRTi4H4f~EHsQr zl(>MF`FN+@H`E{KZA(YCLZ~PI9?KoFyp!vtCv))K#`6Hqe=u~i?j>L z)+}S4&libTRmavqY%f5YJ1q*RX0qJRIACFmt8;#I10`vNaVe{f2+lQVLmW zAXv5G4@hEQ=^AD%{qm}cl~t9d6|0vmtMMbeX2p^fenUohE9cSK5OJ6N0z5s&(`kbO)5YUsVqHoXER{hWuR>3vN0@L)^>Ud9*FZ@g`qQdmm_nP% zKzbIVL=Zd+$*5Y5ka&%VmDv#wH*$%mA0;t|XrA^Uo#k4%JeZY3NK6$`Q}+eM;S&KQ zRro=x@wtRsRM9f+^@wPM7Wrm0L<^P1YHq<+^(uMTLz~=Hs+*MIVR?%`r@;y5FAx*U zpGQcvA-r|Td>$*9Lev_K@u<;^u1_I`b8$Zf^VI#oo7mKuhx%N4Q*kWZRQ%@)`^qD1 z>R~{$D4<*~N33%IPQ5JS^FKKP(KbC*7(V#;mruo8sfn=EqsXK8HpS8VvNy_ zX&=FIt0<0cLv<}pleV~;W=}!1$(;N?Cd#O82Q`8W>1kF(6Kzg4nXzrFL-snvSQbG* zZ592O$6p?!OD??^fseH~s=T*~_@>#cIo&wCx`vrBrZ&mG(n$L`~9Z%$2abxS6^tS*`Qn3_=AZARr_hP6ty&Z=cs2G({Sl_~(aim0o?aqAjW_s?M= zGy8wGdG7S%DP!Pfjl8aFD%MGBXk639m@xRJQoK@Tl4*Qf0`6LJ45M~srP{OQG*T=v zlgB8#&RpwcbY~e-S!c{``l<9)VXpO4pl=!CI2ZLrq;Opu;bYpGiEzgZ8~z+OTohb; zjOcX>XkWf%K@MYqV#|xtWjU4>%bmFcM%?xA#%OHo$8V0W8pBU<$PrYULPQJuqMggdvP9(T6CVVLCtl)6OTHp`1n~R(=BNd(|77MiG`o)$RPKzW~^|F z7qSGnx8!H`N4W=yFSQSgkNd|XEK$MBRfKF>8)%}m_h0U7{!bfNyqgKiZS4%Xj7Mu! zdA%AwE=>pFfR)P-pd&ZR+5hbe)zSDWVg0u^_)l{cfb@6#xK z+OgoR|Mo=|U!t#%`G*>GHgcBQFPB*l2&_21wKWlJ08GRLY*tr%kK4|Q@fvuz7`x@- z_E;4EszYL{Mr!DY;GfaL8{&cs5!mO@zaGKan}hLYR@}I?4OIx3h>0BBY>4CDhy^i_ z7Ynxpsers{Z%MFXz7`havUp<@C|$(EAy$mY^g!u;4!EQI2wog+mUnQP5l-I3Pz~b9 zhdU`EJ#H@1zqZ1qikmw^hPp=O!PW@G3xSxPSTWrTz>Obyuj*3(dM}1T^&_ky`Q6Cr!1%RS9}V71^M*ukHXdaKPHsrNq^E)=`YEw z7Z=nfs(iV;xvQXhZmjB8<(2*S%JNI3K=po;g6e%JDo@Y!?d87(8u>)YXI>zby!;nW z2CT{6f9^-NUf$~!{3ewBQAxq=Wu6rLE-<}(`DG$sLB%Ji^sGW81^a;N<*Q|R1wEfo z|0A~Y>iM98N7ajDwt6MXp0t%$@6RcS$19d(_usR!ylQ_|F4-jUND4NRa$2&9WA7smk8t?dAU5^B zq5Ah9y+qWKs$ao8$j74~FYtUxPpV!;qikinq7l5TO<-5r3fNHgpR2*9ZU6uP diff --git a/test/compile-client.sh b/test/compile-client.sh index c0b31c0..048551b 100644 --- a/test/compile-client.sh +++ b/test/compile-client.sh @@ -1 +1 @@ -g++ client.cpp ../src/* -o client.o \ No newline at end of file +g++ client.cpp ../src/* -o client.o -lssl -lcrypto \ No newline at end of file diff --git a/test/compile-server.sh b/test/compile-server.sh index e6bb7c9..9a9e315 100644 --- a/test/compile-server.sh +++ b/test/compile-server.sh @@ -1 +1 @@ -g++ server.cpp ../src/* -o server.o \ No newline at end of file +g++ server.cpp ../src/* -o server.o -lssl -lcrypto \ No newline at end of file diff --git a/test/server.cpp b/test/server.cpp index c0139ca..be48d89 100644 --- a/test/server.cpp +++ b/test/server.cpp @@ -6,21 +6,31 @@ using namespace std; int main() { + cout << "init server " << endl; server myserver(5000, 10); - comming myclient(&myserver, 1000); + cout << "init cert " << endl; + secure crypto ("../example/cert.pem", "../example/privkey.pem"); + cout << "init client " << endl; + + comming myclient(&myserver, 1000, crypto.fds); + cout << "wait client " << myclient.ipv4 << endl; string fromclient = myclient.obey(); + cout << "tell client " << fromclient << endl; + sleep(5); myclient.tell(fromclient); - myclient.~comming(); - - while (true) { - comming myclient(&myserver, 1000); - string fromclient = myclient.obey(); - cout << fromclient << " " << myclient.conn << endl; - cout << "Poslano: " << myclient.tell(fromclient) << "Veličin: " << fromclient.length() << endl; - // myclient.~comming(); - cout << "IPv4 " << myclient.ipv4 << " ipv6 " << myclient.ipv6; - } + // myclient.~comming(); + + // while (true) { + // comming myclient(&myserver, 1000); + // string fromclient = myclient.obey(); + // cout << fromclient << " " << myclient.conn << endl; + // cout << "Poslano: " << myclient.tell(fromclient) << "Veličin: " << fromclient.length() << endl; + // // myclient.~comming(); + // cout << "IPv4 " << myclient.ipv4 << " ipv6 " << myclient.ipv6; + // } + + // sleep(80); return 0; diff --git a/test/server.o b/test/server.o index 2a5703ac3daab7bf3ec35adc74ffdd238d091d6f..d571660dc4832677c23fde22f4fac8b67656d9e1 100755 GIT binary patch literal 39952 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 R-xWsm`l#UMI0`se_Fp{t8t4E3