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