src/become.c: Reintroduce missing newline in usage message.
[become] / src / class.c
CommitLineData
c4f2d992 1/* -*-c-*-
2 *
af4f4d6a 3 * $Id: class.c,v 1.10 2004/04/08 01:36:20 mdw Exp $
c4f2d992 4 *
5 * Handling classes of things nicely
6 *
c758e654 7 * (c) 1998 EBI
c4f2d992 8 */
9
03f996bd 10/*----- Licensing notice --------------------------------------------------*
c4f2d992 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
03f996bd 25 * along with `become'; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
c4f2d992 27 */
28
c4f2d992 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
f60a3434 48/* --- mLib headers --- */
49
50#include <mLib/alloc.h>
51#include <mLib/report.h>
52#include <mLib/sym.h>
53
c4f2d992 54/* --- Local headers --- */
55
03f996bd 56#include "become.h"
c4f2d992 57#include "class.h"
c4f2d992 58
59/*----- Global variables --------------------------------------------------*/
60
50a87c1f 61static class_node class__all = { clType_all | clNode_any, -1, { 0 }};
62class_node *class_all = &class__all;
63
64static class_node class__none = { clType_all | clNode_any, -1, { 0 }};
65class_node *class_none = &class__none;
c4f2d992 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
79static 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++;
dd9ab078 87 if (!*pat)
88 return (58);
c4f2d992 89 do {
90 if (class__wildMatch(pat, p))
91 return (27); /* Nyahaha */
92 p++;
93 } while (*p);
dd9ab078 94 return (0);
c4f2d992 95 } else if (*pat == '?' || *pat == *p)
96 p++, pat++;
97 else
98 return (0);
99 }
100}
101
50a87c1f 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
117class_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}
c4f2d992 127
50a87c1f 128/* --- @class_fromUser@ --- *
c4f2d992 129 *
50a87c1f 130 * Arguments: @unsigned type@ = a type field
131 * @uid_t u@ = a user-id number
c4f2d992 132 *
50a87c1f 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@.
c4f2d992 136 *
50a87c1f 137 * Use: Given a uid, wrap a class node around it.
c4f2d992 138 */
139
50a87c1f 140class_node *class_fromUser(unsigned type, uid_t u)
c4f2d992 141{
50a87c1f 142 class_node *c = xmalloc(sizeof(*c));
143 c->type = type | clNode_immed | clFlag_friendly;
c4f2d992 144 c->ref = 1;
50a87c1f 145 c->v.u = u;
c4f2d992 146 return (c);
147}
148
50a87c1f 149/*----- Reference counter tricks ------------------------------------------*/
150
c4f2d992 151/* --- @class_inc@ --- *
152 *
50a87c1f 153 * Arguments: @class_node *c@ = pointer to a class block
c4f2d992 154 *
155 * Returns: ---
156 *
157 * Use: Adds a reference to the class definition.
158 */
159
50a87c1f 160void class_inc(class_node *c)
c4f2d992 161{
50a87c1f 162 if (c != class_all && c != class_none)
c4f2d992 163 c->ref++;
164}
165
166/* --- @class_dec@ --- *
167 *
50a87c1f 168 * Arguments: @class_node *c@ = pointer to a class block
c4f2d992 169 *
170 * Returns: ---
171 *
172 * Use: Removes a reference to a class block.
173 */
174
50a87c1f 175void class_dec(class_node *c)
c4f2d992 176{
50a87c1f 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:
f60a3434 191 sym_destroy(&c->v.t);
50a87c1f 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 }
c4f2d992 200 free(c);
201 }
202}
203
50a87c1f 204/* --- @class_mod@ --- *
c4f2d992 205 *
50a87c1f 206 * Arguments: @class_node *c@ = pointer to a class node
c4f2d992 207 *
50a87c1f 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.
c4f2d992 210 *
50a87c1f 211 * Use: Gives you a node which you can modify. Don't call this
212 * for @class_all@ or @class_none@.
c4f2d992 213 */
214
50a87c1f 215class_node *class_mod(class_node *c)
c4f2d992 216{
50a87c1f 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:
f60a3434 227 die(1, "internal: class_mod called on non-modifiable class node");
50a87c1f 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
f60a3434 241 sym_create(&cc->v.t);
242 for (sym_mkiter(&i, &c->v.t); (b = sym_next(&i)) != 0; )
50a87c1f 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;
03f996bd 254 }
50a87c1f 255
256 class_dec(c);
257 return (cc);
c4f2d992 258}
259
50a87c1f 260/*----- Some weirder operations on classes --------------------------------*/
261
262/* --- @class__hashify@ --- *
c4f2d992 263 *
50a87c1f 264 * Arguments: @class_node *c@ = pointer to a node
c4f2d992 265 *
50a87c1f 266 * Returns: A pointer to a hash node containing the node's value.
c4f2d992 267 *
50a87c1f 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.
c4f2d992 271 */
272
50a87c1f 273static class_node *class__hashify(class_node *c)
c4f2d992 274{
50a87c1f 275 /* --- Some sanity checking --- */
c4f2d992 276
50a87c1f 277 if (~c->type & clFlag_friendly)
f60a3434 278 die(1, "internal: class__hashify can't hashify unfriendly nodes");
50a87c1f 279 if ((c->type & clNode_mask) != clNode_immed)
f60a3434 280 die(1, "internal: class__hashify can't hashify non-immediate nodes");
50a87c1f 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;
f60a3434 290 sym_create(&c->v.t);
50a87c1f 291 sym_find(&c->v.t, (char *)&u, sizeof(u), sizeof(sym_base), 0);
03f996bd 292 } else {
50a87c1f 293 char *s = c->v.s;
f60a3434 294 sym_create(&c->v.t);
50a87c1f 295 sym_find(&c->v.t, s, -1, sizeof(sym_base), 0);
296 free(s);
c4f2d992 297 }
50a87c1f 298
299 return (c);
c4f2d992 300}
301
50a87c1f 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
c4f2d992 309 *
50a87c1f 310 * Returns: A class node representing the result of a binary operation
311 * upon two classes.
c4f2d992 312 *
50a87c1f 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.
c4f2d992 318 *
50a87c1f 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.
c4f2d992 331 */
332
50a87c1f 333class_node *class__binop(class_node *l, class_node *r, int op)
c4f2d992 334{
50a87c1f 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 --- */
c4f2d992 339
50a87c1f 340 if (!type) {
341 class_dec(l);
342 class_dec(r);
c4f2d992 343 return (0);
50a87c1f 344 }
c4f2d992 345
50a87c1f 346 /* --- Handle `friendly' nodes --- */
c4f2d992 347
50a87c1f 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;
03f996bd 408 }
607937a4 409
50a87c1f 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 */
c4f2d992 415
50a87c1f 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
f60a3434 443 for (sym_mkiter(&i, &r->v.t); (b = sym_next(&i)) != 0; )
50a87c1f 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
f60a3434 464 for (sym_mkiter(&i, &r->v.t); (b = sym_next(&i)) != 0; ) {
50a87c1f 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
f60a3434 493 for (sym_mkiter(&i, &l->v.t); (b = sym_next(&i)) != 0; ) {
50a87c1f 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 --- *
c4f2d992 502 *
50a87c1f 503 * It may have lost a load of elements. Maybe it can be represented
504 * better as an immediate or even as @class_none@.
c4f2d992 505 */
506
50a87c1f 507 {
508 sym_iter i;
509 sym_base *b;
c4f2d992 510
50a87c1f 511 class_dec(r);
c4f2d992 512
f60a3434 513 sym_mkiter(&i, &l->v.t);
50a87c1f 514 if ((b = sym_next(&i)) == 0) {
515 class_dec(l);
516 return (class_none);
03f996bd 517 }
50a87c1f 518 if (!sym_next(&i)) {
519 if (type & clType_user) {
520 uid_t u = *(uid_t *)b->name;
f60a3434 521 sym_destroy(&l->v.t);
50a87c1f 522 l->type = (l->type & ~clNode_mask) | clNode_immed;
523 l->v.u = u;
524 } else {
525 char *s = xstrdup(b->name);
f60a3434 526 sym_destroy(&l->v.t);
50a87c1f 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
575class_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);
c4f2d992 589
50a87c1f 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
610class_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 }
c4f2d992 619
50a87c1f 620 if (r == class_none)
621 return (l);
c4f2d992 622
50a87c1f 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
643class_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
675class_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
695class_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
717int 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))
c4f2d992 735 return (1);
50a87c1f 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
f60a3434 750 die(1, "internal: can't get here in class_matchUser");
50a87c1f 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
765int class_matchCommand(class_node *c, const char *s)
766{
767 class_node *cc;
607937a4 768
50a87c1f 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
f60a3434 798 die(1, "internal: can't get here in class_matchCommand");
50a87c1f 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
818static int class__doMatchHost(class_node *c, const char *ip,
819 const char *name, char **aliases)
820{
821 class_node *cc;
607937a4 822
50a87c1f 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))
607937a4 835 return (1);
607937a4 836 }
50a87c1f 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;
c4f2d992 863 }
50a87c1f 864 }
03f996bd 865
f60a3434 866 die(1, "internal: can't get here in class_matchUser");
50a87c1f 867 return (0);
868}
869
870int 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;
c4f2d992 882 }
50a87c1f 883
884 return (class__doMatchHost(c, ip, name, aliases));
c4f2d992 885}
886
50a87c1f 887/*----- Debugging code ----------------------------------------------------*/
888
c4f2d992 889/* --- @class_dump@ --- *
890 *
50a87c1f 891 * Argumemnts: @class_node *c@ = pointer to root node
892 * @int indent@ = indent depth
c4f2d992 893 *
894 * Returns: ---
895 *
50a87c1f 896 * Use: Dumps a class to the trace output.
c4f2d992 897 */
898
50a87c1f 899void class_dump(class_node *c, int indent)
c4f2d992 900{
51cf22e0 901#ifdef TRACING
50a87c1f 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
f60a3434 954 for (sym_mkiter(&i, &c->v.t); (b = sym_next(&i)) != 0; ) {
50a87c1f 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);
c4f2d992 960 }
50a87c1f 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;
c4f2d992 968 }
50a87c1f 969
51cf22e0 970#endif
c4f2d992 971}
972
973/*----- That's all, folks -------------------------------------------------*/