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