378 lines
10 KiB
C
378 lines
10 KiB
C
#include <arpa/inet.h>
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "addrbook.h"
|
|
#include "audio.h"
|
|
#include "crypt.h"
|
|
#include "net.h"
|
|
|
|
#define VERSION "0.0.0"
|
|
|
|
#define CHANNELS 1
|
|
#define SAMPLE_RATE 48000
|
|
#define FRAMES_PER_BUFFFER 960
|
|
#define AUDIO_BUF_SIZE (FRAMES_PER_BUFFFER * CHANNELS)
|
|
|
|
#define NET_BUF_SIZE 1024
|
|
|
|
int client_handshake(net_t *const n, crypt_session_t *const s, crypt_key_t *const ok, crypt_key_t *const rk);
|
|
int server_handshake(net_t *const n, crypt_session_t *const s, crypt_key_t *const ok, crypt_key_t *const rk);
|
|
|
|
struct options {
|
|
const char *addr;
|
|
const char *port;
|
|
char *addressbook_path;
|
|
char *key_pub_path;
|
|
char *key_sec_path;
|
|
bool is_alias_passed;
|
|
char mode;
|
|
};
|
|
|
|
void options_destroy(struct options *opts) {
|
|
free(opts->key_pub_path);
|
|
free(opts->key_sec_path);
|
|
free(opts->addressbook_path);
|
|
}
|
|
|
|
char *get_config_dir(void);
|
|
int parse_argv(int argc, const char **argv, struct options *opts);
|
|
|
|
void usage(void) {
|
|
fprintf(stderr,
|
|
"tetatet -vh c|s (ADDR PORT | ALIAS for c)\n"
|
|
" -v,--version show version\n"
|
|
" -h,--help show this help\n\n"
|
|
" c (ADDR PORT | ALIAS) is a client mode. You can specify ADDR and PORT or use\n"
|
|
" an ALIAS that will use the last ADDR and PORT stored in an addressbook.\n"
|
|
" s ADDR PORT is a server mode. Specify ADDR and PORT to listen on.\n");
|
|
}
|
|
|
|
void version(void) {
|
|
fprintf(stdout,
|
|
"tetatet ver. " VERSION
|
|
"\nCopyright (c) Alexander \"Arav\" Andreev <me@arav.su>\n");
|
|
}
|
|
|
|
int main(int argc, const char **argv) {
|
|
int result = 0;
|
|
struct options opts = {0};
|
|
|
|
addrbook_t ab = {0};
|
|
bool is_ab_changed = false;
|
|
net_t n = {0};
|
|
crypt_session_t s = {0};
|
|
crypt_key_t ok = {0}, rk = {0};
|
|
|
|
switch (parse_argv(argc, argv, &opts)) {
|
|
case -1: usage(); return 1;
|
|
case -2: version(); return 0;
|
|
case -3: usage(); return 0;
|
|
}
|
|
|
|
if (sodium_init() == -1) {
|
|
fprintf(stderr, "Cannot initialise libsodium.\n");
|
|
return 1;
|
|
}
|
|
|
|
// if (audio_init_soundsystem() == -1)
|
|
// return 1;
|
|
|
|
FILE *addrbook_file = fopen(opts.addressbook_path, "r");
|
|
if (addrbook_file != NULL) {
|
|
addrbook_load(&ab, addrbook_file);
|
|
fclose(addrbook_file);
|
|
}
|
|
|
|
if (opts.mode == 'c' && opts.is_alias_passed) {
|
|
addrbook_entry_t *aec = addrbook_get_by_alias(&ab, opts.addr);
|
|
if (aec == NULL) {
|
|
fprintf(stderr, "An alias \"%s\" is not in an addressbook!\n", opts.addr);
|
|
return 0;
|
|
} else {
|
|
fprintf(stderr, "An alias \"%s\" was found in an addressbook!\n", opts.addr);
|
|
opts.addr = aec->last_addr;
|
|
opts.port = aec->last_port;
|
|
}
|
|
}
|
|
|
|
FILE *pkey_file = fopen(opts.key_pub_path, "r");
|
|
FILE *skey_file = fopen(opts.key_sec_path, "r");
|
|
if (pkey_file == NULL || skey_file == NULL) {
|
|
if ((result = crypt_key_gen(&ok)) == -1) {
|
|
fprintf(stderr, "Failed to generate the keys.\n");
|
|
result = 1;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (pkey_file != NULL)
|
|
fclose(pkey_file);
|
|
if (skey_file != NULL)
|
|
fclose(skey_file);
|
|
|
|
pkey_file = fopen(opts.key_pub_path, "w+");
|
|
skey_file = fopen(opts.key_sec_path, "w+");
|
|
|
|
if (pkey_file == NULL || skey_file == NULL) {
|
|
fprintf(stderr, "Cannot open tat_own_*_key files to write: %s\n", strerror(errno));
|
|
return 1;
|
|
}
|
|
|
|
if (crypt_store_key(&ok, pkey_file, skey_file) == -1) {
|
|
fprintf(stderr, "Failed to save the keys.\n");
|
|
return 1;
|
|
}
|
|
|
|
fclose(pkey_file);
|
|
fclose(skey_file);
|
|
|
|
printf("The new cryptographical keys was written.\n");
|
|
} else {
|
|
if (crypt_load_key(&ok, pkey_file, skey_file) == -1) {
|
|
fprintf(stderr, "Cannot load the keys.\n");
|
|
result = 1;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (pkey_file != NULL)
|
|
fclose(pkey_file);
|
|
if (skey_file != NULL)
|
|
fclose(skey_file);
|
|
}
|
|
|
|
(opts.mode == 'c') ?
|
|
(result = net_client_init(&n, opts.addr, opts.port))
|
|
: (result = net_server_init(&n, opts.addr , opts.port));
|
|
if (result != 0)
|
|
goto cleanup;
|
|
|
|
(opts.mode == 'c') ?
|
|
(result = client_handshake(&n, &s, &ok, &rk))
|
|
: (result = server_handshake(&n, &s, &ok, &rk));
|
|
if (result != 0)
|
|
goto cleanup;
|
|
|
|
char rkph[CRYPT_PKEY_HEXLEN];
|
|
|
|
crypt_key_export_public(&rk, rkph);
|
|
|
|
fprintf(stdout, "A connection with %s established!\n", rkph);
|
|
|
|
audio_t aud = {0};
|
|
unsigned char aud_buf[AUDIO_BUF_SIZE];
|
|
memset(aud_buf, 0, AUDIO_BUF_SIZE);
|
|
|
|
// audio_init_default(&aud, CHANNELS, SAMPLE_RATE, FRAMES_PER_BUFFFER);
|
|
|
|
// (opts.mode == 'c') ?
|
|
// (audio_stream_input_toggle(&aud))
|
|
// : (audio_stream_output_toggle(&aud));
|
|
|
|
// for (size_t i = 0; i < (20 * SAMPLE_RATE)/FRAMES_PER_BUFFFER; ++i) {
|
|
// if (opts.mode == 'c') {
|
|
// audio_read(&aud, aud_buf, AUDIO_BUF_SIZE);
|
|
// } else {
|
|
// //
|
|
// }
|
|
// }
|
|
|
|
// (opts.mode == 'c') ?
|
|
// (audio_stream_input_toggle(&aud))
|
|
// : (audio_stream_output_toggle(&aud));
|
|
|
|
cleanup:
|
|
|
|
if (is_ab_changed) {
|
|
addrbook_file = fopen(opts.addressbook_path, "w+");
|
|
if (addrbook_file != NULL) {
|
|
if (addrbook_store(&ab, addrbook_file) == -1)
|
|
fprintf(stderr, "Failed to store an addressbook: %s\n", strerror(errno));
|
|
fclose(addrbook_file);
|
|
}
|
|
}
|
|
|
|
options_destroy(&opts);
|
|
addrbook_destroy(&ab);
|
|
audio_destroy(&aud);
|
|
net_destroy(&n);
|
|
crypt_session_destroy(&s);
|
|
crypt_key_destroy(&ok);
|
|
crypt_key_destroy(&rk);
|
|
|
|
// if (audio_terminate_soundsystem() == -1)
|
|
// return 1;
|
|
|
|
return result;
|
|
}
|
|
|
|
int handshake(const unsigned char * hello, const char * remote_hello, crypt_session_t *const s, crypt_key_t *const ok, crypt_key_t *const rk) {
|
|
unsigned char *nonce = NULL;
|
|
|
|
if (crypt_key_from_hex_public(rk, remote_hello) == -1)
|
|
return -1;
|
|
|
|
if (crypt_hello_verify((unsigned char *)remote_hello, rk) == -1)
|
|
return -2;
|
|
|
|
if ((nonce = crypt_hello_get_nonce(hello, (unsigned char *)remote_hello, true)) == NULL)
|
|
return -1;
|
|
|
|
if (crypt_session_init(s, ok, rk, nonce, true) == -1) {
|
|
free(nonce);
|
|
return -3;
|
|
}
|
|
|
|
free(nonce);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int client_handshake(net_t *const n, crypt_session_t *const s, crypt_key_t *const ok, crypt_key_t *const rk) {
|
|
char buffer[NET_BUF_SIZE];
|
|
ssize_t bytes = 0;
|
|
unsigned char *hello = NULL;
|
|
|
|
if ((hello = crypt_hello(ok)) == NULL)
|
|
return -1;
|
|
|
|
if (net_send(n, (char *)hello, CRYPT_HELLO_LEN) == -1) {
|
|
free(hello);
|
|
return -1;
|
|
}
|
|
|
|
if ((bytes = net_recv(n, buffer, NET_BUF_SIZE)) == -1) {
|
|
free(hello);
|
|
return -1;
|
|
}
|
|
|
|
if (CRYPT_HELLO_LEN != bytes) {
|
|
free(hello);
|
|
return -1;
|
|
}
|
|
|
|
int res = handshake(hello, buffer, s, ok, rk);
|
|
|
|
free(hello);
|
|
|
|
return res;
|
|
}
|
|
|
|
int server_handshake(net_t *const n, crypt_session_t *const s, crypt_key_t *const ok, crypt_key_t *const rk) {
|
|
char buffer[NET_BUF_SIZE];
|
|
ssize_t bytes = 0;
|
|
unsigned char *hello = NULL;
|
|
|
|
if ((bytes = net_recv(n, buffer, NET_BUF_SIZE)) == -1)
|
|
return -1;
|
|
|
|
if (CRYPT_HELLO_LEN != bytes)
|
|
return -1;
|
|
|
|
if ((hello = crypt_hello(ok)) == NULL)
|
|
return -1;
|
|
|
|
|
|
int res = handshake(hello, buffer, s, ok, rk);
|
|
if (res != 0) {
|
|
free(hello);
|
|
return res;
|
|
}
|
|
|
|
n->raddr = n->inaddr;
|
|
n->raddr_len = n->inaddr_len;
|
|
|
|
if (net_send(n, (char *)hello, CRYPT_HELLO_LEN) == -1) {
|
|
free(hello);
|
|
return -1;
|
|
}
|
|
|
|
free(hello);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int parse_argv(int argc, const char **argv, struct options *opts) {
|
|
if (argc == 1)
|
|
return -1;
|
|
int mode_arg_num = 0;
|
|
|
|
for (int i = 1; i < argc; ++i) {
|
|
if (argv[i][0] == '-') {
|
|
if (argv[i][1] == 'h' || (argv[i][1] == '-' && argv[i][2] == 'h'))
|
|
return -3;
|
|
else if (argv[i][1] == 'v' || (argv[i][1] == '-' && argv[i][2] == 'v'))
|
|
return -2;
|
|
else
|
|
return -1;
|
|
} else {
|
|
if (opts->mode == 'c' || opts->mode == 's') {
|
|
if (mode_arg_num == 0)
|
|
opts->addr = argv[i];
|
|
else if (mode_arg_num == 1)
|
|
opts->port = argv[i];
|
|
++mode_arg_num;
|
|
} else {
|
|
switch (argv[i][0]) {
|
|
case 'c':
|
|
case 's':
|
|
opts->mode = argv[i][0];
|
|
break;
|
|
default: return -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct sockaddr test = {0};
|
|
int r = inet_pton(AF_INET, opts->addr, &test);
|
|
if (r == -1)
|
|
r = inet_pton(AF_INET6, opts->addr, &test);
|
|
opts->is_alias_passed = r == 0;
|
|
|
|
if (opts->mode == 's' && opts->is_alias_passed)
|
|
return -1;
|
|
|
|
if (opts->mode == '\0' || opts->addr[0] == '\0'
|
|
|| (!opts->is_alias_passed && opts->port == NULL))
|
|
return -1;
|
|
|
|
char *config_dir = get_config_dir();
|
|
int config_dir_len = strlen(config_dir);
|
|
|
|
if ((mkdir(config_dir, 0700) == -1) && (errno != EEXIST)) {
|
|
fprintf(stderr, "Cannot create a config dir %s: %s\n", config_dir, strerror(errno));
|
|
free(config_dir);
|
|
return 1;
|
|
}
|
|
|
|
opts->addressbook_path = (char *)malloc(config_dir_len + 11 + 1);
|
|
strncpy(opts->addressbook_path, config_dir, config_dir_len);
|
|
strcat(opts->addressbook_path, "addressbook");
|
|
|
|
opts->key_pub_path = (char *)malloc(config_dir_len + 11 + 1);
|
|
strncpy(opts->key_pub_path, config_dir, config_dir_len);
|
|
strcat(opts->key_pub_path, "own_pub_key");
|
|
|
|
opts->key_sec_path = (char *)malloc(config_dir_len + 11 + 1);
|
|
strncpy(opts->key_sec_path, config_dir, config_dir_len);
|
|
strcat(opts->key_sec_path, "own_sec_key");
|
|
|
|
free(config_dir);
|
|
|
|
return 0;
|
|
}
|
|
|
|
char *get_config_dir(void) {
|
|
const char *home_dir = getenv("HOME");
|
|
|
|
char *config_dir = (char *)malloc(strlen(home_dir) + 16 + 1);
|
|
|
|
strncpy(config_dir, home_dir, strlen(home_dir));
|
|
strcat(config_dir, "/.config/tetatet");
|
|
|
|
return config_dir;
|
|
}
|