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