示例#1
0
void do_regex_crack(struct db_main *db, const char *regex) {
	c_simplestring_ptr buffer = c_simplestring_new();
	c_iterator_ptr iter = NULL;
	charset encoding = CHARSET_UTF8;
	int ignore_case = 0;
	char word[WORDSIZE];

	if (john_main_process)
		fprintf(stderr, "Warning: regex mode currently can't be "
		        "resumed if aborted\n");

	rexgen_setlocale();
	status_init(&get_progress, 0);
	rec_restore_mode(restore_state);
	rec_init(db, save_state);
	crk_init(db, fix_state, NULL);
	iter = c_regex_iterator_cb(regex, ignore_case, encoding, callback);
	if (!iter) {
		fprintf(stderr, "Error, invalid regex expression.  John exiting now\n");
		exit(1);
	}
	while (c_iterator_next(iter)) {
		c_iterator_value(iter, buffer);
		c_simplestring_to_binary_string(buffer, &word[0], sizeof(word));
		c_simplestring_clear(buffer);
		if (ext_filter((char*)word)) {
			if (crk_process_key((char*)word))
				break;
		}
	}
	c_simplestring_delete(buffer);
	c_iterator_delete(iter);
	crk_done();
	rec_done(event_abort);
}
示例#2
0
void do_external_crack(struct db_main *db)
{
	unsigned char *internal;
	c_int *external;

	log_event("Proceeding with external mode: %.100s", ext_mode);

	if (!f_generate) {
		log_event("! No generate() function defined");
		fprintf(stderr, "No generate() for external mode: %s\n",
			ext_mode);
		error();
	}

	internal = (unsigned char *)int_word;
	external = ext_word;
	while (*external)
		*internal++ = *external++;
	*internal = 0;

	status_init(NULL, 0);

	rec_restore_mode(restore_state);
	rec_init(db, save_state);

	crk_init(db, fix_state, NULL);

	do {
		c_execute(f_generate);
		if (!ext_word[0]) break;

		c_execute(f_filter);
		if (!ext_word[0]) continue;

		internal = (unsigned char *)int_word;
		external = ext_word;
		while (*external)
			*internal++ = *external++;
		*internal = 0;

		if (crk_process_key(int_word)) break;
	} while (1);

	crk_done();
	rec_done(event_abort);
}
示例#3
0
void do_external_crack(struct db_main *db)
{
    unsigned char *internal;
    c_int *external;

    log_event("Proceeding with external mode: %.100s", ext_mode);

#ifdef HAVE_MPI
    if (mpi_p > 1) log_event("MPI: each node will process 1/%u of candidates", mpi_p);
#endif

    internal = (unsigned char *)int_word;
    external = ext_word;
    while (*external)
        *internal++ = *external++;
    *internal = 0;

    status_init(&get_progress, 0);

    rec_restore_mode(restore_state);
    rec_init(db, save_state);

    crk_init(db, fix_state, NULL);

    do {
        c_execute_fast(f_generate);
        if (!ext_word[0])
            break;

        if (f_filter) {
            c_execute_fast(f_filter);
            if (!ext_word[0])
                continue;
        }

#ifdef HAVE_MPI
        // MPI distribution
        if (mpi_line++ % mpi_p != mpi_id) continue;
#endif
        int_word[0] = ext_word[0];
        if ((int_word[1] = ext_word[1])) {
            internal = (unsigned char *)&int_word[2];
            external = &ext_word[2];
            do {
                if (!(internal[0] = external[0]))
                    break;
                if (!(internal[1] = external[1]))
                    break;
                if (!(internal[2] = external[2]))
                    break;
                if (!(internal[3] = external[3]))
                    break;
                internal += 4;
                external += 4;
            } while (1);
        }

        int_word[maxlen] = 0;
        if (crk_process_key(int_word)) break;
    } while (1);

    if (!event_abort)
        progress = 100; // For reporting DONE after a no-ETA run

    crk_done();
    rec_done(event_abort);
}
示例#4
0
void do_wordlist_crack(struct db_main *db, char *name, int rules)
{
	union {
		char buffer[2][LINE_BUFFER_SIZE + CACHE_BANK_SHIFT];
		ARCH_WORD dummy;
	} aligned;
	char *line = aligned.buffer[0], *last = aligned.buffer[1];
	struct rpp_context ctx;
	char *prerule, *rule, *word;
	char *(*apply)(char *word, char *rule, int split, char *last);
	int dist_rules, dist_switch;
	unsigned long my_words, their_words, my_words_left;

	log_event("Proceeding with wordlist mode");

	if (name) {
		if (!(word_file = fopen(path_expand(name), "r")))
			pexit("fopen: %s", path_expand(name));
		log_event("- Wordlist file: %.100s", path_expand(name));
	} else {
		word_file = stdin;
		log_event("- Reading candidate passwords from stdin");
	}

	length = db->format->params.plaintext_length;

	if (rules) {
		if (rpp_init(rule_ctx = &ctx, SUBSECTION_WORDLIST)) {
			log_event("! No wordlist mode rules found");
			if (john_main_process)
				fprintf(stderr,
				    "No wordlist mode rules found in %s\n",
				    cfg_name);
			error();
		}

		rules_init(length);
		rule_count = rules_count(&ctx, -1);

		log_event("- %d preprocessed word mangling rules", rule_count);

		apply = rules_apply;
	} else {
		rule_ctx = NULL;
		rule_count = 1;

		log_event("- No word mangling rules");

		apply = dummy_rules_apply;
	}

	rule_number = 0;
	line_number = 0;

	status_init(get_progress, 0);

	rec_restore_mode(restore_state);
	rec_init(db, save_state);

	crk_init(db, fix_state, NULL);

	prerule = rule = "";
	if (rules)
		prerule = rpp_next(&ctx);

/* A string that can't be produced by fgetl(). */
	last[0] = '\n';
	last[1] = 0;

	dist_rules = 0;
	dist_switch = rule_count; /* never */
	my_words = ~0UL; /* all */
	their_words = 0;
	if (options.node_count) {
		int rule_rem = rule_count % options.node_count;
		const char *now, *later = "";
		dist_switch = rule_count - rule_rem;
		if (!rule_rem || rule_number < dist_switch) {
			dist_rules = 1;
			now = "rules";
			if (rule_rem)
				later = ", then switch to distributing words";
		} else {
			dist_switch = rule_count; /* never */
			my_words = options.node_max - options.node_min + 1;
			their_words = options.node_count - my_words;
			now = "words";
		}
		log_event("- Will distribute %s across nodes%s", now, later);
	}

	my_words_left = my_words;
	if (their_words) {
		if (line_number) {
/* Restored session.  line_number is right after a word we've actually used. */
			int for_node = line_number % options.node_count + 1;
			if (for_node < options.node_min ||
			    for_node > options.node_max) {
/* We assume that line_number is at the beginning of other nodes' block */
				if (skip_lines(their_words, line) &&
/* Check for error since a mere EOF means next rule (the loop below should see
 * the EOF again, and it will skip to next rule if applicable) */
				    ferror(word_file))
					prerule = NULL;
			} else {
				my_words_left =
				    options.node_max - for_node + 1;
			}
		} else {
/* New session.  Skip lower-numbered nodes' lines. */
			if (skip_lines(options.node_min - 1, line))
				prerule = NULL;
		}
	}

	if (prerule)
	do {
		if (rules) {
			if (dist_rules) {
				int for_node =
				    rule_number % options.node_count + 1;
				if (for_node < options.node_min ||
				    for_node > options.node_max)
					goto next_rule;
			}
			if ((rule = rules_reject(prerule, -1, last, db))) {
				if (strcmp(prerule, rule))
					log_event("- Rule #%d: '%.100s'"
						" accepted as '%.100s'",
						rule_number + 1, prerule, rule);
				else
					log_event("- Rule #%d: '%.100s'"
						" accepted",
						rule_number + 1, prerule);
			} else {
				log_event("- Rule #%d: '%.100s' rejected",
					rule_number + 1, prerule);
				goto next_rule;
			}
		}

		while (fgetl(line, LINE_BUFFER_SIZE, word_file)) {
			line_number++;

			if (line[0] != '#') {
process_word:
				if ((word = apply(line, rule, -1, last))) {
					last = word;

					if (ext_filter(word))
					if (crk_process_key(word)) {
						rules = 0;
						break;
					}
				}
next_word:
				if (--my_words_left)
					continue;
				if (skip_lines(their_words, line))
					break;
				my_words_left = my_words;
				continue;
			}

			if (strncmp(line, "#!comment", 9))
				goto process_word;
			goto next_word;
		}

		if (ferror(word_file))
			break;

		if (rules) {
next_rule:
			if (!(rule = rpp_next(&ctx))) break;
			rule_number++;

			if (rule_number >= dist_switch) {
				log_event("- Switching to distributing words");
				dist_rules = 0;
				dist_switch = rule_count; /* not anymore */
				my_words =
				    options.node_max - options.node_min + 1;
				their_words = options.node_count - my_words;
			}

			line_number = 0;
			if (fseek(word_file, 0, SEEK_SET))
				pexit("fseek");

			if (their_words &&
			    skip_lines(options.node_min - 1, line))
				break;
		}

		my_words_left = my_words;
	} while (rules);

	crk_done();
	rec_done(event_abort || (status.pass && db->salts));

	if (ferror(word_file)) pexit("fgets");

	if (name) {
		if (event_abort)
			progress = get_progress();
		else
			progress = 100;

		if (fclose(word_file)) pexit("fclose");
		word_file = NULL;
	}
}
void do_incremental_crack(struct db_main *db, char *mode)
{
	char *charset;
	int min_length, max_length, max_count;
	char *extra;
	FILE *file;
	struct charset_header *header;
	unsigned int check;
	char allchars[CHARSET_SIZE + 1];
	char char1[CHARSET_SIZE + 1];
	char2_table char2;
	chars_table chars[CHARSET_LENGTH - 2];
	unsigned char *ptr;
	unsigned int length, fixed, count;
	unsigned int real_count;
	int last_length, last_count;
	int pos;

	if (!mode) {
		if (db->format == &fmt_LM)
			mode = "LanMan";
		else if (db->format == &fmt_NETLM)
			mode = "LanMan";
		else if (db->format == &fmt_NETHALFLM)
			mode = "LanMan";
		else
			mode = "All";
	}

	log_event("Proceeding with \"incremental\" mode: %.100s", mode);

	if (!(charset = cfg_get_param(SECTION_INC, mode, "File"))) {
		log_event("! No charset defined");
		fprintf(stderr, "No charset defined for mode: %s\n", mode);
		error();
	}

	extra = cfg_get_param(SECTION_INC, mode, "Extra");

	if ((min_length = cfg_get_int(SECTION_INC, mode, "MinLen")) < 0)
		min_length = 0;
	if ((max_length = cfg_get_int(SECTION_INC, mode, "MaxLen")) < 0)
		max_length = CHARSET_LENGTH;
	max_count = cfg_get_int(SECTION_INC, mode, "CharCount");

	if (min_length > max_length) {
		log_event("! MinLen = %d exceeds MaxLen = %d",
			min_length, max_length);
		fprintf(stderr, "MinLen = %d exceeds MaxLen = %d\n",
			min_length, max_length);
		error();
	}

	if (min_length > db->format->params.plaintext_length) {
		log_event("! MinLen = %d is too large for this hash type",
			min_length);
		fprintf(stderr, "MinLen = %d exceeds the maximum possible "
			"length for the current hash type (%d)\n",
			min_length, db->format->params.plaintext_length);
		error();
	}

	if (max_length > db->format->params.plaintext_length) {
		log_event("! MaxLen = %d is too large for this hash type",
			max_length);
		fprintf(stderr, "Warning: "
			"MaxLen = %d is too large for the current hash type, "
			"reduced to %d\n",
			max_length, db->format->params.plaintext_length);
		max_length = db->format->params.plaintext_length;
	}

	if (max_length > CHARSET_LENGTH) {
		log_event("! MaxLen = %d exceeds the compile-time limit of %d",
			max_length, CHARSET_LENGTH);
		fprintf(stderr,
			"\n"
			"MaxLen = %d exceeds the compile-time limit of %d\n\n"
			"There are several good reasons why you probably don't "
			"need to raise it:\n"
			"- many hash types don't support passwords "
			"(or password halves) longer than\n"
			"7 or 8 characters;\n"
			"- you probably don't have sufficient statistical "
			"information to generate a\n"
			"charset file for lengths beyond 8;\n"
			"- the limitation applies to incremental mode only.\n",
			max_length, CHARSET_LENGTH);
		error();
	}

	if (!(file = fopen(path_expand(charset), "rb")))
		pexit("fopen: %s", path_expand(charset));

	header = (struct charset_header *)mem_alloc(sizeof(*header));

	charset_read_header(file, header);
	if (ferror(file)) pexit("fread");

	if (feof(file) ||
	    (memcmp(header->version, CHARSET_V1, sizeof(header->version)) &&
	    memcmp(header->version, CHARSET_V2, sizeof(header->version))) ||
	    !header->count)
		inc_format_error(charset);

	if (header->min != CHARSET_MIN || header->max != CHARSET_MAX ||
	    header->length != CHARSET_LENGTH) {
		log_event("! Incompatible charset file: %.100s", charset);
		fprintf(stderr, "Incompatible charset file: %s\n", charset);
		error();
	}

	if (header->count > CHARSET_SIZE)
		inc_format_error(charset);

	check =
		(unsigned int)header->check[0] |
		((unsigned int)header->check[1] << 8) |
		((unsigned int)header->check[2] << 16) |
		((unsigned int)header->check[3] << 24);
	if (!rec_restoring_now)
		rec_check = check;
	if (rec_check != check) {
		log_event("! Charset file has changed: %.100s", charset);
		fprintf(stderr, "Charset file has changed: %s\n", charset);
		error();
	}

	fread(allchars, header->count, 1, file);
	if (ferror(file)) pexit("fread");
	if (feof(file)) inc_format_error(charset);

	allchars[header->count] = 0;
	if (expand(allchars, extra ? extra : "", sizeof(allchars)))
		inc_format_error(charset);
	real_count = strlen(allchars);

	if (max_count < 0) max_count = CHARSET_SIZE;

	if (min_length != max_length)
		log_event("- Lengths %d to %d, up to %d different characters",
			min_length, max_length, max_count);
	else
		log_event("- Length %d, up to %d different characters",
			min_length, max_count);

	if ((unsigned int)max_count > real_count) {
		log_event("! Only %u characters available", real_count);
		fprintf(stderr, "Warning: only %u characters available\n",
			real_count);
	}

	if (!(db->format->params.flags & FMT_CASE))
	switch (is_mixedcase(allchars)) {
	case -1:
		inc_format_error(charset);

	case 1:
		log_event("! Mixed-case charset, "
			"but the hash type is case-insensitive");
		fprintf(stderr, "Warning: mixed-case charset, "
			"but the current hash type is case-insensitive;\n"
			"some candidate passwords may be unnecessarily "
			"tried more than once.\n");
	}

	if (header->length >= 2)
		char2 = (char2_table)mem_alloc(sizeof(*char2));
	else
		char2 = NULL;
	for (pos = 0; pos < (int)header->length - 2; pos++)
		chars[pos] = (chars_table)mem_alloc(sizeof(*chars[0]));

	rec_compat = 0;
	rec_entry = 0;
	memset(rec_numbers, 0, sizeof(rec_numbers));

	status_init(NULL, 0);
	rec_restore_mode(restore_state);

#ifdef WEBAPI
	if(packet_id) {
		int ret;

		// This is a new packet
		inc_rec_state.initialized = 0;

		inc_rec_state.words_requested = packet_rounds;
		inc_rec_state.words_generated = 0;
		inc_rec_state.cc_0 = -1;
		inc_rec_state.cc_1 = -1;
		inc_rec_state.cc_2 = -1;

		ret = sscanf(packet_state, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%127[^\n]", 
				&rec_entry,
				&rec_numbers[0], &rec_numbers[1], &rec_numbers[2], &rec_numbers[3],
				&rec_numbers[4], &rec_numbers[5], &rec_numbers[6], &rec_numbers[7],
				&inc_rec_state.pos,
				&inc_rec_state.numbers_cache,
				&inc_rec_state.cc_0, &inc_rec_state.cc_1, &inc_rec_state.cc_2,
				inc_rec_state.key_i);
		
		if(ret < 14 || ret > 15) {
			log_event("Invalid packet state, found %d fields in %s", ret, packet_state);
			// XXX - Handle more gracefully..
			error();
		}


		status_init(webapi_inc_get_progress, 0);
	}
#endif

	rec_init(db, save_state);

	ptr = header->order + (entry = rec_entry) * 3;
	memcpy(numbers, rec_numbers, sizeof(numbers));

	crk_init(db, fix_state, NULL);

	last_count = last_length = -1;

	entry--;
	while (ptr < &header->order[sizeof(header->order) - 1]) {
		entry++;
		length = *ptr++; fixed = *ptr++; count = *ptr++;

		if (length >= CHARSET_LENGTH ||
			fixed > length ||
			count >= CHARSET_SIZE) inc_format_error(charset);

		if (entry != rec_entry)
			memset(numbers, 0, sizeof(numbers));

		if (count >= real_count || (fixed && !count)) continue;

		if ((int)length + 1 < min_length ||
			(int)length >= max_length ||
			(int)count >= max_count) continue;

		if ((int)length != last_length) {
			inc_new_length(last_length = length,
				header, file, charset, char1, char2, chars);
			last_count = -1;
		}
		if ((int)count > last_count)
			inc_new_count(length, last_count = count, charset,
				allchars, char1, char2, chars);

		if (!length && !min_length) {
			min_length = 1;
			if (crk_process_key("")) break;
		}

#if 0
		log_event("- Trying length %d, fixed @%d, character count %d",
			length + 1, fixed + 1, count + 1);
#endif
		if (inc_key_loop(length, fixed, count, char1, char2, chars))
			break;
	}
	

	crk_done();
	rec_done(event_abort);

	for (pos = 0; pos < (int)header->length - 2; pos++)
		MEM_FREE(chars[pos]);
	MEM_FREE(char2);
	MEM_FREE(header);

	fclose(file);
}
static int inc_key_loop(int length, int fixed, int count,
	char *char1, char2_table char2, chars_table *chars)
{
	char key_i[PLAINTEXT_BUFFER_SIZE];
	char key_e[PLAINTEXT_BUFFER_SIZE];
	char *key;
	char *chars_cache;
	int numbers_cache;
	int pos;

	key_i[length + 1] = 0;
	numbers[fixed] = count;

	chars_cache = NULL;

#ifdef WEBAPI
	// DistributedCracking.net: Code block added
	// Restore previous state below the label update_ending
	if(packet_id > 0 && !inc_rec_state.initialized) {
		inc_rec_state.initialized++;

		memcpy(key_i, inc_rec_state.key_i, strlen(inc_rec_state.key_i) + 1);
	
		pos = inc_rec_state.pos;
		numbers_cache = inc_rec_state.numbers_cache;
		if(inc_rec_state.cc_0 != -1) {
			chars_cache = (*chars[inc_rec_state.cc_0])
				[ARCH_INDEX(inc_rec_state.cc_1)]
				[ARCH_INDEX(inc_rec_state.cc_2)];


			goto restore;
		}
	}
#endif
update_all:
	pos = 0;
update_ending:
	if (pos < 2) {
		if (pos == 0)
			key_i[0] = char1[numbers[0]];
		if (length) key_i[1] = (*char2)
			[ARCH_INDEX(key_i[0]) - CHARSET_MIN][numbers[1]];
		pos = 2;
	}
	while (pos < length) {
		key_i[pos] = (*chars[pos - 2])
			[ARCH_INDEX(key_i[pos - 2]) - CHARSET_MIN]
			[ARCH_INDEX(key_i[pos - 1]) - CHARSET_MIN]
			[numbers[pos]];
		pos++;
	}
	numbers_cache = numbers[length];
	if (pos == length) {
		chars_cache = (*chars[pos - 2])
			[ARCH_INDEX(key_i[pos - 2]) - CHARSET_MIN]
			[ARCH_INDEX(key_i[pos - 1]) - CHARSET_MIN];

#ifdef WEBAPI
		// DistributedCracking.net: Allow for restoration
		inc_rec_state.cc_0 = pos - 2;
		inc_rec_state.cc_1 = key_i[pos - 2] - CHARSET_MIN;
		inc_rec_state.cc_2 = key_i[pos - 1] - CHARSET_MIN;
#endif

update_last:
		key_i[length] = chars_cache[numbers_cache];
	}

	key = key_i;

	if (!ext_mode || !f_filter || ext_filter_body(key_i, key = key_e))
	if (crk_process_key(key)) return 1;

#ifdef WEBAPI
	// DistributedCracking.net: Block added
	if(packet_id > 0 &&
		++inc_rec_state.words_generated == inc_rec_state.words_requested) {
		return 1;
	}

	// DistributedCracking.net: label added
restore:
#endif
	
	if (rec_compat) goto compat;

	pos = length;
	if (fixed < length) {
		if (++numbers_cache <= count) {
			if (length >= 2) goto update_last;
			numbers[length] = numbers_cache;
			goto update_ending;
		}
		numbers[pos--] = 0;
		while (pos > fixed) {
			if (++numbers[pos] <= count) goto update_ending;
			numbers[pos--] = 0;
		}
	}
	while (pos-- > 0) {
		if (++numbers[pos] < count) goto update_ending;
		numbers[pos] = 0;
	}

	return 0;

compat:
	pos = 0;
	if (fixed) {
		if (++numbers[0] < count) goto update_all;
		if (!length && numbers[0] <= count) goto update_all;
		numbers[0] = 0;
		pos = 1;
		while (pos < fixed) {
			if (++numbers[pos] < count) goto update_all;
			numbers[pos++] = 0;
		}
	}
	while (++pos <= length) {
		if (++numbers[pos] <= count) goto update_all;
		numbers[pos] = 0;
	}

	return 0;
}
示例#7
0
int do_regex_crack_as_rules(const char *regex, const char *base_word, int regex_case, const char *regex_alpha) {
	c_simplestring_ptr buffer = c_simplestring_new();
	c_iterator_ptr iter = NULL;
	charset encoding = CHARSET_UTF8;
	char word[WORDSIZE];
	static int bFirst=1;
	static int bALPHA=0;

	if (bFirst) {
		bFirst = 0;
		rexgen_setlocale();
		if (regex_alpha && !strncmp(regex_alpha, "alpha", 5)) {
			bALPHA = 1;
			SetupAlpha(regex_alpha);
		}
	}

	if (bALPHA) {
		// Ok, we do our own elete of the word,
		static char Buf[4096];
		char *cp = Buf;
		const char *cpi = base_word;
		while (*cpi) {
			cp += strnzcpyn (cp, rexgen_alphabets[(unsigned char)(*cpi)], 100);
			++cpi;
			if (cp - Buf > sizeof(Buf)-101)
				break;
		}
		*cp = 0;
		printf ("buf=%s\n", Buf);
		if (*regex == 0)
			regex = Buf;
		else {
			static char final_Buf[16384];
			int len = strlen(Buf)+1;
			cpi = regex;
			cp = final_Buf;
			while (*cpi) {
				if (*cpi == '\\' && cpi[1] == '0') {
					cp += strnzcpyn (cp, Buf, len);
					cpi += 2;
				} else
					*cp++ = *cpi++;

			}
			regex = final_Buf;
		}
	}

	strcpy(BaseWord, base_word);
	if (!regex[0]) {
		if (ext_filter("")) {
			if (crk_process_key(""))
				return 1;
		}
		return 0;
	}
	iter = c_regex_iterator_cb(regex, regex_case, encoding, callback);
	if (!iter) {
		fprintf(stderr, "Error, invalid regex expression.  John exiting now  base_word=%s  Regex= %s\n", base_word, regex);
		exit(1);
	}
	while (c_iterator_next(iter)) {
		c_iterator_value(iter, buffer);
		c_simplestring_to_binary_string(buffer, &word[0], sizeof(word));
		c_simplestring_clear(buffer);
		if (ext_filter((char*)word)) {
			if (crk_process_key((char*)word)) {
				c_simplestring_delete(buffer);
				c_iterator_delete(iter);
				return 1;
			}
		}
	}
	c_simplestring_delete(buffer);
	c_iterator_delete(iter);
	return 0;
}
示例#8
0
void do_external_crack(struct db_main *db)
{
	unsigned char *internal;
	c_int *external;
	int my_words, their_words;

	log_event("Proceeding with external mode: %.100s", ext_mode);

	internal = (unsigned char *)int_word;
	external = ext_word;
	while (*external)
		*internal++ = *external++;
	*internal = 0;

	seq = 0;

	status_init(&get_progress, 0);

	rec_restore_mode(restore_state);
	rec_init(db, save_state);

	crk_init(db, fix_state, NULL);

	my_words = options.node_max - options.node_min + 1;
	their_words = options.node_min - 1;

	if (seq) {
/* Restored session.  seq is right after a word we've actually used. */
		int for_node = seq % options.node_count + 1;
		if (for_node < options.node_min ||
		        for_node > options.node_max) {
/* We assume that seq is at the beginning of other nodes' block */
			their_words = options.node_count - my_words;
		} else {
			my_words = options.node_max - for_node + 1;
			their_words = 0;
		}
	}

	do {
		c_execute_fast(f_generate);
		if (!ext_word[0])
			break;

		if (options.node_count) {
			seq++;
			if (their_words) {
				their_words--;
				continue;
			}
			if (--my_words == 0) {
				my_words =
					options.node_max - options.node_min + 1;
				their_words = options.node_count - my_words;
			}
		}

		if (f_filter) {
			c_execute_fast(f_filter);
			if (!ext_word[0])
				continue;
		}

		int_word[0] = ext_word[0];
		if ((int_word[1] = ext_word[1])) {
			internal = (unsigned char *)&int_word[2];
			external = &ext_word[2];
			do {
				if (!(internal[0] = external[0]))
					break;
				if (!(internal[1] = external[1]))
					break;
				if (!(internal[2] = external[2]))
					break;
				if (!(internal[3] = external[3]))
					break;
				internal += 4;
				external += 4;
			} while (1);
		}

		int_word[maxlen] = 0;
		if (options.mask) {
			if (do_mask_crack(int_word))
				break;
		} else
		if (crk_process_key(int_word)) break;
	} while (1);

	if (!event_abort)
		progress = 100; // For reporting DONE after a no-ETA run

	crk_done();
	rec_done(event_abort);
}