chiark / gitweb /
Major overhaul. Now uses DSA signatures rather than the bogus symmetric
[become] / src / class.c
1 /* -*-c-*-
2  *
3  * $Id: class.c,v 1.9 2003/10/12 00:14:55 mdw Exp $
4  *
5  * Handling classes of things nicely
6  *
7  * (c) 1998 EBI
8  */
9
10 /*----- Licensing notice --------------------------------------------------*
11  *
12  * This file is part of `become'
13  *
14  * `Become' is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * `Become' is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with `become'; if not, write to the Free Software Foundation,
26  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27  */
28
29 /*----- Revision history --------------------------------------------------*
30  *
31  * $Log: class.c,v $
32  * Revision 1.9  2003/10/12 00:14:55  mdw
33  * Major overhaul.  Now uses DSA signatures rather than the bogus symmetric
34  * encrypt-and-hope thing.  Integrated with mLib and Catacomb.
35  *
36  * Revision 1.8  1998/06/08 11:20:36  mdw
37  * (class__wildMatch) Fixed bug which overran pattern string, spotted by
38  * Mark Rison.
39  *
40  * Revision 1.7  1998/01/12 16:45:50  mdw
41  * Fix copyright date.
42  *
43  * Revision 1.6  1997/09/17  10:14:56  mdw
44  * Complete rewrite to support class trees.  Makes the behaviour of the set
45  * operators much more logical.
46  *
47  * Revision 1.5  1997/08/20  16:16:13  mdw
48  * Patch memory leak.  Don't try to trace when tracing's turned off.
49  *
50  * Revision 1.4  1997/08/07 09:56:37  mdw
51  * (Log entry for previous version is bogus.)  Minor changes to host
52  * checking code.
53  *
54  * Revision 1.2  1997/08/04 10:24:21  mdw
55  * Sources placed under CVS control.
56  *
57  * Revision 1.1  1997/07/21  13:47:52  mdw
58  * Initial revision
59  *
60  */
61
62 /*----- Header files ------------------------------------------------------*/
63
64 /* --- ANSI headers --- */
65
66 #include <stdio.h>
67 #include <stdlib.h>
68 #include <string.h>
69
70 /* --- Unix headers --- */
71
72 #include <sys/types.h>
73 #include <sys/socket.h>
74
75 #include <netinet/in.h>
76
77 #include <arpa/inet.h>
78
79 #include <netdb.h>
80
81 /* --- mLib headers --- */
82
83 #include <mLib/alloc.h>
84 #include <mLib/report.h>
85 #include <mLib/sym.h>
86
87 /* --- Local headers --- */
88
89 #include "become.h"
90 #include "class.h"
91
92 /*----- Global variables --------------------------------------------------*/
93
94 static class_node class__all = { clType_all | clNode_any, -1, { 0 }};
95 class_node *class_all = &class__all;
96
97 static class_node class__none = { clType_all | clNode_any, -1, { 0 }};
98 class_node *class_none = &class__none;
99
100 /*----- Wildcard matching -------------------------------------------------*/
101
102 /* --- @class__wildMatch@ --- *
103  *
104  * Arguments:   @const char *pat@ = pointer to pattern string
105  *              @const char *p@ = pointer to target string
106  *
107  * Returns:     Zero if no match, nonzero if match.
108  *
109  * Use:         Wildcard-matches the pattern against the target string.
110  */
111
112 static int class__wildMatch(const char *pat, const char *p)
113 {
114   for (;;) {
115     if (*pat == 0 && *p == 0)
116       return (42); /* For sadism's sake */
117     else if (*pat == '*') {
118       while (*pat == '*')
119         pat++;
120       if (!*pat)
121         return (58);
122       do {
123         if (class__wildMatch(pat, p))
124           return (27); /* Nyahaha */
125         p++;
126       } while (*p);
127       return (0);
128     } else if (*pat == '?' || *pat == *p)
129       p++, pat++;
130     else
131       return (0);
132   }
133 }
134
135 /*----- Creating new class nodes ------------------------------------------*/
136
137 /* --- @class_fromString@ --- *
138  *
139  * Arguments:   @unsigned type@ = a type field
140  *              @const char *s@ = pointer to string to copy
141  *
142  * Returns:     A pointer to a class node containing that string, typed to
143  *              be the right thing.
144  *
145  * Use:         Given a string, wrap a class node around it.  The node has
146  *              one reference (the one you get returned).  The string is
147  *              copied, so you can get rid of your original one if you like.
148  */
149
150 class_node *class_fromString(unsigned type, const char *s)
151 {
152   class_node *c = xmalloc(sizeof(*c));
153   c->type = type | clNode_immed;
154   if (s[strcspn(s, "*?")] == 0)
155     c->type |= clFlag_friendly;
156   c->ref = 1;
157   c->v.s = xstrdup(s);
158   return (c);
159 }
160
161 /* --- @class_fromUser@ --- *
162  *
163  * Arguments:   @unsigned type@ = a type field
164  *              @uid_t u@ = a user-id number
165  *
166  * Returns:     A pointer to a class node containing the uid, typed to be
167  *              the thing you asked for.  Hopefully this will be
168  *              @clType_user@.
169  *
170  * Use:         Given a uid, wrap a class node around it.
171  */
172
173 class_node *class_fromUser(unsigned type, uid_t u)
174 {
175   class_node *c = xmalloc(sizeof(*c));
176   c->type = type | clNode_immed | clFlag_friendly;
177   c->ref = 1;
178   c->v.u = u;
179   return (c);
180 }
181
182 /*----- Reference counter tricks ------------------------------------------*/
183
184 /* --- @class_inc@ --- *
185  *
186  * Arguments:   @class_node *c@ = pointer to a class block
187  *
188  * Returns:     ---
189  *
190  * Use:         Adds a reference to the class definition.
191  */
192
193 void class_inc(class_node *c)
194 {
195   if (c != class_all && c != class_none)
196     c->ref++;
197 }
198
199 /* --- @class_dec@ --- *
200  *
201  * Arguments:   @class_node *c@ = pointer to a class block
202  *
203  * Returns:     ---
204  *
205  * Use:         Removes a reference to a class block.
206  */
207
208 void class_dec(class_node *c)
209 {
210   class_node *cc;
211
212   for (cc = 0; c; c = cc, cc = 0) {
213     if (c == class_all || c == class_none || --c->ref)
214       continue;
215     switch (c->type & clNode_mask) {
216       case clNode_any:
217         /* Nothing to do */
218         break;
219       case clNode_immed:
220         if (c->type & (clType_host | clType_command))
221           free(c->v.s);
222         break;
223       case clNode_hash:
224         sym_destroy(&c->v.t);
225         break;
226       case clNode_union:
227       case clNode_diff:
228       case clNode_isect:
229         class_dec(c->v.c.l);
230         cc = c->v.c.r;
231         break;
232     }
233     free(c);
234   }
235 }
236
237 /* --- @class_mod@ --- *
238  *
239  * Arguments:   @class_node *c@ = pointer to a class node
240  *
241  * Returns:     A pointer to a class node, maybe the same one, maybe not,
242  *              with a reference count of 1, containing the same data.
243  *
244  * Use:         Gives you a node which you can modify.  Don't call this
245  *              for @class_all@ or @class_none@.
246  */
247
248 class_node *class_mod(class_node *c)
249 {
250   class_node *cc;
251
252   if (c->ref == 1)
253     return (c);
254
255   cc = xmalloc(sizeof(*cc));
256   cc->type = c->type;
257   cc->ref = 1;
258   switch (c->type & clNode_mask) {
259     case clNode_any:
260       die(1, "internal: class_mod called on non-modifiable class node");
261       break;
262
263     case clNode_immed:
264       if (c->type & clType_user)
265         cc->v.u = c->v.u;
266       else
267         cc->v.s = xstrdup(c->v.s);
268       break;
269
270     case clNode_hash: {
271       sym_iter i;
272       sym_base *b;
273
274       sym_create(&cc->v.t);
275       for (sym_mkiter(&i, &c->v.t); (b = sym_next(&i)) != 0; )
276         sym_find(&cc->v.t, b->name, b->len, sizeof(sym_base), 0);
277     } break;
278
279     case clNode_union:
280     case clNode_diff:
281     case clNode_isect:
282       cc->v.c.l = c->v.c.l;
283       cc->v.c.r = c->v.c.r;
284       class_inc(cc->v.c.l);
285       class_inc(cc->v.c.r);
286       break;
287   }
288
289   class_dec(c);
290   return (cc);
291 }
292
293 /*----- Some weirder operations on classes --------------------------------*/
294
295 /* --- @class__hashify@ --- *
296  *
297  * Arguments:   @class_node *c@ = pointer to a node
298  *
299  * Returns:     A pointer to a hash node containing the node's value.
300  *
301  * Use:         The original node must have type `immediate', and it must
302  *              be friendly.  The old reference is discarded -- you get this
303  *              one instead.
304  */
305
306 static class_node *class__hashify(class_node *c)
307 {
308   /* --- Some sanity checking --- */
309
310   if (~c->type & clFlag_friendly)
311     die(1, "internal: class__hashify can't hashify unfriendly nodes");
312   if ((c->type & clNode_mask) != clNode_immed)
313     die(1, "internal: class__hashify can't hashify non-immediate nodes");
314
315   /* --- Split off a private copy of the node --- */
316
317   c = class_mod(c);
318   
319   c->type = (c->type & clType_mask) | clNode_hash | clFlag_friendly;
320
321   if (c->type & clType_user) {
322     uid_t u = c->v.u;
323     sym_create(&c->v.t);
324     sym_find(&c->v.t, (char *)&u, sizeof(u), sizeof(sym_base), 0);
325   } else {
326     char *s = c->v.s;
327     sym_create(&c->v.t);
328     sym_find(&c->v.t, s, -1, sizeof(sym_base), 0);
329     free(s);
330   }
331
332   return (c);
333 }
334
335 /*----- Arithmetic on classes ---------------------------------------------*/
336
337 /* --- @class__binop@ --- *
338  *
339  * Arguments:   @class_node *l@ = left argument
340  *              @class_node *r@ = right argument
341  *              @unsigned op@ = the binop code
342  *
343  * Returns:     A class node representing the result of a binary operation
344  *              upon two classes.
345  *
346  * Use:         Performs a binary operation on classes.  If the types don't
347  *              match, then a null pointer is returned.  Both @l@ and @r@
348  *              may be modified, and will be decremented before they get
349  *              returned to you.  If you don't want that to happen, ensure
350  *              that you've claimed a reference to the original versions.
351  *
352  *              If both nodes are `friendly' (see below), then an attempt is
353  *              made to combine them sensibly using a hashtable.
354  *
355  *              If one or both nodes is/are unfriendly, a binop node is
356  *              created with type @op@ to allow the matcher to decide on
357  *              membership appropriately at match time.
358  *
359  *              A friendly node is one which can be placed in a hash table to
360  *              speed up searching.  It's friendly, because it doesn't mind
361  *              living with other nodes.  Uid numbers are friendly.
362  *              Wildcarded strings aren't.  Hashtables are trivially
363  *              friendly.
364  */
365
366 class_node *class__binop(class_node *l, class_node *r, int op)
367 {
368   unsigned type = l->type & r->type & clType_mask;
369   unsigned lnode = l->type & clNode_mask, rnode = r->type & clNode_mask;
370
371   /* --- Check for compatible types --- */
372
373   if (!type) {
374     class_dec(l);
375     class_dec(r);
376     return (0);
377   }
378
379   /* --- Handle `friendly' nodes --- */
380
381   if ((l->type & r->type & clFlag_friendly)) {
382
383     /* --- Consider promoting an item to a hash --- *
384      *
385      * If both are immediate nodes, and they're both `friendly', we can
386      * profitably hash them together.
387      *
388      * Life gets complicated when we do subtraction, because it's not
389      * commutative.  In this case, I have to promote the left operand anyway.
390      */
391
392     if (lnode == clNode_immed &&
393         (rnode == clNode_immed || op == clNode_diff)) {
394
395       /* --- Quick check for triviality --- *
396        *
397        * There are some more short-cuts I can employ if the values are
398        * the same.
399        */
400
401       if (rnode == clNode_immed) {
402
403         /* --- See whether the two items are equal --- */
404
405         int eq = (type & clType_user ?
406                   l->v.u == r->v.u : strcmp(l->v.s, r->v.s) == 0);
407
408         /* --- Now do something appropriate --- */
409
410         switch (op) {
411           case clNode_union:
412             if (eq) {
413               class_dec(r);
414               return (l);
415             }
416             break;
417           case clNode_diff:
418             if (eq) {
419               class_dec(l);
420               class_dec(r);
421               return (class_none);
422             }
423             break;
424           case clNode_isect:
425             if (eq) {
426               class_dec(r);
427               return (l);
428             } else {
429               class_dec(l);
430               class_dec(r);
431               return (class_none);
432             }
433             break;
434         }
435       }
436
437       /* --- Turn @l@ into a hash containing itself --- */
438
439       l = class__hashify(l);
440       lnode = clNode_hash;
441     }
442
443     /* --- Otherwise, make @l@ the hash --- *
444      *
445      * Both @l@ and @r@ are friendly.  Since they're not both immediates,
446      * one must be a hash.
447      */
448
449     else if ((l->type & clNode_mask) == clNode_immed) {
450       class_node *tn;
451       unsigned tt;
452
453       tn = l, l = r, r = tn;
454       tt = lnode, lnode = rnode, rnode = tt;
455     }
456
457     /* --- Now merge @r@ with @l@ --- */
458
459     l = class_mod(l);
460
461     switch (op) {
462
463       /* --- The union operation isn't hard --- */
464
465       case clNode_union:
466         if (rnode == clNode_immed) {
467           if (type & clType_user) {
468             sym_find(&l->v.t, (char *)&r->v.u,
469                      sizeof(r->v.u), sizeof(sym_base), 0);
470           } else
471             sym_find(&l->v.t, r->v.s, -1, sizeof(sym_base), 0);
472         } else {
473           sym_iter i;
474           sym_base *b;
475
476           for (sym_mkiter(&i, &r->v.t); (b = sym_next(&i)) != 0; )
477             sym_find(&l->v.t, b->name, b->len, sizeof(sym_base), 0);
478         }
479         break;
480
481       /* --- Set difference is similar in spirit --- */
482
483       case clNode_diff:
484         if (rnode == clNode_immed) {
485           sym_base *f;
486
487           if (type & clType_user)
488             f = sym_find(&l->v.t, (char *)&r->v.u, sizeof(r->v.u), 0, 0);
489           else
490             f = sym_find(&l->v.t, r->v.s, -1, 0, 0);
491           if (f)
492             sym_remove(&l->v.t, f);
493         } else {
494           sym_iter i;
495           sym_base *b, *f;
496
497           for (sym_mkiter(&i, &r->v.t); (b = sym_next(&i)) != 0; ) {
498             if ((f = sym_find(&l->v.t, b->name, b->len, 0, 0)) != 0)
499               sym_remove(&l->v.t, f);
500           }
501         }
502         break;
503
504       /* --- Intersection is wild and wacky --- */
505
506       case clNode_isect:
507         if (rnode == clNode_immed) {
508           sym_base *f;
509
510           if (type & clType_user)
511             f = sym_find(&l->v.t, (char *)&r->v.u, sizeof(r->v.u), 0, 0);
512           else
513             f = sym_find(&l->v.t, r->v.s, -1, 0, 0);
514           if (f) {
515             class_dec(l);
516             return (r);
517           } else {
518             class_dec(l);
519             class_dec(r);
520             return (class_none);
521           }         
522         } else {
523           sym_iter i;
524           sym_base *b;
525
526           for (sym_mkiter(&i, &l->v.t); (b = sym_next(&i)) != 0; ) {
527             if (!sym_find(&r->v.t, b->name, b->len, 0, 0))
528               sym_remove(&l->v.t, b);
529           }
530         }
531         break;
532     }
533
534     /* --- Now trim the @l@ table to size --- *
535      *
536      * It may have lost a load of elements.  Maybe it can be represented
537      * better as an immediate or even as @class_none@.
538      */
539
540     {
541       sym_iter i;
542       sym_base *b;
543
544       class_dec(r);
545
546       sym_mkiter(&i, &l->v.t);
547       if ((b = sym_next(&i)) == 0) {
548         class_dec(l);
549         return (class_none);
550       }
551       if (!sym_next(&i)) {
552         if (type & clType_user) {
553           uid_t u = *(uid_t *)b->name;
554           sym_destroy(&l->v.t);
555           l->type = (l->type & ~clNode_mask) | clNode_immed;
556           l->v.u = u;
557         } else {
558           char *s = xstrdup(b->name);
559           sym_destroy(&l->v.t);
560           l->type = (l->type & ~clNode_mask) | clNode_immed;
561           l->v.s = s;
562         }
563       }
564     }
565
566     /* --- Done --- */
567
568     return (l);
569   }
570
571   /* --- Unfriendly nodes --- *
572    *
573    * Create a binop node and return that.  If @l@ is a binop node, and @r@
574    * isn't, and the operation isn't set difference (i.e., it's commutative)
575    * then swap the two over to lessen the depth of recursion later.
576    */
577
578   else {
579     class_node *c = xmalloc(sizeof(*c));
580
581     c->type = type | op;
582     c->ref = 1;
583     if (lnode >= clNode_binop && rnode < clNode_binop && op != clNode_diff) {
584       c->v.c.l = r;
585       c->v.c.r = l;
586     } else {
587       c->v.c.l = l;
588       c->v.c.r = r;
589     }
590     return (c);
591   }
592 }
593
594 /* --- @class_union@ --- *
595  *
596  * Arguments:   @class_node *l@ = left argument
597  *              @class_node *r@ = right argument
598  *
599  * Returns:     A class node representing the union of the two classes.
600  *
601  * Use:         Performs the union operation on classes.  If the types don't
602  *              match, then a null pointer is returned.  Both @l@ and @r@
603  *              may be modified, and will be decremented before they get
604  *              returned to you.  If you don't want that to happen, ensure
605  *              that you've claimed a reference to the original versions.
606  */
607
608 class_node *class_union(class_node *l, class_node *r)
609 {
610   /* --- Check for the really simple cases --- */
611
612   if (l == class_all || r == class_all) {
613     class_dec(l);
614     class_dec(r);
615     return (class_all);
616   }
617
618   if (l == class_none)
619     return (r);
620   if (r == class_none)
621     return (l);
622
623   /* --- Do the job --- */
624
625   return (class__binop(l, r, clNode_union));
626 }
627
628 /* --- @class_diff@ --- *
629  *
630  * Arguments:   @class_node *l@ = left argument
631  *              @class_node *r@ = right argument
632  *
633  * Returns:     A class node representing the difference of the two classes.
634  *
635  * Use:         Performs the set difference operation on classes.  If the
636  *              types don't match, then a null pointer is returned.  Both
637  *              @l@ and @r@ may be modified, and will be decremented before
638  *              they get returned to you.  If you don't want that to happen,
639  *              ensure that you've claimed a reference to the original
640  *              versions.
641  */
642
643 class_node *class_diff(class_node *l, class_node *r)
644 {
645   /* --- Check for the really simple cases --- */
646
647   if (l == class_none || r == class_all) {
648     class_dec(l);
649     class_dec(r);
650     return (class_none);
651   }
652
653   if (r == class_none)
654     return (l);
655
656   /* --- Do the job --- */
657
658   return (class__binop(l, r, clNode_diff));
659 }
660
661 /* --- @class_isect@ --- *
662  *
663  * Arguments:   @class_node *l@ = left argument
664  *              @class_node *r@ = right argument
665  *
666  * Returns:     A class node representing the intersection of the two
667  *              classes.
668  *
669  * Use:         Performs the intersecion operation on classes.  If the types
670  *              don't match, then a null pointer is returned.  Both @l@ and
671  *              @r@ may be modified, and will be decremented before they get
672  *              returned to you.  If you don't want that to happen, ensure
673  *              that you've claimed a reference to the original versions.
674  */
675
676 class_node *class_isect(class_node *l, class_node *r)
677 {
678   /* --- Check for the really simple cases --- */
679
680   if (l == class_none || r == class_none) {
681     class_dec(l);
682     class_dec(r);
683     return (class_none);
684   }
685
686   if (l == class_all)
687     return (r);
688   if (r == class_all)
689     return (l);
690
691   /* --- Do the job --- */
692
693   return (class__binop(l, r, clNode_isect));
694 }
695
696 /*----- Building the predefined classes -----------------------------------*/
697
698 /* --- @class_addUser@ --- *
699  *
700  * Arguments:   @class_node *c@ = pointer to a class node (may be null)
701  *              @uid_t u@ = user id number
702  *
703  * Returns:     Pointer to the combined node.
704  *
705  * Use:         Adds a user to a node, maybe hashifying it.
706  */
707
708 class_node *class_addUser(class_node *c, uid_t u)
709 {
710   if (!c)
711     return (class_fromUser(clType_user, u));
712   if ((c->type & clNode_mask) == clNode_immed)
713     c = class__hashify(c);
714   sym_find(&c->v.t, (char *)&u, sizeof(u), sizeof(sym_base), 0);
715   return (c);
716 }
717
718 /* --- @class_addString@ --- *
719  *
720  * Arguments:   @class_node *c@ = pointer to a class node (may be null)
721  *              @const char *s@ = pointer to a string
722  *
723  * Returns:     Pointer to the combined node.
724  *
725  * Use:         Adds a user to a node, maybe hashifying it.
726  */
727
728 class_node *class_addString(class_node *c, const char *s)
729 {
730   class_node *n = class_fromString(clType_host, s); /* hack */
731   if (c)
732     return (class_union(c, n));
733   else
734     return (n);
735 }
736
737 /*----- Matching functions ------------------------------------------------*/
738
739 /* --- @class_matchUser@ --- *
740  *
741  * Arguments:   @class_node *c@ = pointer to root class node
742  *              @uid_t u@ = user id number
743  *
744  * Returns:     Nonzero if it matches, zero if it doesn't.
745  *
746  * Use:         Determines whether a user is matched by a class.  Assumes
747  *              that the types are correct.
748  */
749
750 int class_matchUser(class_node *c, uid_t u)
751 {
752   class_node *cc;
753
754   for (cc = 0; c; c = cc, cc = 0) {
755     if (c == class_none)
756       return (0);
757     if (c == class_all)
758       return (1);
759     switch (c->type & clNode_mask) {
760       case clNode_immed:
761         return (u == c->v.u);
762         break;
763       case clNode_hash:
764         return (sym_find(&c->v.t, (char *)&u, sizeof(u), 0, 0) != 0);
765         break;
766       case clNode_union:
767         if (class_matchUser(c->v.c.l, u))
768           return (1);
769         cc = c->v.c.r;
770         break;
771       case clNode_isect:
772         if (!class_matchUser(c->v.c.l, u))
773           return (0);
774         cc = c->v.c.r;
775         break;
776       case clNode_diff:
777         return (class_matchUser(c->v.c.l, u) &&
778                 !class_matchUser(c->v.c.r, u));
779         break;
780     }
781   }
782
783   die(1, "internal: can't get here in class_matchUser");
784   return (0);
785 }
786
787 /* --- @class_matchCommand@ --- *
788  *
789  * Arguments:   @class_node *c@ = pointer to root class node
790  *              @const char *s@ = pointer to a string
791  *
792  * Returns:     Nonzero if it matches, zero if it doesn't.
793  *
794  * Use:         Determines whether a string is matched by a class.  Assumes
795  *              that the types are correct.
796  */
797
798 int class_matchCommand(class_node *c, const char *s)
799 {
800   class_node *cc;
801
802   for (cc = 0; c; c = cc, cc = 0) {
803     if (c == class_none)
804       return (0);
805     if (c == class_all)
806       return (1);
807     switch (c->type & clNode_mask) {
808       case clNode_immed:
809         return (class__wildMatch(c->v.s, s));
810         break;
811       case clNode_hash:
812         return (sym_find(&c->v.t, s, -1, 0, 0) != 0);
813         break;
814       case clNode_union:
815         if (class_matchCommand(c->v.c.l, s))
816           return (1);
817         cc = c->v.c.r;
818         break;
819       case clNode_isect:
820         if (!class_matchCommand(c->v.c.l, s))
821           return (0);
822         cc = c->v.c.r;
823         break;
824       case clNode_diff:
825         return (class_matchCommand(c->v.c.l, s) &&
826                 !class_matchCommand(c->v.c.r, s));
827         break;
828     }
829   }
830
831   die(1, "internal: can't get here in class_matchCommand");
832   return (0);
833 }
834
835 /* --- @class_matchHost@ --- *
836  *
837  * Arguments:   @class_node *c@ = pointer to root class node
838  *              @struct in_addr a@ = IP address to match
839  *
840  * Returns:     Nonzero if it matches, zero if it doesn't.
841  *
842  * Use:         Determines whether a host matches a host class.  Assumes
843  *              that the types are correct.  The actual mechanism is a bit
844  *              odd here, but I think this is the Right Thing.  At each stage
845  *              I try to match %%@/all/%% of the possible names for the host.
846  *              Thus host `splat' with address 1.2.3.4 would fail to match
847  *              the class "1.2.*" - "splat".  This seems to be what the
848  *              author intuitively expects.  It's just a bit weird.
849  */
850
851 static int class__doMatchHost(class_node *c, const char *ip,
852                               const char *name, char **aliases)
853 {
854   class_node *cc;
855
856   for (cc = 0; c; c = cc, cc = 0) {
857     if (c == class_none)
858       return (0);
859     if (c == class_all)
860       return (1);
861     switch (c->type & clNode_mask) {
862       case clNode_immed:
863         if ((ip && class__wildMatch(c->v.s, ip)) ||
864             (name && class__wildMatch(c->v.s, name)))
865           return (1);
866         if (aliases) for (; *aliases; aliases++) {
867           if (class__wildMatch(c->v.s, *aliases))
868             return (1);
869         }
870         return (0);
871         break;
872       case clNode_hash:
873         if ((ip && sym_find(&c->v.t, ip, -1, 0, 0)) ||
874             (name && sym_find(&c->v.t, name, -1, 0, 0)))
875           return (1);
876         if (aliases) for (; *aliases; aliases++) {
877           if (sym_find(&c->v.t, *aliases, -1, 0, 0))
878             return (1);
879         }
880         return (0);
881         break;
882       case clNode_union:
883         if (class__doMatchHost(c->v.c.l, ip, name, aliases))
884           return (1);
885         cc = c->v.c.r;
886         break;
887       case clNode_isect:
888         if (!class__doMatchHost(c->v.c.l, ip, name, aliases))
889           return (0);
890         cc = c->v.c.r;
891         break;
892       case clNode_diff:
893         return (class__doMatchHost(c->v.c.l, ip, name, aliases) &&
894                 !class__doMatchHost(c->v.c.r, ip, name, aliases));
895         break;
896     }
897   }
898
899   die(1, "internal: can't get here in class_matchUser");
900   return (0);
901 }  
902
903 int class_matchHost(class_node *c, struct in_addr a)
904 {
905   char *ip, *name, **aliases;
906   struct hostent *h;
907
908   ip = inet_ntoa(a);
909   if ((h = gethostbyaddr((char *)&a, sizeof(a), AF_INET)) != 0) {
910     name = h->h_name;
911     aliases = h->h_aliases;
912   } else {
913     name = 0;
914     aliases = 0;
915   }
916
917   return (class__doMatchHost(c, ip, name, aliases));
918 }
919
920 /*----- Debugging code ----------------------------------------------------*/
921
922 /* --- @class_dump@ --- *
923  *
924  * Argumemnts:  @class_node *c@ = pointer to root node
925  *              @int indent@ = indent depth
926  *
927  * Returns:     ---
928  *
929  * Use:         Dumps a class to the trace output.
930  */
931
932 void class_dump(class_node *c, int indent)
933 {
934 #ifdef TRACING
935
936   static char *types[] = {
937     "<duff>",
938     "user",
939     "command",
940     "<duff>",
941     "host"
942   };
943
944   static char *nodes[] = {
945     "<duff>",
946     "<magical>",
947     "immediate",
948     "hash",
949     "binop: union",
950     "binop: difference",
951     "binop: intersection"
952   };
953
954   /* --- Handle some magical cases --- */
955
956   if (c == class_all) {
957     trace(TRACE_RULE, "rule:%*s class ALL", indent * 2, "");
958     return;
959   }
960   if (c == class_none) {
961     trace(TRACE_RULE, "rule:%*s class NONE", indent * 2, "");
962     return;
963   }
964
965   /* --- Dump basic type information --- */
966
967   trace(TRACE_RULE, "rule:%*s type == [%s], node type == [%s]%s",
968         indent * 2, "",
969         types[c->type & clType_mask],
970         nodes[(c->type & clNode_mask) >> 4],
971         (c->type & clFlag_friendly) ? " Friendly" : "");
972
973   /* --- Now trace the contents --- */
974
975   switch (c->type & clNode_mask) {
976     case clNode_immed:
977       if (c->type & clType_user) {
978         trace(TRACE_RULE, "rule:%*s   user %lu",
979               indent * 2, "", (unsigned long)c->v.u);
980       } else
981         trace(TRACE_RULE, "rule:%*s   `%s'", indent * 2, "", c->v.s);
982       break;
983     case clNode_hash: {
984       sym_iter i;
985       sym_base *b;
986
987       for (sym_mkiter(&i, &c->v.t); (b = sym_next(&i)) != 0; ) {
988         if (c->type & clType_user) {
989           trace(TRACE_RULE, "rule:%*s   user %lu",
990                 indent * 2, "", (unsigned long)*(uid_t *)b->name);
991         } else
992           trace(TRACE_RULE, "rule:%*s   `%s'", indent * 2, "", b->name);
993       }
994     } break;
995     case clNode_union:
996     case clNode_diff:
997     case clNode_isect:
998       class_dump(c->v.c.l, indent + 1);
999       class_dump(c->v.c.r, indent + 1);
1000       break;
1001   }
1002
1003 #endif
1004 }
1005
1006 /*----- That's all, folks -------------------------------------------------*/