2 #define _POSIX_C_SOURCE 200112L
14 #include <sodium/randombytes.h>
22 #include "ed25519/ed25519.h"
24 // additional leading zero is added by C
25 static const char * const pkprefix
= "== ed25519v1-public: type0 ==\0\0";
26 #define pkprefixlen (29 + 3)
27 static const char * const skprefix
= "== ed25519v1-secret: type0 ==\0\0";
28 #define skprefixlen (29 + 3)
29 static const char * const checksumstr
= ".onion checksum";
30 #define checksumstrlen 15
33 static char *workdir
= 0;
34 static size_t workdirlen
= 0;
36 static int quietflag
= 0;
41 // with checksum + version num
42 #define PUBONION_LEN (PUBLIC_LEN + 3)
43 // with newline included
46 static size_t onionendpos
; // end of .onion within string
47 static size_t direndpos
; // end of dir before .onion within string
48 static size_t printstartpos
; // where to start printing from
49 static size_t printlen
; // precalculated, related to printstartpos
51 static pthread_mutex_t fout_mutex
;
53 static size_t numneedgenerate
= 0;
54 static pthread_mutex_t keysgenerated_mutex
;
55 static volatile size_t keysgenerated
= 0;
56 static volatile int endwork
= 0;
58 static void termhandler(int sig
)
71 #define BINFILTERLEN PUBLIC_LEN
75 size_t len
; // real len minus one
89 VEC_STRUCT(ifiltervec
,struct intfilter
) ifilters
;
94 VEC_STRUCT(bfiltervec
,struct binfilter
) bfilters
;
97 static void filters_init()
107 // o - old filter, n - new
108 // return -1 - old stays, 0 - no conflict, 1 - new overrides old
109 // assumes masked bits are cleared already
111 static inline int ifilter_conflict(struct intfilter
*o
,struct intfilter
*n
)
113 if ((o
->f
& n
->m
) != (n
->f
& o
->m
))
115 // determine which filter contain less bits
122 // o - old filter, n - new
123 // return: -1 - old stays, 0 - no conflict, 1 - new overrides old
124 // assumes irrelevant bits are cleared already
125 static inline int bfilter_conflict(struct binfilter
*o
,struct binfilter
*n
)
127 for (size_t i
= 0;i
< sizeof(o
->f
);++i
) {
131 else if (i
== n
->len
)
132 oo
= o
->f
[i
] & n
->mask
;
137 else if (i
== o
->len
)
138 nn
= n
->f
[i
] & o
->mask
;
144 // functional filters subset was the same
145 // determine which filter contain less bits
150 if (o
->mask
<= n
->mask
)
159 * raw representation -- FF.FF.F0.00
160 * big endian -- 0xFFFFF000
161 * little endian -- 0x00F0FFFF
162 * b: 0xFFffF000 ^ 0xFFff0000 -> 0x0000F000
163 * 0x0000F000 + 1 -> 0x0000F001
164 * 0x0000F000 & 0x0000F001 -> 0x0000F000 <- shifted mask
165 * 0x0000F000 ^ 0x0000F000 -> 0x00000000 <- direct mask
166 * 0x0000F000 ^ 0x00000000 -> 0x0000F000 <- shifted mask
167 * l: 0x00f0FFff ^ 0x0000FFff -> 0x00f00000
168 * 0x00f00000 + 1 -> 0x00f00001
169 * 0x00f00000 & 0x00f00001 -> 0x00f00000 <- shifted mask
170 * 0x00f00000 ^ 0x00f00000 -> 0x00000000 <- direct mask
171 * 0x00f00000 ^ 0x00000000 -> 0x00f00000 <- shifted mask
173 * b: 0xFFffFFff ^ 0xF0000000 -> 0x0FffFFff
174 * 0x0FffFFff + 1 -> 0x10000000
175 * 0x0FffFFff & 0x10000000 -> 0x00000000 <- shifted mask
176 * 0x0FffFFff ^ 0x00000000 -> 0x0FffFFff <- direct mask
177 * 0x0FffFFff ^ 0x0FffFFff -> 0x00000000 <- shifted mask
178 * l: 0xFFffFFff ^ 0x000000f0 -> 0xFFffFF0f
179 * 0xFFffFF0f + 1 -> 0xFFffFF10
180 * 0xFFffFF0f & 0xFFffFF10 -> 0xFFffFF00 <- shifted mask
181 * 0xFFffFF0f ^ 0xFFffFF00 -> 0x0000000f <- direct mask
182 * 0xFFffFF0f ^ 0x0000000f -> 0xFFffFF00 <- shifted mask
184 * essentially, we have to make direct mask + shifted mask bits worth of information
185 * and then split it into 2 parts
186 * we do not need absolute shifted mask shifting value, just relative to direct mask
187 * 0x0sss00dd - shifted & direct mask combo
188 * 0x000sssdd - combined mask
190 * generate values from 0x00000000 to 0x000sssdd
191 * for each value, realmask <- (val & 0x000000dd) | ((val & 0x000sss00) << relshiftval)
193 * realmask <- (val & 0x000000dd) | ((val << relshiftval) & 0x0sss0000)
195 * above method doesn't work in some cases. better way:
196 * l: 0x80ffFFff ^ 0x00f0FFff -> 0x800f0000
197 * 0x800f0000 >> 16 -> 0x0000800f
198 * 0x0000800f + 1 -> 0x00008010
199 * 0x0000800f & 0x00008010 -> 0x00008000 <- smask
200 * 0x0000800f ^ 0x00008000 -> 0x0000000f <- dmask
203 static const int littleendian
= 1;
205 static int ifilter_compare(const void *p1
,const void *p2
)
207 if (((const struct intfilter
*)p1
)->f
< ((const struct intfilter
*)p2
)->f
) return -1;
208 if (((const struct intfilter
*)p1
)->f
> ((const struct intfilter
*)p2
)->f
) return 1;
212 static void ifilter_sort()
214 qsort(&VEC_BUF(ifilters
,0),VEC_LENGTH(ifilters
),sizeof(struct intfilter
),ifilter_compare
);
217 // if res1==res2 then no results found but res1 tells where it should be inserted
218 static void ifilter_find(IFT fltr
,size_t start
,size_t end
,size_t *res1
,size_t *res2
)
221 for (size_t down
= start
, up
= end
;it
= (up
+ down
) / 2, down
< up
;) {
222 if (fltr
< VEC_BUF(ifilters
,it
).f
)
224 else if (fltr
> VEC_BUF(ifilters
,it
).f
)
230 while (*res1
> start
&& VEC_BUF(ifilters
,*res1
- 1).f
== fltr
)
233 while (*res2
< end
&& VEC_BUF(ifilters
,*res2
).f
== fltr
)
237 #define EXPVAL(init,j,dmask,smask,ishift,rshift) \
238 ((init) | ((((j) & (dmask)) | (((j) << (rshift)) & (smask))) << (ishift)))
239 // add expanded set of values
240 // allocates space on its own
241 static void ifilter_addexpanded(
242 struct intfilter
*ifltr
,
243 IFT dmask
,IFT smask
,IFT cmask
,
244 int ishift
,int rshift
)
246 if (*(const u8
*)&littleendian
) {
247 for (size_t j
= 0,r1
,r2
= 0;;++j
,++r2
) {
248 IFT cval
= EXPVAL(ifltr
->f
,j
,dmask
,smask
,ishift
,rshift
);
249 ifilter_find(cval
,r2
,VEC_LENGTH(ifilters
),&r1
,&r2
);
254 VEC_INSERT1(ifilters
,r1
);
255 VEC_BUF(ifilters
,r1
).f
= cval
;
262 ifilter_find(ifltr
->f
,0,VEC_LENGTH(ifilters
),&r1
,&r2
);
263 ifilter_find(EXPVAL(ifltr
->f
,cmask
,dmask
,smask
,ishift
,rshift
),r2
,VEC_LENGTH(ifilters
),&r3
,&r4
);
264 if (r4
- r1
>= cmask
+ 1)
265 return; // already have all of needed stuff
266 VEC_INSERTN(ifilters
,r4
,cmask
+ 1 - (r4
- r1
));
267 for (size_t j
= 0;;++j
) {
268 VEC_BUF(ifilters
,r1
+ j
).f
= EXPVAL(ifltr
->f
,j
,dmask
,smask
,ishift
,rshift
);
275 // expand existing stuff
276 // allocates needed stuff on its own
277 static void ifilter_expand(IFT dmask
,IFT smask
,IFT cmask
,int ishift
,int rshift
)
279 size_t len
= VEC_LENGTH(ifilters
);
280 VEC_ADDN(ifilters
,cmask
* len
);
281 size_t esz
= cmask
+ 1; // size of expanded elements
282 for (size_t i
= len
- 1;;--i
) {
283 for (IFT j
= 0;;++j
) {
284 VEC_BUF(ifilters
,i
* esz
+ j
).f
= EXPVAL(VEC_BUF(ifilters
,i
).f
,j
,dmask
,smask
,ishift
,rshift
);
293 static inline void ifilter_addflatten(struct intfilter
*ifltr
,IFT mask
)
295 if (VEC_LENGTH(ifilters
) == 0) {
297 VEC_ADD(ifilters
,*ifltr
);
301 if (ifiltermask
== mask
) {
302 // lucky, only need to insert at the right place
304 ifilter_find(ifltr
->f
,0,VEC_LENGTH(ifilters
),&r1
,&r2
);
309 VEC_INSERT(ifilters
,r1
,*ifltr
);
312 IFT cross
= ifiltermask
^ mask
;
314 while ((cross
& 1) == 0) {
318 IFT smask
= cross
& (cross
+ 1); // shift mask
319 IFT dmask
= cross
^ smask
; // direct mask
320 IFT cmask
; // combined mask
321 int rshift
= 0; // relative shift
322 while (cmask
= (smask
>> rshift
) | dmask
,(cmask
& (cmask
+ 1)) != 0)
325 if (ifiltermask
> mask
) {
326 // already existing stuff has more precise mask than we
327 // so we need to expand our stuff
328 ifilter_addexpanded(ifltr
,dmask
,smask
,cmask
,ishift
,rshift
);
332 // check if not already exists
333 ifilter_find(ifltr
->f
& ifiltermask
,0,VEC_LENGTH(ifilters
),&r1
,&r2
);
335 // this filter alreaedy exists in wider form
338 // adjust existing mask
340 // already existing stuff needs to be expanded
341 ifilter_expand(dmask
,smask
,cmask
,ishift
,rshift
);
342 if (*(const u8
*)&littleendian
) {
343 VEC_ADD(ifilters
,*ifltr
);
347 // we already know place to insert
348 // well except this time it gon b expanded a bit
349 VEC_INSERT(ifilters
,r1
* (cmask
+ 1),*ifltr
);
355 static int ifilter_check()
357 for (size_t i
= 1;i
< VEC_LENGTH(ifilters
);++i
) {
358 if (VEC_BUF(ifilters
,i
- 1).f
== VEC_BUF(ifilters
,i
).f
) {
361 if (VEC_BUF(ifilters
,i
- 1).f
> VEC_BUF(ifilters
,i
).f
) {
371 static void filters_add(const char *filter
)
382 // skip regex start symbol. we do not support regex tho
386 memset(&bf
,0,sizeof(bf
));
388 if (!base32_valid(filter
,&ret
)) {
389 fprintf(stderr
,"filter \"%s\" is invalid\n",filter
);
392 ret
= BASE32_FROM_LEN(ret
);
396 if (ret
> sizeof(IFT
))
398 if (ret
> sizeof(bf
.f
))
401 fprintf(stderr
,"filter \"%s\" is too long\n",filter
);
404 ret2
= base32_from(bf
.f
,&bf
.mask
,filter
);
409 for (size_t i
= 0;i
< bf
.len
;++i
)
411 mc
.b
[bf
.len
] = bf
.mask
;
412 memcpy(fc
.b
,bf
.f
,sizeof(fc
.b
));
414 struct intfilter ifltr
= {
421 ifilter_addflatten(&ifltr
,mc
.i
);
423 int ifiltererr
= ifilter_check();
424 if (ifiltererr
!= 0) {
426 fprintf(stderr
,"bug: duplicate filter found!\n");
427 else if (ifiltererr
== 2)
428 fprintf(stderr
,"bug: ifilters aint sorted!\n");
433 VEC_FOR(ifilters
,i
) {
435 c
= ifilter_conflict(&VEC_BUF(ifilters
,i
),&ifltr
);
437 return; // old filter eats us
439 VEC_REMOVE(ifilters
,i
);
444 VEC_FOR(ifilters
,i
) {
445 // filter with least bits first
446 if (VEC_BUF(ifilters
,i
).m
> ifltr
.m
) {
447 VEC_INSERT(ifilters
,i
,ifltr
);
451 VEC_ADD(ifilters
,ifltr
);
454 VEC_FOR(bfilters
,i
) {
455 int c
= bfilter_conflict(&VEC_BUF(bfilters
,i
),&bf
);
457 return; // old filter eats us
459 VEC_REMOVE(bfilters
,i
);
465 VEC_FOR(bfilters
,i
) {
467 * mask is irrelevant, as they're not
468 * conflicting and have proper order
469 * (unlike when using little endian words)
471 if (memcmp(VEC_BUF(bfilters
,i
).f
,bf
.f
,sizeof(bf
.f
)) > 0) {
472 VEC_INSERT(bfilters
,i
,bf
);
476 VEC_ADD(bfilters
,bf
);
478 VEC_FOR(bfilters
,i
) {
479 // filter with least bits first
480 if (VEC_BUF(bfilters
,i
).len
> bf
.len
||
481 (VEC_BUF(bfilters
,i
).len
== bf
.len
&&
482 (VEC_BUF(bfilters
,i
).mask
> bf
.mask
)))
484 VEC_INSERT(bfilters
,i
,bf
);
488 VEC_ADD(bfilters
,bf
);
493 static void filters_clean()
502 static size_t filters_count()
505 return VEC_LENGTH(ifilters
);
507 return VEC_LENGTH(bfilters
);
515 #define MATCHFILTER(it,pk) \
516 ((*(IFT *)(pk) & VEC_BUF(ifilters,it).m) == VEC_BUF(ifilters,it).f)
518 #define DOFILTER(it,pk,code) { \
519 for (it = 0;it < VEC_LENGTH(ifilters);++it) { \
520 if (unlikely(MATCHFILTER(it,pk))) { \
529 #define DOFILTER(it,pk,code) { \
530 register IFT maskedpk = *(IFT *)(pk) & ifiltermask; \
531 for (size_t down = 0,up = VEC_LENGTH(ifilters);down < up;) { \
532 it = (up + down) / 2; \
533 if (maskedpk < VEC_BUF(ifilters,it).f) \
535 else if (maskedpk > VEC_BUF(ifilters,it).f) \
550 #define MATCHFILTER(it,pk) ( \
551 memcmp(pk,VEC_BUF(bfilters,it).f,VEC_BUF(bfilters,it).len) == 0 && \
552 (pk[VEC_BUF(bfilters,it).len] & VEC_BUF(bfilters,it).mask) == VEC_BUF(bfilters,it).f[VEC_BUF(bfilters,it).len])
554 #define DOFILTER(it,pk,code) { \
555 for (it = 0;it < VEC_LENGTH(bfilters);++it) { \
556 if (unlikely(MATCHFILTER(it,pk))) { \
565 #define DOFILTER(it,pk,code) { \
566 for (size_t down = 0,up = VEC_LENGTH(bfilters);down < up;) { \
567 it = (up + down) / 2; \
569 register int filterdiff = memcmp(pk,VEC_BUF(bfilters,it).f,VEC_BUF(bfilters,it).len); \
570 if (filterdiff < 0) { \
574 if (filterdiff > 0) { \
579 if ((pk[VEC_BUF(bfilters,it).len] & VEC_BUF(bfilters,it).mask) < \
580 VEC_BUF(bfilters,it).f[VEC_BUF(bfilters,it).len]) \
585 if ((pk[VEC_BUF(bfilters,it).len] & VEC_BUF(bfilters,it).mask) > \
586 VEC_BUF(bfilters,it).f[VEC_BUF(bfilters,it).len]) \
602 static void loadfilterfile(const char *fname
)
605 FILE *f
= fopen(fname
,"r");
606 while (fgets(buf
,sizeof(buf
),f
)) {
607 for (char *p
= buf
;*p
;++p
) {
613 if (*buf
&& *buf
!= '#' && memcmp(buf
,"//",2) != 0)
618 static void printfilters()
622 l
= VEC_LENGTH(ifilters
);
624 l
= VEC_LENGTH(bfilters
);
627 fprintf(stderr
, "filters:\n");
629 fprintf(stderr
, "no filters defined\n");
631 for (i
= 0;i
< l
;++i
) {
632 char buf0
[256],buf1
[256];
638 imraw
= (u8
*)&VEC_BUF(ifilters
,i
).m
;
640 imraw
= (u8
*)&ifiltermask
;
642 while (len
< sizeof(IFT
) && imraw
[len
] != 0x00) ++len
;
643 u8 mask
= imraw
[len
-1];
644 u8
*ifraw
= (u8
*)&VEC_BUF(ifilters
,i
).f
;
646 size_t len
= VEC_BUF(bfilters
,i
).len
+ 1;
647 u8 mask
= VEC_BUF(bfilters
,i
).mask
;
648 u8
*ifraw
= VEC_BUF(bfilters
,i
).f
;
650 base32_to(buf0
,ifraw
,len
);
651 memcpy(bufx
,ifraw
,len
);
652 bufx
[len
- 1] |= ~mask
;
653 base32_to(buf1
,bufx
,len
);
654 char *a
= buf0
,*b
= buf1
;
655 while (*a
&& *a
== *b
)
658 fprintf(stderr
, "\t%s\n",buf0
);
662 // statistics, if enabled
678 VEC_STRUCT(statsvec
,struct statstruct
);
688 VEC_STRUCT(tstatsvec
,struct tstatstruct
);
692 static void onionready(char *sname
, const u8
*secret
, const u8
*pubonion
)
699 if (numneedgenerate
) {
700 pthread_mutex_lock(&keysgenerated_mutex
);
701 if (keysgenerated
>= numneedgenerate
) {
702 pthread_mutex_unlock(&keysgenerated_mutex
);
707 if (mkdir(sname
,0700) != 0) {
709 pthread_mutex_unlock(&keysgenerated_mutex
);
713 if (numneedgenerate
) {
715 if (keysgenerated
>= numneedgenerate
)
717 pthread_mutex_unlock(&keysgenerated_mutex
);
720 strcpy(&sname
[onionendpos
], "/hs_ed25519_secret_key");
721 fh
= fopen(sname
, "wb");
723 fwrite(secret
, skprefixlen
+ SECRET_LEN
, 1, fh
);
727 strcpy(&sname
[onionendpos
], "/hostname");
728 fh
= fopen(sname
, "w");
730 sname
[onionendpos
] = '\n';
731 fwrite(&sname
[direndpos
], ONIONLEN
+1, 1, fh
);
735 strcpy(&sname
[onionendpos
], "/hs_ed25519_public_key");
736 fh
= fopen(sname
, "wb");
738 fwrite(pubonion
, pkprefixlen
+ PUBLIC_LEN
, 1, fh
);
743 sname
[onionendpos
] = '\n';
744 pthread_mutex_lock(&fout_mutex
);
745 fwrite(&sname
[printstartpos
], printlen
, 1, fout
);
747 pthread_mutex_unlock(&fout_mutex
);
752 static void addseed(u8
*seed
)
754 register unsigned int c
= 1;
755 for (size_t i
= 0;i
< SEED_LEN
;++i
) {
756 c
= (unsigned int)seed
[i
] + c
; seed
[i
] = c
& 0xFF; c
>>= 8;
762 static void *dowork(void *task
)
764 union pubonionunion
{
765 u8 raw
[pkprefixlen
+ PUBLIC_LEN
+ 32];
772 u8
* const pk
= &pubonion
.raw
[pkprefixlen
];
773 u8 secret
[skprefixlen
+ SECRET_LEN
];
774 u8
* const sk
= &secret
[skprefixlen
];
776 u8 hashsrc
[checksumstrlen
+ PUBLIC_LEN
+ 1];
780 struct statstruct
*st
= (struct statstruct
*)task
;
783 memcpy(secret
,skprefix
,skprefixlen
);
784 memcpy(pubonion
.raw
,pkprefix
,pkprefixlen
);
785 // write version later as it will be overwritten by hash
786 memcpy(hashsrc
,checksumstr
,checksumstrlen
);
787 hashsrc
[checksumstrlen
+ PUBLIC_LEN
] = 0x03; // version
789 sname
= malloc(workdirlen
+ ONIONLEN
+ 63 + 1);
793 memcpy(sname
,workdir
,workdirlen
);
796 randombytes(seed
,sizeof(seed
));
802 if (unlikely(endwork
))
805 ed25519_seckey_expand(sk
,seed
);
806 ed25519_pubkey(pk
,sk
);
817 memcpy(&hashsrc
[checksumstrlen
],pk
,PUBLIC_LEN
);
818 FIPS202_SHA3_256(hashsrc
,sizeof(hashsrc
),&pk
[PUBLIC_LEN
]);
820 pk
[PUBLIC_LEN
+ 2] = 0x03;
822 strcpy(base32_to(&sname
[direndpos
],pk
,PUBONION_LEN
), ".onion");
823 onionready(sname
, secret
, pubonion
.raw
);
834 static void addu64toscalar32(u8
*dst
,u64 v
)
838 for (i
= 0;i
< 32;++i
) {
839 c
+= *dst
+ (v
& 0xFF); *dst
= c
& 0xFF; c
>>= 8;
845 static void *dofastwork(void *task
)
847 union pubonionunion
{
848 u8 raw
[pkprefixlen
+ PUBLIC_LEN
+ 32];
855 u8
* const pk
= &pubonion
.raw
[pkprefixlen
];
856 u8 secret
[skprefixlen
+ SECRET_LEN
];
857 u8
* const sk
= &secret
[skprefixlen
];
859 u8 hashsrc
[checksumstrlen
+ PUBLIC_LEN
+ 1];
865 struct statstruct
*st
= (struct statstruct
*)task
;
868 memcpy(secret
, skprefix
, skprefixlen
);
869 memcpy(pubonion
.raw
, pkprefix
, pkprefixlen
);
870 // write version later as it will be overwritten by hash
871 memcpy(hashsrc
, checksumstr
, checksumstrlen
);
872 hashsrc
[checksumstrlen
+ PUBLIC_LEN
] = 0x03; // version
874 sname
= malloc(workdirlen
+ ONIONLEN
+ 63 + 1);
878 memcpy(sname
, workdir
, workdirlen
);
884 randombytes(seed
,sizeof(seed
));
885 ed25519_seckey_expand(sk
,seed
);
887 ge_scalarmult_base(&ge_public
,sk
);
888 ge_p3_tobytes(pk
,&ge_public
);
890 for (counter
= 0;counter
< SIZE_MAX
-8;counter
+= 8) {
893 if (unlikely(endwork
))
898 // update secret key with counter
899 addu64toscalar32(sk
,counter
);
901 if (((sk
[0] & 248) == sk
[0]) && (((sk
[31] & 63) | 64) == sk
[31])) {
902 /* These operations should be a no-op. */
912 memcpy(&hashsrc
[checksumstrlen
],pk
,PUBLIC_LEN
);
913 FIPS202_SHA3_256(hashsrc
,sizeof(hashsrc
),&pk
[PUBLIC_LEN
]);
915 pk
[PUBLIC_LEN
+ 2] = 0x03;
917 strcpy(base32_to(&sname
[direndpos
],pk
,PUBONION_LEN
),".onion");
918 onionready(sname
,secret
,pubonion
.raw
);
919 // don't reuse same seed
924 ge_add(&sum
, &ge_public
,&ge_eightpoint
);
925 ge_p1p1_to_p3(&ge_public
,&sum
);
926 ge_p3_tobytes(pk
,&ge_public
);
938 void printhelp(const char *progname
)
941 "Usage: %s filter [filter...] [options]\n"
942 " %s -f filterfile [options]\n"
944 "\t-h - print help\n"
945 "\t-f - instead of specifying filter(s) via commandline, specify filter file which contains filters separated by newlines\n"
946 "\t-q - do not print diagnostic output to stderr\n"
947 "\t-x - do not print onion names\n"
948 "\t-o filename - output onion names to specified file\n"
949 "\t-F - include directory names in onion names output\n"
950 "\t-d dirname - output directory\n"
951 "\t-t numthreads - specify number of threads (default - auto)\n"
952 "\t-j numthreads - same as -t\n"
953 "\t-n numkeys - specify number of keys (default - 0 - unlimited)\n"
954 "\t-z - use faster key generation method. this is now default\n"
955 "\t-Z - use slower key generation method\n"
956 "\t-s - print statistics each 10 seconds\n"
957 "\t-S t - print statistics every specified ammount of seconds\n"
958 "\t-T - do not reset statistics counters when printing\n"
963 void setworkdir(const char *wd
)
966 size_t l
= strlen(wd
);
971 fprintf(stderr
, "unset workdir\n");
977 char *s
= malloc(l
+ needslash
+ 1);
988 fprintf(stderr
,"set workdir: %s\n",workdir
);
991 VEC_STRUCT(threadvec
, pthread_t
);
993 int main(int argc
,char **argv
)
1001 struct threadvec threads
;
1003 struct statsvec stats
;
1004 struct tstatsvec tstats
;
1005 u64 reportdelay
= 0;
1006 int realtimestats
= 1;
1010 ge_initeightpoint();
1014 pthread_mutex_init(&keysgenerated_mutex
, 0);
1015 pthread_mutex_init(&fout_mutex
, 0);
1017 const char *progname
= argv
[0];
1019 printhelp(progname
);
1024 if (!ignoreargs
&& *arg
== '-') {
1031 fprintf(stderr
, "unrecognised argument: -\n");
1037 else if (!strcmp(arg
, "help"))
1038 printhelp(progname
);
1040 fprintf(stderr
, "unrecognised argument: --%s\n", arg
);
1045 else if (*arg
== 0) {
1050 else if (*arg
== 'h')
1051 printhelp(progname
);
1052 else if (*arg
== 'f') {
1054 loadfilterfile(*argv
++);
1056 fprintf(stderr
, "additional argument required\n");
1060 else if (*arg
== 'q')
1062 else if (*arg
== 'x')
1064 else if (*arg
== 'o') {
1068 fprintf(stderr
, "additional argument required\n");
1072 else if (*arg
== 'F')
1074 else if (*arg
== 'd') {
1076 setworkdir(*argv
++);
1079 fprintf(stderr
, "additional argument required\n");
1082 else if (*arg
== 't' || *arg
== 'j') {
1084 numthreads
= atoi(*argv
++);
1086 fprintf(stderr
, "additional argument required\n");
1090 else if (*arg
== 'n') {
1092 numneedgenerate
= (size_t)atoll(*argv
++);
1094 fprintf(stderr
, "additional argument required\n");
1098 else if (*arg
== 'Z')
1100 else if (*arg
== 'z')
1102 else if (*arg
== 's') {
1104 reportdelay
= 10000000;
1106 fprintf(stderr
,"statistics support not compiled in\n");
1110 else if (*arg
== 'S') {
1113 reportdelay
= (u64
)atoll(*argv
++) * 1000000;
1115 fprintf(stderr
, "additional argument required\n");
1119 fprintf(stderr
,"statistics support not compiled in\n");
1123 else if (*arg
== 'T') {
1127 fprintf(stderr
,"statistics support not compiled in\n");
1132 fprintf(stderr
, "unrecognised argument: -%c\n", *arg
);
1138 else filters_add(arg
);
1145 if (!filters_count() && !reportdelay
)
1147 if (!filters_count())
1152 fout
= fopen(outfile
, "w");
1155 mkdir(workdir
, 0700);
1157 direndpos
= workdirlen
;
1158 onionendpos
= workdirlen
+ ONIONLEN
;
1161 printstartpos
= direndpos
;
1162 printlen
= ONIONLEN
+ 1;
1165 printlen
= onionendpos
+ 1;
1168 if (numthreads
<= 0) {
1169 numthreads
= cpucount();
1170 if (numthreads
<= 0)
1174 signal(SIGTERM
,termhandler
);
1175 signal(SIGINT
,termhandler
);
1178 VEC_ADDN(threads
,numthreads
);
1181 VEC_ADDN(stats
,numthreads
);
1184 VEC_ADDN(tstats
,numthreads
);
1188 for (size_t i
= 0;i
< VEC_LENGTH(threads
);++i
) {
1191 tp
= &VEC_BUF(stats
,i
);
1193 tret
= pthread_create(&VEC_BUF(threads
,i
),0,fastkeygen
? dofastwork
: dowork
,tp
);
1195 fprintf(stderr
,"error while making %dth thread: %d\n",(int)i
,tret
);
1201 struct timespec nowtime
;
1202 u64 istarttime
,inowtime
,ireporttime
= 0,elapsedoffset
= 0;
1203 if (clock_gettime(CLOCK_MONOTONIC
,&nowtime
) < 0) {
1204 fprintf(stderr
, "failed to get time\n");
1207 istarttime
= (1000000 * (u64
)nowtime
.tv_sec
) + (nowtime
.tv_nsec
/ 1000);
1210 memset(&ts
,0,sizeof(ts
));
1211 ts
.tv_nsec
= 100000000;
1213 if (numneedgenerate
&& keysgenerated
>= numneedgenerate
) {
1220 clock_gettime(CLOCK_MONOTONIC
,&nowtime
);
1221 inowtime
= (1000000 * (u64
)nowtime
.tv_sec
) + (nowtime
.tv_nsec
/ 1000);
1222 u64 sumcalc
= 0,sumsuccess
= 0,sumrestart
= 0;
1223 for (size_t i
= 0;i
< numthreads
;++i
) {
1226 newt
= VEC_BUF(stats
,i
).numcalc
.v
;
1227 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumcalc
;
1228 VEC_BUF(tstats
,i
).oldnumcalc
= newt
;
1229 VEC_BUF(tstats
,i
).numcalc
+= (u64
)tdiff
;
1230 sumcalc
+= VEC_BUF(tstats
,i
).numcalc
;
1232 newt
= VEC_BUF(stats
,i
).numsuccess
.v
;
1233 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumsuccess
;
1234 VEC_BUF(tstats
,i
).oldnumsuccess
= newt
;
1235 VEC_BUF(tstats
,i
).numsuccess
+= (u64
)tdiff
;
1236 sumsuccess
+= VEC_BUF(tstats
,i
).numsuccess
;
1238 newt
= VEC_BUF(stats
,i
).numrestart
.v
;
1239 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumrestart
;
1240 VEC_BUF(tstats
,i
).oldnumrestart
= newt
;
1241 VEC_BUF(tstats
,i
).numrestart
+= (u64
)tdiff
;
1242 sumrestart
+= VEC_BUF(tstats
,i
).numrestart
;
1244 if (reportdelay
&& (!ireporttime
|| inowtime
- ireporttime
>= reportdelay
)) {
1246 ireporttime
+= reportdelay
;
1248 ireporttime
= inowtime
;
1252 double calcpersec
= (1000000.0 * sumcalc
) / (inowtime
- istarttime
);
1253 double succpersec
= (1000000.0 * sumsuccess
) / (inowtime
- istarttime
);
1254 double restpersec
= (1000000.0 * sumrestart
) / (inowtime
- istarttime
);
1255 fprintf(stderr
,">calc/sec:%8lf, succ/sec:%8lf, rest/sec:%8lf, elapsed:%5.6lfsec\n",
1256 calcpersec
,succpersec
,restpersec
,
1257 (inowtime
- istarttime
+ elapsedoffset
) / 1000000.0);
1259 if (realtimestats
) {
1260 for (size_t i
= 0;i
< numthreads
;++i
) {
1261 VEC_BUF(tstats
,i
).numcalc
= 0;
1262 VEC_BUF(tstats
,i
).numsuccess
= 0;
1263 VEC_BUF(tstats
,i
).numrestart
= 0;
1265 elapsedoffset
+= inowtime
- istarttime
;
1266 istarttime
= inowtime
;
1269 if (sumcalc
> U64_MAX
/ 2) {
1270 for (size_t i
= 0;i
< numthreads
;++i
) {
1271 VEC_BUF(tstats
,i
).numcalc
/= 2;
1272 VEC_BUF(tstats
,i
).numsuccess
/= 2;
1273 VEC_BUF(tstats
,i
).numrestart
/= 2;
1275 u64 timediff
= (inowtime
- istarttime
+ 1) / 2;
1276 elapsedoffset
+= timediff
;
1277 istarttime
+= timediff
;
1283 fprintf(stderr
, "waiting for threads to finish...\n");
1284 for (size_t i
= 0;i
< VEC_LENGTH(threads
);++i
)
1285 pthread_join(VEC_BUF(threads
,i
),0);
1287 fprintf(stderr
, "done, quitting\n");
1289 pthread_mutex_destroy(&keysgenerated_mutex
);
1290 pthread_mutex_destroy(&fout_mutex
);