implemented client interactive mode
This commit is contained in:
parent
c008d759ae
commit
f01c5fc8a9
2
dependencies/tlibc
vendored
2
dependencies/tlibc
vendored
@ -1 +1 @@
|
||||
Subproject commit bf56984482d83d1a178f4da6483fbd350457e438
|
||||
Subproject commit c415e2ca8ff51f41984ace8fe796187e6ad0fa27
|
||||
@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include "tlibc/errors.h"
|
||||
|
||||
Result(void) client_run();
|
||||
Result(void) server_run(cstr server_endpoint_str);
|
||||
236
src/client.c
236
src/client.c
@ -1,9 +1,10 @@
|
||||
#include "client.h"
|
||||
#include "chat.h"
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
#include "term.h"
|
||||
|
||||
#define inp_eq(LITERAL) str_equals(input, STR(LITERAL))
|
||||
#include "network/socket.h"
|
||||
#include "cryptography/cryptography.h"
|
||||
#include "tlibc/string/StringBuilder.h"
|
||||
|
||||
static const str greeting_art = STR(
|
||||
" ^,,^ ╱|\n"
|
||||
@ -19,8 +20,30 @@ static const str farewell_art = STR(
|
||||
"\\(_,J J L l`,)/\n"
|
||||
);
|
||||
|
||||
static Result(void) commandExec(str command, bool* stop);
|
||||
|
||||
typedef struct ClientCredential {
|
||||
str username;
|
||||
u8 passhash_lvl2[password_hash_size];
|
||||
EncryptorAES aes_enc;
|
||||
DecryptorAES aes_dec;
|
||||
EncryptorRSA rsa_enc;
|
||||
DecryptorRSA rsa_dec;
|
||||
} ClientCredential;
|
||||
|
||||
typedef struct ServerConnection {
|
||||
EndpointIPv4 server_end;
|
||||
Socket system_socket;
|
||||
Socket content_socket;
|
||||
EncryptorRSA rsa_enc;
|
||||
EncryptorAES session_aes_enc;
|
||||
DecryptorAES session_aes_dec;
|
||||
} ServerConnection;
|
||||
|
||||
Result(ServerConnection*) connectToServer(EndpointIPv4 server_end, str server_key);
|
||||
|
||||
Result(void) client_run() {
|
||||
Deferral(128);
|
||||
Deferral(32);
|
||||
if(!term_init()){
|
||||
Return RESULT_ERROR("can't init terminal", false);
|
||||
}
|
||||
@ -28,74 +51,159 @@ Result(void) client_run() {
|
||||
|
||||
fputs(greeting_art.data, stdout);
|
||||
|
||||
char* input_prev = NULL;
|
||||
char* input_raw = NULL;
|
||||
Defer(rl_free(input_prev));
|
||||
str input = str_null;
|
||||
while((input_raw = readline("> "))){
|
||||
rl_free(input_prev);
|
||||
input_prev = input_raw;
|
||||
input = str_from_cstr(input_raw);
|
||||
line_trim(&input, true);
|
||||
add_history(input.data);
|
||||
if(input.size == 0){
|
||||
char* command_input_prev = NULL;
|
||||
char* command_input_raw = NULL;
|
||||
Defer(rl_free(command_input_prev));
|
||||
str command_input = str_null;
|
||||
bool stop = false;
|
||||
while((command_input_raw = readline("> ")) && !stop){
|
||||
rl_free(command_input_prev);
|
||||
command_input_prev = command_input_raw;
|
||||
command_input = str_from_cstr(command_input_raw);
|
||||
str_trim(&command_input, true);
|
||||
if(command_input.size == 0)
|
||||
continue;
|
||||
}
|
||||
else if(inp_eq("q") || inp_eq("quit") || inp_eq("exit")){
|
||||
fputs(farewell_art.data, stdout);
|
||||
break;
|
||||
}
|
||||
else if(inp_eq("clear")){
|
||||
term_clear();
|
||||
}
|
||||
else if(inp_eq("h") || inp_eq("help")){
|
||||
|
||||
}
|
||||
else if(inp_eq("c") || inp_eq("connect")){
|
||||
|
||||
}
|
||||
else {
|
||||
printf("ERROR: unknown kommand: '%s'\n"
|
||||
"Use 'h' to see list of avaliable commands\n",
|
||||
input.data);
|
||||
|
||||
add_history(command_input.data);
|
||||
Result(void) com_result = commandExec(command_input, &stop);
|
||||
if(com_result.error){
|
||||
str e_str = Error_toStr(com_result.error);
|
||||
printfe("%s\n", e_str.data);
|
||||
free(e_str.data);
|
||||
Error_free(com_result.error);
|
||||
}
|
||||
}
|
||||
|
||||
Return RESULT_VOID;
|
||||
}
|
||||
|
||||
void line_trim(str* line, bool set_zero_at_end){
|
||||
bool stop = false;
|
||||
// loop forward
|
||||
while(line->size > 0 && !stop){
|
||||
char first_char = line->data[line->size - 1];
|
||||
switch(first_char){
|
||||
case '\0': case '\r': case '\n':
|
||||
case '\t': case ' ':
|
||||
line->data++;
|
||||
line->size--;
|
||||
break;
|
||||
default:
|
||||
stop = true;
|
||||
break;
|
||||
}
|
||||
#define inp_eq(LITERAL) str_equals(command, STR(LITERAL))
|
||||
|
||||
static ClientCredential* client_credential = NULL;
|
||||
|
||||
static Result(void) commandExec(str command, bool* stop){
|
||||
Deferral(64);
|
||||
char answer_buf[512];
|
||||
const u32 answer_buf_size = sizeof(answer_buf);
|
||||
if(is_alias("q") || is_alias("quit") || is_alias("exit")){
|
||||
fputs(farewell_art.data, stdout);
|
||||
*stop = true;
|
||||
}
|
||||
// loop backward
|
||||
while(line->size > 0 && !stop)
|
||||
{
|
||||
char last_char = line->data[line->size - 1];
|
||||
switch(last_char){
|
||||
case '\0': case '\r': case '\n':
|
||||
case '\t': case ' ':
|
||||
line->size--;
|
||||
break;
|
||||
default:
|
||||
stop = true;
|
||||
break;
|
||||
}
|
||||
else if(is_alias("clear")){
|
||||
term_clear();
|
||||
}
|
||||
if(set_zero_at_end){
|
||||
line->data[line->size] = '\0';
|
||||
line->isZeroTerminated = true;
|
||||
else if(is_alias("h") || is_alias("help")){
|
||||
puts(
|
||||
"COMMANDS:\n"
|
||||
"q, quit, exit Close the program.\n"
|
||||
"clear Clear the screen.\n"
|
||||
"j, join Join a server.\n"
|
||||
"c, connect Connect to a server you joined.\n"
|
||||
);
|
||||
}
|
||||
else if (is_alias("j") || is_alias("join")){
|
||||
puts("Enter server address (ip:port): ");
|
||||
fgets(answer_buf, answer_buf_size, stdin);
|
||||
EndpointIPv4 new_server_end;
|
||||
try_void(EndpointIPv4_parse(answer_buf, &new_server_end));
|
||||
puts("Enter server key (): ");
|
||||
fgets(answer_buf, answer_buf_size, stdin);
|
||||
str new_server_key = str_from_cstr(answer_buf);
|
||||
try(ServerConnection* conn, p, connectToServer(new_server_end, new_server_key));
|
||||
}
|
||||
else if(is_alias("c") || is_alias("connect")){
|
||||
// TODO: read saved servers from database
|
||||
}
|
||||
else {
|
||||
Return RESULT_ERROR_FMT("unknown kommand: '%s'\n"
|
||||
"Use 'h' to see list of avaliable commands\n",
|
||||
command.data);
|
||||
}
|
||||
|
||||
Return RESULT_VOID;
|
||||
}
|
||||
|
||||
void ClientCredential_free(ClientCredential* cred){
|
||||
if(cred == NULL)
|
||||
return;
|
||||
free(cred->username.data);
|
||||
free(cred);
|
||||
}
|
||||
|
||||
#define __passhash_lvl_iter 1e5
|
||||
|
||||
static Result(ClientCredential*) ClientCredential_create(str username, str password){
|
||||
Deferral(32);
|
||||
ClientCredential* cred = (ClientCredential*)malloc(sizeof(ClientCredential));
|
||||
memset(cred, 0, sizeof(ClientCredential));
|
||||
bool success = false;
|
||||
Defer(
|
||||
if(!success)
|
||||
ClientCredential_free(cred);
|
||||
);
|
||||
|
||||
cred->username = str_copy(username);
|
||||
|
||||
// concat password and username
|
||||
StringBuilder sb = StringBuilder_alloc(username.size + password.size + 1);
|
||||
Defer(StringBuilder_destroy(&sb));
|
||||
StringBuilder_append_str(&sb, password);
|
||||
StringBuilder_append_str(&sb, username);
|
||||
|
||||
// lvl 1 hash - is being used to generate rsa keys
|
||||
u8 passhash_lvl1[password_hash_size];
|
||||
hash_password(StringBuilder_getStr(&sb), passhash_lvl1, __passhash_lvl_iter);
|
||||
// lvl 2 hash - is being used to authorize client on server
|
||||
str _passhash_lvl1_str = str_construct(passhash_lvl1, password_hash_size, false);
|
||||
hash_password(_passhash_lvl1_str, cred->passhash_lvl2, __passhash_lvl_iter);
|
||||
|
||||
|
||||
success = true;
|
||||
return RESULT_VALUE(p, cred);
|
||||
}
|
||||
|
||||
static void ServerConnection_close(ServerConnection* conn){
|
||||
if(conn == NULL)
|
||||
return;
|
||||
socket_close(conn->system_socket);
|
||||
socket_close(conn->content_socket);
|
||||
free(conn);
|
||||
}
|
||||
|
||||
static Result(ServerConnection*) connectToServer(EndpointIPv4 server_end, str server_key){
|
||||
Deferral(64);
|
||||
if(client_credential == NULL){
|
||||
|
||||
}
|
||||
|
||||
str end_str = EndpointIPv4_toStr(server_end);
|
||||
Defer(free(end_str.data));
|
||||
if(EndpointIPv4_is_invalid(server_end)){
|
||||
Return RESULT_ERROR_FMT("endpoint is invalid: %s", end_str.data);
|
||||
}
|
||||
|
||||
ServerConnection* conn = (ServerConnection*)malloc(sizeof(ServerConnection));
|
||||
memset(conn, 0, sizeof(ServerConnection));
|
||||
bool success = false;
|
||||
Defer(
|
||||
if(!success)
|
||||
ServerConnection_close(conn);
|
||||
);
|
||||
|
||||
printf("connecting to server %s\n", end_str.data);
|
||||
try(conn->system_socket, i, socket_open_TCP());
|
||||
try_void(socket_connect(conn->system_socket, server_end));
|
||||
|
||||
// ask user name and password
|
||||
// calculate key pair from password hash
|
||||
// send client public key to server
|
||||
// request server info
|
||||
// show server info
|
||||
// save server info to user's db
|
||||
// hash password more times
|
||||
// request log in
|
||||
// if not registered, request registration and then log in
|
||||
|
||||
success = true;
|
||||
Return RESULT_VALUE(p, conn);
|
||||
}
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
#include "tlibc/errors.h"
|
||||
|
||||
Result(void) client_run();
|
||||
|
||||
/// @brief removes blank characters from start and end of the line
|
||||
void line_trim(str* line, bool set_zero_at_end);
|
||||
@ -4,13 +4,14 @@
|
||||
#include "tlibc/string/str.h"
|
||||
#include "bearssl_block.h"
|
||||
#include "bearssl_rand.h"
|
||||
#include "bearssl_rsa.h"
|
||||
|
||||
/// @brief hashes password multiple times using its own hash as salt
|
||||
/// @param password some byte array
|
||||
/// @param out_buffer u8[hash_password_out_size]
|
||||
/// @param out_buffer u8[password_hash_size]
|
||||
/// @param iterations number of iterations
|
||||
void hash_password(str password, u8* out_buffer, i32 iterations);
|
||||
#define hash_password_out_size 32
|
||||
#define password_hash_size 32
|
||||
|
||||
|
||||
typedef struct EncryptedBlockInfo {
|
||||
@ -59,7 +60,11 @@ void DecryptorAES_decrypt(DecryptorAES* ptr, Array(u8) src, Array(u8) dst, u32*
|
||||
|
||||
|
||||
|
||||
typedef struct EncryptorRSA EncryptorRSA;
|
||||
typedef struct EncryptorRSA {
|
||||
br_rsa_public_key public_key;
|
||||
} EncryptorRSA;
|
||||
|
||||
|
||||
typedef struct DecryptorRSA DecryptorRSA;
|
||||
typedef struct DecryptorRSA {
|
||||
br_rsa_private_key private_key;
|
||||
} DecryptorRSA;
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
#include "assert.h"
|
||||
|
||||
void hash_password(str password, u8* out_buffer, i32 iterations){
|
||||
assert(hash_password_out_size == br_sha256_SIZE);;
|
||||
assert(password_hash_size == br_sha256_SIZE);;
|
||||
memset(out_buffer, 0, br_sha256_SIZE);
|
||||
br_sha256_context sha256_ctx;
|
||||
br_sha256_init(&sha256_ctx);
|
||||
@ -11,7 +11,7 @@ void hash_password(str password, u8* out_buffer, i32 iterations){
|
||||
for(i32 i = 0; i < iterations; i++){
|
||||
br_sha256_update(&sha256_ctx, password.data, password.size);
|
||||
br_sha256_out(&sha256_ctx, out_buffer);
|
||||
br_sha256_update(&sha256_ctx, out_buffer, hash_password_out_size);
|
||||
br_sha256_update(&sha256_ctx, out_buffer, password_hash_size);
|
||||
}
|
||||
br_sha256_out(&sha256_ctx, out_buffer);
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
#include "network/network.h"
|
||||
#include "client.h"
|
||||
#include "server.h"
|
||||
#include "chat.h"
|
||||
|
||||
typedef enum ProgramMode {
|
||||
Client,
|
||||
@ -20,9 +19,9 @@ int main(const int argc, cstr const* argv){
|
||||
if(arg_is("-h") || arg_is("--help")){
|
||||
printf(
|
||||
"USAGE:\n"
|
||||
"no arguments Interactive client mode.\n"
|
||||
"-h, --help Show this message.\n"
|
||||
"-l, --listen [addr:port] Start server.\n"
|
||||
"no arguments Interactive client mode.\n"
|
||||
"-h, --help Show this message.\n"
|
||||
"-l, --listen [addr:port] Start server.\n"
|
||||
);
|
||||
Return 0;
|
||||
}
|
||||
|
||||
@ -18,20 +18,42 @@ EndpointIPv4 EndpointIPv4_fromSockaddr(struct sockaddr_in saddr){
|
||||
return end;
|
||||
}
|
||||
|
||||
//TODO Endpoint functions
|
||||
AddressIPv4 AddressIPv4_fromStr(cstr s);
|
||||
|
||||
str AddressIPv4_toStr(AddressIPv4 address);
|
||||
|
||||
EndpointIPv4 EndpointIPv4_fromStr(cstr s){
|
||||
u32 a, b, c, d, p;
|
||||
sscanf(s, "%u.%u.%u.%u:%u", &a, &b, &c, &d, &p);
|
||||
EndpointIPv4 e = (EndpointIPv4){
|
||||
.address = AddressIPv4_fromBytes(a, b, c, d),
|
||||
.port = p
|
||||
};
|
||||
return e;
|
||||
Result(void) AddressIPv4_parse(cstr s, AddressIPv4* addr){
|
||||
*addr = AddressIPv4_INVALID;
|
||||
u32 a, b, c, d;
|
||||
if(sscanf(s, "%u.%u.%u.%u", &a, &b, &c, &d) != 4){
|
||||
return RESULT_ERROR_FMT("can't parse as AddressIPv4: '%s'", s);
|
||||
}
|
||||
*addr = AddressIPv4_fromBytes(a, b, c, d);
|
||||
return RESULT_VOID;
|
||||
}
|
||||
|
||||
str EndpointIPv4_toStr(EndpointIPv4 end);
|
||||
|
||||
Result(void) EndpointIPv4_parse(cstr s, EndpointIPv4* end){
|
||||
*end = EndpointIPv4_INVALID;
|
||||
u32 a, b, c, d, p;
|
||||
if(sscanf(s, "%u.%u.%u.%u:%u", &a, &b, &c, &d, &p) != 5){
|
||||
return RESULT_ERROR_FMT("can't parse as EndpointIPv4: '%s'", s);
|
||||
}
|
||||
*end = EndpointIPv4_create(AddressIPv4_fromBytes(a, b, c, d), p);
|
||||
return RESULT_VOID;
|
||||
}
|
||||
|
||||
str AddressIPv4_toStr(AddressIPv4 addr){
|
||||
char* data = malloc(16);
|
||||
memset(data, 0, 16);
|
||||
sprintf(data, "%u.%u.%u.%u",
|
||||
addr.bytes[0], addr.bytes[1],
|
||||
addr.bytes[2], addr.bytes[3]);
|
||||
return str_from_cstr(data);
|
||||
}
|
||||
|
||||
str EndpointIPv4_toStr(EndpointIPv4 end){
|
||||
char* data = malloc(24);
|
||||
memset(data, 0, 24);
|
||||
sprintf(data, "%u.%u.%u.%u:%u",
|
||||
end.address.bytes[0], end.address.bytes[1],
|
||||
end.address.bytes[2], end.address.bytes[3],
|
||||
end.port);
|
||||
return str_from_cstr(data);
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
#include "tlibc/std.h"
|
||||
#include "tlibc/string/str.h"
|
||||
|
||||
#define port_INVALID ((port)~0)
|
||||
#define port_INVALID ((u16)~0)
|
||||
#define port_is_invalid(PORT) (PORT == port_INVALID)
|
||||
|
||||
|
||||
@ -18,8 +18,8 @@ typedef union AddressIPv4 {
|
||||
|
||||
#define AddressIPv4_fromBytes(A, B, C, D) ((AddressIPv4){ .bytes = {A,B,C,D} })
|
||||
#define AddressIPv4_fromU32(N) ((AddressIPv4){ .UintBigEndian = N })
|
||||
AddressIPv4 AddressIPv4_fromStr(cstr s);
|
||||
str AddressIPv4_toStr(AddressIPv4 address);
|
||||
Result(void) AddressIPv4_parse(cstr s, AddressIPv4* addr);
|
||||
str AddressIPv4_toStr(AddressIPv4 addr);
|
||||
|
||||
|
||||
typedef struct EndpointIPv4 {
|
||||
@ -31,8 +31,5 @@ typedef struct EndpointIPv4 {
|
||||
#define EndpointIPv4_is_invalid(ENDP) (AddressIPv4_is_invalid(ENDP.address) || port_is_invalid(ENDP.port))
|
||||
|
||||
#define EndpointIPv4_create(ADDR, PORT) ((EndpointIPv4){ADDR, PORT})
|
||||
EndpointIPv4 EndpointIPv4_fromStr(cstr s);
|
||||
Result(void) EndpointIPv4_parse(cstr s, EndpointIPv4* end);
|
||||
str EndpointIPv4_toStr(EndpointIPv4 end);
|
||||
|
||||
struct sockaddr_in EndpointIPv4_toSockaddr(EndpointIPv4 end);
|
||||
EndpointIPv4 EndpointIPv4_fromSockaddr(struct sockaddr_in saddr);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "tlibc/errors.h"
|
||||
#include "endpoint.h"
|
||||
|
||||
#if !defined(KN_USE_WINSOCK)
|
||||
#if defined(_WIN64) || defined(_WIN32)
|
||||
@ -24,3 +25,6 @@
|
||||
|
||||
#define RESULT_ERROR_SOCKET() RESULT_ERROR(strerror(errno), false)
|
||||
#endif
|
||||
|
||||
struct sockaddr_in EndpointIPv4_toSockaddr(EndpointIPv4 end);
|
||||
EndpointIPv4 EndpointIPv4_fromSockaddr(struct sockaddr_in saddr);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#include "server.h"
|
||||
#include "chat.h"
|
||||
#include "network/socket.h"
|
||||
#include "db/idb.h"
|
||||
#include <pthread.h>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user