]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - contrib/bind9/lib/isccc/cc.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / contrib / bind9 / lib / isccc / cc.c
1 /*
2  * Portions Copyright (C) 2004-2006  Internet Systems Consortium, Inc. ("ISC")
3  * Portions Copyright (C) 2001-2003  Internet Software Consortium.
4  * Portions Copyright (C) 2001  Nominum, 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 NOMINUM DISCLAIMS ALL
11  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
12  * OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY
13  * 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
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 /* $Id: cc.c,v 1.10.18.5 2006/12/07 23:57:58 marka Exp $ */
20
21 /*! \file */
22
23 #include <config.h>
24
25 #include <stdio.h>
26 #include <string.h>
27 #include <errno.h>
28
29 #include <isc/assertions.h>
30 #include <isc/hmacmd5.h>
31 #include <isc/print.h>
32 #include <isc/stdlib.h>
33
34 #include <isccc/alist.h>
35 #include <isccc/base64.h>
36 #include <isccc/cc.h>
37 #include <isccc/result.h>
38 #include <isccc/sexpr.h>
39 #include <isccc/symtab.h>
40 #include <isccc/symtype.h>
41 #include <isccc/util.h>
42
43 #define MAX_TAGS                256
44 #define DUP_LIFETIME            900
45
46 typedef isccc_sexpr_t *sexpr_ptr;
47
48 static unsigned char auth_hmd5[] = {
49         0x05, 0x5f, 0x61, 0x75, 0x74, 0x68,             /*%< len + _auth */
50         ISCCC_CCMSGTYPE_TABLE,                          /*%< message type */
51         0x00, 0x00, 0x00, 0x20,                         /*%< length == 32 */
52         0x04, 0x68, 0x6d, 0x64, 0x35,                   /*%< len + hmd5 */
53         ISCCC_CCMSGTYPE_BINARYDATA,                     /*%< message type */
54         0x00, 0x00, 0x00, 0x16,                         /*%< length == 22 */
55         /*
56          * The base64 encoding of one of our HMAC-MD5 signatures is
57          * 22 bytes.
58          */
59         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
60         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
61         0x00, 0x00, 0x00, 0x00, 0x00, 0x00
62 };
63
64 #define HMD5_OFFSET     21              /*%< 21 = 6 + 1 + 4 + 5 + 1 + 4 */
65 #define HMD5_LENGTH     22
66
67 static isc_result_t
68 table_towire(isccc_sexpr_t *alist, isccc_region_t *target);
69
70 static isc_result_t
71 list_towire(isccc_sexpr_t *alist, isccc_region_t *target);
72
73 static isc_result_t
74 value_towire(isccc_sexpr_t *elt, isccc_region_t *target)
75 {
76         size_t len;
77         unsigned char *lenp;
78         isccc_region_t *vr;
79         isc_result_t result;
80
81         if (isccc_sexpr_binaryp(elt)) {
82                 vr = isccc_sexpr_tobinary(elt);
83                 len = REGION_SIZE(*vr);
84                 if (REGION_SIZE(*target) < 1 + 4 + len)
85                         return (ISC_R_NOSPACE);
86                 PUT8(ISCCC_CCMSGTYPE_BINARYDATA, target->rstart);
87                 PUT32(len, target->rstart);
88                 if (REGION_SIZE(*target) < len)
89                         return (ISC_R_NOSPACE);
90                 PUT_MEM(vr->rstart, len, target->rstart);
91         } else if (isccc_alist_alistp(elt)) {
92                 if (REGION_SIZE(*target) < 1 + 4)
93                         return (ISC_R_NOSPACE);
94                 PUT8(ISCCC_CCMSGTYPE_TABLE, target->rstart);
95                 /*
96                  * Emit a placeholder length.
97                  */
98                 lenp = target->rstart;
99                 PUT32(0, target->rstart);
100                 /*
101                  * Emit the table.
102                  */
103                 result = table_towire(elt, target);
104                 if (result != ISC_R_SUCCESS)
105                         return (result);
106                 len = (size_t)(target->rstart - lenp);
107                 /*
108                  * 'len' is 4 bytes too big, since it counts
109                  * the placeholder length too.  Adjust and
110                  * emit.
111                  */
112                 INSIST(len >= 4U);
113                 len -= 4;
114                 PUT32(len, lenp);
115         } else if (isccc_sexpr_listp(elt)) {
116                 if (REGION_SIZE(*target) < 1 + 4)
117                         return (ISC_R_NOSPACE);
118                 PUT8(ISCCC_CCMSGTYPE_LIST, target->rstart);
119                 /*
120                  * Emit a placeholder length and count.
121                  */
122                 lenp = target->rstart;
123                 PUT32(0, target->rstart);
124                 /*
125                  * Emit the list.
126                  */
127                 result = list_towire(elt, target);
128                 if (result != ISC_R_SUCCESS)
129                         return (result);
130                 len = (size_t)(target->rstart - lenp);
131                 /*
132                  * 'len' is 4 bytes too big, since it counts
133                  * the placeholder length.  Adjust and emit.
134                  */
135                 INSIST(len >= 4U);
136                 len -= 4;
137                 PUT32(len, lenp);
138         }
139
140         return (ISC_R_SUCCESS);
141 }
142
143 static isc_result_t
144 table_towire(isccc_sexpr_t *alist, isccc_region_t *target)
145 {
146         isccc_sexpr_t *kv, *elt, *k, *v;
147         char *ks;
148         isc_result_t result;
149         size_t len;
150
151         for (elt = isccc_alist_first(alist);
152              elt != NULL;
153              elt = ISCCC_SEXPR_CDR(elt)) {
154                 kv = ISCCC_SEXPR_CAR(elt);
155                 k = ISCCC_SEXPR_CAR(kv);
156                 ks = isccc_sexpr_tostring(k);
157                 v = ISCCC_SEXPR_CDR(kv);
158                 len = strlen(ks);
159                 INSIST(len <= 255U);
160                 /*
161                  * Emit the key name.
162                  */
163                 if (REGION_SIZE(*target) < 1 + len)
164                         return (ISC_R_NOSPACE);
165                 PUT8(len, target->rstart);
166                 PUT_MEM(ks, len, target->rstart);
167                 /*
168                  * Emit the value.
169                  */
170                 result = value_towire(v, target);
171                 if (result != ISC_R_SUCCESS)
172                         return (result);
173         }
174
175         return (ISC_R_SUCCESS);
176 }
177
178 static isc_result_t
179 list_towire(isccc_sexpr_t *list, isccc_region_t *target)
180 {
181         isc_result_t result;
182
183         while (list != NULL) {
184                 result = value_towire(ISCCC_SEXPR_CAR(list), target);
185                 if (result != ISC_R_SUCCESS)
186                         return (result);
187                 list = ISCCC_SEXPR_CDR(list);
188         }
189
190         return (ISC_R_SUCCESS);
191 }
192
193 static isc_result_t
194 sign(unsigned char *data, unsigned int length, unsigned char *hmd5,
195      isccc_region_t *secret)
196 {
197         isc_hmacmd5_t ctx;
198         isc_result_t result;
199         isccc_region_t source, target;
200         unsigned char digest[ISC_MD5_DIGESTLENGTH];
201         unsigned char digestb64[ISC_MD5_DIGESTLENGTH * 4];
202
203         isc_hmacmd5_init(&ctx, secret->rstart, REGION_SIZE(*secret));
204         isc_hmacmd5_update(&ctx, data, length);
205         isc_hmacmd5_sign(&ctx, digest);
206         source.rstart = digest;
207         source.rend = digest + ISC_MD5_DIGESTLENGTH;
208         target.rstart = digestb64;
209         target.rend = digestb64 + ISC_MD5_DIGESTLENGTH * 4;
210         result = isccc_base64_encode(&source, 64, "", &target);
211         if (result != ISC_R_SUCCESS)
212                 return (result);
213         PUT_MEM(digestb64, HMD5_LENGTH, hmd5);
214
215         return (ISC_R_SUCCESS);
216 }
217
218 isc_result_t
219 isccc_cc_towire(isccc_sexpr_t *alist, isccc_region_t *target,
220               isccc_region_t *secret)
221 {
222         unsigned char *hmd5_rstart, *signed_rstart;
223         isc_result_t result;
224
225         if (REGION_SIZE(*target) < 4 + sizeof(auth_hmd5))
226                 return (ISC_R_NOSPACE);
227         /*
228          * Emit protocol version.
229          */
230         PUT32(1, target->rstart);
231         if (secret != NULL) {
232                 /*
233                  * Emit _auth section with zeroed HMAC-MD5 signature.
234                  * We'll replace the zeros with the real signature once
235                  * we know what it is.
236                  */
237                 hmd5_rstart = target->rstart + HMD5_OFFSET;
238                 PUT_MEM(auth_hmd5, sizeof(auth_hmd5), target->rstart);
239         } else
240                 hmd5_rstart = NULL;
241         signed_rstart = target->rstart;
242         /*
243          * Delete any existing _auth section so that we don't try
244          * to encode it.
245          */
246         isccc_alist_delete(alist, "_auth");
247         /*
248          * Emit the message.
249          */
250         result = table_towire(alist, target);
251         if (result != ISC_R_SUCCESS)
252                 return (result);
253         if (secret != NULL)
254                 return (sign(signed_rstart, (target->rstart - signed_rstart),
255                              hmd5_rstart, secret));
256         return (ISC_R_SUCCESS);
257 }
258
259 static isc_result_t
260 verify(isccc_sexpr_t *alist, unsigned char *data, unsigned int length,
261        isccc_region_t *secret)
262 {
263         isc_hmacmd5_t ctx;
264         isccc_region_t source;
265         isccc_region_t target;
266         isc_result_t result;
267         isccc_sexpr_t *_auth, *hmd5;
268         unsigned char digest[ISC_MD5_DIGESTLENGTH];
269         unsigned char digestb64[ISC_MD5_DIGESTLENGTH * 4];
270
271         /*
272          * Extract digest.
273          */
274         _auth = isccc_alist_lookup(alist, "_auth");
275         if (_auth == NULL)
276                 return (ISC_R_FAILURE);
277         hmd5 = isccc_alist_lookup(_auth, "hmd5");
278         if (hmd5 == NULL)
279                 return (ISC_R_FAILURE);
280         /*
281          * Compute digest.
282          */
283         isc_hmacmd5_init(&ctx, secret->rstart, REGION_SIZE(*secret));
284         isc_hmacmd5_update(&ctx, data, length);
285         isc_hmacmd5_sign(&ctx, digest);
286         source.rstart = digest;
287         source.rend = digest + ISC_MD5_DIGESTLENGTH;
288         target.rstart = digestb64;
289         target.rend = digestb64 + ISC_MD5_DIGESTLENGTH * 4;
290         result = isccc_base64_encode(&source, 64, "", &target);
291         if (result != ISC_R_SUCCESS)
292                 return (result);
293         /*
294          * Strip trailing == and NUL terminate target.
295          */
296         target.rstart -= 2;
297         *target.rstart++ = '\0';
298         /*
299          * Verify.
300          */
301         if (strcmp((char *)digestb64, isccc_sexpr_tostring(hmd5)) != 0)
302                 return (ISCCC_R_BADAUTH);
303
304         return (ISC_R_SUCCESS);
305 }
306
307 static isc_result_t
308 table_fromwire(isccc_region_t *source, isccc_region_t *secret,
309                isccc_sexpr_t **alistp);
310
311 static isc_result_t
312 list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp);
313
314 static isc_result_t
315 value_fromwire(isccc_region_t *source, isccc_sexpr_t **valuep)
316 {
317         unsigned int msgtype;
318         isc_uint32_t len;
319         isccc_sexpr_t *value;
320         isccc_region_t active;
321         isc_result_t result;
322
323         if (REGION_SIZE(*source) < 1 + 4)
324                 return (ISC_R_UNEXPECTEDEND);
325         GET8(msgtype, source->rstart);
326         GET32(len, source->rstart);
327         if (REGION_SIZE(*source) < len)
328                 return (ISC_R_UNEXPECTEDEND);
329         active.rstart = source->rstart;
330         active.rend = active.rstart + len;
331         source->rstart = active.rend;
332         if (msgtype == ISCCC_CCMSGTYPE_BINARYDATA) {
333                 value = isccc_sexpr_frombinary(&active);
334                 if (value != NULL) {
335                         *valuep = value;
336                         result = ISC_R_SUCCESS;
337                 } else
338                         result = ISC_R_NOMEMORY;
339         } else if (msgtype == ISCCC_CCMSGTYPE_TABLE)
340                 result = table_fromwire(&active, NULL, valuep);
341         else if (msgtype == ISCCC_CCMSGTYPE_LIST)
342                 result = list_fromwire(&active, valuep);
343         else
344                 result = ISCCC_R_SYNTAX;
345
346         return (result);
347 }
348
349 static isc_result_t
350 table_fromwire(isccc_region_t *source, isccc_region_t *secret,
351                isccc_sexpr_t **alistp)
352 {
353         char key[256];
354         isc_uint32_t len;
355         isc_result_t result;
356         isccc_sexpr_t *alist, *value;
357         isc_boolean_t first_tag;
358         unsigned char *checksum_rstart;
359
360         REQUIRE(alistp != NULL && *alistp == NULL);
361
362         checksum_rstart = NULL;
363         first_tag = ISC_TRUE;
364         alist = isccc_alist_create();
365         if (alist == NULL)
366                 return (ISC_R_NOMEMORY);
367
368         while (!REGION_EMPTY(*source)) {
369                 GET8(len, source->rstart);
370                 if (REGION_SIZE(*source) < len) {
371                         result = ISC_R_UNEXPECTEDEND;
372                         goto bad;
373                 }
374                 GET_MEM(key, len, source->rstart);
375                 key[len] = '\0';        /* Ensure NUL termination. */
376                 value = NULL;
377                 result = value_fromwire(source, &value);
378                 if (result != ISC_R_SUCCESS)
379                         goto bad;
380                 if (isccc_alist_define(alist, key, value) == NULL) {
381                         result = ISC_R_NOMEMORY;
382                         goto bad;
383                 }
384                 if (first_tag && secret != NULL && strcmp(key, "_auth") == 0)
385                         checksum_rstart = source->rstart;
386                 first_tag = ISC_FALSE;
387         }
388
389         *alistp = alist;
390
391         if (secret != NULL) {
392                 if (checksum_rstart != NULL)
393                         return (verify(alist, checksum_rstart,
394                                        (source->rend - checksum_rstart),
395                                        secret));
396                 return (ISCCC_R_BADAUTH);
397         }
398
399         return (ISC_R_SUCCESS);
400
401  bad:
402         isccc_sexpr_free(&alist);
403
404         return (result);
405 }
406
407 static isc_result_t
408 list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp)
409 {
410         isccc_sexpr_t *list, *value;
411         isc_result_t result;
412
413         list = NULL;
414         while (!REGION_EMPTY(*source)) {
415                 value = NULL;
416                 result = value_fromwire(source, &value);
417                 if (result != ISC_R_SUCCESS) {
418                         isccc_sexpr_free(&list);
419                         return (result);
420                 }
421                 if (isccc_sexpr_addtolist(&list, value) == NULL) {
422                         isccc_sexpr_free(&value);
423                         isccc_sexpr_free(&list);
424                         return (result);
425                 }
426         }
427
428         *listp = list;
429         
430         return (ISC_R_SUCCESS);
431 }
432
433 isc_result_t
434 isccc_cc_fromwire(isccc_region_t *source, isccc_sexpr_t **alistp,
435                 isccc_region_t *secret)
436 {
437         unsigned int size;
438         isc_uint32_t version;
439
440         size = REGION_SIZE(*source);
441         if (size < 4)
442                 return (ISC_R_UNEXPECTEDEND);
443         GET32(version, source->rstart);
444         if (version != 1)
445                 return (ISCCC_R_UNKNOWNVERSION);        
446         
447         return (table_fromwire(source, secret, alistp));
448 }
449
450 static isc_result_t
451 createmessage(isc_uint32_t version, const char *from, const char *to,
452               isc_uint32_t serial, isccc_time_t now,
453               isccc_time_t expires, isccc_sexpr_t **alistp,
454               isc_boolean_t want_expires)
455 {
456         isccc_sexpr_t *alist, *_ctrl, *_data;
457         isc_result_t result;
458
459         REQUIRE(alistp != NULL && *alistp == NULL);
460
461         if (version != 1)
462                 return (ISCCC_R_UNKNOWNVERSION);
463
464         alist = isccc_alist_create();
465         if (alist == NULL)
466                 return (ISC_R_NOMEMORY);
467
468         result = ISC_R_NOMEMORY;
469
470         _ctrl = isccc_alist_create();
471         if (_ctrl == NULL)
472                 goto bad;
473         if (isccc_alist_define(alist, "_ctrl", _ctrl) == NULL) {
474                 isccc_sexpr_free(&_ctrl);
475                 goto bad;
476         }
477
478         _data = isccc_alist_create();
479         if (_data == NULL)
480                 goto bad;
481         if (isccc_alist_define(alist, "_data", _data) == NULL) {
482                 isccc_sexpr_free(&_data);
483                 goto bad;
484         }
485
486         if (isccc_cc_defineuint32(_ctrl, "_ser", serial) == NULL ||
487             isccc_cc_defineuint32(_ctrl, "_tim", now) == NULL ||
488             (want_expires &&
489              isccc_cc_defineuint32(_ctrl, "_exp", expires) == NULL))
490                 goto bad;
491         if (from != NULL &&
492             isccc_cc_definestring(_ctrl, "_frm", from) == NULL)
493                 goto bad;
494         if (to != NULL &&
495             isccc_cc_definestring(_ctrl, "_to", to) == NULL)
496                 goto bad;
497                 
498         *alistp = alist;
499
500         return (ISC_R_SUCCESS);
501
502  bad:
503         isccc_sexpr_free(&alist);
504
505         return (result);
506 }
507
508 isc_result_t
509 isccc_cc_createmessage(isc_uint32_t version, const char *from, const char *to,
510                      isc_uint32_t serial, isccc_time_t now,
511                      isccc_time_t expires, isccc_sexpr_t **alistp)
512 {
513         return (createmessage(version, from, to, serial, now, expires,
514                               alistp, ISC_TRUE));
515 }
516
517 isc_result_t
518 isccc_cc_createack(isccc_sexpr_t *message, isc_boolean_t ok,
519                  isccc_sexpr_t **ackp)
520 {
521         char *_frm, *_to;
522         isc_uint32_t serial;
523         isccc_sexpr_t *ack, *_ctrl;
524         isc_result_t result;
525         isccc_time_t t;
526
527         REQUIRE(ackp != NULL && *ackp == NULL);
528
529         _ctrl = isccc_alist_lookup(message, "_ctrl");
530         if (_ctrl == NULL ||
531             isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
532             isccc_cc_lookupuint32(_ctrl, "_tim", &t) != ISC_R_SUCCESS)
533                 return (ISC_R_FAILURE);
534         /*
535          * _frm and _to are optional.
536          */
537         _frm = NULL;
538         (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
539         _to = NULL;
540         (void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
541         /*
542          * Create the ack.
543          */
544         ack = NULL;
545         result = createmessage(1, _to, _frm, serial, t, 0, &ack, ISC_FALSE);
546         if (result != ISC_R_SUCCESS)
547                 return (result);
548
549         _ctrl = isccc_alist_lookup(ack, "_ctrl");
550         if (_ctrl == NULL)
551                 return (ISC_R_FAILURE);
552         if (isccc_cc_definestring(ack, "_ack", (ok) ? "1" : "0") == NULL) {
553                 result = ISC_R_NOMEMORY;
554                 goto bad;
555         }
556
557         *ackp = ack;
558
559         return (ISC_R_SUCCESS);
560
561  bad:
562         isccc_sexpr_free(&ack);
563
564         return (result);
565 }
566
567 isc_boolean_t
568 isccc_cc_isack(isccc_sexpr_t *message)
569 {
570         isccc_sexpr_t *_ctrl;
571
572         _ctrl = isccc_alist_lookup(message, "_ctrl");
573         if (_ctrl == NULL)
574                 return (ISC_FALSE);
575         if (isccc_cc_lookupstring(_ctrl, "_ack", NULL) == ISC_R_SUCCESS)
576                 return (ISC_TRUE);
577         return (ISC_FALSE);
578 }
579
580 isc_boolean_t
581 isccc_cc_isreply(isccc_sexpr_t *message)
582 {
583         isccc_sexpr_t *_ctrl;
584
585         _ctrl = isccc_alist_lookup(message, "_ctrl");
586         if (_ctrl == NULL)
587                 return (ISC_FALSE);
588         if (isccc_cc_lookupstring(_ctrl, "_rpl", NULL) == ISC_R_SUCCESS)
589                 return (ISC_TRUE);
590         return (ISC_FALSE);
591 }
592
593 isc_result_t
594 isccc_cc_createresponse(isccc_sexpr_t *message, isccc_time_t now,
595                       isccc_time_t expires, isccc_sexpr_t **alistp)
596 {
597         char *_frm, *_to, *type;
598         isc_uint32_t serial;
599         isccc_sexpr_t *alist, *_ctrl, *_data;
600         isc_result_t result;
601
602         REQUIRE(alistp != NULL && *alistp == NULL);
603
604         _ctrl = isccc_alist_lookup(message, "_ctrl");
605         _data = isccc_alist_lookup(message, "_data");
606         if (_ctrl == NULL ||
607             _data == NULL ||
608             isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
609             isccc_cc_lookupstring(_data, "type", &type) != ISC_R_SUCCESS)
610                 return (ISC_R_FAILURE);
611         /*
612          * _frm and _to are optional.
613          */
614         _frm = NULL;
615         (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
616         _to = NULL;
617         (void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
618         /*
619          * Create the response.
620          */
621         alist = NULL;
622         result = isccc_cc_createmessage(1, _to, _frm, serial, now, expires,
623                                          &alist);
624         if (result != ISC_R_SUCCESS)
625                 return (result);
626         _ctrl = isccc_alist_lookup(alist, "_ctrl");
627         if (_ctrl == NULL)
628                 return (ISC_R_FAILURE);
629         _data = isccc_alist_lookup(alist, "_data");
630         if (_data == NULL)
631                 return (ISC_R_FAILURE);
632         if (isccc_cc_definestring(_ctrl, "_rpl", "1") == NULL ||
633             isccc_cc_definestring(_data, "type", type) == NULL) {
634                 isccc_sexpr_free(&alist);
635                 return (ISC_R_NOMEMORY);
636         }
637
638         *alistp = alist;
639
640         return (ISC_R_SUCCESS);
641 }
642
643 isccc_sexpr_t *
644 isccc_cc_definestring(isccc_sexpr_t *alist, const char *key, const char *str)
645 {
646         size_t len;
647         isccc_region_t r;
648
649         len = strlen(str);
650         DE_CONST(str, r.rstart);
651         r.rend = r.rstart + len;
652
653         return (isccc_alist_definebinary(alist, key, &r));
654 }
655
656 isccc_sexpr_t *
657 isccc_cc_defineuint32(isccc_sexpr_t *alist, const char *key, isc_uint32_t i)
658 {
659         char b[100];
660         size_t len;
661         isccc_region_t r;
662
663         snprintf(b, sizeof(b), "%u", i);
664         len = strlen(b);
665         r.rstart = (unsigned char *)b;
666         r.rend = (unsigned char *)b + len;
667
668         return (isccc_alist_definebinary(alist, key, &r));
669 }
670
671 isc_result_t
672 isccc_cc_lookupstring(isccc_sexpr_t *alist, const char *key, char **strp)
673 {
674         isccc_sexpr_t *kv, *v;
675
676         kv = isccc_alist_assq(alist, key);
677         if (kv != NULL) {
678                 v = ISCCC_SEXPR_CDR(kv);
679                 if (isccc_sexpr_binaryp(v)) {
680                         if (strp != NULL)
681                                 *strp = isccc_sexpr_tostring(v);
682                         return (ISC_R_SUCCESS);
683                 } else
684                         return (ISC_R_EXISTS);
685         }
686
687         return (ISC_R_NOTFOUND);
688 }
689
690 isc_result_t
691 isccc_cc_lookupuint32(isccc_sexpr_t *alist, const char *key,
692                        isc_uint32_t *uintp)
693 {
694         isccc_sexpr_t *kv, *v;
695
696         kv = isccc_alist_assq(alist, key);
697         if (kv != NULL) {
698                 v = ISCCC_SEXPR_CDR(kv);
699                 if (isccc_sexpr_binaryp(v)) {
700                         if (uintp != NULL)
701                                 *uintp = (isc_uint32_t)
702                                         strtoul(isccc_sexpr_tostring(v),
703                                                 NULL, 10);
704                         return (ISC_R_SUCCESS);
705                 } else
706                         return (ISC_R_EXISTS);
707         }
708
709         return (ISC_R_NOTFOUND);
710 }
711
712 static void
713 symtab_undefine(char *key, unsigned int type, isccc_symvalue_t value,
714                 void *arg)
715 {
716         UNUSED(type);
717         UNUSED(value);
718         UNUSED(arg);
719
720         free(key);
721 }
722
723 static isc_boolean_t
724 symtab_clean(char *key, unsigned int type, isccc_symvalue_t value,
725              void *arg)
726 {
727         isccc_time_t *now;
728
729         UNUSED(key);
730         UNUSED(type);
731
732         now = arg;
733
734         if (*now < value.as_uinteger)
735                 return (ISC_FALSE);
736         if ((*now - value.as_uinteger) < DUP_LIFETIME)
737                 return (ISC_FALSE);
738         return (ISC_TRUE);
739 }
740
741 isc_result_t
742 isccc_cc_createsymtab(isccc_symtab_t **symtabp)
743 {
744         return (isccc_symtab_create(11897, symtab_undefine, NULL, ISC_FALSE,
745                                   symtabp));
746 }
747
748 void
749 isccc_cc_cleansymtab(isccc_symtab_t *symtab, isccc_time_t now)
750 {
751         isccc_symtab_foreach(symtab, symtab_clean, &now);
752 }
753
754 static isc_boolean_t
755 has_whitespace(const char *str)
756 {
757         char c;
758
759         if (str == NULL)
760                 return (ISC_FALSE);
761         while ((c = *str++) != '\0') {
762                 if (c == ' ' || c == '\t' || c == '\n')
763                         return (ISC_TRUE);
764         }
765         return (ISC_FALSE);
766 }
767
768 isc_result_t
769 isccc_cc_checkdup(isccc_symtab_t *symtab, isccc_sexpr_t *message,
770                 isccc_time_t now)
771 {
772         const char *_frm;
773         const char *_to;
774         char *_ser, *_tim, *tmp;
775         isc_result_t result;
776         char *key;
777         size_t len;
778         isccc_symvalue_t value;
779         isccc_sexpr_t *_ctrl;
780
781         _ctrl = isccc_alist_lookup(message, "_ctrl");
782         if (_ctrl == NULL ||
783             isccc_cc_lookupstring(_ctrl, "_ser", &_ser) != ISC_R_SUCCESS ||
784             isccc_cc_lookupstring(_ctrl, "_tim", &_tim) != ISC_R_SUCCESS)
785                 return (ISC_R_FAILURE);
786         /*
787          * _frm and _to are optional.
788          */
789         if (isccc_cc_lookupstring(_ctrl, "_frm", &tmp) != ISC_R_SUCCESS)
790                 _frm = "";
791         else
792                 _frm = tmp;
793         if (isccc_cc_lookupstring(_ctrl, "_to", &tmp) != ISC_R_SUCCESS)
794                 _to = "";
795         else
796                 _to = tmp;
797         /*
798          * Ensure there is no newline in any of the strings.  This is so
799          * we can write them to a file later.
800          */
801         if (has_whitespace(_frm) || has_whitespace(_to) ||
802             has_whitespace(_ser) || has_whitespace(_tim))
803                 return (ISC_R_FAILURE);
804         len = strlen(_frm) + strlen(_to) + strlen(_ser) + strlen(_tim) + 4;
805         key = malloc(len);
806         if (key == NULL)
807                 return (ISC_R_NOMEMORY);
808         snprintf(key, len, "%s;%s;%s;%s", _frm, _to, _ser, _tim);
809         value.as_uinteger = now;
810         result = isccc_symtab_define(symtab, key, ISCCC_SYMTYPE_CCDUP, value,
811                                    isccc_symexists_reject);
812         if (result != ISC_R_SUCCESS) {
813                 free(key);
814                 return (result);
815         }
816
817         return (ISC_R_SUCCESS);
818 }