/** * 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; }