154 lines
5.6 KiB
C
154 lines
5.6 KiB
C
#include "client.h"
|
|
#include "network/tcp-chat-protocol/v1.h"
|
|
|
|
void ServerConnection_close(ServerConnection* conn){
|
|
if(conn == NULL)
|
|
return;
|
|
RSA_destroyPublicKey(&conn->server_pk);
|
|
EncryptedSocketTCP_destroy(&conn->sock);
|
|
free(conn->session_key.data);
|
|
free(conn);
|
|
}
|
|
|
|
/// @brief
|
|
/// @param server_link_cstr address:port:public_key
|
|
/// @return
|
|
Result(void) ServerLink_parse(cstr server_link_cstr, EndpointIPv4* server_end_out, br_rsa_public_key* server_key_out){
|
|
Deferral(8);
|
|
str server_link_str = str_from_cstr(server_link_cstr);
|
|
|
|
// parse address and port
|
|
i32 sep_pos = str_seekChar(server_link_str, ':', 0);
|
|
if(sep_pos == -1){
|
|
Return RESULT_ERROR_FMT("server link is invalid: %s", server_link_cstr);
|
|
}
|
|
*server_end_out = EndpointIPv4_INVALID;
|
|
try_void(EndpointIPv4_parse(server_link_cstr, server_end_out));
|
|
if(EndpointIPv4_is_invalid(*server_end_out)){
|
|
Return RESULT_ERROR_FMT("server address or port is invalid: %s", server_link_cstr);
|
|
}
|
|
|
|
// parse public key
|
|
sep_pos = str_seekChar(server_link_str, ':', sep_pos + 1);
|
|
if(sep_pos == -1){
|
|
Return RESULT_ERROR_FMT("server link is invalid: %s", server_link_cstr);
|
|
}
|
|
str server_key_str = str_sliceAfter(server_link_str, sep_pos + 1);
|
|
char* server_key_cstr = str_copy(server_key_str).data;
|
|
Defer(free(server_key_cstr));
|
|
try_void(RSA_parsePublicKey_base64(server_key_cstr, server_key_out));
|
|
|
|
Return RESULT_VOID;
|
|
}
|
|
|
|
Result(ServerConnection*) ServerConnection_open(ClientCredentials* client_credentials, cstr server_link_cstr){
|
|
Deferral(64);
|
|
|
|
ServerConnection* conn = (ServerConnection*)malloc(sizeof(ServerConnection));
|
|
memset(conn, 0, sizeof(*conn));
|
|
bool success = false;
|
|
Defer(
|
|
if(!success)
|
|
ServerConnection_close(conn);
|
|
);
|
|
|
|
try_void(ServerLink_parse(server_link_cstr, &conn->server_end, &conn->server_pk));
|
|
RSAEncryptor_construct(&conn->rsa_enc, &conn->server_pk);
|
|
|
|
conn->session_key = Array_alloc_size(AES_SESSION_KEY_SIZE);
|
|
// generate random session key
|
|
br_hmac_drbg_context key_rng = { .vtable = &br_hmac_drbg_vtable };
|
|
rng_init_sha256_seedFromSystem(&key_rng.vtable);
|
|
br_hmac_drbg_generate(&key_rng, conn->session_key.data, conn->session_key.size);
|
|
|
|
// connect to server address
|
|
try(Socket _s, i, socket_open_TCP());
|
|
// TODO: set socket timeout to 5 seconds
|
|
try_void(socket_connect(_s, conn->server_end));
|
|
EncryptedSocketTCP_construct(&conn->sock, _s, NETWORK_BUFFER_SIZE, conn->session_key);
|
|
|
|
Array(u8) buffer = Array_alloc_size(NETWORK_BUFFER_SIZE);
|
|
// fix for valgrind false detected errors about uninitialized memory
|
|
Array_memset(buffer, 0xCC);
|
|
Defer(free(buffer.data));
|
|
|
|
// construct PacketHeader and ClientHandshake in buffer
|
|
PacketHeader_construct(buffer.data, PROTOCOL_VERSION,
|
|
PacketType_ClientHandshake, sizeof(ClientHandshake));
|
|
ClientHandshake_construct(
|
|
Array_sliceAfter(buffer, sizeof(PacketHeader)).data,
|
|
conn->session_key);
|
|
u32 header_and_message_size = sizeof(PacketHeader) + sizeof(ClientHandshake);
|
|
// encrypt message by server public key
|
|
Array(u8) bufferPart_encryptedClientHandshake = Array_sliceAfter(buffer, header_and_message_size);
|
|
try(u32 rsa_enc_size, u,
|
|
RSAEncryptor_encrypt(
|
|
&conn->rsa_enc,
|
|
Array_sliceBefore(buffer, header_and_message_size),
|
|
bufferPart_encryptedClientHandshake
|
|
)
|
|
);
|
|
bufferPart_encryptedClientHandshake.size = rsa_enc_size;
|
|
// send encrypted message
|
|
try_void(socket_send(conn->sock.sock, bufferPart_encryptedClientHandshake));
|
|
|
|
// receive server response
|
|
try_void(
|
|
EncryptedSocketTCP_recv(&conn->sock,
|
|
Array_sliceBefore(buffer, sizeof(PacketHeader)),
|
|
SocketRecvFlag_WaitAll
|
|
)
|
|
);
|
|
PacketHeader* packet_header = buffer.data;
|
|
try_void(PacketHeader_validateMagic(packet_header));
|
|
|
|
// handle server response
|
|
switch(packet_header->type){
|
|
case PacketType_ErrorMessage: {
|
|
u32 err_msg_size = packet_header->content_size;
|
|
if(err_msg_size > conn->sock.recv_buf.size)
|
|
err_msg_size = conn->sock.recv_buf.size;
|
|
Array(u8) err_buf = Array_alloc_size(err_msg_size + 1);
|
|
bool err_msg_completed = false;
|
|
Defer(
|
|
if(!err_msg_completed)
|
|
free(err_buf.data);
|
|
);
|
|
|
|
// receive error message
|
|
try_void(
|
|
EncryptedSocketTCP_recv(
|
|
&conn->sock,
|
|
Array_sliceBefore(err_buf, err_msg_size),
|
|
SocketRecvFlag_WaitAll
|
|
)
|
|
);
|
|
|
|
((u8*)err_buf.data)[err_msg_size] = 0;
|
|
err_msg_completed = true;
|
|
Return RESULT_ERROR((char*)err_buf.data, true);
|
|
}
|
|
case PacketType_ServerHandshake: {
|
|
Array(u8) bufferPart_ServerHandshake = {
|
|
.data = (u8*)buffer.data + sizeof(PacketHeader),
|
|
.size = sizeof(ServerHandshake)
|
|
};
|
|
try_void(
|
|
EncryptedSocketTCP_recv(
|
|
&conn->sock,
|
|
bufferPart_ServerHandshake,
|
|
SocketRecvFlag_WaitAll
|
|
)
|
|
);
|
|
ServerHandshake* server_handshake = bufferPart_ServerHandshake.data;
|
|
conn->session_id = server_handshake->session_id;
|
|
break;
|
|
}
|
|
default:
|
|
Return RESULT_ERROR_FMT("unexpected response type: %i", packet_header->type);
|
|
}
|
|
|
|
success = true;
|
|
Return RESULT_VALUE(p, conn);
|
|
}
|