1
0
mirror of https://github.com/hashcat/hashcat.git synced 2024-12-26 16:38:35 +00:00
hashcat/src/brain.c

3296 lines
87 KiB
C

/**
* Author......: See docs/credits.txt
* License.....: MIT
*/
#include "common.h"
#include "types.h"
#include "bitops.h"
#include "timer.h"
#include "memory.h"
#include "thread.h"
#include "convert.h"
#include "shared.h"
#include "hashes.h"
#include "brain.h"
static bool keep_running = true;
static hc_timer_t timer_logging;
static hc_thread_mutex_t mux_display;
int brain_logging (FILE *stream, const int client_idx, const char *format, ...)
{
const double ms = hc_timer_get (timer_logging);
hc_timer_set (&timer_logging);
hc_thread_mutex_lock (mux_display);
struct timeval v;
gettimeofday (&v, NULL);
fprintf (stream, "%d.%06d | %6.2fs | %3d | ", (u32) v.tv_sec, (u32) v.tv_usec, ms / 1000, client_idx);
va_list ap;
va_start (ap, format);
const int len = vfprintf (stream, format, ap);
va_end (ap);
hc_thread_mutex_unlock (mux_display);
return len;
}
u32 brain_compute_session (hashcat_ctx_t *hashcat_ctx)
{
hashes_t *hashes = hashcat_ctx->hashes;
hashconfig_t *hashconfig = hashcat_ctx->hashconfig;
user_options_t *user_options = hashcat_ctx->user_options;
if (user_options->brain_session != 0) return user_options->brain_session;
const u64 seed = (const u64) hashconfig->hash_mode;
XXH64_state_t *state = XXH64_createState ();
XXH64_reset (state, seed);
if (hashconfig->opts_type & OPTS_TYPE_BINARY_HASHFILE)
{
// digest
u32 digests_cnt = hashes->digests_cnt;
u32 *digests_buf = hashes->digests_buf;
XXH64_update (state, digests_buf, digests_cnt * hashconfig->dgst_size);
// salt
u32 salts_cnt = hashes->salts_cnt;
salt_t *salts_buf = hashes->salts_buf;
for (u32 salts_idx = 0; salts_idx < salts_cnt; salts_idx++)
{
salt_t *salt = salts_buf + salts_idx;
XXH64_update (state, &salt->salt_iter, sizeof (salt->salt_iter));
XXH64_update (state, salt->salt_buf, sizeof (salt->salt_buf));
}
// esalt
if (hashconfig->esalt_size > 0)
{
void *esalts_buf = hashes->esalts_buf;
XXH64_update (state, esalts_buf, digests_cnt * hashconfig->esalt_size);
}
}
else
{
// using hash_encode is an easy workaround for dealing with optimizations
// like OPTI_TYPE_PRECOMPUTE_MERKLE which cause diffrent hashes in digests_buf
// in case -O is used
char **out_bufs = (char **) hccalloc (hashes->digests_cnt, sizeof (char *));
int out_idx = 0;
u8 *out_buf = (u8 *) hcmalloc (HCBUFSIZ_LARGE);
u32 salts_cnt = hashes->salts_cnt;
for (u32 salts_idx = 0; salts_idx < salts_cnt; salts_idx++)
{
salt_t *salt_buf = &hashes->salts_buf[salts_idx];
for (u32 digest_idx = 0; digest_idx < salt_buf->digests_cnt; digest_idx++)
{
const int out_len = hash_encode (hashcat_ctx->hashconfig, hashcat_ctx->hashes, hashcat_ctx->module_ctx, (char *) out_buf, HCBUFSIZ_LARGE, salts_idx, digest_idx);
out_buf[out_len] = 0;
out_bufs[out_idx] = hcstrdup ((char *) out_buf);
}
}
hcfree (out_buf);
qsort (out_bufs, out_idx, sizeof (char *), sort_by_string);
for (int i = 0; i <= out_idx; i++)
{
const size_t out_len = strlen (out_bufs[out_idx]);
XXH64_update (state, out_bufs[out_idx], out_len);
hcfree (out_bufs[out_idx]);
}
hcfree (out_bufs);
}
const u32 session = (const u32) XXH64_digest (state);
XXH64_freeState (state);
return session;
}
u32 brain_compute_attack (hashcat_ctx_t *hashcat_ctx)
{
const combinator_ctx_t *combinator_ctx = hashcat_ctx->combinator_ctx;
const hashconfig_t *hashconfig = hashcat_ctx->hashconfig;
const mask_ctx_t *mask_ctx = hashcat_ctx->mask_ctx;
const straight_ctx_t *straight_ctx = hashcat_ctx->straight_ctx;
const user_options_t *user_options = hashcat_ctx->user_options;
XXH64_state_t *state = XXH64_createState ();
XXH64_reset (state, user_options->brain_session);
const int hash_mode = hashconfig->hash_mode;
const int attack_mode = user_options->attack_mode;
XXH64_update (state, &hash_mode, sizeof (hash_mode));
XXH64_update (state, &attack_mode, sizeof (attack_mode));
const int skip = user_options->skip;
const int limit = user_options->limit;
XXH64_update (state, &skip, sizeof (skip));
XXH64_update (state, &limit, sizeof (limit));
const int hex_salt = user_options->hex_salt;
XXH64_update (state, &hex_salt, sizeof (hex_salt));
const int hccapx_message_pair = user_options->hccapx_message_pair;
XXH64_update (state, &hccapx_message_pair, sizeof (hccapx_message_pair));
const int nonce_error_corrections = user_options->nonce_error_corrections;
XXH64_update (state, &nonce_error_corrections, sizeof (nonce_error_corrections));
const int veracrypt_pim_start = user_options->veracrypt_pim_start;
XXH64_update (state, &veracrypt_pim_start, sizeof (veracrypt_pim_start));
const int veracrypt_pim_stop = user_options->veracrypt_pim_stop;
XXH64_update (state, &veracrypt_pim_stop, sizeof (veracrypt_pim_stop));
if (user_options->attack_mode == ATTACK_MODE_STRAIGHT)
{
if (straight_ctx->dict)
{
const u64 wordlist_hash = brain_compute_attack_wordlist (straight_ctx->dict);
XXH64_update (state, &wordlist_hash, sizeof (wordlist_hash));
}
const int hex_wordlist = user_options->hex_wordlist;
XXH64_update (state, &hex_wordlist, sizeof (hex_wordlist));
const int wordlist_autohex_disable = user_options->wordlist_autohex_disable;
XXH64_update (state, &wordlist_autohex_disable, sizeof (wordlist_autohex_disable));
if (user_options->encoding_from)
{
const char *encoding_from = user_options->encoding_from;
XXH64_update (state, encoding_from, strlen (encoding_from));
}
if (user_options->encoding_to)
{
const char *encoding_to = user_options->encoding_to;
XXH64_update (state, encoding_to, strlen (encoding_to));
}
if (user_options->rule_buf_l)
{
const char *rule_buf_l = user_options->rule_buf_l;
XXH64_update (state, rule_buf_l, strlen (rule_buf_l));
}
if (user_options->rule_buf_r)
{
const char *rule_buf_r = user_options->rule_buf_r;
XXH64_update (state, rule_buf_r, strlen (rule_buf_r));
}
const int loopback = user_options->loopback;
XXH64_update (state, &loopback, sizeof (loopback));
XXH64_update (state, straight_ctx->kernel_rules_buf, straight_ctx->kernel_rules_cnt * sizeof (kernel_rule_t));
}
else if (user_options->attack_mode == ATTACK_MODE_COMBI)
{
const u64 wordlist1_hash = brain_compute_attack_wordlist (combinator_ctx->dict1);
const u64 wordlist2_hash = brain_compute_attack_wordlist (combinator_ctx->dict2);
XXH64_update (state, &wordlist1_hash, sizeof (wordlist1_hash));
XXH64_update (state, &wordlist2_hash, sizeof (wordlist2_hash));
const int hex_wordlist = user_options->hex_wordlist;
XXH64_update (state, &hex_wordlist, sizeof (hex_wordlist));
const int wordlist_autohex_disable = user_options->wordlist_autohex_disable;
XXH64_update (state, &wordlist_autohex_disable, sizeof (wordlist_autohex_disable));
if (user_options->encoding_from)
{
const char *encoding_from = user_options->encoding_from;
XXH64_update (state, encoding_from, strlen (encoding_from));
}
if (user_options->encoding_to)
{
const char *encoding_to = user_options->encoding_to;
XXH64_update (state, encoding_to, strlen (encoding_to));
}
if (user_options->rule_buf_l)
{
const char *rule_buf_l = user_options->rule_buf_l;
XXH64_update (state, rule_buf_l, strlen (rule_buf_l));
}
if (user_options->rule_buf_r)
{
const char *rule_buf_r = user_options->rule_buf_r;
XXH64_update (state, rule_buf_r, strlen (rule_buf_r));
}
}
else if (user_options->attack_mode == ATTACK_MODE_BF)
{
const char *mask = mask_ctx->mask;
XXH64_update (state, mask, strlen (mask));
const int hex_charset = user_options->hex_charset;
XXH64_update (state, &hex_charset, sizeof (hex_charset));
const int markov_classic = user_options->markov_classic;
const int markov_disable = user_options->markov_disable;
const int markov_threshold = user_options->markov_threshold;
XXH64_update (state, &markov_classic, sizeof (markov_classic));
XXH64_update (state, &markov_disable, sizeof (markov_disable));
XXH64_update (state, &markov_threshold, sizeof (markov_threshold));
if (user_options->markov_hcstat2)
{
const char *markov_hcstat2 = filename_from_filepath (user_options->markov_hcstat2);
XXH64_update (state, markov_hcstat2, strlen (markov_hcstat2));
}
if (user_options->custom_charset_1)
{
const char *custom_charset_1 = user_options->custom_charset_1;
XXH64_update (state, custom_charset_1, strlen (custom_charset_1));
}
if (user_options->custom_charset_2)
{
const char *custom_charset_2 = user_options->custom_charset_2;
XXH64_update (state, custom_charset_2, strlen (custom_charset_2));
}
if (user_options->custom_charset_3)
{
const char *custom_charset_3 = user_options->custom_charset_3;
XXH64_update (state, custom_charset_3, strlen (custom_charset_3));
}
if (user_options->custom_charset_4)
{
const char *custom_charset_4 = user_options->custom_charset_4;
XXH64_update (state, custom_charset_4, strlen (custom_charset_4));
}
}
else if (user_options->attack_mode == ATTACK_MODE_HYBRID1)
{
const u64 wordlist_hash = brain_compute_attack_wordlist (straight_ctx->dict);
XXH64_update (state, &wordlist_hash, sizeof (wordlist_hash));
const char *mask = mask_ctx->mask;
XXH64_update (state, mask, strlen (mask));
const int hex_charset = user_options->hex_charset;
XXH64_update (state, &hex_charset, sizeof (hex_charset));
const int markov_classic = user_options->markov_classic;
const int markov_disable = user_options->markov_disable;
const int markov_threshold = user_options->markov_threshold;
XXH64_update (state, &markov_classic, sizeof (markov_classic));
XXH64_update (state, &markov_disable, sizeof (markov_disable));
XXH64_update (state, &markov_threshold, sizeof (markov_threshold));
if (user_options->markov_hcstat2)
{
const char *markov_hcstat2 = filename_from_filepath (user_options->markov_hcstat2);
XXH64_update (state, markov_hcstat2, strlen (markov_hcstat2));
}
if (user_options->custom_charset_1)
{
const char *custom_charset_1 = user_options->custom_charset_1;
XXH64_update (state, custom_charset_1, strlen (custom_charset_1));
}
if (user_options->custom_charset_2)
{
const char *custom_charset_2 = user_options->custom_charset_2;
XXH64_update (state, custom_charset_2, strlen (custom_charset_2));
}
if (user_options->custom_charset_3)
{
const char *custom_charset_3 = user_options->custom_charset_3;
XXH64_update (state, custom_charset_3, strlen (custom_charset_3));
}
if (user_options->custom_charset_4)
{
const char *custom_charset_4 = user_options->custom_charset_4;
XXH64_update (state, custom_charset_4, strlen (custom_charset_4));
}
const int hex_wordlist = user_options->hex_wordlist;
XXH64_update (state, &hex_wordlist, sizeof (hex_wordlist));
const int wordlist_autohex_disable = user_options->wordlist_autohex_disable;
XXH64_update (state, &wordlist_autohex_disable, sizeof (wordlist_autohex_disable));
if (user_options->encoding_from)
{
const char *encoding_from = user_options->encoding_from;
XXH64_update (state, encoding_from, strlen (encoding_from));
}
if (user_options->encoding_to)
{
const char *encoding_to = user_options->encoding_to;
XXH64_update (state, encoding_to, strlen (encoding_to));
}
if (user_options->rule_buf_l)
{
const char *rule_buf_l = user_options->rule_buf_l;
XXH64_update (state, rule_buf_l, strlen (rule_buf_l));
}
if (user_options->rule_buf_r)
{
const char *rule_buf_r = user_options->rule_buf_r;
XXH64_update (state, rule_buf_r, strlen (rule_buf_r));
}
}
else if (user_options->attack_mode == ATTACK_MODE_HYBRID2)
{
const char *mask = mask_ctx->mask;
XXH64_update (state, mask, strlen (mask));
const u64 wordlist_hash = brain_compute_attack_wordlist (straight_ctx->dict);
XXH64_update (state, &wordlist_hash, sizeof (wordlist_hash));
const int hex_charset = user_options->hex_charset;
XXH64_update (state, &hex_charset, sizeof (hex_charset));
const int markov_classic = user_options->markov_classic;
const int markov_disable = user_options->markov_disable;
const int markov_threshold = user_options->markov_threshold;
XXH64_update (state, &markov_classic, sizeof (markov_classic));
XXH64_update (state, &markov_disable, sizeof (markov_disable));
XXH64_update (state, &markov_threshold, sizeof (markov_threshold));
if (user_options->markov_hcstat2)
{
const char *markov_hcstat2 = filename_from_filepath (user_options->markov_hcstat2);
XXH64_update (state, markov_hcstat2, strlen (markov_hcstat2));
}
if (user_options->custom_charset_1)
{
const char *custom_charset_1 = user_options->custom_charset_1;
XXH64_update (state, custom_charset_1, strlen (custom_charset_1));
}
if (user_options->custom_charset_2)
{
const char *custom_charset_2 = user_options->custom_charset_2;
XXH64_update (state, custom_charset_2, strlen (custom_charset_2));
}
if (user_options->custom_charset_3)
{
const char *custom_charset_3 = user_options->custom_charset_3;
XXH64_update (state, custom_charset_3, strlen (custom_charset_3));
}
if (user_options->custom_charset_4)
{
const char *custom_charset_4 = user_options->custom_charset_4;
XXH64_update (state, custom_charset_4, strlen (custom_charset_4));
}
const int hex_wordlist = user_options->hex_wordlist;
XXH64_update (state, &hex_wordlist, sizeof (hex_wordlist));
const int wordlist_autohex_disable = user_options->wordlist_autohex_disable;
XXH64_update (state, &wordlist_autohex_disable, sizeof (wordlist_autohex_disable));
if (user_options->encoding_from)
{
const char *encoding_from = user_options->encoding_from;
XXH64_update (state, encoding_from, strlen (encoding_from));
}
if (user_options->encoding_to)
{
const char *encoding_to = user_options->encoding_to;
XXH64_update (state, encoding_to, strlen (encoding_to));
}
if (user_options->rule_buf_l)
{
const char *rule_buf_l = user_options->rule_buf_l;
XXH64_update (state, rule_buf_l, strlen (rule_buf_l));
}
if (user_options->rule_buf_r)
{
const char *rule_buf_r = user_options->rule_buf_r;
XXH64_update (state, rule_buf_r, strlen (rule_buf_r));
}
}
const u32 brain_attack = (const u32) XXH64_digest (state);
XXH64_freeState (state);
return brain_attack;
}
u64 brain_compute_attack_wordlist (const char *filename)
{
XXH64_state_t *state = XXH64_createState ();
XXH64_reset (state, 0);
#define FBUFSZ 8192
char buf[FBUFSZ];
FILE *fd = fopen (filename, "rb");
while (!feof (fd))
{
const size_t nread = fread (buf, 1, FBUFSZ, fd);
XXH64_update (state, buf, nread);
}
fclose (fd);
const u64 hash = XXH64_digest (state);
XXH64_freeState (state);
return hash;
}
u64 brain_auth_hash (const u32 challenge, const char *pw_buf, const int pw_len)
{
// nothing for production but good enough for testing
u64 response = XXH64 (pw_buf, pw_len, challenge);
for (int i = 0; i < 100000; i++)
{
response = XXH64 (&response, 8, 0);
}
return response;
}
u32 brain_auth_challenge (void)
{
srand (time (NULL));
u32 val = rand (); // just a fallback value
#if defined (_WIN)
// from
HCRYPTPROV hCryptProv;
if (CryptAcquireContext (&hCryptProv, NULL, NULL, PROV_RSA_FULL, 0) == true)
{
if (CryptGenRandom (hCryptProv, sizeof (val), (BYTE *) &val) == true)
{
// all good
}
else
{
brain_logging (stderr, 0, "CryptGenRandom: %d\n", (int) GetLastError ());
return val;
}
CryptReleaseContext (hCryptProv, 0);
}
else
{
brain_logging (stderr, 0, "CryptAcquireContext: %d\n", (int) GetLastError ());
return val;
}
#else
static const char *urandom = "/dev/urandom";
FILE *fd = fopen (urandom, "rb");
if (fd == NULL)
{
brain_logging (stderr, 0, "%s: %s\n", urandom, strerror (errno));
return val;
}
if (fread (&val, sizeof (val), 1, fd) != 1)
{
brain_logging (stderr, 0, "%s: %s\n", urandom, strerror (errno));
fclose (fd);
return val;
}
fclose (fd);
#endif
return val;
}
int brain_connect (int sockfd, const struct sockaddr *addr, socklen_t addrlen, const int timeout)
{
#if defined (_WIN)
if (timeout == 99999999)
{
// timeout not support on windows
}
const int rc_connect = connect (sockfd, addr, addrlen);
if (rc_connect == SOCKET_ERROR)
{
int err = WSAGetLastError ();
char msg[256] = { 0 };
FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, // flags
NULL, // lpsource
err, // message id
MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT), // languageid
msg, // output buffer
sizeof (msg), // size of msgbuf, bytes
NULL); // va_list of arguments
brain_logging (stderr, 0, "connect: %s\n", msg);
return -1;
}
#else
const int old_mode = fcntl (sockfd, F_GETFL, 0);
if (fcntl (sockfd, F_SETFL, old_mode | O_NONBLOCK) == -1)
{
brain_logging (stderr, 0, "fcntl: %s\n", strerror (errno));
return -1;
}
connect (sockfd, addr, addrlen);
const int rc_select = select_write_timeout (sockfd, timeout);
if (rc_select == -1) return -1;
if (rc_select == 0)
{
brain_logging (stderr, 0, "connect: timeout\n");
return -1;
}
int so_error;
socklen_t len = sizeof (so_error);
if (getsockopt (sockfd, SOL_SOCKET, SO_ERROR, &so_error, &len) == -1)
{
brain_logging (stderr, 0, "getsockopt: %s\n", strerror (errno));
return -1;
}
if (fcntl (sockfd, F_SETFL, old_mode) == -1)
{
brain_logging (stderr, 0, "fcntl: %s\n", strerror (errno));
return -1;
}
if (so_error != 0)
{
brain_logging (stderr, 0, "connect: %s\n", strerror (so_error));
return -1;
}
#endif
return 0;
}
bool brain_send (int sockfd, void *buf, size_t len, int flags, hc_device_param_t *device_param, const status_ctx_t *status_ctx)
{
char *ptr = (char *) buf;
ssize_t s_pos;
ssize_t s_len = len;
for (s_pos = 0; s_pos < s_len - BRAIN_LINK_CHUNK_SIZE; s_pos += BRAIN_LINK_CHUNK_SIZE)
{
if (brain_send_all (sockfd, ptr + s_pos, BRAIN_LINK_CHUNK_SIZE, flags, device_param, status_ctx) == false) return false;
if (status_ctx) if (status_ctx->run_thread_level1 == false) return false;
}
if (brain_send_all (sockfd, ptr + s_pos, s_len - s_pos, flags, device_param, status_ctx) == false) return false;
if (status_ctx) if (status_ctx->run_thread_level1 == false) return false;
return true;
}
bool brain_recv (int sockfd, void *buf, size_t len, int flags, hc_device_param_t *device_param, const status_ctx_t *status_ctx)
{
char *ptr = (char *) buf;
ssize_t s_pos;
ssize_t s_len = len;
for (s_pos = 0; s_pos < s_len - BRAIN_LINK_CHUNK_SIZE; s_pos += BRAIN_LINK_CHUNK_SIZE)
{
if (brain_recv_all (sockfd, ptr + s_pos, BRAIN_LINK_CHUNK_SIZE, flags, device_param, status_ctx) == false) return false;
if (status_ctx) if (status_ctx->run_thread_level1 == false) return false;
}
if (brain_recv_all (sockfd, ptr + s_pos, s_len - s_pos, flags, device_param, status_ctx) == false) return false;
if (status_ctx) if (status_ctx->run_thread_level1 == false) return false;
return true;
}
bool brain_send_all (int sockfd, void *buf, size_t len, int flags, hc_device_param_t *device_param, const status_ctx_t *status_ctx)
{
link_speed_t *link_speed = &device_param->brain_link_send_speed;
if (device_param)
{
device_param->brain_link_send_active = true;
hc_timer_set (&link_speed->timer[link_speed->pos]);
}
ssize_t nsend = send (sockfd, buf, len, flags);
if (device_param)
{
link_speed->bytes[link_speed->pos] = nsend;
if (link_speed->pos++ == LINK_SPEED_COUNT) link_speed->pos = 0;
device_param->brain_link_send_bytes += nsend;
}
if (nsend <= 0) return false;
if (status_ctx) if (status_ctx->run_thread_level1 == false) return false;
while (nsend < (ssize_t) len)
{
char *buf_new = (char *) buf;
if (device_param)
{
hc_timer_set (&link_speed->timer[link_speed->pos]);
}
ssize_t nsend_new = send (sockfd, buf_new + nsend, len - nsend, flags);
if (device_param)
{
link_speed->bytes[link_speed->pos] = nsend_new;
if (link_speed->pos++ == LINK_SPEED_COUNT) link_speed->pos = 0;
device_param->brain_link_send_bytes += nsend_new;
}
if (nsend_new <= 0) return false;
if (status_ctx) if (status_ctx->run_thread_level1 == false) break;
nsend += nsend_new;
}
if (device_param)
{
device_param->brain_link_send_active = false;
}
return true;
}
bool brain_recv_all (int sockfd, void *buf, size_t len, int flags, hc_device_param_t *device_param, const status_ctx_t *status_ctx)
{
link_speed_t *link_speed = &device_param->brain_link_recv_speed;
if (device_param)
{
device_param->brain_link_recv_active = true;
hc_timer_set (&link_speed->timer[link_speed->pos]);
}
ssize_t nrecv = recv (sockfd, buf, len, flags);
if (device_param)
{
link_speed->bytes[link_speed->pos] = nrecv;
if (link_speed->pos++ == LINK_SPEED_COUNT) link_speed->pos = 0;
device_param->brain_link_recv_bytes += nrecv;
}
if (nrecv <= 0) return false;
if (status_ctx) if (status_ctx->run_thread_level1 == false) return false;
while (nrecv < (ssize_t) len)
{
char *buf_new = (char *) buf;
if (device_param)
{
hc_timer_set (&link_speed->timer[link_speed->pos]);
}
ssize_t nrecv_new = recv (sockfd, buf_new + nrecv, len - nrecv, flags);
if (device_param)
{
link_speed->bytes[link_speed->pos] = nrecv_new;
if (link_speed->pos++ == LINK_SPEED_COUNT) link_speed->pos = 0;
device_param->brain_link_recv_bytes += nrecv_new;
}
if (nrecv_new <= 0) return false;
if (status_ctx) if (status_ctx->run_thread_level1 == false) break;
nrecv += nrecv_new;
}
if (device_param)
{
device_param->brain_link_recv_active = false;
}
return true;
}
bool brain_client_connect (hc_device_param_t *device_param, const status_ctx_t *status_ctx, const char *host, const int port, const char *password, u32 brain_session, u32 brain_attack, i64 passwords_max, u64 *highest)
{
device_param->brain_link_client_fd = 0;
device_param->brain_link_recv_bytes = 0;
device_param->brain_link_send_bytes = 0;
device_param->brain_link_recv_active = false;
device_param->brain_link_send_active = false;
memset (&device_param->brain_link_recv_speed, 0, sizeof (link_speed_t));
memset (&device_param->brain_link_send_speed, 0, sizeof (link_speed_t));
const int brain_link_client_fd = socket (AF_INET, SOCK_STREAM, 0);
if (brain_link_client_fd == -1)
{
brain_logging (stderr, 0, "socket: %s\n", strerror (errno));
return false;
}
#if defined (__linux__)
const int one = 1;
if (setsockopt (brain_link_client_fd, SOL_TCP, TCP_NODELAY, &one, sizeof (one)) == -1)
{
brain_logging (stderr, 0, "setsockopt: %s\n", strerror (errno));
close (brain_link_client_fd);
return false;
}
#else
#endif
struct addrinfo hints;
memset (&hints, 0, sizeof (hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
char port_str[8];
snprintf (port_str, sizeof (port_str), "%i", port);
const char *host_real = (host == NULL) ? "127.0.0.1" : (const char *) host;
bool connected = false;
struct addrinfo *address_info;
const int rc_getaddrinfo = getaddrinfo (host_real, port_str, &hints, &address_info);
if (rc_getaddrinfo == 0)
{
struct addrinfo *address_info_ptr;
for (address_info_ptr = address_info; address_info_ptr != NULL; address_info_ptr = address_info_ptr->ai_next)
{
if (brain_connect (brain_link_client_fd, address_info_ptr->ai_addr, address_info_ptr->ai_addrlen, BRAIN_CLIENT_CONNECT_TIMEOUT) == 0)
{
connected = true;
break;
}
}
freeaddrinfo (address_info);
}
else
{
brain_logging (stderr, 0, "%s: %s\n", host_real, gai_strerror (rc_getaddrinfo));
close (brain_link_client_fd);
return false;
}
if (connected == false)
{
close (brain_link_client_fd);
return false;
}
device_param->brain_link_client_fd = brain_link_client_fd;
u32 brain_link_version = BRAIN_LINK_VERSION_CUR;
if (brain_send (brain_link_client_fd, &brain_link_version, sizeof (brain_link_version), 0, NULL, NULL) == false)
{
brain_logging (stderr, 0, "brain_send: %s\n", strerror (errno));
close (brain_link_client_fd);
return false;
}
u32 brain_link_version_ok;
if (brain_recv (brain_link_client_fd, &brain_link_version_ok, sizeof (brain_link_version_ok), 0, NULL, NULL) == false)
{
brain_logging (stderr, 0, "brain_recv: %s\n", strerror (errno));
close (brain_link_client_fd);
return false;
}
if (brain_link_version_ok == 0)
{
brain_logging (stderr, 0, "Invalid brain server version\n");
close (brain_link_client_fd);
return false;
}
u32 challenge;
if (brain_recv (brain_link_client_fd, &challenge, sizeof (challenge), 0, NULL, NULL) == false)
{
brain_logging (stderr, 0, "brain_recv: %s\n", strerror (errno));
close (brain_link_client_fd);
return false;
}
u64 response = brain_auth_hash (challenge, password, strlen (password));
if (brain_send (brain_link_client_fd, &response, sizeof (response), 0, NULL, NULL) == false)
{
brain_logging (stderr, 0, "brain_send: %s\n", strerror (errno));
close (brain_link_client_fd);
return false;
}
u32 password_ok;
if (brain_recv (brain_link_client_fd, &password_ok, sizeof (password_ok), 0, NULL, NULL) == false)
{
brain_logging (stderr, 0, "brain_recv: %s\n", strerror (errno));
close (brain_link_client_fd);
return false;
}
if (password_ok == 0)
{
brain_logging (stderr, 0, "Invalid brain server password\n");
close (brain_link_client_fd);
return false;
}
if (brain_send (brain_link_client_fd, &brain_session, sizeof (brain_session), SEND_FLAGS, device_param, status_ctx) == false)
{
brain_logging (stderr, 0, "brain_send: %s\n", strerror (errno));
close (brain_link_client_fd);
return false;
}
if (brain_send (brain_link_client_fd, &brain_attack, sizeof (brain_attack), SEND_FLAGS, device_param, status_ctx) == false)
{
brain_logging (stderr, 0, "brain_send: %s\n", strerror (errno));
close (brain_link_client_fd);
return false;
}
if (brain_send (brain_link_client_fd, &passwords_max, sizeof (passwords_max), SEND_FLAGS, device_param, status_ctx) == false)
{
brain_logging (stderr, 0, "brain_send: %s\n", strerror (errno));
close (brain_link_client_fd);
return false;
}
if (brain_recv (brain_link_client_fd, highest, sizeof (u64), 0, NULL, NULL) == false)
{
brain_logging (stderr, 0, "brain_recv: %s\n", strerror (errno));
close (brain_link_client_fd);
return false;
}
return true;
}
void brain_client_disconnect (hc_device_param_t *device_param)
{
if (device_param->brain_link_client_fd > 2)
{
close (device_param->brain_link_client_fd);
}
device_param->brain_link_client_fd = -1;
}
bool brain_client_reserve (hc_device_param_t *device_param, const status_ctx_t *status_ctx, u64 words_off, u64 work, u64 *overlap)
{
const int brain_link_client_fd = device_param->brain_link_client_fd;
if (brain_link_client_fd == -1) return false;
u8 operation = BRAIN_OPERATION_ATTACK_RESERVE;
if (brain_send (brain_link_client_fd, &operation, sizeof (operation), SEND_FLAGS, device_param, status_ctx) == false) return false;
if (brain_send (brain_link_client_fd, &words_off, sizeof (words_off), 0, device_param, status_ctx) == false) return false;
if (brain_send (brain_link_client_fd, &work, sizeof (work), 0, device_param, status_ctx) == false) return false;
if (brain_recv (brain_link_client_fd, overlap, sizeof (u64), 0, device_param, status_ctx) == false) return false;
return true;
}
bool brain_client_commit (hc_device_param_t *device_param, const status_ctx_t *status_ctx)
{
if (device_param->pws_cnt == 0) return true;
const int brain_link_client_fd = device_param->brain_link_client_fd;
if (brain_link_client_fd == -1) return false;
u8 operation = BRAIN_OPERATION_COMMIT;
if (brain_send (brain_link_client_fd, &operation, sizeof (operation), SEND_FLAGS, device_param, status_ctx) == false) return false;
return true;
}
bool brain_client_lookup (hc_device_param_t *device_param, const status_ctx_t *status_ctx)
{
if (device_param->pws_pre_cnt == 0) return true;
const int brain_link_client_fd = device_param->brain_link_client_fd;
if (brain_link_client_fd == -1) return false;
char *recvbuf = (char *) device_param->brain_link_in_buf;
char *sendbuf = (char *) device_param->brain_link_out_buf;
int out_size = device_param->pws_pre_cnt * BRAIN_HASH_SIZE;
u8 operation = BRAIN_OPERATION_HASH_LOOKUP;
if (brain_send (brain_link_client_fd, &operation, sizeof (operation), SEND_FLAGS, device_param, status_ctx) == false) return false;
if (brain_send (brain_link_client_fd, &out_size, sizeof (out_size), SEND_FLAGS, device_param, status_ctx) == false) return false;
if (brain_send (brain_link_client_fd, sendbuf, out_size, SEND_FLAGS, device_param, status_ctx) == false) return false;
int in_size;
if (brain_recv (brain_link_client_fd, &in_size, sizeof (in_size), 0, device_param, status_ctx) == false) return false;
if (in_size > (int) device_param->size_brain_link_in) return false;
if (brain_recv (brain_link_client_fd, recvbuf, (size_t) in_size, 0, device_param, status_ctx) == false) return false;
return true;
}
void brain_client_generate_hash (u64 *hash, const char *line_buf, const size_t line_len)
{
const u64 seed = 0;
hash[0] = XXH64 (line_buf, line_len, seed);
}
void brain_server_db_hash_init (brain_server_db_hash_t *brain_server_db_hash, const u32 brain_session)
{
brain_server_db_hash->brain_session = brain_session;
brain_server_db_hash->long_alloc = 0;
brain_server_db_hash->long_cnt = 0;
brain_server_db_hash->long_buf = NULL;
brain_server_db_hash->write_hashes = false;
brain_server_db_hash->hb = 0;
hc_thread_mutex_init (brain_server_db_hash->mux_hr);
hc_thread_mutex_init (brain_server_db_hash->mux_hg);
}
bool brain_server_db_hash_realloc (brain_server_db_hash_t *brain_server_db_hash, const i64 new_long_cnt)
{
if ((brain_server_db_hash->long_cnt + new_long_cnt) > brain_server_db_hash->long_alloc)
{
const i64 realloc_size_total = (i64) mydivc64 ((const u64) new_long_cnt, (const u64) BRAIN_SERVER_REALLOC_HASH_SIZE) * BRAIN_SERVER_REALLOC_HASH_SIZE;
brain_server_hash_long_t *long_buf = (brain_server_hash_long_t *) hcrealloc (brain_server_db_hash->long_buf, brain_server_db_hash->long_alloc * sizeof (brain_server_hash_long_t), realloc_size_total * sizeof (brain_server_hash_long_t));
if (long_buf == NULL) return false;
brain_server_db_hash->long_buf = long_buf;
brain_server_db_hash->long_alloc += realloc_size_total;
}
return true;
}
void brain_server_db_hash_free (brain_server_db_hash_t *brain_server_db_hash)
{
hc_thread_mutex_delete (brain_server_db_hash->mux_hg);
hc_thread_mutex_delete (brain_server_db_hash->mux_hr);
brain_server_db_hash->hb = 0;
hcfree (brain_server_db_hash->long_buf);
brain_server_db_hash->long_alloc = 0;
brain_server_db_hash->long_cnt = 0;
brain_server_db_hash->long_buf = NULL;
brain_server_db_hash->write_hashes = false;
brain_server_db_hash->brain_session = 0;
}
void brain_server_db_attack_init (brain_server_db_attack_t *brain_server_db_attack, const u32 brain_attack)
{
brain_server_db_attack->brain_attack = brain_attack;
brain_server_db_attack->short_alloc = 0;
brain_server_db_attack->short_cnt = 0;
brain_server_db_attack->short_buf = NULL;
brain_server_db_attack->long_alloc = 0;
brain_server_db_attack->long_cnt = 0;
brain_server_db_attack->long_buf = NULL;
brain_server_db_attack->write_attacks = false;
brain_server_db_attack->ab = 0;
hc_thread_mutex_init (brain_server_db_attack->mux_ar);
hc_thread_mutex_init (brain_server_db_attack->mux_ag);
}
bool brain_server_db_attack_realloc (brain_server_db_attack_t *brain_server_db_attack, const i64 new_long_cnt, const i64 new_short_cnt)
{
if ((brain_server_db_attack->long_cnt + new_long_cnt) > brain_server_db_attack->long_alloc)
{
const i64 realloc_size_total = (i64) mydivc64 ((const u64) new_long_cnt, (const u64) BRAIN_SERVER_REALLOC_ATTACK_SIZE) * BRAIN_SERVER_REALLOC_ATTACK_SIZE;
brain_server_attack_long_t *long_buf = (brain_server_attack_long_t *) hcrealloc (brain_server_db_attack->long_buf, brain_server_db_attack->long_alloc * sizeof (brain_server_attack_long_t), realloc_size_total * sizeof (brain_server_attack_long_t));
if (long_buf == NULL) return false;
brain_server_db_attack->long_buf = long_buf;
brain_server_db_attack->long_alloc += realloc_size_total;
}
if ((brain_server_db_attack->short_cnt + new_short_cnt) > brain_server_db_attack->short_alloc)
{
const i64 realloc_size_total = (i64) mydivc64 ((const u64) new_short_cnt, (const u64) BRAIN_SERVER_REALLOC_ATTACK_SIZE) * BRAIN_SERVER_REALLOC_ATTACK_SIZE;
brain_server_attack_short_t *short_buf = (brain_server_attack_short_t *) hcrealloc (brain_server_db_attack->short_buf, brain_server_db_attack->short_alloc * sizeof (brain_server_attack_short_t), realloc_size_total * sizeof (brain_server_attack_short_t));
if (short_buf == NULL) return false;
brain_server_db_attack->short_buf = short_buf;
brain_server_db_attack->short_alloc += realloc_size_total;
}
return true;
}
void brain_server_db_attack_free (brain_server_db_attack_t *brain_server_db_attack)
{
hc_thread_mutex_delete (brain_server_db_attack->mux_ag);
hc_thread_mutex_delete (brain_server_db_attack->mux_ar);
brain_server_db_attack->ab = 0;
hcfree (brain_server_db_attack->long_buf);
brain_server_db_attack->long_alloc = 0;
brain_server_db_attack->long_cnt = 0;
brain_server_db_attack->long_buf = NULL;
hcfree (brain_server_db_attack->short_buf);
brain_server_db_attack->short_alloc = 0;
brain_server_db_attack->short_cnt = 0;
brain_server_db_attack->short_buf = NULL;
brain_server_db_attack->write_attacks = false;
brain_server_db_attack->brain_attack = 0;
}
u64 brain_server_highest_attack (const brain_server_db_attack_t *buf)
{
const brain_server_attack_long_t *long_buf = buf->long_buf;
const brain_server_attack_short_t *short_buf = buf->short_buf;
const u64 long_cnt = buf->long_cnt;
const u64 short_cnt = buf->short_cnt;
u64 highest_long = brain_server_highest_attack_long (long_buf, long_cnt, 0);
u64 highest_short = brain_server_highest_attack_short (short_buf, short_cnt, 0);
u64 highest = MAX (highest_long, highest_short);
highest_long = brain_server_highest_attack_long (long_buf, long_cnt, highest);
highest_short = brain_server_highest_attack_short (short_buf, short_cnt, highest);
highest = MAX (highest_long, highest_short);
return highest;
}
u64 brain_server_highest_attack_long (const brain_server_attack_long_t *buf, const i64 cnt, const u64 start)
{
u64 highest = start;
for (i64 idx = 0; idx < cnt; idx++)
{
const u64 offset = buf[idx].offset;
const u64 length = buf[idx].length;
if (offset > highest) break;
const u64 next = offset + length;
highest = MAX (highest, next);
}
return highest;
}
u64 brain_server_highest_attack_short (const brain_server_attack_short_t *buf, const i64 cnt, const u64 start)
{
u64 highest = start;
for (i64 idx = 0; idx < cnt; idx++)
{
const u64 offset = buf[idx].offset;
const u64 length = buf[idx].length;
if (offset > highest) break;
const u64 next = offset + length;
highest = MAX (highest, next);
}
return highest;
}
u64 brain_server_find_attack_long (const brain_server_attack_long_t *buf, const i64 cnt, const u64 offset, const u64 length)
{
const u64 end = offset + length;
u64 overlap = 0;
for (i64 idx = 0; idx < cnt; idx++)
{
const u64 element_length = buf[idx].length;
if (element_length == 0) continue;
const u64 element_start = buf[idx].offset;
const u64 element_end = element_start + element_length;
const u64 start = offset + overlap;
if (element_start > start) break; // we can't ever do it since this list is sorted
if (element_end > start)
{
const u64 limited_end = MIN (end, element_end);
overlap += limited_end - start;
if (overlap == length) break;
}
}
return overlap;
}
u64 brain_server_find_attack_short (const brain_server_attack_short_t *buf, const i64 cnt, const u64 offset, const u64 length)
{
const u64 end = offset + length;
u64 overlap = 0;
for (i64 idx = 0; idx < cnt; idx++)
{
const u64 element_length = buf[idx].length;
if (element_length == 0) continue;
const u64 element_start = buf[idx].offset;
const u64 element_end = element_start + element_length;
const u64 start = offset + overlap;
if (element_start > start) break; // we can't ever do it since this list is sorted
if (element_end > start)
{
const u64 limited_end = MIN (end, element_end);
overlap += limited_end - start;
if (overlap == length) break;
}
}
return overlap;
}
int brain_server_sort_db_hash (const void *v1, const void *v2)
{
const brain_server_db_hash_t *d1 = (const brain_server_db_hash_t *) v1;
const brain_server_db_hash_t *d2 = (const brain_server_db_hash_t *) v2;
if (d1->brain_session > d2->brain_session) return 1;
if (d1->brain_session < d2->brain_session) return -1;
return 0;
}
int brain_server_sort_db_attack (const void *v1, const void *v2)
{
const brain_server_db_attack_t *d1 = (const brain_server_db_attack_t *) v1;
const brain_server_db_attack_t *d2 = (const brain_server_db_attack_t *) v2;
if (d1->brain_attack > d2->brain_attack) return 1;
if (d1->brain_attack < d2->brain_attack) return -1;
return 0;
}
int brain_server_sort_hash (const void *v1, const void *v2)
{
const u32 *d1 = (const u32 *) v1;
const u32 *d2 = (const u32 *) v2;
if (d1[1] > d2[1]) return 1;
if (d1[1] < d2[1]) return -1;
if (d1[0] > d2[0]) return 1;
if (d1[0] < d2[0]) return -1;
return 0;
}
int brain_server_sort_attack_long (const void *v1, const void *v2)
{
const brain_server_attack_long_t *d1 = (const brain_server_attack_long_t *) v1;
const brain_server_attack_long_t *d2 = (const brain_server_attack_long_t *) v2;
if (d1->offset > d2->offset) return 1;
if (d1->offset < d2->offset) return -1;
return 0;
}
int brain_server_sort_attack_short (const void *v1, const void *v2)
{
const brain_server_attack_short_t *d1 = (const brain_server_attack_short_t *) v1;
const brain_server_attack_short_t *d2 = (const brain_server_attack_short_t *) v2;
if (d1->offset > d2->offset) return 1;
if (d1->offset < d2->offset) return -1;
return 0;
}
int brain_server_sort_hash_long (const void *v1, const void *v2)
{
const brain_server_hash_long_t *d1 = (const brain_server_hash_long_t *) v1;
const brain_server_hash_long_t *d2 = (const brain_server_hash_long_t *) v2;
return brain_server_sort_hash (d1->hash, d2->hash);
}
int brain_server_sort_hash_short (const void *v1, const void *v2)
{
const brain_server_hash_short_t *d1 = (const brain_server_hash_short_t *) v1;
const brain_server_hash_short_t *d2 = (const brain_server_hash_short_t *) v2;
return brain_server_sort_hash (d1->hash, d2->hash);
}
int brain_server_sort_hash_unique (const void *v1, const void *v2)
{
const brain_server_hash_unique_t *d1 = (const brain_server_hash_unique_t *) v1;
const brain_server_hash_unique_t *d2 = (const brain_server_hash_unique_t *) v2;
return brain_server_sort_hash (d1->hash, d2->hash);
}
bool brain_server_read_hash_dumps (brain_server_dbs_t *brain_server_dbs, const char *path)
{
brain_server_dbs->hash_cnt = 0;
if (chdir (path) == -1)
{
brain_logging (stderr, 0, "%s: %s\n", path, strerror (errno));
return false;
}
DIR *dirp = opendir (path);
if (dirp == NULL)
{
brain_logging (stderr, 0, "%s: %s\n", path, strerror (errno));
return false;
}
struct dirent *entry;
while ((entry = readdir (dirp)) != NULL)
{
char *file = entry->d_name;
const size_t len = strlen (file);
if (len != 19) continue;
if (file[ 0] != 'b') continue;
if (file[ 1] != 'r') continue;
if (file[ 2] != 'a') continue;
if (file[ 3] != 'i') continue;
if (file[ 4] != 'n') continue;
if (file[ 5] != '.') continue;
if (file[14] != '.') continue;
if (file[15] != 'l') continue;
if (file[16] != 'd') continue;
if (file[17] != 'm') continue;
if (file[18] != 'p') continue;
const u32 brain_session = byte_swap_32 (hex_to_u32 ((const u8 *) file + 6));
brain_server_db_hash_t *brain_server_db_hash = &brain_server_dbs->hash_buf[brain_server_dbs->hash_cnt];
brain_server_db_hash_init (brain_server_db_hash, brain_session);
if (brain_server_read_hash_dump (brain_server_db_hash, file) == false)
{
continue;
}
brain_server_dbs->hash_cnt++;
}
closedir (dirp);
return true;
}
bool brain_server_write_hash_dumps (brain_server_dbs_t *brain_server_dbs, const char *path)
{
for (i64 idx = 0; idx < brain_server_dbs->hash_cnt; idx++)
{
brain_server_db_hash_t *brain_server_db_hash = &brain_server_dbs->hash_buf[idx];
hc_thread_mutex_lock (brain_server_db_hash->mux_hg);
char file[100];
snprintf (file, sizeof (file), "%s/brain.%08x.ldmp", path, brain_server_db_hash->brain_session);
brain_server_write_hash_dump (brain_server_db_hash, file);
hc_thread_mutex_unlock (brain_server_db_hash->mux_hg);
}
return true;
}
bool brain_server_read_hash_dump (brain_server_db_hash_t *brain_server_db_hash, const char *file)
{
hc_timer_t timer_dump;
hc_timer_set (&timer_dump);
// read from file
struct stat sb;
if (stat (file, &sb) == -1)
{
brain_logging (stderr, 0, "%s: %s\n", file, strerror (errno));
return false;
}
FILE *fd = fopen (file, "rb");
if (fd == NULL)
{
brain_logging (stderr, 0, "%s: %s\n", file, strerror (errno));
return false;
}
else
{
i64 temp_cnt = (u64) sb.st_size / sizeof (brain_server_hash_long_t);
if (brain_server_db_hash_realloc (brain_server_db_hash, temp_cnt) == false)
{
brain_logging (stderr, 0, "%s\n", MSG_ENOMEM);
fclose (fd);
return false;
}
const size_t nread = fread (brain_server_db_hash->long_buf, sizeof (brain_server_hash_long_t), temp_cnt, fd);
if (nread != (size_t) temp_cnt)
{
brain_logging (stderr, 0, "%s: only %" PRIu64 " bytes read\n", file, (u64) nread * sizeof (brain_server_hash_long_t));
fclose (fd);
return false;
}
brain_server_db_hash->long_cnt = temp_cnt;
brain_server_db_hash->write_hashes = false;
fclose (fd);
}
const double ms = hc_timer_get (timer_dump);
brain_logging (stdout, 0, "Read %" PRIu64 " bytes from session 0x%08x in %.2f ms\n", (u64) sb.st_size, brain_server_db_hash->brain_session, ms);
return true;
}
bool brain_server_write_hash_dump (brain_server_db_hash_t *brain_server_db_hash, const char *file)
{
if (brain_server_db_hash->write_hashes == false) return true;
hc_timer_t timer_dump;
hc_timer_set (&timer_dump);
// write to file
FILE *fd = fopen (file, "wb");
if (fd == NULL)
{
brain_logging (stderr, 0, "%s: %s\n", file, strerror (errno));
return false;
}
else
{
const size_t nwrite = fwrite (brain_server_db_hash->long_buf, sizeof (brain_server_hash_long_t), brain_server_db_hash->long_cnt, fd);
if (nwrite != (size_t) brain_server_db_hash->long_cnt)
{
brain_logging (stderr, 0, "%s: only %" PRIu64 " bytes written\n", file, (u64) nwrite * sizeof (brain_server_hash_long_t));
fclose (fd);
return false;
}
fclose (fd);
brain_server_db_hash->write_hashes = false;
}
// stats
const double ms = hc_timer_get (timer_dump);
struct stat sb;
if (stat (file, &sb) == -1)
{
brain_logging (stderr, 0, "%s: %s\n", file, strerror (errno));
return false;
}
brain_logging (stdout, 0, "Wrote %" PRIu64 " bytes from session 0x%08x in %.2f ms\n", (u64) sb.st_size, brain_server_db_hash->brain_session, ms);
return true;
}
bool brain_server_read_attack_dumps (brain_server_dbs_t *brain_server_dbs, const char *path)
{
brain_server_dbs->attack_cnt = 0;
if (chdir (path) == -1)
{
brain_logging (stderr, 0, "%s: %s\n", path, strerror (errno));
return false;
}
DIR *dirp = opendir (path);
if (dirp == NULL)
{
brain_logging (stderr, 0, "%s: %s\n", path, strerror (errno));
return false;
}
struct dirent *entry;
while ((entry = readdir (dirp)) != NULL)
{
char *file = entry->d_name;
const size_t len = strlen (file);
if (len != 19) continue;
if (file[ 0] != 'b') continue;
if (file[ 1] != 'r') continue;
if (file[ 2] != 'a') continue;
if (file[ 3] != 'i') continue;
if (file[ 4] != 'n') continue;
if (file[ 5] != '.') continue;
if (file[14] != '.') continue;
if (file[15] != 'a') continue;
if (file[16] != 'd') continue;
if (file[17] != 'm') continue;
if (file[18] != 'p') continue;
const u32 brain_attack = byte_swap_32 (hex_to_u32 ((const u8 *) file + 6));
brain_server_db_attack_t *brain_server_db_attack = &brain_server_dbs->attack_buf[brain_server_dbs->attack_cnt];
brain_server_db_attack_init (brain_server_db_attack, brain_attack);
if (brain_server_read_attack_dump (brain_server_db_attack, file) == false)
{
continue;
}
brain_server_dbs->attack_cnt++;
}
closedir (dirp);
return true;
}
bool brain_server_write_attack_dumps (brain_server_dbs_t *brain_server_dbs, const char *path)
{
for (i64 idx = 0; idx < brain_server_dbs->attack_cnt; idx++)
{
brain_server_db_attack_t *brain_server_db_attack = &brain_server_dbs->attack_buf[idx];
hc_thread_mutex_lock (brain_server_db_attack->mux_ag);
char file[100];
snprintf (file, sizeof (file), "%s/brain.%08x.admp", path, brain_server_db_attack->brain_attack);
brain_server_write_attack_dump (brain_server_db_attack, file);
hc_thread_mutex_unlock (brain_server_db_attack->mux_ag);
}
return true;
}
bool brain_server_read_attack_dump (brain_server_db_attack_t *brain_server_db_attack, const char *file)
{
hc_timer_t timer_dump;
hc_timer_set (&timer_dump);
// read from file
struct stat sb;
if (stat (file, &sb) == -1)
{
brain_logging (stderr, 0, "%s: %s\n", file, strerror (errno));
return false;
}
FILE *fd = fopen (file, "rb");
if (fd == NULL)
{
brain_logging (stderr, 0, "%s: %s\n", file, strerror (errno));
return false;
}
else
{
i64 temp_cnt = (u64) sb.st_size / sizeof (brain_server_attack_long_t);
if (brain_server_db_attack_realloc (brain_server_db_attack, temp_cnt, 0) == false)
{
brain_logging (stderr, 0, "%s\n", MSG_ENOMEM);
fclose (fd);
return false;
}
const size_t nread = fread (brain_server_db_attack->long_buf, sizeof (brain_server_attack_long_t), temp_cnt, fd);
if (nread != (size_t) temp_cnt)
{
brain_logging (stderr, 0, "%s: only %" PRIu64 " bytes read\n", file, (u64) nread * sizeof (brain_server_attack_long_t));
fclose (fd);
return false;
}
brain_server_db_attack->long_cnt = temp_cnt;
brain_server_db_attack->write_attacks = false;
fclose (fd);
}
const double ms = hc_timer_get (timer_dump);
brain_logging (stdout, 0, "Read %" PRIu64 " bytes from attack 0x%08x in %.2f ms\n", (u64) sb.st_size, brain_server_db_attack->brain_attack, ms);
return true;
}
bool brain_server_write_attack_dump (brain_server_db_attack_t *brain_server_db_attack, const char *file)
{
if (brain_server_db_attack->write_attacks == false) return true;
hc_timer_t timer_dump;
hc_timer_set (&timer_dump);
// write to file
FILE *fd = fopen (file, "wb");
if (fd == NULL)
{
brain_logging (stderr, 0, "%s: %s\n", file, strerror (errno));
return false;
}
else
{
// storing should not include reserved attacks only finished
const size_t nwrite = fwrite (brain_server_db_attack->long_buf, sizeof (brain_server_attack_long_t), brain_server_db_attack->long_cnt, fd);
if (nwrite != (size_t) brain_server_db_attack->long_cnt)
{
brain_logging (stderr, 0, "%s: only %" PRIu64 " bytes written\n", file, (u64) nwrite * sizeof (brain_server_attack_long_t));
fclose (fd);
return false;
}
fclose (fd);
brain_server_db_attack->write_attacks = false;
}
// stats
const double ms = hc_timer_get (timer_dump);
struct stat sb;
if (stat (file, &sb) == -1)
{
brain_logging (stderr, 0, "%s: %s\n", file, strerror (errno));
return false;
}
brain_logging (stdout, 0, "Wrote %" PRIu64 " bytes from attack 0x%08x in %.2f ms\n", (u64) sb.st_size, brain_server_db_attack->brain_attack, ms);
return true;
}
int brain_server_get_client_idx (brain_server_dbs_t *brain_server_dbs)
{
for (int i = 1; i < BRAIN_SERVER_CLIENTS_MAX; i++)
{
if (brain_server_dbs->client_slots[i] == 0)
{
brain_server_dbs->client_slots[i] = 1;
return i;
}
}
return -1;
}
i64 brain_server_find_hash_long (const u32 *search, const brain_server_hash_long_t *buf, const i64 cnt)
{
for (i64 l = 0, r = cnt; r; r >>= 1)
{
const i64 m = r >> 1;
const i64 c = l + m;
const int cmp = brain_server_sort_hash_long (search, buf + c);
if (cmp > 0)
{
l += m + 1;
r--;
}
if (cmp == 0) return (c);
}
return (-1);
}
i64 brain_server_find_hash_short (const u32 *search, const brain_server_hash_short_t *buf, const i64 cnt)
{
for (i64 l = 0, r = cnt; r; r >>= 1)
{
const i64 m = r >> 1;
const i64 c = l + m;
const int cmp = brain_server_sort_hash_short (search, buf + c);
if (cmp > 0)
{
l += m + 1;
r--;
}
if (cmp == 0) return (c);
}
return (-1);
}
void brain_server_handle_signal (int signo)
{
if (signo == SIGINT)
{
keep_running = false;
}
}
void *brain_server_handle_dumps (void *p)
{
brain_server_dumper_options_t *brain_server_dumper_options = (brain_server_dumper_options_t *) p;
brain_server_dbs_t *brain_server_dbs = brain_server_dumper_options->brain_server_dbs;
int i = 0;
while (keep_running == true)
{
if (i == BRAIN_SERVER_DUMP_EVERY)
{
brain_server_write_hash_dumps (brain_server_dbs, ".");
brain_server_write_attack_dumps (brain_server_dbs, ".");
i = 0;
}
else
{
i++;
}
sleep (1);
}
return NULL;
}
void *brain_server_handle_client (void *p)
{
brain_server_client_options_t *brain_server_client_options = (brain_server_client_options_t *) p;
const int client_idx = brain_server_client_options->client_idx;
const int client_fd = brain_server_client_options->client_fd;
const char *auth_password = brain_server_client_options->auth_password;
const u32 *session_whitelist_buf = brain_server_client_options->session_whitelist_buf;
const int session_whitelist_cnt = brain_server_client_options->session_whitelist_cnt;
brain_server_dbs_t *brain_server_dbs = brain_server_client_options->brain_server_dbs;
// client configuration
#if defined (__linux__)
const int one = 1;
if (setsockopt (client_fd, SOL_TCP, TCP_NODELAY, &one, sizeof (one)) == -1)
{
brain_logging (stderr, client_idx, "setsockopt: %s\n", strerror (errno));
brain_server_dbs->client_slots[client_idx] = 0;
close (client_fd);
return NULL;
}
#else
#endif
u32 brain_link_version;
if (brain_recv (client_fd, &brain_link_version, sizeof (brain_link_version), 0, NULL, NULL) == false)
{
brain_logging (stderr, client_idx, "brain_recv: %s\n", strerror (errno));
brain_server_dbs->client_slots[client_idx] = 0;
close (client_fd);
return NULL;
}
u32 brain_link_version_ok = (brain_link_version >= (u32) BRAIN_LINK_VERSION_MIN) ? 1 : 0;
if (brain_send (client_fd, &brain_link_version_ok, sizeof (brain_link_version_ok), 0, NULL, NULL) == false)
{
brain_logging (stderr, client_idx, "brain_send: %s\n", strerror (errno));
brain_server_dbs->client_slots[client_idx] = 0;
close (client_fd);
return NULL;
}
if (brain_link_version_ok == 0)
{
brain_logging (stderr, client_idx, "Invalid version\n");
brain_server_dbs->client_slots[client_idx] = 0;
close (client_fd);
return NULL;
}
u32 challenge = brain_auth_challenge ();
if (brain_send (client_fd, &challenge, sizeof (challenge), 0, NULL, NULL) == false)
{
brain_logging (stderr, client_idx, "brain_send: %s\n", strerror (errno));
brain_server_dbs->client_slots[client_idx] = 0;
close (client_fd);
return NULL;
}
u64 response;
if (brain_recv (client_fd, &response, sizeof (response), 0, NULL, NULL) == false)
{
brain_logging (stderr, client_idx, "brain_recv: %s\n", strerror (errno));
brain_server_dbs->client_slots[client_idx] = 0;
close (client_fd);
return NULL;
}
u64 auth_hash = brain_auth_hash (challenge, auth_password, strlen (auth_password));
u32 password_ok = (auth_hash == response) ? 1 : 0;
if (brain_send (client_fd, &password_ok, sizeof (password_ok), 0, NULL, NULL) == false)
{
brain_logging (stderr, client_idx, "brain_send: %s\n", strerror (errno));
brain_server_dbs->client_slots[client_idx] = 0;
close (client_fd);
return NULL;
}
if (password_ok == 0)
{
brain_logging (stderr, client_idx, "Invalid password\n");
brain_server_dbs->client_slots[client_idx] = 0;
close (client_fd);
return NULL;
}
u32 brain_session;
if (brain_recv (client_fd, &brain_session, sizeof (brain_session), 0, NULL, NULL) == false)
{
brain_logging (stderr, client_idx, "brain_recv: %s\n", strerror (errno));
brain_server_dbs->client_slots[client_idx] = 0;
close (client_fd);
return NULL;
}
if (session_whitelist_cnt > 0)
{
bool found = false;
for (int idx = 0; idx < session_whitelist_cnt; idx++)
{
if (session_whitelist_buf[idx] == brain_session)
{
found = true;
break;
}
}
if (found == false)
{
brain_logging (stderr, client_idx, "Invalid brain session: 0x%08x\n", brain_session);
brain_server_dbs->client_slots[client_idx] = 0;
close (client_fd);
return NULL;
}
}
u32 brain_attack;
if (brain_recv (client_fd, &brain_attack, sizeof (brain_attack), 0, NULL, NULL) == false)
{
brain_logging (stderr, client_idx, "brain_recv: %s\n", strerror (errno));
brain_server_dbs->client_slots[client_idx] = 0;
close (client_fd);
return NULL;
}
i64 passwords_max;
if (brain_recv (client_fd, &passwords_max, sizeof (passwords_max), 0, NULL, NULL) == false)
{
brain_logging (stderr, client_idx, "brain_recv: %s\n", strerror (errno));
brain_server_dbs->client_slots[client_idx] = 0;
close (client_fd);
return NULL;
}
if (passwords_max >= BRAIN_LINK_CANDIDATES_MAX)
{
brain_logging (stderr, client_idx, "Too large candidate allocation buffer size\n");
brain_server_dbs->client_slots[client_idx] = 0;
close (client_fd);
return NULL;
}
brain_logging (stdout, client_idx, "Session: 0x%08x, Attack: 0x%08x, Kernel-power: %" PRIu64 "\n", brain_session, brain_attack, passwords_max);
// so far so good
hc_thread_mutex_lock (brain_server_dbs->mux_dbs);
// long term memory
brain_server_db_hash_t key_hash;
key_hash.brain_session = brain_session;
#if defined (_WIN)
unsigned int find_hash_cnt = (unsigned int) brain_server_dbs->hash_cnt;
#else
size_t find_hash_cnt = (size_t) brain_server_dbs->hash_cnt;
#endif
brain_server_db_hash_t *brain_server_db_hash = (brain_server_db_hash_t *) lfind (&key_hash, brain_server_dbs->hash_buf, &find_hash_cnt, sizeof (brain_server_db_hash_t), brain_server_sort_db_hash);
if (brain_server_db_hash == NULL)
{
if (brain_server_dbs->hash_cnt >= BRAIN_SERVER_SESSIONS_MAX)
{
brain_logging (stderr, 0, "too many sessions\n");
brain_server_dbs->client_slots[client_idx] = 0;
close (client_fd);
return NULL;
}
brain_server_db_hash = &brain_server_dbs->hash_buf[brain_server_dbs->hash_cnt];
brain_server_db_hash_init (brain_server_db_hash, brain_session);
brain_server_dbs->hash_cnt++;
}
// attack memory
brain_server_db_attack_t key_attack;
key_attack.brain_attack = brain_attack;
#if defined (_WIN)
unsigned int find_attack_cnt = (unsigned int) brain_server_dbs->attack_cnt;
#else
size_t find_attack_cnt = (size_t) brain_server_dbs->attack_cnt;
#endif
brain_server_db_attack_t *brain_server_db_attack = (brain_server_db_attack_t *) lfind (&key_attack, brain_server_dbs->attack_buf, &find_attack_cnt, sizeof (brain_server_db_attack_t), brain_server_sort_db_attack);
if (brain_server_db_attack == NULL)
{
if (brain_server_dbs->attack_cnt >= BRAIN_SERVER_ATTACKS_MAX)
{
brain_logging (stderr, 0, "too many attacks\n");
brain_server_dbs->client_slots[client_idx] = 0;
close (client_fd);
return NULL;
}
brain_server_db_attack = &brain_server_dbs->attack_buf[brain_server_dbs->attack_cnt];
brain_server_db_attack_init (brain_server_db_attack, brain_attack);
brain_server_dbs->attack_cnt++;
}
hc_thread_mutex_unlock (brain_server_dbs->mux_dbs);
// higest position of that attack
u64 highest = brain_server_highest_attack (brain_server_db_attack);
if (brain_send (client_fd, &highest, sizeof (highest), 0, NULL, NULL) == false)
{
brain_logging (stderr, client_idx, "brain_send: %s\n", strerror (errno));
brain_server_dbs->client_slots[client_idx] = 0;
close (client_fd);
return NULL;
}
// recv
const size_t recv_size = passwords_max * BRAIN_HASH_SIZE;
u32 *recv_buf = (u32 *) hcmalloc (recv_size);
if (recv_buf == NULL)
{
brain_logging (stderr, 0, "%s\n", MSG_ENOMEM);
return NULL;
}
// send
const size_t send_size = passwords_max * sizeof (char);
u8 *send_buf = (u8 *) hcmalloc (send_size); // we can reduce this to 1/8 if we use bits instead of bytes
if (send_buf == NULL)
{
brain_logging (stderr, 0, "%s\n", MSG_ENOMEM);
brain_server_dbs->client_slots[client_idx] = 0;
close (client_fd);
return NULL;
}
// temp
brain_server_hash_unique_t *temp_buf = (brain_server_hash_unique_t *) hccalloc (passwords_max, sizeof (brain_server_hash_unique_t));
if (temp_buf == NULL)
{
brain_logging (stderr, 0, "%s\n", MSG_ENOMEM);
brain_server_dbs->client_slots[client_idx] = 0;
close (client_fd);
return NULL;
}
// short global alloc
brain_server_db_short_t *brain_server_db_short = hcmalloc (sizeof (brain_server_db_short_t));
brain_server_db_short->short_cnt = 0;
brain_server_db_short->short_buf = (brain_server_hash_short_t *) hccalloc (passwords_max, sizeof (brain_server_hash_short_t));
if (brain_server_db_short->short_buf == NULL)
{
brain_logging (stderr, 0, "%s\n", MSG_ENOMEM);
brain_server_dbs->client_slots[client_idx] = 0;
close (client_fd);
return NULL;
}
// main loop
while (keep_running == true)
{
// wait for client to send data, but not too long
const int rc_select = select_read_timeout (client_fd, 1);
if (rc_select == -1) break;
if (rc_select == 0) continue;
// there's data
u8 operation;
if (brain_recv (client_fd, &operation, sizeof (operation), 0, NULL, NULL) == false) break;
// U = update
// R = request
// C = commit
/**
* L = lookup
*
* In this section the client sends a number of password hashes (max = passwords_max).
* The goal is to check them against the long-term memory
* to find out if the password is either reserved by any client (can be the same, too)
* or if it was already checked in the past and then to send a reject.
* This is a complicated process as we have to deal with lots of duplicate data
* and with lots of clients both at the same time.
* We also have to be very fast in looking up the information otherwise the clients
* lose too much performance.
* Once a client sends a commit message, all short-term data related to the client
* is moved to the long-term memory.
* To do that in the commit section, we're storing each hash in the short-term memory
* along with client_fd.
* The short-term memory itself is limited in size. That's possible because each client
* tells the server in the handshake the maximum number of passwords it will send
* before it will either disconnect or send a commit signal.
* The first procedure for each package of hashes sent by the client is to sort them.
* This is done in the client thread and without any mutex barriers, therefore the server
* is able to use multiple threads for this action.
* This is the only time in the entire process when data is being sorted because
* of a smart way of using the data in the following process up to
* and later even in the commit process.
* We need to make sure that a hash which is stored in the short-term memory is not
* already in both the short-term and the long-term memory otherwise we end up in a
* corrupted database.
* Therefor, as a first step after the data has been sorted, we need to remove all duplicates.
* Such duplicates can occur easily in hashcat, for example if hashcat uses a 's' rule.
* If such a 's' rule searches for a character which does not exist in the base word
* the password is not changed.
* If we have multiple of such rules we create lots of duplicates.
* As to this point there was no need to use any mutex.
* But from now on we need a mutex because we will access two shared memory regions
* which both can be written to from any other client.
* We'll check the both databases and remove any existing hashes before the go into
* the short-term memory but at the same time, update the send[] buffer in case we
* need to reject the hash.
* This is possible because along with the hash, we also keep track of its original position
* in the client stream.
* No we ne'll add the remaining hashes to the short-term memory.
* This process needs no additional sorting, but we need to update the hashes
* at the correct position because this is important for the binary tree search.
* So we can not simply append it to the end.
* We do not need to care about the short-term memory size because it was preallocated
* and it is safe the client does not send more hashes that max_passwords.
* The trick here is, since all data at this point is sorted, to merge them in a reverse order.
* Using the reverse order allows us to reuse the existing memory, we do not need to
* have two buffer allocated. This is more important to the long-term memory which is
* using the same technique but has an always growing size.
* Basically what we do is that we will use the hashes of the current one of the new hash array
* and the current one of the short-term memory as a representation of a pure number.
* We take the larger on (a comparison can always be only smaller or larger, not equal)
* and store it at the highest array index. We repeat this process till both buffers
* have iterate through all of their elements.
* It's like a broken zipper.
*/
if (operation == BRAIN_OPERATION_ATTACK_RESERVE)
{
u64 offset = 0;
u64 length = 0;
if (brain_recv (client_fd, &offset, sizeof (offset), 0, NULL, NULL) == false) break;
if (brain_recv (client_fd, &length, sizeof (length), 0, NULL, NULL) == false) break;
// time the lookups for debugging
hc_timer_t timer_reserved;
hc_timer_set (&timer_reserved);
hc_thread_mutex_lock (brain_server_db_attack->mux_ag);
u64 overlap = 0;
overlap += brain_server_find_attack_short (brain_server_db_attack->short_buf, brain_server_db_attack->short_cnt, offset, length);
overlap += brain_server_find_attack_long (brain_server_db_attack->long_buf, brain_server_db_attack->long_cnt, offset + overlap, length - overlap);
if (overlap < length)
{
if (brain_server_db_attack_realloc (brain_server_db_attack, 0, 1) == true)
{
brain_server_db_attack->short_buf[brain_server_db_attack->short_cnt].offset = offset + overlap;
brain_server_db_attack->short_buf[brain_server_db_attack->short_cnt].length = length - overlap;
brain_server_db_attack->short_buf[brain_server_db_attack->short_cnt].client_idx = client_idx;
brain_server_db_attack->short_cnt++;
qsort (brain_server_db_attack->short_buf, brain_server_db_attack->short_cnt, sizeof (brain_server_attack_short_t), brain_server_sort_attack_short);
}
}
hc_thread_mutex_unlock (brain_server_db_attack->mux_ag);
if (brain_send (client_fd, &overlap, sizeof (overlap), SEND_FLAGS, NULL, NULL) == false) break;
const double ms = hc_timer_get (timer_reserved);
brain_logging (stdout, client_idx, "R | %8.2f ms | Offset: %" PRIu64 ", Length: %" PRIu64 ", Overlap: %" PRIu64 "\n", ms, offset, length, overlap);
}
else if (operation == BRAIN_OPERATION_COMMIT)
{
// time the lookups for debugging
hc_timer_t timer_commit;
hc_timer_set (&timer_commit);
hc_thread_mutex_lock (brain_server_db_attack->mux_ag);
i64 new_attacks = 0;
for (i64 idx = 0; idx < brain_server_db_attack->short_cnt; idx++)
{
if (brain_server_db_attack->short_buf[idx].client_idx == client_idx)
{
if (brain_server_db_attack_realloc (brain_server_db_attack, 1, 0) == true)
{
brain_server_db_attack->long_buf[brain_server_db_attack->long_cnt].offset = brain_server_db_attack->short_buf[idx].offset;
brain_server_db_attack->long_buf[brain_server_db_attack->long_cnt].length = brain_server_db_attack->short_buf[idx].length;
brain_server_db_attack->long_cnt++;
qsort (brain_server_db_attack->long_buf, brain_server_db_attack->long_cnt, sizeof (brain_server_attack_long_t), brain_server_sort_attack_long);
}
else
{
brain_logging (stderr, 0, "%s\n", MSG_ENOMEM);
}
brain_server_db_attack->short_buf[idx].offset = 0;
brain_server_db_attack->short_buf[idx].length = 0;
brain_server_db_attack->short_buf[idx].client_idx = 0;
new_attacks++;
}
}
brain_server_db_attack->write_attacks = true;
hc_thread_mutex_unlock (brain_server_db_attack->mux_ag);
if (new_attacks)
{
const double ms_attacks = hc_timer_get (timer_commit);
brain_logging (stdout, client_idx, "C | %8.2f ms | Attacks: %" PRIi64 "\n", ms_attacks, new_attacks);
}
// time the lookups for debugging
hc_timer_set (&timer_commit);
hc_thread_mutex_lock (brain_server_db_hash->mux_hg);
// long-term memory merge
if (brain_server_db_short->short_cnt)
{
if (brain_server_db_hash_realloc (brain_server_db_hash, brain_server_db_short->short_cnt) == true)
{
if (brain_server_db_hash->long_cnt == 0)
{
for (i64 idx = 0; idx < brain_server_db_short->short_cnt; idx++)
{
brain_server_db_hash->long_buf[idx].hash[0] = brain_server_db_short->short_buf[idx].hash[0];
brain_server_db_hash->long_buf[idx].hash[1] = brain_server_db_short->short_buf[idx].hash[1];
}
brain_server_db_hash->long_cnt = brain_server_db_short->short_cnt;
}
else
{
const i64 cnt_total = brain_server_db_hash->long_cnt + brain_server_db_short->short_cnt;
i64 long_left = brain_server_db_hash->long_cnt - 1;
i64 short_left = brain_server_db_short->short_cnt - 1;
i64 long_dupes = 0;
for (i64 idx = cnt_total - 1; idx >= long_dupes; idx--)
{
const brain_server_hash_long_t *long_entry = &brain_server_db_hash->long_buf[long_left];
const brain_server_hash_short_t *short_entry = &brain_server_db_short->short_buf[short_left];
int rc = 0;
if ((long_left >= 0) && (short_left >= 0))
{
rc = brain_server_sort_hash (long_entry->hash, short_entry->hash);
}
else if (long_left >= 0)
{
rc = 1;
}
else if (short_left >= 0)
{
rc = -1;
}
else
{
brain_logging (stderr, client_idx, "unexpected remaining buffers in compare: %" PRIi64 " - %" PRIi64 "\n", long_left, short_left);
}
brain_server_hash_long_t *next = &brain_server_db_hash->long_buf[idx];
if (rc == -1)
{
next->hash[0] = short_entry->hash[0];
next->hash[1] = short_entry->hash[1];
short_left--;
}
else if (rc == 1)
{
next->hash[0] = long_entry->hash[0];
next->hash[1] = long_entry->hash[1];
long_left--;
}
else
{
next->hash[0] = long_entry->hash[0];
next->hash[1] = long_entry->hash[1];
short_left--;
long_left--;
long_dupes++;
}
}
if ((long_left != -1) || (short_left != -1))
{
brain_logging (stderr, client_idx, "unexpected remaining buffers in commit: %" PRIi64 " - %" PRIi64 "\n", long_left, short_left);
}
brain_server_db_hash->long_cnt = cnt_total - long_dupes;
if (long_dupes)
{
for (i64 idx = 0; idx < brain_server_db_hash->long_cnt; idx++)
{
brain_server_db_hash->long_buf[idx].hash[0] = brain_server_db_hash->long_buf[long_dupes + idx].hash[0];
brain_server_db_hash->long_buf[idx].hash[1] = brain_server_db_hash->long_buf[long_dupes + idx].hash[1];
}
}
}
}
else
{
brain_logging (stderr, 0, "%s\n", MSG_ENOMEM);
}
brain_server_db_hash->write_hashes = true;
}
hc_thread_mutex_unlock (brain_server_db_hash->mux_hg);
if (brain_server_db_short->short_cnt)
{
const double ms_hashes = hc_timer_get (timer_commit);
brain_logging (stdout, client_idx, "C | %8.2f ms | Hashes: %" PRIi64 "\n", ms_hashes, brain_server_db_short->short_cnt);
}
brain_server_db_short->short_cnt = 0;
}
else if (operation == BRAIN_OPERATION_HASH_LOOKUP)
{
int in_size = 0;
if (brain_recv (client_fd, &in_size, sizeof (in_size), 0, NULL, NULL) == false) break;
if (in_size == 0)
{
brain_logging (stderr, client_idx, "Zero in_size value\n");
break;
}
if (in_size > (int) recv_size) break;
if (brain_recv (client_fd, recv_buf, (size_t) in_size, 0, NULL, NULL) == false) break;
const int hashes_cnt = in_size / BRAIN_HASH_SIZE;
if (hashes_cnt == 0)
{
brain_logging (stderr, client_idx, "Zero passwords\n");
break;
}
if ((brain_server_db_short->short_cnt + hashes_cnt) > passwords_max)
{
brain_logging (stderr, client_idx, "Too many passwords\n");
break;
}
// time the lookups for debugging
hc_timer_t timer_lookup;
hc_timer_set (&timer_lookup);
// make it easier to work with
for (int hash_idx = 0, recv_idx = 0; hash_idx < hashes_cnt; hash_idx += 1, recv_idx += 2)
{
temp_buf[hash_idx].hash[0] = recv_buf[recv_idx + 0];
temp_buf[hash_idx].hash[1] = recv_buf[recv_idx + 1];
temp_buf[hash_idx].hash_idx = hash_idx;
send_buf[hash_idx] = 0;
}
// unique temp memory
i64 temp_cnt = 0;
qsort (temp_buf, hashes_cnt, sizeof (brain_server_hash_unique_t), brain_server_sort_hash_unique);
brain_server_hash_unique_t *prev = temp_buf + temp_cnt;
for (i64 temp_idx = 1; temp_idx < hashes_cnt; temp_idx++)
{
brain_server_hash_unique_t *cur = temp_buf + temp_idx;
if ((cur->hash[0] == prev->hash[0]) && (cur->hash[1] == prev->hash[1]))
{
send_buf[cur->hash_idx] = 1;
}
else
{
temp_cnt++;
prev = temp_buf + temp_cnt;
prev->hash[0] = cur->hash[0];
prev->hash[1] = cur->hash[1];
prev->hash_idx = cur->hash_idx; // we need this in a later stage
}
}
temp_cnt++;
// check if they are in long term memory
hc_thread_mutex_lock (brain_server_db_hash->mux_hr);
brain_server_db_hash->hb++;
if (brain_server_db_hash->hb == 1)
{
hc_thread_mutex_lock (brain_server_db_hash->mux_hg);
}
hc_thread_mutex_unlock (brain_server_db_hash->mux_hr);
if (temp_cnt > 0)
{
i64 temp_idx_new = 0;
for (i64 temp_idx = 0; temp_idx < temp_cnt; temp_idx++)
{
brain_server_hash_unique_t *cur = &temp_buf[temp_idx];
const i64 r = brain_server_find_hash_long (cur->hash, brain_server_db_hash->long_buf, brain_server_db_hash->long_cnt);
if (r != -1)
{
send_buf[cur->hash_idx] = 1;
}
else
{
brain_server_hash_unique_t *save = temp_buf + temp_idx_new;
temp_idx_new++;
save->hash[0] = cur->hash[0];
save->hash[1] = cur->hash[1];
save->hash_idx = cur->hash_idx; // we need this in a later stage
}
}
temp_cnt = temp_idx_new;
}
hc_thread_mutex_lock (brain_server_db_hash->mux_hr);
brain_server_db_hash->hb--;
if (brain_server_db_hash->hb == 0)
{
hc_thread_mutex_unlock (brain_server_db_hash->mux_hg);
}
hc_thread_mutex_unlock (brain_server_db_hash->mux_hr);
// check if they are in short term memory
if (temp_cnt > 0)
{
i64 temp_idx_new = 0;
for (i64 temp_idx = 0; temp_idx < temp_cnt; temp_idx++)
{
brain_server_hash_unique_t *cur = &temp_buf[temp_idx];
const i64 r = brain_server_find_hash_short (cur->hash, brain_server_db_short->short_buf, brain_server_db_short->short_cnt);
if (r != -1)
{
send_buf[cur->hash_idx] = 1;
}
else
{
brain_server_hash_unique_t *save = temp_buf + temp_idx_new;
temp_idx_new++;
save->hash[0] = cur->hash[0];
save->hash[1] = cur->hash[1];
save->hash_idx = cur->hash_idx; // we need this in a later stage
}
}
temp_cnt = temp_idx_new;
}
// update remaining
if (temp_cnt > 0)
{
if (brain_server_db_short->short_cnt == 0)
{
for (i64 idx = 0; idx < temp_cnt; idx++)
{
brain_server_db_short->short_buf[idx].hash[0] = temp_buf[idx].hash[0];
brain_server_db_short->short_buf[idx].hash[1] = temp_buf[idx].hash[1];
}
brain_server_db_short->short_cnt = temp_cnt;
}
else
{
const i64 cnt_total = brain_server_db_short->short_cnt + temp_cnt;
i64 short_left = brain_server_db_short->short_cnt - 1;
i64 unique_left = temp_cnt - 1;
for (i64 idx = cnt_total - 1; idx >= 0; idx--)
{
const brain_server_hash_short_t *short_entry = brain_server_db_short->short_buf + short_left;
const brain_server_hash_unique_t *unique_entry = temp_buf + unique_left;
int rc = 0;
if ((short_left >= 0) && (unique_left >= 0))
{
rc = brain_server_sort_hash (short_entry->hash, unique_entry->hash);
}
else if (short_left >= 0)
{
rc = 1;
}
else if (unique_left >= 0)
{
rc = -1;
}
else
{
brain_logging (stderr, client_idx, "unexpected remaining buffers in compare: %" PRIi64 " - %" PRIi64 "\n", short_left, unique_left);
}
brain_server_hash_short_t *next = brain_server_db_short->short_buf + idx;
if (rc == -1)
{
next->hash[0] = unique_entry->hash[0];
next->hash[1] = unique_entry->hash[1];
unique_left--;
}
else if (rc == 1)
{
next->hash[0] = short_entry->hash[0];
next->hash[1] = short_entry->hash[1];
short_left--;
}
else
{
brain_logging (stderr, client_idx, "unexpected zero comparison in commit\n");
}
}
if ((short_left != -1) || (unique_left != -1))
{
brain_logging (stderr, client_idx, "unexpected remaining buffers in commit: %" PRIi64 " - %" PRIi64 "\n", short_left, unique_left);
}
brain_server_db_short->short_cnt = cnt_total;
}
}
// opportunity to set counters for stats
int local_lookup_new = 0;
for (i64 hashes_idx = 0; hashes_idx < hashes_cnt; hashes_idx++)
{
if (send_buf[hashes_idx] == 0)
{
local_lookup_new++;
}
}
// needs anti-flood fix
const double ms = hc_timer_get (timer_lookup);
brain_logging (stdout, client_idx, "L | %8.2f ms | Long: %" PRIi64 ", Inc: %d, New: %d\n", ms, brain_server_db_hash->long_cnt, hashes_cnt, local_lookup_new);
// send
int out_size = hashes_cnt;
if (brain_send (client_fd, &out_size, sizeof (out_size), SEND_FLAGS, NULL, NULL) == false) break;
if (brain_send (client_fd, send_buf, out_size, SEND_FLAGS, NULL, NULL) == false) break;
}
else
{
break;
}
}
// client reservations
hc_thread_mutex_lock (brain_server_db_attack->mux_ag);
for (i64 idx = 0; idx < brain_server_db_attack->short_cnt; idx++)
{
if (brain_server_db_attack->short_buf[idx].client_idx == client_idx)
{
brain_server_db_attack->short_buf[idx].offset = 0;
brain_server_db_attack->short_buf[idx].length = 0;
brain_server_db_attack->short_buf[idx].client_idx = 0;
}
}
hc_thread_mutex_unlock (brain_server_db_attack->mux_ag);
// short free
hcfree (brain_server_db_short->short_buf);
hcfree (brain_server_db_short);
// free local memory
hcfree (send_buf);
hcfree (temp_buf);
hcfree (recv_buf);
brain_logging (stdout, client_idx, "Disconnected\n");
brain_server_dbs->client_slots[client_idx] = 0;
close (client_fd);
return NULL;
}
int brain_server (const char *listen_host, const int listen_port, const char *brain_password, const char *brain_session_whitelist)
{
#if defined (_WIN)
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD (2,2);
const int iResult = WSAStartup (wVersionRequested, &wsaData);
if (iResult != NO_ERROR)
{
fprintf (stderr, "WSAStartup: %s\n", strerror (errno));
return -1;
}
#endif
hc_timer_set (&timer_logging);
hc_thread_mutex_init (mux_display);
// generate random brain password if not specified by user
char *auth_password = NULL;
if (brain_password == NULL)
{
#define BRAIN_PASSWORD_SZ 20
auth_password = (char *) hcmalloc (BRAIN_PASSWORD_SZ);
snprintf (auth_password, BRAIN_PASSWORD_SZ, "%08x%08x", brain_auth_challenge (), brain_auth_challenge ());
brain_logging (stdout, 0, "Generated authentication password: %s\n", auth_password);
}
else
{
auth_password = (char *) brain_password;
}
// socket stuff
const int server_fd = socket (AF_INET, SOCK_STREAM, 0);
if (server_fd == -1)
{
brain_logging (stderr, 0, "socket: %s\n", strerror (errno));
return -1;
}
#if defined (__linux__)
const int one = 1;
if (setsockopt (server_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof (one)) == -1)
{
brain_logging (stderr, 0, "setsockopt: %s\n", strerror (errno));
return -1;
}
if (setsockopt (server_fd, SOL_TCP, TCP_NODELAY, &one, sizeof (one)) == -1)
{
brain_logging (stderr, 0, "setsockopt: %s\n", strerror (errno));
return -1;
}
#else
#endif
struct sockaddr_in sa;
memset (&sa, 0, sizeof (sa));
size_t salen = sizeof (sa);
sa.sin_family = AF_INET;
sa.sin_port = htons (listen_port);
sa.sin_addr.s_addr = INADDR_ANY;
if (listen_host)
{
struct addrinfo hints;
memset (&hints, 0, sizeof (hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
struct addrinfo *address_info = NULL;
const int rc_getaddrinfo = getaddrinfo (listen_host, NULL, &hints, &address_info);
if (rc_getaddrinfo == 0)
{
struct sockaddr_in *tmp = (struct sockaddr_in *) address_info->ai_addr;
sa.sin_addr.s_addr = tmp->sin_addr.s_addr;
freeaddrinfo (address_info);
}
else
{
brain_logging (stderr, 0, "%s: %s\n", listen_host, gai_strerror (rc_getaddrinfo));
return -1;
}
}
if (bind (server_fd, (struct sockaddr *) &sa, salen) == -1)
{
brain_logging (stderr, 0, "bind: %s\n", strerror (errno));
return -1;
}
if (listen (server_fd, 5) == -1)
{
brain_logging (stderr, 0, "listen: %s\n", strerror (errno));
return -1;
}
brain_server_dbs_t *brain_server_dbs = (brain_server_dbs_t *) hcmalloc (sizeof (brain_server_dbs_t));
if (brain_server_dbs == NULL)
{
brain_logging (stderr, 0, "%s\n", MSG_ENOMEM);
return -1;
}
hc_thread_mutex_init (brain_server_dbs->mux_dbs);
brain_server_dbs->hash_buf = (brain_server_db_hash_t *) hccalloc (BRAIN_SERVER_SESSIONS_MAX, sizeof (brain_server_db_hash_t));
brain_server_dbs->hash_cnt = 0;
if (brain_server_dbs->hash_buf == NULL)
{
brain_logging (stderr, 0, "%s\n", MSG_ENOMEM);
return -1;
}
if (brain_server_read_hash_dumps (brain_server_dbs, ".") == false)
{
return -1;
}
brain_server_dbs->attack_buf = (brain_server_db_attack_t *) hccalloc (BRAIN_SERVER_ATTACKS_MAX, sizeof (brain_server_db_attack_t));
brain_server_dbs->attack_cnt = 0;
if (brain_server_dbs->attack_buf == NULL)
{
brain_logging (stderr, 0, "%s\n", MSG_ENOMEM);
return -1;
}
if (brain_server_read_attack_dumps (brain_server_dbs, ".") == false)
{
return -1;
}
brain_server_dbs->client_slots = (int *) hccalloc (BRAIN_SERVER_CLIENTS_MAX, sizeof (int));
if (brain_server_dbs->client_slots == NULL)
{
brain_logging (stderr, 0, "%s\n", MSG_ENOMEM);
return -1;
}
// session whitelists
u32 *session_whitelist_buf = (u32 *) hccalloc (BRAIN_SERVER_SESSIONS_MAX, sizeof (u32));
int session_whitelist_cnt = 0;
if (brain_session_whitelist != NULL)
{
char *sessions = hcstrdup (brain_session_whitelist);
if (sessions == NULL)
{
brain_logging (stderr, 0, "%s\n", MSG_ENOMEM);
return -1;
}
char *saveptr = NULL;
char *next = strtok_r (sessions, ",", &saveptr);
do
{
const int session = (const int) hc_strtoul (next, NULL, 16);
session_whitelist_buf[session_whitelist_cnt] = session;
session_whitelist_cnt++;
} while ((next = strtok_r ((char *) NULL, ",", &saveptr)) != NULL);
hcfree (sessions);
}
// client options
brain_server_client_options_t *brain_server_client_options = (brain_server_client_options_t *) hccalloc (BRAIN_SERVER_CLIENTS_MAX, sizeof (brain_server_client_options_t));
if (brain_server_client_options == NULL)
{
brain_logging (stderr, 0, "%s\n", MSG_ENOMEM);
return -1;
}
for (int client_idx = 0; client_idx < BRAIN_SERVER_CLIENTS_MAX; client_idx++)
{
// none of these value change
brain_server_client_options[client_idx].brain_server_dbs = brain_server_dbs;
brain_server_client_options[client_idx].auth_password = auth_password;
brain_server_client_options[client_idx].client_idx = client_idx;
brain_server_client_options[client_idx].session_whitelist_buf = session_whitelist_buf;
brain_server_client_options[client_idx].session_whitelist_cnt = session_whitelist_cnt;
}
// ready to serve
brain_logging (stdout, 0, "Brain server started\n");
if (signal (SIGINT, brain_server_handle_signal) == SIG_ERR)
{
brain_logging (stderr, 0, "signal: %s\n", strerror (errno));
return -1;
}
brain_server_dumper_options_t brain_server_dumper_options;
brain_server_dumper_options.brain_server_dbs = brain_server_dbs;
hc_thread_t dump_thr;
hc_thread_create (dump_thr, brain_server_handle_dumps, &brain_server_dumper_options);
while (keep_running == true)
{
// wait for a client to connect, but not too long
const int rc_select = select_read_timeout (server_fd, 1);
if (rc_select == -1)
{
keep_running = false;
break;
}
if (rc_select == 0) continue;
// there's a client!
struct sockaddr_in ca;
memset (&ca, 0, sizeof (ca));
size_t calen = sizeof (ca);
const int client_fd = accept (server_fd, (struct sockaddr *) &ca, (socklen_t *) &calen);
brain_logging (stdout, 0, "Connection from %s:%d\n", inet_ntoa (ca.sin_addr), ntohs (ca.sin_port));
const int client_idx = brain_server_get_client_idx (brain_server_dbs);
if (client_idx == -1)
{
brain_logging (stderr, client_idx, "Too many clients\n");
close (client_fd);
continue;
}
brain_server_client_options[client_idx].client_fd = client_fd;
hc_thread_t client_thr;
hc_thread_create (client_thr, brain_server_handle_client, &brain_server_client_options[client_idx]);
if (client_thr == 0)
{
brain_logging (stderr, 0, "pthread_create: %s\n", strerror (errno));
close (client_fd);
continue;
}
hc_thread_detach (client_thr);
}
brain_logging (stdout, 0, "Brain server stopping\n");
hc_thread_wait (1, &dump_thr);
if (brain_server_write_hash_dumps (brain_server_dbs, ".") == false)
{
return -1;
}
if (brain_server_write_attack_dumps (brain_server_dbs, ".") == false)
{
return -1;
}
for (i64 idx = 0; idx < brain_server_dbs->hash_cnt; idx++)
{
brain_server_db_hash_t *brain_server_db_hash = &brain_server_dbs->hash_buf[idx];
brain_server_db_hash_free (brain_server_db_hash);
}
for (i64 idx = 0; idx < brain_server_dbs->attack_cnt; idx++)
{
brain_server_db_attack_t *brain_server_db_attack = &brain_server_dbs->attack_buf[idx];
brain_server_db_attack_free (brain_server_db_attack);
}
hcfree (brain_server_dbs->hash_buf);
hcfree (brain_server_dbs->attack_buf);
hcfree (brain_server_dbs);
hcfree (brain_server_client_options);
close (server_fd);
#if defined (_WIN)
WSACleanup();
#endif
return 0;
}