Merge from vendor branch NTPD:
[dragonfly.git] / contrib / bind-9.2.4rc7 / bin / dnssec / dnssec-signzone.c
1 /*
2  * Portions Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
3  * Portions Copyright (C) 1999-2001, 2003  Internet Software Consortium.
4  * Portions Copyright (C) 1995-2000 by Network Associates, Inc.
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NETWORK ASSOCIATES DISCLAIMS
11  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12  * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE
13  * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
16  * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 /* $Id: dnssec-signzone.c,v 1.139.2.5 2004/04/15 02:16:24 marka Exp $ */
20
21 #include <config.h>
22
23 #include <stdlib.h>
24 #include <time.h>
25
26 #include <isc/app.h>
27 #include <isc/commandline.h>
28 #include <isc/entropy.h>
29 #include <isc/event.h>
30 #include <isc/file.h>
31 #include <isc/mem.h>
32 #include <isc/mutex.h>
33 #include <isc/os.h>
34 #include <isc/stdio.h>
35 #include <isc/string.h>
36 #include <isc/task.h>
37 #include <isc/util.h>
38 #include <isc/time.h>
39
40 #include <dns/db.h>
41 #include <dns/dbiterator.h>
42 #include <dns/diff.h>
43 #include <dns/dnssec.h>
44 #include <dns/fixedname.h>
45 #include <dns/keyvalues.h>
46 #include <dns/log.h>
47 #include <dns/master.h>
48 #include <dns/masterdump.h>
49 #include <dns/nxt.h>
50 #include <dns/rdata.h>
51 #include <dns/rdataset.h>
52 #include <dns/rdataclass.h>
53 #include <dns/rdatasetiter.h>
54 #include <dns/rdatastruct.h>
55 #include <dns/rdatatype.h>
56 #include <dns/result.h>
57 #include <dns/secalg.h>
58 #include <dns/time.h>
59
60 #include <dst/dst.h>
61 #include <dst/result.h>
62
63 #include "dnssectool.h"
64
65 const char *program = "dnssec-signzone";
66 int verbose;
67
68 #define BUFSIZE 2048
69
70 typedef struct signer_key_struct signer_key_t;
71
72 struct signer_key_struct {
73         dst_key_t *key;
74         isc_boolean_t isdefault;
75         unsigned int position;
76         ISC_LINK(signer_key_t) link;
77 };
78
79 #define SIGNER_EVENTCLASS       ISC_EVENTCLASS(0x4453)
80 #define SIGNER_EVENT_WRITE      (SIGNER_EVENTCLASS + 0)
81 #define SIGNER_EVENT_WORK       (SIGNER_EVENTCLASS + 1)
82
83 typedef struct signer_event sevent_t;
84 struct signer_event {
85         ISC_EVENT_COMMON(sevent_t);
86         dns_fixedname_t *fname;
87         dns_fixedname_t *fnextname;
88         dns_dbnode_t *node;
89 };
90
91 static ISC_LIST(signer_key_t) keylist;
92 static unsigned int keycount = 0;
93 static isc_stdtime_t starttime = 0, endtime = 0, now;
94 static int cycle = -1;
95 static isc_boolean_t tryverify = ISC_FALSE;
96 static isc_boolean_t printstats = ISC_FALSE;
97 static isc_mem_t *mctx = NULL;
98 static isc_entropy_t *ectx = NULL;
99 static dns_ttl_t zonettl;
100 static FILE *fp;
101 static char *tempfile = NULL;
102 static const dns_master_style_t *masterstyle;
103 static unsigned int nsigned = 0, nretained = 0, ndropped = 0;
104 static unsigned int nverified = 0, nverifyfailed = 0;
105 static const char *directory;
106 static isc_mutex_t namelock, statslock;
107 static isc_taskmgr_t *taskmgr = NULL;
108 static dns_db_t *gdb;                   /* The database */
109 static dns_dbversion_t *gversion;       /* The database version */
110 static dns_dbiterator_t *gdbiter;       /* The database iterator */
111 static dns_name_t *gorigin;             /* The database origin */
112 static dns_dbnode_t *gnode = NULL;      /* The "current" database node */
113 static dns_name_t *lastzonecut;
114 static isc_task_t *master = NULL;
115 static unsigned int ntasks = 0;
116 static isc_boolean_t shuttingdown = ISC_FALSE, finished = ISC_FALSE;
117 static unsigned int assigned = 0, completed = 0;
118 static isc_boolean_t nokeys = ISC_FALSE;
119 static isc_boolean_t removefile = ISC_FALSE;
120
121 #define INCSTAT(counter)                \
122         if (printstats) {               \
123                 LOCK(&statslock);       \
124                 counter++;              \
125                 UNLOCK(&statslock);     \
126         }
127
128 static void
129 sign(isc_task_t *task, isc_event_t *event);
130
131
132 static inline void
133 set_bit(unsigned char *array, unsigned int index, unsigned int bit) {
134         unsigned int shift, mask;
135
136         shift = 7 - (index % 8);
137         mask = 1 << shift;
138
139         if (bit != 0)
140                 array[index / 8] |= mask;
141         else
142                 array[index / 8] &= (~mask & 0xFF);
143 }
144
145 static signer_key_t *
146 newkeystruct(dst_key_t *dstkey, isc_boolean_t isdefault) {
147         signer_key_t *key;
148
149         key = isc_mem_get(mctx, sizeof(signer_key_t));
150         if (key == NULL)
151                 fatal("out of memory");
152         key->key = dstkey;
153         key->isdefault = isdefault;
154         key->position = keycount++;
155         ISC_LINK_INIT(key, link);
156         return (key);
157 }
158
159 static void
160 signwithkey(dns_name_t *name, dns_rdataset_t *rdataset, dns_rdata_t *rdata,
161             dst_key_t *key, isc_buffer_t *b)
162 {
163         isc_result_t result;
164
165         result = dns_dnssec_sign(name, rdataset, key, &starttime, &endtime,
166                                  mctx, b, rdata);
167         isc_entropy_stopcallbacksources(ectx);
168         if (result != ISC_R_SUCCESS) {
169                 char keystr[KEY_FORMATSIZE];
170                 key_format(key, keystr, sizeof keystr);
171                 fatal("key '%s' failed to sign data: %s",
172                       keystr, isc_result_totext(result));
173         }
174         INCSTAT(nsigned);
175
176         if (tryverify) {
177                 result = dns_dnssec_verify(name, rdataset, key,
178                                            ISC_TRUE, mctx, rdata);
179                 if (result == ISC_R_SUCCESS) {
180                         vbprintf(3, "\tsignature verified\n");
181                         INCSTAT(nverified);
182                 } else {
183                         vbprintf(3, "\tsignature failed to verify\n");
184                         INCSTAT(nverifyfailed);
185                 }
186         }
187 }
188
189 static inline isc_boolean_t
190 issigningkey(signer_key_t *key) {
191         return (key->isdefault);
192 }
193
194 static inline isc_boolean_t
195 iszonekey(signer_key_t *key) {
196         return (ISC_TF(dns_name_equal(dst_key_name(key->key), gorigin) &&
197                        dst_key_iszonekey(key->key)));
198 }
199
200 /*
201  * Finds the key that generated a SIG, if possible.  First look at the keys
202  * that we've loaded already, and then see if there's a key on disk.
203  */
204 static signer_key_t *
205 keythatsigned(dns_rdata_sig_t *sig) {
206         isc_result_t result;
207         dst_key_t *pubkey = NULL, *privkey = NULL;
208         signer_key_t *key;
209
210         key = ISC_LIST_HEAD(keylist);
211         while (key != NULL) {
212                 if (sig->keyid == dst_key_id(key->key) &&
213                     sig->algorithm == dst_key_alg(key->key) &&
214                     dns_name_equal(&sig->signer, dst_key_name(key->key)))
215                         return key;
216                 key = ISC_LIST_NEXT(key, link);
217         }
218
219         result = dst_key_fromfile(&sig->signer, sig->keyid, sig->algorithm,
220                                   DST_TYPE_PUBLIC, NULL, mctx, &pubkey);
221         if (result != ISC_R_SUCCESS)
222                 return (NULL);
223
224         result = dst_key_fromfile(&sig->signer, sig->keyid, sig->algorithm,
225                                   DST_TYPE_PUBLIC | DST_TYPE_PRIVATE,
226                                   NULL, mctx, &privkey);
227         if (result == ISC_R_SUCCESS) {
228                 dst_key_free(&pubkey);
229                 key = newkeystruct(privkey, ISC_FALSE);
230         } else
231                 key = newkeystruct(pubkey, ISC_FALSE);
232         ISC_LIST_APPEND(keylist, key, link);
233         return (key);
234 }
235
236 /*
237  * Check to see if we expect to find a key at this name.  If we see a SIG
238  * and can't find the signing key that we expect to find, we drop the sig.
239  * I'm not sure if this is completely correct, but it seems to work.
240  */
241 static isc_boolean_t
242 expecttofindkey(dns_name_t *name) {
243         unsigned int options = DNS_DBFIND_NOWILD;
244         dns_fixedname_t fname;
245         isc_result_t result;
246         char namestr[DNS_NAME_FORMATSIZE];
247
248         dns_fixedname_init(&fname);
249         result = dns_db_find(gdb, name, gversion, dns_rdatatype_key, options,
250                              0, NULL, dns_fixedname_name(&fname), NULL, NULL);
251         switch (result) {
252         case ISC_R_SUCCESS:
253         case DNS_R_NXDOMAIN:
254         case DNS_R_NXRRSET:
255                 return (ISC_TRUE);
256         case DNS_R_DELEGATION:
257         case DNS_R_CNAME:
258         case DNS_R_DNAME:
259                 return (ISC_FALSE);
260         }
261         dns_name_format(name, namestr, sizeof namestr);
262         fatal("failure looking for '%s KEY' in database: %s",
263               namestr, isc_result_totext(result));
264         return (ISC_FALSE); /* removes a warning */
265 }
266
267 static inline isc_boolean_t
268 setverifies(dns_name_t *name, dns_rdataset_t *set, signer_key_t *key,
269             dns_rdata_t *sig)
270 {
271         isc_result_t result;
272         result = dns_dnssec_verify(name, set, key->key, ISC_FALSE, mctx, sig);
273         if (result == ISC_R_SUCCESS) {
274                 INCSTAT(nverified);
275                 return (ISC_TRUE);
276         } else {
277                 INCSTAT(nverifyfailed);
278                 return (ISC_FALSE);
279         }
280 }
281
282 /*
283  * Signs a set.  Goes through contortions to decide if each SIG should
284  * be dropped or retained, and then determines if any new SIGs need to
285  * be generated.
286  */
287 static void
288 signset(dns_diff_t *diff, dns_dbnode_t *node, dns_name_t *name,
289         dns_rdataset_t *set)
290 {
291         dns_rdataset_t sigset;
292         dns_rdata_t sigrdata = DNS_RDATA_INIT;
293         dns_rdata_sig_t sig;
294         signer_key_t *key;
295         isc_result_t result;
296         isc_boolean_t nosigs = ISC_FALSE;
297         isc_boolean_t *wassignedby, *nowsignedby;
298         int arraysize;
299         dns_difftuple_t *tuple;
300         dns_ttl_t ttl;
301         int i;
302         char namestr[DNS_NAME_FORMATSIZE];
303         char typestr[TYPE_FORMATSIZE];
304         char sigstr[SIG_FORMATSIZE];
305
306         dns_name_format(name, namestr, sizeof namestr);
307         type_format(set->type, typestr, sizeof typestr);
308
309         ttl = ISC_MIN(set->ttl, endtime - starttime);
310
311         dns_rdataset_init(&sigset);
312         result = dns_db_findrdataset(gdb, node, gversion, dns_rdatatype_sig,
313                                      set->type, 0, &sigset, NULL);
314         if (result == ISC_R_NOTFOUND) {
315                 result = ISC_R_SUCCESS;
316                 nosigs = ISC_TRUE;
317         }
318         if (result != ISC_R_SUCCESS)
319                 fatal("failed while looking for '%s SIG %s': %s",
320                       namestr, typestr, isc_result_totext(result));
321
322         vbprintf(1, "%s/%s:\n", namestr, typestr);
323
324         arraysize = keycount;
325         if (!nosigs)
326                 arraysize += dns_rdataset_count(&sigset);
327         wassignedby = isc_mem_get(mctx, arraysize * sizeof(isc_boolean_t));
328         nowsignedby = isc_mem_get(mctx, arraysize * sizeof(isc_boolean_t));
329         if (wassignedby == NULL || nowsignedby == NULL)
330                 fatal("out of memory");
331
332         for (i = 0; i < arraysize; i++)
333                 wassignedby[i] = nowsignedby[i] = ISC_FALSE;
334
335         if (nosigs)
336                 result = ISC_R_NOMORE;
337         else
338                 result = dns_rdataset_first(&sigset);
339
340         while (result == ISC_R_SUCCESS) {
341                 isc_boolean_t expired, future;
342                 isc_boolean_t keep = ISC_FALSE, resign = ISC_FALSE;
343
344                 dns_rdataset_current(&sigset, &sigrdata);
345
346                 result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
347                 check_result(result, "dns_rdata_tostruct");
348
349                 expired = ISC_TF(now + cycle > sig.timeexpire);
350                 future = ISC_TF(now < sig.timesigned);
351
352                 key = keythatsigned(&sig);
353                 sig_format(&sig, sigstr, sizeof sigstr);
354
355                 if (sig.timesigned > sig.timeexpire) {
356                         /* sig is dropped and not replaced */
357                         vbprintf(2, "\tsig by %s dropped - "
358                                  "invalid validity period\n",
359                                  sigstr);
360                 } else if (key == NULL && !future &&
361                          expecttofindkey(&sig.signer))
362                 {
363                         /* sig is dropped and not replaced */
364                         vbprintf(2, "\tsig by %s dropped - "
365                                  "private key not found\n",
366                                  sigstr);
367                 } else if (key == NULL || future) {
368                         vbprintf(2, "\tsig by %s %s - key not found\n",
369                                  expired ? "retained" : "dropped", sigstr);
370                         if (!expired)
371                                 keep = ISC_TRUE;
372                 } else if (issigningkey(key)) {
373                         if (!expired && setverifies(name, set, key, &sigrdata))
374                         {
375                                 vbprintf(2, "\tsig by %s retained\n", sigstr);
376                                 keep = ISC_TRUE;
377                                 wassignedby[key->position] = ISC_TRUE;
378                                 nowsignedby[key->position] = ISC_TRUE;
379                         } else {
380                                 vbprintf(2, "\tsig by %s dropped - %s\n",
381                                          sigstr,
382                                          expired ? "expired" :
383                                                    "failed to verify");
384                                 wassignedby[key->position] = ISC_TRUE;
385                                 resign = ISC_TRUE;
386                         }
387                 } else if (iszonekey(key)) {
388                         if (!expired && setverifies(name, set, key, &sigrdata))
389                         {
390                                 vbprintf(2, "\tsig by %s retained\n", sigstr);
391                                 keep = ISC_TRUE;
392                                 wassignedby[key->position] = ISC_TRUE;
393                                 nowsignedby[key->position] = ISC_TRUE;
394                         } else {
395                                 vbprintf(2, "\tsig by %s dropped - %s\n",
396                                          sigstr,
397                                          expired ? "expired" :
398                                                    "failed to verify");
399                                 wassignedby[key->position] = ISC_TRUE;
400                         }
401                 } else if (!expired) {
402                         vbprintf(2, "\tsig by %s retained\n", sigstr);
403                         keep = ISC_TRUE;
404                 } else {
405                         vbprintf(2, "\tsig by %s expired\n", sigstr);
406                 }
407
408                 if (keep) {
409                         nowsignedby[key->position] = ISC_TRUE;
410                         INCSTAT(nretained);
411                 } else {
412                         tuple = NULL;
413                         result = dns_difftuple_create(mctx, DNS_DIFFOP_DEL,
414                                                       name, sigset.ttl,
415                                                       &sigrdata, &tuple);
416                         check_result(result, "dns_difftuple_create");
417                         dns_diff_append(diff, &tuple);
418                         INCSTAT(ndropped);
419                 }
420
421                 if (resign) {
422                         isc_buffer_t b;
423                         dns_rdata_t trdata = DNS_RDATA_INIT;
424                         unsigned char array[BUFSIZE];
425                         char keystr[KEY_FORMATSIZE];
426
427                         key_format(key->key, keystr, sizeof keystr);
428                         vbprintf(1, "\tresigning with key %s\n", keystr);
429                         isc_buffer_init(&b, array, sizeof(array));
430                         signwithkey(name, set, &trdata, key->key, &b);
431                         nowsignedby[key->position] = ISC_TRUE;
432                         tuple = NULL;
433                         result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD,
434                                                       name, ttl, &trdata,
435                                                       &tuple);
436                         check_result(result, "dns_difftuple_create");
437                         dns_diff_append(diff, &tuple);
438                 }
439
440                 dns_rdata_reset(&sigrdata);
441                 dns_rdata_freestruct(&sig);
442                 result = dns_rdataset_next(&sigset);
443         }
444         if (result == ISC_R_NOMORE)
445                 result = ISC_R_SUCCESS;
446
447         check_result(result, "dns_rdataset_first/next");
448         if (dns_rdataset_isassociated(&sigset))
449                 dns_rdataset_disassociate(&sigset);
450
451         key = ISC_LIST_HEAD(keylist);
452         while (key != NULL) {
453                 if (key->isdefault && !nowsignedby[key->position]) {
454                         isc_buffer_t b;
455                         dns_rdata_t trdata = DNS_RDATA_INIT;
456                         unsigned char array[BUFSIZE];
457                         char keystr[KEY_FORMATSIZE];
458
459                         key_format(key->key, keystr, sizeof keystr);
460                         vbprintf(1, "\tsigning with key %s\n", keystr);
461                         isc_buffer_init(&b, array, sizeof(array));
462                         signwithkey(name, set, &trdata, key->key, &b);
463                         tuple = NULL;
464                         result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD,
465                                                       name, ttl, &trdata,
466                                                       &tuple);
467                         check_result(result, "dns_difftuple_create");
468                         dns_diff_append(diff, &tuple);
469                 }
470                 key = ISC_LIST_NEXT(key, link);
471         }
472
473         isc_mem_put(mctx, wassignedby, arraysize * sizeof(isc_boolean_t));
474         isc_mem_put(mctx, nowsignedby, arraysize * sizeof(isc_boolean_t));
475 }
476
477 /* Determine if a KEY set contains a null key */
478 static isc_boolean_t
479 hasnullkey(dns_rdataset_t *rdataset) {
480         isc_result_t result;
481         dns_rdata_t rdata = DNS_RDATA_INIT;
482         isc_boolean_t found = ISC_FALSE;
483
484         result = dns_rdataset_first(rdataset);
485         while (result == ISC_R_SUCCESS) {
486                 dst_key_t *key = NULL;
487
488                 dns_rdata_reset(&rdata);
489                 dns_rdataset_current(rdataset, &rdata);
490                 result = dns_dnssec_keyfromrdata(dns_rootname,
491                                                  &rdata, mctx, &key);
492                 if (result != ISC_R_SUCCESS)
493                         fatal("could not convert KEY into internal format: %s",
494                               isc_result_totext(result));
495                 if (dst_key_isnullkey(key))
496                         found = ISC_TRUE;
497                 dst_key_free(&key);
498                 if (found == ISC_TRUE)
499                         return (ISC_TRUE);
500                 result = dns_rdataset_next(rdataset);
501         }
502         if (result != ISC_R_NOMORE)
503                 fatal("failure looking for null keys");
504         return (ISC_FALSE);
505 }
506
507 static void
508 opendb(const char *prefix, dns_name_t *name, dns_rdataclass_t rdclass,
509        dns_db_t **dbp)
510 {
511         char filename[256];
512         isc_buffer_t b;
513         isc_result_t result;
514
515         isc_buffer_init(&b, filename, sizeof(filename));
516         if (directory != NULL) {
517                 isc_buffer_putstr(&b, directory);
518                 if (directory[strlen(directory) - 1] != '/')
519                         isc_buffer_putstr(&b, "/");
520         }
521         isc_buffer_putstr(&b, prefix);
522         result = dns_name_tofilenametext(name, ISC_FALSE, &b);
523         check_result(result, "dns_name_tofilenametext()");
524         if (isc_buffer_availablelength(&b) == 0) {
525                 char namestr[DNS_NAME_FORMATSIZE];
526                 dns_name_format(name, namestr, sizeof namestr);
527                 fatal("name '%s' is too long", namestr);
528         }
529         isc_buffer_putuint8(&b, 0);
530
531         result = dns_db_create(mctx, "rbt", dns_rootname, dns_dbtype_zone,
532                                rdclass, 0, NULL, dbp);
533         check_result(result, "dns_db_create()");
534
535         result = dns_db_load(*dbp, filename);
536         if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE)
537                 dns_db_detach(dbp);
538 }
539
540 /*
541  * Looks for signatures of the zone keys by the parent, and imports them
542  * if found.
543  */
544 static void
545 importparentsig(dns_diff_t *diff, dns_name_t *name, dns_rdataset_t *set) {
546         dns_db_t *newdb = NULL;
547         dns_dbnode_t *newnode = NULL;
548         dns_rdataset_t newset, sigset;
549         dns_rdata_t rdata = DNS_RDATA_INIT, newrdata = DNS_RDATA_INIT;
550         isc_result_t result;
551
552         dns_rdataset_init(&newset);
553         dns_rdataset_init(&sigset);
554
555         opendb("signedkey-", name, dns_db_class(gdb), &newdb);
556         if (newdb == NULL)
557                 return;
558
559         result = dns_db_findnode(newdb, name, ISC_FALSE, &newnode);
560         if (result != ISC_R_SUCCESS)
561                 goto failure;
562         result = dns_db_findrdataset(newdb, newnode, NULL, dns_rdatatype_key,
563                                      0, 0, &newset, &sigset);
564         if (result != ISC_R_SUCCESS)
565                 goto failure;
566
567         if (!dns_rdataset_isassociated(&newset) ||
568             !dns_rdataset_isassociated(&sigset))
569                 goto failure;
570
571         if (dns_rdataset_count(set) != dns_rdataset_count(&newset)) {
572                 result = DNS_R_BADDB;
573                 goto failure;
574         }
575
576         result = dns_rdataset_first(set);
577         check_result(result, "dns_rdataset_first()");
578         for (; result == ISC_R_SUCCESS; result = dns_rdataset_next(set)) {
579                 dns_rdataset_current(set, &rdata);
580                 result = dns_rdataset_first(&newset);
581                 check_result(result, "dns_rdataset_first()");
582                 for (;
583                      result == ISC_R_SUCCESS;
584                      result = dns_rdataset_next(&newset))
585                 {
586                         dns_rdataset_current(&newset, &newrdata);
587                         if (dns_rdata_compare(&rdata, &newrdata) == 0)
588                                 break;
589                         dns_rdata_reset(&newrdata);
590                 }
591                 dns_rdata_reset(&newrdata);
592                 dns_rdata_reset(&rdata);
593                 if (result != ISC_R_SUCCESS)
594                         break;
595         }
596         if (result != ISC_R_NOMORE)
597                 goto failure;
598
599         vbprintf(2, "found the parent's signature of our zone key\n");
600
601         result = dns_rdataset_first(&sigset);
602         while (result == ISC_R_SUCCESS) {
603                 dns_difftuple_t *tuple = NULL;
604
605                 dns_rdataset_current(&sigset, &rdata);
606                 result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD, name, 
607                                               sigset.ttl, &rdata, &tuple);
608                 check_result(result, "dns_difftuple_create");
609                 dns_diff_append(diff, &tuple);
610                 result = dns_rdataset_next(&sigset);
611                 dns_rdata_reset(&rdata);
612         }
613         if (result == ISC_R_NOMORE)
614                 result = ISC_R_SUCCESS;
615
616  failure:
617         if (dns_rdataset_isassociated(&newset))
618                 dns_rdataset_disassociate(&newset);
619         if (dns_rdataset_isassociated(&sigset))
620                 dns_rdataset_disassociate(&sigset);
621         if (newnode != NULL)
622                 dns_db_detachnode(newdb, &newnode);
623         if (newdb != NULL)
624                 dns_db_detach(&newdb);
625         if (result != ISC_R_SUCCESS)
626                 fatal("zone signedkey file is invalid or does not match zone");
627 }
628
629 /*
630  * Looks for our signatures of child keys.  If present, inform the caller.
631  */
632 static isc_boolean_t
633 haschildkey(dns_name_t *name) {
634         dns_db_t *newdb = NULL;
635         dns_dbnode_t *newnode = NULL;
636         dns_rdataset_t set, sigset;
637         dns_rdata_t sigrdata = DNS_RDATA_INIT;
638         isc_result_t result;
639         isc_boolean_t found = ISC_FALSE;
640         dns_rdata_sig_t sig;
641         signer_key_t *key;
642
643         dns_rdataset_init(&set);
644         dns_rdataset_init(&sigset);
645
646         opendb("signedkey-", name, dns_db_class(gdb), &newdb);
647         if (newdb == NULL)
648                 return (ISC_FALSE);
649
650         result = dns_db_findnode(newdb, name, ISC_FALSE, &newnode);
651         if (result != ISC_R_SUCCESS)
652                 goto failure;
653         result = dns_db_findrdataset(newdb, newnode, NULL, dns_rdatatype_key,
654                                      0, 0, &set, &sigset);
655         if (result != ISC_R_SUCCESS)
656                 goto failure;
657
658         if (!dns_rdataset_isassociated(&set) ||
659             !dns_rdataset_isassociated(&sigset))
660                 goto failure;
661
662         result = dns_rdataset_first(&sigset);
663         check_result(result, "dns_rdataset_first()");
664         dns_rdata_init(&sigrdata);
665         for (; result == ISC_R_SUCCESS; result = dns_rdataset_next(&sigset)) {
666                 dns_rdataset_current(&sigset, &sigrdata);
667                 result = dns_rdata_tostruct(&sigrdata, &sig, NULL);
668                 if (result != ISC_R_SUCCESS)
669                         goto failure;
670                 key = keythatsigned(&sig);
671                 dns_rdata_freestruct(&sig);
672                 if (key == NULL) {
673                         char namestr[DNS_NAME_FORMATSIZE];
674                         dns_name_format(name, namestr, sizeof namestr);
675                         fprintf(stderr,
676                                 "creating KEY from signedkey file for %s: "
677                                 "%s\n",
678                                 namestr, isc_result_totext(result));
679                         goto failure;
680                 }
681                 result = dns_dnssec_verify(name, &set, key->key,
682                                            ISC_FALSE, mctx, &sigrdata);
683                 if (result == ISC_R_SUCCESS) {
684                         found = ISC_TRUE;
685                         break;
686                 } else {
687                         char namestr[DNS_NAME_FORMATSIZE];
688                         dns_name_format(name, namestr, sizeof namestr);
689                         fprintf(stderr,
690                                 "verifying SIG in signedkey file for %s: %s\n",
691                                 namestr, isc_result_totext(result));
692                 }
693                 dns_rdata_reset(&sigrdata);
694         }
695
696  failure:
697         if (dns_rdataset_isassociated(&set))
698                 dns_rdataset_disassociate(&set);
699         if (dns_rdataset_isassociated(&sigset))
700                 dns_rdataset_disassociate(&sigset);
701         if (newnode != NULL)
702                 dns_db_detachnode(newdb, &newnode);
703         if (newdb != NULL)
704                 dns_db_detach(&newdb);
705
706         return (found);
707 }
708
709 /*
710  * There probably should be a dns_nxt_setbit, but it can get complicated if
711  * the length of the bit set needs to be increased.  In this case, since the
712  * NXT bit is set and both SIG and KEY are less than NXT, the easy way works.
713  */
714 static void
715 nxt_setbit(dns_rdataset_t *rdataset, dns_rdatatype_t type) {
716         isc_result_t result;
717         dns_rdata_t rdata = DNS_RDATA_INIT;
718         dns_rdata_nxt_t nxt;
719
720         result = dns_rdataset_first(rdataset);
721         check_result(result, "dns_rdataset_first()");
722         dns_rdataset_current(rdataset, &rdata);
723         result = dns_rdata_tostruct(&rdata, &nxt, NULL);
724         check_result(result, "dns_rdata_tostruct");
725         set_bit(nxt.typebits, type, 1);
726         dns_rdata_freestruct(&nxt);
727 }
728
729 static void
730 createnullkey(dns_db_t *db, dns_dbversion_t *version, dns_name_t *name,
731               dns_ttl_t ttl)
732 {
733         unsigned char keydata[4];
734         dns_rdata_t keyrdata = DNS_RDATA_INIT;
735         dns_rdata_key_t key;
736         dns_diff_t diff;
737         dns_difftuple_t *tuple = NULL;
738         isc_buffer_t b;
739         isc_result_t result;
740         char namestr[DNS_NAME_FORMATSIZE];
741
742         dns_name_format(name, namestr, sizeof namestr);
743         vbprintf(2, "adding null key at %s\n", namestr);
744
745         key.common.rdclass = dns_db_class(db);
746         key.common.rdtype = dns_rdatatype_key;
747         ISC_LINK_INIT(&key.common, link);
748         key.mctx = NULL;
749         key.flags = DNS_KEYTYPE_NOKEY | DNS_KEYOWNER_ZONE;
750         key.protocol = DNS_KEYPROTO_DNSSEC;
751         key.algorithm = DNS_KEYALG_DSA;
752         key.datalen = 0;
753         key.data = NULL;
754         isc_buffer_init(&b, keydata, sizeof keydata);
755         result = dns_rdata_fromstruct(&keyrdata, dns_db_class(db),
756                                       dns_rdatatype_key, &key, &b);
757         if (result != ISC_R_SUCCESS)
758                 fatal("failed to build null key");
759
760         dns_diff_init(mctx, &diff);
761
762         result = dns_difftuple_create(mctx, DNS_DIFFOP_ADD, name, ttl,
763                                       &keyrdata, &tuple);
764         check_result(result, "dns_difftuple_create");
765
766         dns_diff_append(&diff, &tuple);
767
768         result = dns_diff_apply(&diff, db, version);
769         check_result(result, "dns_diff_apply");
770
771         dns_diff_clear(&diff);
772 }
773
774 /*
775  * Signs all records at a name.  This mostly just signs each set individually,
776  * but also adds the SIG bit to any NXTs generated earlier, deals with
777  * parent/child KEY signatures, and handles other exceptional cases.
778  */
779 static void
780 signname(dns_dbnode_t *node, dns_name_t *name) {
781         isc_result_t result;
782         dns_rdataset_t rdataset;
783         dns_rdatasetiter_t *rdsiter;
784         isc_boolean_t isdelegation = ISC_FALSE;
785         isc_boolean_t childkey = ISC_FALSE;
786         static int warnwild = 0;
787         isc_boolean_t atorigin;
788         isc_boolean_t neednullkey = ISC_FALSE;
789         dns_diff_t diff;
790
791         if (dns_name_iswildcard(name)) {
792                 char namestr[DNS_NAME_FORMATSIZE];
793                 dns_name_format(name, namestr, sizeof namestr);
794                 if (warnwild++ == 0) {
795                         fprintf(stderr, "%s: warning: BIND 9 doesn't properly "
796                                 "handle wildcards in secure zones:\n",
797                                 program);
798                         fprintf(stderr, "\t- wildcard nonexistence proof is "
799                                 "not generated by the server\n");
800                         fprintf(stderr, "\t- wildcard nonexistence proof is "
801                                 "not required by the resolver\n");
802                 }
803                 fprintf(stderr, "%s: warning: wildcard name seen: %s\n",
804                         program, namestr);
805         }
806
807         atorigin = dns_name_equal(name, gorigin);
808
809         /*
810          * If this is not the origin, determine if it's a delegation point.
811          */
812         if (!atorigin) {
813                 dns_rdataset_t nsset;
814
815                 dns_rdataset_init(&nsset);
816                 result = dns_db_findrdataset(gdb, node, gversion,
817                                              dns_rdatatype_ns, 0, 0, &nsset,
818                                              NULL);
819                 /* Is this a delegation point? */
820                 if (result == ISC_R_SUCCESS) {
821                         isdelegation = ISC_TRUE;
822                         dns_rdataset_disassociate(&nsset);
823                 }
824         }
825
826         /*
827          * If this is a delegation point, determine if we need to generate
828          * a null key.
829          */
830         if (isdelegation) {
831                 dns_rdataset_t keyset;
832                 dns_ttl_t nullkeyttl;
833
834                 childkey = haschildkey(name);
835                 neednullkey = ISC_TRUE;
836                 nullkeyttl = zonettl;
837
838                 dns_rdataset_init(&keyset);
839                 result = dns_db_findrdataset(gdb, node, gversion,
840                                              dns_rdatatype_key, 0, 0, &keyset,
841                                              NULL);
842                 if (result == ISC_R_SUCCESS && childkey) {
843                         char namestr[DNS_NAME_FORMATSIZE];
844                         dns_name_format(name, namestr, sizeof namestr);
845                         if (hasnullkey(&keyset)) {
846                                 fatal("%s has both a signedkey file and "
847                                       "null keys in the zone.  Aborting.",
848                                       namestr);
849                         }
850                         vbprintf(2, "child key for %s found\n", namestr);
851                         neednullkey = ISC_FALSE;
852                         dns_rdataset_disassociate(&keyset);
853                 }
854                 else if (result == ISC_R_SUCCESS) {
855                         if (hasnullkey(&keyset))
856                                 neednullkey = ISC_FALSE;
857                         nullkeyttl = keyset.ttl;
858                         dns_rdataset_disassociate(&keyset);
859                 } else if (childkey) {
860                         char namestr[DNS_NAME_FORMATSIZE];
861                         dns_name_format(name, namestr, sizeof namestr);
862                         vbprintf(2, "child key for %s found\n", namestr);
863                         neednullkey = ISC_FALSE;
864                 }
865
866                 if (neednullkey)
867                         createnullkey(gdb, gversion, name, nullkeyttl);
868         }
869
870         /*
871          * Now iterate through the rdatasets.
872          */
873         dns_diff_init(mctx, &diff);
874         dns_rdataset_init(&rdataset);
875         rdsiter = NULL;
876         result = dns_db_allrdatasets(gdb, node, gversion, 0, &rdsiter);
877         check_result(result, "dns_db_allrdatasets()");
878         result = dns_rdatasetiter_first(rdsiter);
879         while (result == ISC_R_SUCCESS) {
880                 dns_rdatasetiter_current(rdsiter, &rdataset);
881
882                 /* If this is a SIG set, skip it. */
883                 if (rdataset.type == dns_rdatatype_sig)
884                         goto skip;
885
886                 /*
887                  * If this is a KEY set at the apex, look for a signedkey file.
888                  */
889                 if (atorigin && rdataset.type == dns_rdatatype_key) {
890                         importparentsig(&diff, name, &rdataset);
891                         goto skip;
892                 }
893
894                 /*
895                  * If this name is a delegation point, skip all records
896                  * except an NXT set a KEY set containing a null key.
897                  */
898                 if (isdelegation) {
899                         if (!(rdataset.type == dns_rdatatype_nxt ||
900                               (rdataset.type == dns_rdatatype_key &&
901                                hasnullkey(&rdataset))))
902                                 goto skip;
903                 }
904
905                 if (rdataset.type == dns_rdatatype_nxt) {
906                         if (!nokeys)
907                                 nxt_setbit(&rdataset, dns_rdatatype_sig);
908                         if (neednullkey)
909                                 nxt_setbit(&rdataset, dns_rdatatype_key);
910                 }
911
912                 signset(&diff, node, name, &rdataset);
913
914  skip:
915                 dns_rdataset_disassociate(&rdataset);
916                 result = dns_rdatasetiter_next(rdsiter);
917         }
918         if (result != ISC_R_NOMORE) {
919                 char namestr[DNS_NAME_FORMATSIZE];
920                 dns_name_format(name, namestr, sizeof namestr);
921                 fatal("rdataset iteration for name '%s' failed: %s",
922                       namestr, isc_result_totext(result));
923         }
924         dns_rdatasetiter_destroy(&rdsiter);
925
926         result = dns_diff_apply(&diff, gdb, gversion);
927         if (result != ISC_R_SUCCESS) {
928                 char namestr[DNS_NAME_FORMATSIZE];
929                 dns_name_format(name, namestr, sizeof namestr);
930                 fatal("failed to add SIGs at node '%s': %s",
931                       namestr, isc_result_totext(result));
932         }
933         dns_diff_clear(&diff);
934 }
935
936 static inline isc_boolean_t
937 active_node(dns_dbnode_t *node) {
938         dns_rdatasetiter_t *rdsiter;
939         isc_boolean_t active = ISC_FALSE;
940         isc_result_t result;
941         dns_rdataset_t rdataset;
942
943         dns_rdataset_init(&rdataset);
944         rdsiter = NULL;
945         result = dns_db_allrdatasets(gdb, node, gversion, 0, &rdsiter);
946         check_result(result, "dns_db_allrdatasets()");
947         result = dns_rdatasetiter_first(rdsiter);
948         while (result == ISC_R_SUCCESS) {
949                 dns_rdatasetiter_current(rdsiter, &rdataset);
950                 if (rdataset.type != dns_rdatatype_nxt)
951                         active = ISC_TRUE;
952                 dns_rdataset_disassociate(&rdataset);
953                 if (!active)
954                         result = dns_rdatasetiter_next(rdsiter);
955                 else
956                         result = ISC_R_NOMORE;
957         }
958         if (result != ISC_R_NOMORE)
959                 fatal("rdataset iteration failed: %s",
960                       isc_result_totext(result));
961         dns_rdatasetiter_destroy(&rdsiter);
962
963         if (!active) {
964                 /*
965                  * Make sure there is no NXT record for this node.
966                  */
967                 result = dns_db_deleterdataset(gdb, node, gversion,
968                                                dns_rdatatype_nxt, 0);
969                 if (result == DNS_R_UNCHANGED)
970                         result = ISC_R_SUCCESS;
971                 check_result(result, "dns_db_deleterdataset");
972         }
973
974         return (active);
975 }
976
977 static inline isc_result_t
978 next_active(dns_name_t *name, dns_dbnode_t **nodep) {
979         isc_result_t result;
980         isc_boolean_t active;
981
982         do {
983                 active = ISC_FALSE;
984                 result = dns_dbiterator_current(gdbiter, nodep, name);
985                 if (result == ISC_R_SUCCESS) {
986                         active = active_node(*nodep);
987                         if (!active) {
988                                 dns_db_detachnode(gdb, nodep);
989                                 result = dns_dbiterator_next(gdbiter);
990                         }
991                 }
992         } while (result == ISC_R_SUCCESS && !active);
993
994         return (result);
995 }
996
997 static inline isc_result_t
998 next_nonglue(dns_name_t *name, dns_dbnode_t **nodep, dns_name_t *origin,
999              dns_name_t *lastcut)
1000 {
1001         isc_result_t result;
1002
1003         do {
1004                 result = next_active(name, nodep);
1005                 if (result == ISC_R_SUCCESS) {
1006                         if (dns_name_issubdomain(name, origin) &&
1007                             (lastcut == NULL ||
1008                              !dns_name_issubdomain(name, lastcut)))
1009                                 return (ISC_R_SUCCESS);
1010                         result = dns_master_dumpnodetostream(mctx, gdb,
1011                                                              gversion,
1012                                                              *nodep, name,
1013                                                              masterstyle, fp);
1014                         check_result(result, "dns_master_dumpnodetostream");
1015                         dns_db_detachnode(gdb, nodep);
1016                         result = dns_dbiterator_next(gdbiter);
1017                 }
1018         } while (result == ISC_R_SUCCESS);
1019         return (result);
1020 }
1021
1022 /*
1023  * Extracts the TTL from the SOA.
1024  */
1025 static dns_ttl_t
1026 soattl(void) {
1027         dns_rdataset_t soaset;
1028         dns_fixedname_t fname;
1029         dns_name_t *name;
1030         isc_result_t result;
1031         dns_ttl_t ttl;
1032
1033         dns_fixedname_init(&fname);
1034         name = dns_fixedname_name(&fname);
1035         dns_rdataset_init(&soaset);
1036         result = dns_db_find(gdb, gorigin, gversion, dns_rdatatype_soa,
1037                              0, 0, NULL, name, &soaset, NULL);
1038         if (result != ISC_R_SUCCESS) {
1039                 char namestr[DNS_NAME_FORMATSIZE];
1040                 dns_name_format(name, namestr, sizeof namestr);
1041                 fatal("failed to find '%s SOA' in the zone: %s",
1042                       namestr, isc_result_totext(result));
1043         }
1044         ttl = soaset.ttl;
1045         dns_rdataset_disassociate(&soaset);
1046         return (ttl);
1047 }
1048
1049 /*
1050  * Delete any SIG records at a node.
1051  */
1052 static void
1053 cleannode(dns_db_t *db, dns_dbversion_t *version, dns_dbnode_t *node) {
1054         dns_rdatasetiter_t *rdsiter = NULL;
1055         dns_rdataset_t set;
1056         isc_result_t result, dresult;
1057
1058         dns_rdataset_init(&set);
1059         result = dns_db_allrdatasets(db, node, version, 0, &rdsiter);
1060         check_result(result, "dns_db_allrdatasets");
1061         result = dns_rdatasetiter_first(rdsiter);
1062         while (result == ISC_R_SUCCESS) {
1063                 isc_boolean_t destroy = ISC_FALSE;
1064                 dns_rdatatype_t covers = 0;
1065                 dns_rdatasetiter_current(rdsiter, &set);
1066                 if (set.type == dns_rdatatype_sig) {
1067                         covers = set.covers;
1068                         destroy = ISC_TRUE;
1069                 }
1070                 dns_rdataset_disassociate(&set);
1071                 result = dns_rdatasetiter_next(rdsiter);
1072                 if (destroy) {
1073                         dresult = dns_db_deleterdataset(db, node, version,
1074                                                         dns_rdatatype_sig,
1075                                                         covers);
1076                         check_result(dresult, "dns_db_deleterdataset");
1077                 }
1078         }
1079         if (result != ISC_R_NOMORE)
1080                 fatal("rdataset iteration failed: %s",
1081                       isc_result_totext(result));
1082         dns_rdatasetiter_destroy(&rdsiter);
1083 }
1084
1085 /*
1086  * Set up the iterator and global state before starting the tasks.
1087  */
1088 static void
1089 presign(void) {
1090         isc_result_t result;
1091
1092         gdbiter = NULL;
1093         result = dns_db_createiterator(gdb, ISC_FALSE, &gdbiter);
1094         check_result(result, "dns_db_createiterator()");
1095
1096         result = dns_dbiterator_first(gdbiter);
1097         check_result(result, "dns_dbiterator_first()");
1098
1099         lastzonecut = NULL;
1100
1101         zonettl = soattl();
1102
1103 }
1104
1105 /*
1106  * Clean up the iterator and global state after the tasks complete.
1107  */
1108 static void
1109 postsign(void) {
1110         if (lastzonecut != NULL) {
1111                 dns_name_free(lastzonecut, mctx);
1112                 isc_mem_put(mctx, lastzonecut, sizeof(dns_name_t));
1113         }
1114         dns_dbiterator_destroy(&gdbiter);
1115 }
1116
1117 /*
1118  * Find the next name to nxtify & sign
1119  */
1120 static isc_result_t
1121 getnextname(dns_name_t *name, dns_name_t *nextname, dns_dbnode_t **nodep) {
1122         isc_result_t result;
1123         dns_dbnode_t *nextnode, *curnode;
1124
1125         LOCK(&namelock);
1126
1127         if (shuttingdown || finished) {
1128                 result = ISC_R_NOMORE;
1129                 if (gnode != NULL)
1130                         dns_db_detachnode(gdb, &gnode);
1131                 goto out;
1132         }
1133
1134         if (gnode == NULL) {
1135                 dns_fixedname_t ftname;
1136                 dns_name_t *tname;
1137
1138                 dns_fixedname_init(&ftname);
1139                 tname = dns_fixedname_name(&ftname);
1140
1141                 result = next_nonglue(tname, &gnode, gorigin, lastzonecut);
1142                 if (result != ISC_R_SUCCESS)
1143                         fatal("failed to iterate through the zone");
1144         }
1145
1146         nextnode = NULL;
1147         curnode = NULL;
1148         dns_dbiterator_current(gdbiter, &curnode, name);
1149         if (!dns_name_equal(name, gorigin)) {
1150                 dns_rdatasetiter_t *rdsiter = NULL;
1151                 dns_rdataset_t set;
1152
1153                 dns_rdataset_init(&set);
1154                 result = dns_db_allrdatasets(gdb, curnode, gversion, 0,
1155                                              &rdsiter);
1156                 check_result(result, "dns_db_allrdatasets");
1157                 result = dns_rdatasetiter_first(rdsiter);
1158                 while (result == ISC_R_SUCCESS) {
1159                         dns_rdatasetiter_current(rdsiter, &set);
1160                         if (set.type == dns_rdatatype_ns) {
1161                                 dns_rdataset_disassociate(&set);
1162                                 break;
1163                         }
1164                         dns_rdataset_disassociate(&set);
1165                         result = dns_rdatasetiter_next(rdsiter);
1166                 }
1167                 if (result != ISC_R_SUCCESS && result != ISC_R_NOMORE)
1168                         fatal("rdataset iteration failed: %s",
1169                               isc_result_totext(result));
1170                 if (result == ISC_R_SUCCESS) {
1171                         if (lastzonecut != NULL)
1172                                 dns_name_free(lastzonecut, mctx);
1173                         else {
1174                                 lastzonecut = isc_mem_get(mctx,
1175                                                           sizeof(dns_name_t));
1176                                 if (lastzonecut == NULL)
1177                                         fatal("out of memory");
1178                         }
1179                         dns_name_init(lastzonecut, NULL);
1180                         result = dns_name_dup(name, mctx, lastzonecut);
1181                         check_result(result, "dns_name_dup()");
1182                 }
1183                 dns_rdatasetiter_destroy(&rdsiter);
1184         }
1185         result = dns_dbiterator_next(gdbiter);
1186         if (result == ISC_R_SUCCESS)
1187                 result = next_nonglue(nextname, &nextnode, gorigin,
1188                                       lastzonecut);
1189         if (result == ISC_R_NOMORE) {
1190                 dns_name_clone(gorigin, nextname);
1191                 finished = ISC_TRUE;
1192                 result = ISC_R_SUCCESS;
1193         } else if (result != ISC_R_SUCCESS)
1194                 fatal("iterating through the database failed: %s",
1195                       isc_result_totext(result));
1196         dns_db_detachnode(gdb, &curnode);
1197
1198         *nodep = gnode;
1199         gnode = nextnode;
1200
1201  out:
1202         UNLOCK(&namelock);
1203         return (result);
1204 }
1205
1206 /*
1207  * Assigns a node to a worker thread.  This is protected by the master task's
1208  * lock.
1209  */
1210 static void
1211 assignwork(isc_task_t *task, isc_task_t *worker) {
1212         dns_fixedname_t *fname, *fnextname;
1213         dns_dbnode_t *node;
1214         sevent_t *sevent;
1215         isc_result_t result;
1216
1217         fname = isc_mem_get(mctx, sizeof(dns_fixedname_t));
1218         fnextname = isc_mem_get(mctx, sizeof(dns_fixedname_t));
1219         if (fname == NULL || fnextname == NULL)
1220                 fatal("out of memory");
1221         dns_fixedname_init(fname);
1222         dns_fixedname_init(fnextname);
1223         node = NULL;
1224         result = getnextname(dns_fixedname_name(fname),
1225                              dns_fixedname_name(fnextname), &node);
1226         if (result == ISC_R_NOMORE) {
1227                 isc_mem_put(mctx, fname, sizeof(dns_fixedname_t));
1228                 isc_mem_put(mctx, fnextname, sizeof(dns_fixedname_t));
1229                 if (assigned == completed) {
1230                         isc_task_detach(&task);
1231                         isc_app_shutdown();
1232                 }
1233                 return;
1234         }
1235         sevent = (sevent_t *)
1236                  isc_event_allocate(mctx, task, SIGNER_EVENT_WORK,
1237                                     sign, NULL, sizeof(sevent_t));
1238         if (sevent == NULL)
1239                 fatal("failed to allocate event\n");
1240
1241         sevent->node = node;
1242         sevent->fname = fname;
1243         sevent->fnextname = fnextname;
1244         isc_task_send(worker, ISC_EVENT_PTR(&sevent));
1245         assigned++;
1246 }
1247
1248 /*
1249  * Start a worker task
1250  */
1251 static void
1252 startworker(isc_task_t *task, isc_event_t *event) {
1253         isc_task_t *worker;
1254
1255         worker = (isc_task_t *)event->ev_arg;
1256         assignwork(task, worker);
1257         isc_event_free(&event);
1258 }
1259
1260 /*
1261  * Write a node to the output file, and restart the worker task.
1262  */
1263 static void
1264 writenode(isc_task_t *task, isc_event_t *event) {
1265         isc_result_t result;
1266         isc_task_t *worker;
1267         sevent_t *sevent = (sevent_t *)event;
1268
1269         completed++;
1270         worker = (isc_task_t *)event->ev_sender;
1271         result = dns_master_dumpnodetostream(mctx, gdb, gversion,
1272                                              sevent->node,
1273                                              dns_fixedname_name(sevent->fname),
1274                                              masterstyle, fp);
1275         check_result(result, "dns_master_dumpnodetostream");
1276         cleannode(gdb, gversion, sevent->node);
1277         dns_db_detachnode(gdb, &sevent->node);
1278         isc_mem_put(mctx, sevent->fname, sizeof(dns_fixedname_t));
1279         assignwork(task, worker);
1280         isc_event_free(&event);
1281 }
1282
1283 /*
1284  *  Sign and nxtify a database node.
1285  */
1286 static void
1287 sign(isc_task_t *task, isc_event_t *event) {
1288         dns_fixedname_t *fname, *fnextname;
1289         dns_dbnode_t *node;
1290         sevent_t *sevent, *wevent;
1291         isc_result_t result;
1292
1293         sevent = (sevent_t *)event;
1294         node = sevent->node;
1295         fname = sevent->fname;
1296         fnextname = sevent->fnextname;
1297         isc_event_free(&event);
1298
1299         result = dns_nxt_build(gdb, gversion, node,
1300                                dns_fixedname_name(fnextname), zonettl);
1301         check_result(result, "dns_nxt_build()");
1302         isc_mem_put(mctx, fnextname, sizeof(dns_fixedname_t));
1303         signname(node, dns_fixedname_name(fname));
1304         wevent = (sevent_t *)
1305                  isc_event_allocate(mctx, task, SIGNER_EVENT_WRITE,
1306                                     writenode, NULL, sizeof(sevent_t));
1307         if (wevent == NULL)
1308                 fatal("failed to allocate event\n");
1309         wevent->node = node;
1310         wevent->fname = fname;
1311         isc_task_send(master, ISC_EVENT_PTR(&wevent));
1312 }
1313
1314 /*
1315  * Load the zone file from disk
1316  */
1317 static void
1318 loadzone(char *file, char *origin, dns_rdataclass_t rdclass, dns_db_t **db) {
1319         isc_buffer_t b;
1320         int len;
1321         dns_fixedname_t fname;
1322         dns_name_t *name;
1323         isc_result_t result;
1324
1325         len = strlen(origin);
1326         isc_buffer_init(&b, origin, len);
1327         isc_buffer_add(&b, len);
1328
1329         dns_fixedname_init(&fname);
1330         name = dns_fixedname_name(&fname);
1331         result = dns_name_fromtext(name, &b, dns_rootname, ISC_FALSE, NULL);
1332         if (result != ISC_R_SUCCESS)
1333                 fatal("failed converting name '%s' to dns format: %s",
1334                       origin, isc_result_totext(result));
1335
1336         result = dns_db_create(mctx, "rbt", name, dns_dbtype_zone,
1337                                rdclass, 0, NULL, db);
1338         check_result(result, "dns_db_create()");
1339
1340         result = dns_db_load(*db, file);
1341         if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE)
1342                 fatal("failed loading zone from '%s': %s",
1343                       file, isc_result_totext(result));
1344 }
1345
1346 /*
1347  * Finds all public zone keys in the zone, and attempts to load the
1348  * private keys from disk.
1349  */
1350 static void
1351 loadzonekeys(dns_db_t *db) {
1352         dns_dbnode_t *node;
1353         dns_dbversion_t *currentversion;
1354         isc_result_t result;
1355         dst_key_t *keys[20];
1356         unsigned int nkeys, i;
1357
1358         currentversion = NULL;
1359         dns_db_currentversion(db, &currentversion);
1360
1361         node = NULL;
1362         result = dns_db_findnode(db, gorigin, ISC_FALSE, &node);
1363         if (result != ISC_R_SUCCESS)
1364                 fatal("failed to find the zone's origin: %s",
1365                       isc_result_totext(result));
1366
1367         result = dns_dnssec_findzonekeys(db, currentversion, node, gorigin,
1368                                          mctx, 20, keys, &nkeys);
1369         if (result == ISC_R_NOTFOUND)
1370                 result = ISC_R_SUCCESS;
1371         if (result != ISC_R_SUCCESS)
1372                 fatal("failed to find the zone keys: %s",
1373                       isc_result_totext(result));
1374
1375         for (i = 0; i < nkeys; i++) {
1376                 signer_key_t *key;
1377
1378                 key = newkeystruct(keys[i], ISC_FALSE);
1379                 ISC_LIST_APPEND(keylist, key, link);
1380         }
1381         dns_db_detachnode(db, &node);
1382         dns_db_closeversion(db, &currentversion, ISC_FALSE);
1383 }
1384
1385 /*
1386  * Finds all public zone keys in the zone.
1387  */
1388 static void
1389 loadzonepubkeys(dns_db_t *db) {
1390         dns_dbversion_t *currentversion = NULL;
1391         dns_dbnode_t *node = NULL;
1392         dns_rdataset_t rdataset;
1393         dns_rdata_t rdata = DNS_RDATA_INIT;
1394         dst_key_t *pubkey;
1395         signer_key_t *key;
1396         isc_result_t result;
1397
1398         dns_db_currentversion(db, &currentversion);
1399
1400         result = dns_db_findnode(db, gorigin, ISC_FALSE, &node);
1401         if (result != ISC_R_SUCCESS)
1402                 fatal("failed to find the zone's origin: %s",
1403                       isc_result_totext(result));
1404
1405         dns_rdataset_init(&rdataset);
1406         result = dns_db_findrdataset(db, node, currentversion,
1407                                      dns_rdatatype_key, 0, 0, &rdataset, NULL);
1408         if (result != ISC_R_SUCCESS)
1409                 fatal("failed to find keys at the zone apex: %s",
1410                       isc_result_totext(result));
1411         result = dns_rdataset_first(&rdataset);
1412         check_result(result, "dns_rdataset_first");
1413         while (result == ISC_R_SUCCESS) {
1414                 pubkey = NULL;
1415                 dns_rdata_reset(&rdata);
1416                 dns_rdataset_current(&rdataset, &rdata);
1417                 result = dns_dnssec_keyfromrdata(gorigin, &rdata, mctx,
1418                                                  &pubkey);
1419                 if (result != ISC_R_SUCCESS)
1420                         goto next;
1421                 if (!dst_key_iszonekey(pubkey)) {
1422                         dst_key_free(&pubkey);
1423                         goto next;
1424                 }
1425
1426                 key = newkeystruct(pubkey, ISC_FALSE);
1427                 ISC_LIST_APPEND(keylist, key, link);
1428  next:
1429                 result = dns_rdataset_next(&rdataset);
1430         }
1431         dns_rdataset_disassociate(&rdataset);
1432         dns_db_detachnode(db, &node);
1433         dns_db_closeversion(db, &currentversion, ISC_FALSE);
1434 }
1435
1436 static void
1437 print_time(FILE *fp) {
1438         time_t currenttime;
1439
1440         currenttime = time(NULL);
1441         fprintf(fp, "; File written on %s", ctime(&currenttime));
1442 }
1443
1444 static void
1445 print_version(FILE *fp) {
1446         fprintf(fp, "; dnssec_signzone version " VERSION "\n");
1447 }
1448
1449 static void
1450 usage(void) {
1451         fprintf(stderr, "Usage:\n");
1452         fprintf(stderr, "\t%s [options] zonefile [keys]\n", program);
1453
1454         fprintf(stderr, "\n");
1455
1456         fprintf(stderr, "Options: (default value in parenthesis) \n");
1457         fprintf(stderr, "\t-c class (IN)\n");
1458         fprintf(stderr, "\t-d directory\n");
1459         fprintf(stderr, "\t\tdirectory to find signedkey files (.)\n");
1460         fprintf(stderr, "\t-s YYYYMMDDHHMMSS|+offset:\n");
1461         fprintf(stderr, "\t\tSIG start time - absolute|offset (now)\n");
1462         fprintf(stderr, "\t-e YYYYMMDDHHMMSS|+offset|\"now\"+offset]:\n");
1463         fprintf(stderr, "\t\tSIG end time  - absolute|from start|from now "
1464                                 "(now + 30 days)\n");
1465         fprintf(stderr, "\t-i interval:\n");
1466         fprintf(stderr, "\t\tcycle interval - resign "
1467                                 "if < interval from end ( (end-start)/4 )\n");
1468         fprintf(stderr, "\t-v debuglevel (0)\n");
1469         fprintf(stderr, "\t-o origin:\n");
1470         fprintf(stderr, "\t\tzone origin (name of zonefile)\n");
1471         fprintf(stderr, "\t-f outfile:\n");
1472         fprintf(stderr, "\t\tfile the signed zone is written in "
1473                                 "(zonefile + .signed)\n");
1474         fprintf(stderr, "\t-r randomdev:\n");
1475         fprintf(stderr, "\t\ta file containing random data\n");
1476         fprintf(stderr, "\t-a:\t");
1477         fprintf(stderr, "verify generated signatures\n");
1478         fprintf(stderr, "\t-p:\t");
1479         fprintf(stderr, "use pseudorandom data (faster but less secure)\n");
1480         fprintf(stderr, "\t-t:\t");
1481         fprintf(stderr, "print statistics\n");
1482         fprintf(stderr, "\t-n ncpus (number of cpus present)\n");
1483
1484         fprintf(stderr, "\n");
1485
1486         fprintf(stderr, "Signing Keys: ");
1487         fprintf(stderr, "(default: all zone keys that have private keys)\n");
1488         fprintf(stderr, "\tkeyfile (Kname+alg+tag)\n");
1489 #ifndef ISC_RFC2535
1490         fprintf(stderr,
1491 "WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING\n"
1492 "WARNING                                                         WARNING\n"
1493 "WARNING This version of dnssec-signzone produces zones that are WARNING\n"
1494 "WARNING incompatible with the forthcoming DS based DNSSEC       WARNING\n"
1495 "WARNING standard.                                               WARNING\n"
1496 "WARNING                                                         WARNING\n"
1497 "WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING\n");
1498 #endif
1499         exit(0);
1500 }
1501
1502 static void
1503 removetempfile(void) {
1504         if (removefile)
1505                 isc_file_remove(tempfile);
1506 }
1507
1508 int
1509 main(int argc, char *argv[]) {
1510         int i, ch;
1511         char *startstr = NULL, *endstr = NULL, *classname = NULL;
1512         char *origin = NULL, *file = NULL, *output = NULL;
1513         char *randomfile = NULL;
1514         char *endp;
1515         isc_time_t timer_start, timer_finish;
1516         signer_key_t *key;
1517         isc_result_t result;
1518         isc_log_t *log = NULL;
1519         isc_boolean_t pseudorandom = ISC_FALSE;
1520         unsigned int eflags;
1521         isc_boolean_t free_output = ISC_FALSE;
1522         int tempfilelen;
1523         dns_rdataclass_t rdclass;
1524         isc_textregion_t r;
1525         isc_task_t **tasks = NULL;
1526         masterstyle = &dns_master_style_explicitttl;
1527
1528         check_result(isc_app_start(), "isc_app_start");
1529
1530         result = isc_mem_create(0, 0, &mctx);
1531         if (result != ISC_R_SUCCESS)
1532                 fatal("out of memory");
1533
1534         dns_result_register();
1535
1536         while ((ch = isc_commandline_parse(argc, argv,
1537                                            "c:s:e:i:v:o:f:ahpr:td:n:"))
1538                != -1) {
1539                 switch (ch) {
1540                 case 'c':
1541                         classname = isc_commandline_argument;
1542                         break;
1543
1544                 case 's':
1545                         startstr = isc_commandline_argument;
1546                         break;
1547
1548                 case 'e':
1549                         endstr = isc_commandline_argument;
1550                         break;
1551
1552                 case 'i':
1553                         endp = NULL;
1554                         cycle = strtol(isc_commandline_argument, &endp, 0);
1555                         if (*endp != '\0' || cycle < 0)
1556                                 fatal("cycle period must be numeric and "
1557                                       "positive");
1558                         break;
1559
1560                 case 'p':
1561                         pseudorandom = ISC_TRUE;
1562                         break;
1563
1564                 case 'r':
1565                         randomfile = isc_commandline_argument;
1566                         break;
1567
1568                 case 'v':
1569                         endp = NULL;
1570                         verbose = strtol(isc_commandline_argument, &endp, 0);
1571                         if (*endp != '\0')
1572                                 fatal("verbose level must be numeric");
1573                         break;
1574
1575                 case 'o':
1576                         origin = isc_commandline_argument;
1577                         break;
1578
1579                 case 'f':
1580                         output = isc_commandline_argument;
1581                         break;
1582
1583                 case 'a':
1584                         tryverify = ISC_TRUE;
1585                         break;
1586
1587                 case 't':
1588                         printstats = ISC_TRUE;
1589                         break;
1590
1591                 case 'd':
1592                         directory = isc_commandline_argument;
1593                         break;
1594
1595                 case 'n':
1596                         endp = NULL;
1597                         ntasks = strtol(isc_commandline_argument, &endp, 0);
1598                         if (*endp != '\0' || ntasks > ISC_INT32_MAX)
1599                                 fatal("number of cpus must be numeric");
1600                         break;
1601
1602                 case 'h':
1603                 default:
1604                         usage();
1605
1606                 }
1607         }
1608
1609 #ifndef ISC_RFC2535
1610         fprintf(stderr,
1611 "WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING\n"
1612 "WARNING                                                         WARNING\n"
1613 "WARNING This version of dnssec-signzone produces zones that are WARNING\n"
1614 "WARNING incompatible with the forth coming DS based DNSSEC      WARNING\n"
1615 "WARNING standard.                                               WARNING\n"
1616 "WARNING                                                         WARNING\n"
1617 "WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING\n");
1618 #endif
1619
1620         setup_entropy(mctx, randomfile, &ectx);
1621         eflags = ISC_ENTROPY_BLOCKING;
1622         if (!pseudorandom)
1623                 eflags |= ISC_ENTROPY_GOODONLY;
1624         result = dst_lib_init(mctx, ectx, eflags);
1625         if (result != ISC_R_SUCCESS)
1626                 fatal("could not initialize dst");
1627
1628         isc_stdtime_get(&now);
1629
1630         if (startstr != NULL)
1631                 starttime = strtotime(startstr, now, now);
1632         else
1633                 starttime = now;
1634
1635         if (endstr != NULL)
1636                 endtime = strtotime(endstr, now, starttime);
1637         else
1638                 endtime = starttime + (30 * 24 * 60 * 60);
1639
1640         if (cycle == -1)
1641                 cycle = (endtime - starttime) / 4;
1642
1643         if (ntasks == 0)
1644                 ntasks = isc_os_ncpus();
1645         vbprintf(4, "using %d cpus\n", ntasks);
1646
1647
1648         if (classname != NULL) {
1649                 r.base = classname;
1650                 r.length = strlen(classname);
1651                 result = dns_rdataclass_fromtext(&rdclass, &r);
1652                 if (result != ISC_R_SUCCESS)
1653                         fatal("unknown class %s",classname);
1654         } else
1655                 rdclass = dns_rdataclass_in;
1656
1657         setup_logging(verbose, mctx, &log);
1658
1659         argc -= isc_commandline_index;
1660         argv += isc_commandline_index;
1661
1662         if (argc < 1)
1663                 usage();
1664
1665         file = argv[0];
1666
1667         argc -= 1;
1668         argv += 1;
1669
1670         if (output == NULL) {
1671                 free_output = ISC_TRUE;
1672                 output = isc_mem_allocate(mctx,
1673                                           strlen(file) + strlen(".signed") + 1);
1674                 if (output == NULL)
1675                         fatal("out of memory");
1676                 sprintf(output, "%s.signed", file);
1677         }
1678
1679         if (origin == NULL)
1680                 origin = file;
1681
1682         gdb = NULL;
1683         isc_time_now(&timer_start);
1684         loadzone(file, origin, rdclass, &gdb);
1685         gorigin = dns_db_origin(gdb);
1686
1687         ISC_LIST_INIT(keylist);
1688
1689         if (argc == 0) {
1690                 signer_key_t *key;
1691
1692                 loadzonekeys(gdb);
1693
1694                 key = ISC_LIST_HEAD(keylist);
1695                 while (key != NULL) {
1696                         key->isdefault = ISC_TRUE;
1697                         key = ISC_LIST_NEXT(key, link);
1698                 }
1699         } else {
1700                 for (i = 0; i < argc; i++) {
1701                         dst_key_t *newkey = NULL;
1702
1703                         result = dst_key_fromnamedfile(argv[i],
1704                                                        DST_TYPE_PUBLIC |
1705                                                        DST_TYPE_PRIVATE,
1706                                                        mctx, &newkey);
1707                         if (result != ISC_R_SUCCESS)
1708                                 fatal("cannot load key %s: %s", argv[i],
1709                                       isc_result_totext(result)); 
1710
1711                         key = ISC_LIST_HEAD(keylist);
1712                         while (key != NULL) {
1713                                 dst_key_t *dkey = key->key;
1714                                 if (dst_key_id(dkey) == dst_key_id(newkey) &&
1715                                     dst_key_alg(dkey) == dst_key_alg(newkey) &&
1716                                     dns_name_equal(dst_key_name(dkey),
1717                                                    dst_key_name(newkey)))
1718                                 {
1719                                         key->isdefault = ISC_TRUE;
1720                                         if (!dst_key_isprivate(dkey))
1721                                                 fatal("cannot sign zone with "
1722                                                       "non-private key %s",
1723                                                       argv[i]);
1724                                         break;
1725                                 }
1726                                 key = ISC_LIST_NEXT(key, link);
1727                         }
1728                         if (key == NULL) {
1729                                 key = newkeystruct(newkey, ISC_TRUE);
1730                                 ISC_LIST_APPEND(keylist, key, link);
1731                         } else
1732                                 dst_key_free(&newkey);
1733                 }
1734
1735                 loadzonepubkeys(gdb);
1736         }
1737
1738         if (ISC_LIST_EMPTY(keylist)) {
1739                 fprintf(stderr, "%s: warning: No keys specified or found\n",
1740                         program);
1741                 nokeys = ISC_TRUE;
1742         }
1743
1744         gversion = NULL;
1745         result = dns_db_newversion(gdb, &gversion);
1746         check_result(result, "dns_db_newversion()");
1747
1748         tempfilelen = strlen(output) + 20;
1749         tempfile = isc_mem_get(mctx, tempfilelen);
1750         if (tempfile == NULL)
1751                 fatal("out of memory");
1752
1753         result = isc_file_mktemplate(output, tempfile, tempfilelen);
1754         check_result(result, "isc_file_mktemplate");
1755
1756         fp = NULL;
1757         result = isc_file_openunique(tempfile, &fp);
1758         if (result != ISC_R_SUCCESS)
1759                 fatal("failed to open temporary output file: %s",
1760                       isc_result_totext(result));
1761         removefile = ISC_TRUE;
1762         setfatalcallback(&removetempfile);
1763
1764         print_time(fp);
1765         print_version(fp);
1766
1767         result = isc_taskmgr_create(mctx, ntasks, 0, &taskmgr);
1768         if (result != ISC_R_SUCCESS)
1769                 fatal("failed to create task manager: %s",
1770                       isc_result_totext(result));
1771
1772         master = NULL;
1773         result = isc_task_create(taskmgr, 0, &master);
1774         if (result != ISC_R_SUCCESS)
1775                 fatal("failed to create task: %s", isc_result_totext(result));
1776
1777         tasks = isc_mem_get(mctx, ntasks * sizeof(isc_task_t *));
1778         if (tasks == NULL)
1779                 fatal("out of memory");
1780         for (i = 0; i < (int)ntasks; i++) {
1781                 tasks[i] = NULL;
1782                 result = isc_task_create(taskmgr, 0, &tasks[i]);
1783                 if (result != ISC_R_SUCCESS)
1784                         fatal("failed to create task: %s",
1785                               isc_result_totext(result));
1786                 result = isc_app_onrun(mctx, master, startworker, tasks[i]);
1787                 if (result != ISC_R_SUCCESS)
1788                         fatal("failed to start task: %s",
1789                               isc_result_totext(result));
1790         }
1791
1792         RUNTIME_CHECK(isc_mutex_init(&namelock) == ISC_R_SUCCESS);
1793         if (printstats)
1794                 RUNTIME_CHECK(isc_mutex_init(&statslock) == ISC_R_SUCCESS);
1795
1796         presign();
1797         (void)isc_app_run();
1798         if (!finished)
1799                 fatal("process aborted by user");
1800         shuttingdown = ISC_TRUE;
1801         for (i = 0; i < (int)ntasks; i++)
1802                 isc_task_detach(&tasks[i]);
1803         isc_taskmgr_destroy(&taskmgr);
1804         isc_mem_put(mctx, tasks, ntasks * sizeof(isc_task_t *));
1805         postsign();
1806
1807         result = isc_stdio_close(fp);
1808         check_result(result, "isc_stdio_close");
1809         removefile = ISC_FALSE;
1810
1811         result = isc_file_rename(tempfile, output);
1812         if (result != ISC_R_SUCCESS)
1813                 fatal("failed to rename temp file to %s: %s\n",
1814                       output, isc_result_totext(result));
1815
1816         DESTROYLOCK(&namelock);
1817         if (printstats)
1818                 DESTROYLOCK(&statslock);
1819
1820         printf("%s\n", output);
1821
1822         dns_db_closeversion(gdb, &gversion, ISC_FALSE);
1823
1824         dns_db_detach(&gdb);
1825
1826         while (!ISC_LIST_EMPTY(keylist)) {
1827                 key = ISC_LIST_HEAD(keylist);
1828                 ISC_LIST_UNLINK(keylist, key, link);
1829                 dst_key_free(&key->key);
1830                 isc_mem_put(mctx, key, sizeof(signer_key_t));
1831         }
1832
1833         isc_mem_put(mctx, tempfile, tempfilelen);
1834
1835         if (free_output)
1836                 isc_mem_free(mctx, output);
1837
1838         cleanup_logging(&log);
1839         dst_lib_destroy();
1840         cleanup_entropy(&ectx);
1841         if (verbose > 10)
1842                 isc_mem_stats(mctx, stdout);
1843         isc_mem_destroy(&mctx);
1844
1845         (void) isc_app_finish();
1846
1847         if (printstats) {
1848                 isc_uint64_t runtime_us;   /* Runtime in microseconds */
1849                 isc_uint64_t runtime_ms;   /* Runtime in milliseconds */
1850                 isc_uint64_t sig_ms;       /* Signatures per millisecond */
1851
1852                 isc_time_now(&timer_finish);
1853
1854                 runtime_us = isc_time_microdiff(&timer_finish, &timer_start);
1855
1856                 printf("Signatures generated:               %10d\n",
1857                        nsigned);
1858                 printf("Signatures retained:                %10d\n",
1859                        nretained);
1860                 printf("Signatures dropped:                 %10d\n",
1861                        ndropped);
1862                 printf("Signatures successfully verified:   %10d\n",
1863                        nverified);
1864                 printf("Signatures unsuccessfully verified: %10d\n",
1865                        nverifyfailed);
1866                 runtime_ms = runtime_us / 1000;
1867                 printf("Runtime in seconds:                %7u.%03u\n", 
1868                        (unsigned int) (runtime_ms / 1000), 
1869                        (unsigned int) (runtime_ms % 1000));
1870                 if (runtime_us > 0) {
1871                         sig_ms = ((isc_uint64_t)nsigned * 1000000000) /
1872                                  runtime_us;
1873                         printf("Signatures per second:             %7u.%03u\n",
1874                                (unsigned int) sig_ms / 1000, 
1875                                (unsigned int) sig_ms % 1000);
1876                 }
1877         }
1878
1879         return (0);
1880 }