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