--- /dev/null
+/* Tries to find data with a given MD5 (up to N bits). */
+#include "ccan/antithread/antithread.h"
+#include "ccan/string/string.h"
+#include "ccan/talloc/talloc.h"
+#include "md5_finder.h"
+#include <err.h>
+#include <sys/select.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static void usage(void)
+{
+ errx(1, "Usage: md5calc <hexstring> <numcpus>");
+}
+
+static void parse_hexstring(const char *string, struct md5_search *md5s)
+{
+ unsigned int i;
+
+ if (strstarts(string, "0x") || strstarts(string, "0X"))
+ string += 2;
+
+ for (i = 0; i < MD5_HASH_WORDS; i++) {
+ unsigned int n[4], j;
+ int ret;
+
+ ret = sscanf(string, "%02x%02x%02x%02x",
+ &n[0], &n[1], &n[2], &n[3]);
+ string += 8;
+
+ if (ret == EOF)
+ break;
+ for (j = 0; j < ret; j++) {
+ md5s->mask[MD5_HASH_WORDS-i-1] |= (0xFF << (8*j));
+ md5s->md5[MD5_HASH_WORDS-i-1] |= (n[j] << (8*j));
+ }
+
+ if (ret != 4)
+ break;
+ }
+}
+
+static void init_pattern(u8 *pattern, unsigned int num_bytes, u64 total)
+{
+ unsigned int i;
+
+ for (i = 0; i < num_bytes; i++) {
+ pattern[i] = 'A' + (total % 26);
+ total /= 26;
+ }
+}
+
+#define PATTERN_BYTES 32
+
+int main(int argc, char *argv[])
+{
+ struct at_pool *atp;
+ struct md5_search md5s;
+ unsigned int i, maxfd, numathreads = argc == 3 ? atoi(argv[2]) : 0;
+ u64 total = 0;
+ fd_set fds;
+ char *cmdline[] = { "./md5_worker", NULL };
+ struct athread *at[numathreads];
+
+ if (numathreads == 0)
+ usage();
+
+ memset(&md5s, 0, sizeof(md5s));
+ parse_hexstring(argv[1], &md5s);
+
+ md5s.num_tries = 1024*1024;
+ md5s.num_bytes = PATTERN_BYTES;
+
+ /* *2 to allow for allocation inefficiency. */
+ atp = at_pool((sizeof(md5s) + PATTERN_BYTES) * (numathreads + 1) * 2);
+ if (!atp)
+ err(1, "Can't create pool");
+
+ /* Free pool on exit. */
+// talloc_steal(talloc_autofree_context(), atp);
+
+ FD_ZERO(&fds);
+ maxfd = 0;
+ for (i = 0; i < numathreads; i++) {
+ at[i] = at_spawn(atp, NULL, cmdline);
+ if (!at[i])
+ err(1, "Can't spawn child");
+ FD_SET(at_fd(at[i]), &fds);
+ if (at_fd(at[i]) > maxfd)
+ maxfd = at_fd(at[i]);
+ }
+
+ for (;;) {
+ struct md5_search *m, *res;
+ fd_set in = fds;
+
+ /* Shouldn't fail! */
+ m = talloc(at_pool_ctx(atp), struct md5_search);
+ *m = md5s;
+ md5s.num_tries++;
+ m->pattern = talloc_array(m, u8, m->num_bytes);
+ init_pattern(m->pattern, m->num_bytes, total);
+
+ select(maxfd+1, &in, NULL, NULL, NULL);
+ for (i = 0; i < numathreads; i++)
+ if (FD_ISSET(at_fd(at[i]), &in))
+ break;
+ if (i == numathreads)
+ errx(1, "Select returned, but noone ready?");
+
+ res = at_read(at[i]);
+ if (res == NULL) {
+ warn("Thread died?");
+ FD_CLR(at_fd(at[i]), &fds);
+ continue;
+ }
+ if (res != INITIAL_POINTER) {
+ if (res->success) {
+ printf("Success! '%.*s'\n",
+ res->num_bytes, (char *)res->pattern);
+ exit(0);
+ }
+ m->num_tries++;
+ talloc_free(res);
+ }
+ at_tell(at[i], m);
+ total += m->num_tries;
+ }
+}