c4f2d992 |
1 | /* -*-c-*- |
2 | * |
50a87c1f |
3 | * $Id: class.c,v 1.6 1997/09/17 10:14:56 mdw Exp $ |
c4f2d992 |
4 | * |
5 | * Handling classes of things nicely |
6 | * |
7 | * (c) 1997 EBI |
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 | |
29 | /*----- Revision history --------------------------------------------------* |
30 | * |
31 | * $Log: class.c,v $ |
50a87c1f |
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 |
51cf22e0 |
37 | * Patch memory leak. Don't try to trace when tracing's turned off. |
38 | * |
4f67acfa |
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. |
607937a4 |
42 | * |
03f996bd |
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 |
c4f2d992 |
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 | |
03f996bd |
72 | #include "become.h" |
c4f2d992 |
73 | #include "class.h" |
c4f2d992 |
74 | #include "sym.h" |
75 | #include "utils.h" |
76 | |
77 | /*----- Global variables --------------------------------------------------*/ |
78 | |
50a87c1f |
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; |
c4f2d992 |
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); |
50a87c1f |
110 | return (pat[1] == 0); |
c4f2d992 |
111 | } else if (*pat == '?' || *pat == *p) |
112 | p++, pat++; |
113 | else |
114 | return (0); |
115 | } |
116 | } |
117 | |
50a87c1f |
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 | } |
c4f2d992 |
143 | |
50a87c1f |
144 | /* --- @class_fromUser@ --- * |
c4f2d992 |
145 | * |
50a87c1f |
146 | * Arguments: @unsigned type@ = a type field |
147 | * @uid_t u@ = a user-id number |
c4f2d992 |
148 | * |
50a87c1f |
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@. |
c4f2d992 |
152 | * |
50a87c1f |
153 | * Use: Given a uid, wrap a class node around it. |
c4f2d992 |
154 | */ |
155 | |
50a87c1f |
156 | class_node *class_fromUser(unsigned type, uid_t u) |
c4f2d992 |
157 | { |
50a87c1f |
158 | class_node *c = xmalloc(sizeof(*c)); |
159 | c->type = type | clNode_immed | clFlag_friendly; |
c4f2d992 |
160 | c->ref = 1; |
50a87c1f |
161 | c->v.u = u; |
c4f2d992 |
162 | return (c); |
163 | } |
164 | |
50a87c1f |
165 | /*----- Reference counter tricks ------------------------------------------*/ |
166 | |
c4f2d992 |
167 | /* --- @class_inc@ --- * |
168 | * |
50a87c1f |
169 | * Arguments: @class_node *c@ = pointer to a class block |
c4f2d992 |
170 | * |
171 | * Returns: --- |
172 | * |
173 | * Use: Adds a reference to the class definition. |
174 | */ |
175 | |
50a87c1f |
176 | void class_inc(class_node *c) |
c4f2d992 |
177 | { |
50a87c1f |
178 | if (c != class_all && c != class_none) |
c4f2d992 |
179 | c->ref++; |
180 | } |
181 | |
182 | /* --- @class_dec@ --- * |
183 | * |
50a87c1f |
184 | * Arguments: @class_node *c@ = pointer to a class block |
c4f2d992 |
185 | * |
186 | * Returns: --- |
187 | * |
188 | * Use: Removes a reference to a class block. |
189 | */ |
190 | |
50a87c1f |
191 | void class_dec(class_node *c) |
c4f2d992 |
192 | { |
50a87c1f |
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 | } |
c4f2d992 |
216 | free(c); |
217 | } |
218 | } |
219 | |
50a87c1f |
220 | /* --- @class_mod@ --- * |
c4f2d992 |
221 | * |
50a87c1f |
222 | * Arguments: @class_node *c@ = pointer to a class node |
c4f2d992 |
223 | * |
50a87c1f |
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. |
c4f2d992 |
226 | * |
50a87c1f |
227 | * Use: Gives you a node which you can modify. Don't call this |
228 | * for @class_all@ or @class_none@. |
c4f2d992 |
229 | */ |
230 | |
50a87c1f |
231 | class_node *class_mod(class_node *c) |
c4f2d992 |
232 | { |
50a87c1f |
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; |
03f996bd |
270 | } |
50a87c1f |
271 | |
272 | class_dec(c); |
273 | return (cc); |
c4f2d992 |
274 | } |
275 | |
50a87c1f |
276 | /*----- Some weirder operations on classes --------------------------------*/ |
277 | |
278 | /* --- @class__hashify@ --- * |
c4f2d992 |
279 | * |
50a87c1f |
280 | * Arguments: @class_node *c@ = pointer to a node |
c4f2d992 |
281 | * |
50a87c1f |
282 | * Returns: A pointer to a hash node containing the node's value. |
c4f2d992 |
283 | * |
50a87c1f |
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. |
c4f2d992 |
287 | */ |
288 | |
50a87c1f |
289 | static class_node *class__hashify(class_node *c) |
c4f2d992 |
290 | { |
50a87c1f |
291 | /* --- Some sanity checking --- */ |
c4f2d992 |
292 | |
50a87c1f |
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); |
03f996bd |
308 | } else { |
50a87c1f |
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); |
c4f2d992 |
313 | } |
50a87c1f |
314 | |
315 | return (c); |
c4f2d992 |
316 | } |
317 | |
50a87c1f |
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 |
c4f2d992 |
325 | * |
50a87c1f |
326 | * Returns: A class node representing the result of a binary operation |
327 | * upon two classes. |
c4f2d992 |
328 | * |
50a87c1f |
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. |
c4f2d992 |
334 | * |
50a87c1f |
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. |
c4f2d992 |
347 | */ |
348 | |
50a87c1f |
349 | class_node *class__binop(class_node *l, class_node *r, int op) |
c4f2d992 |
350 | { |
50a87c1f |
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 --- */ |
c4f2d992 |
355 | |
50a87c1f |
356 | if (!type) { |
357 | class_dec(l); |
358 | class_dec(r); |
c4f2d992 |
359 | return (0); |
50a87c1f |
360 | } |
c4f2d992 |
361 | |
50a87c1f |
362 | /* --- Handle `friendly' nodes --- */ |
c4f2d992 |
363 | |
50a87c1f |
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; |
03f996bd |
424 | } |
607937a4 |
425 | |
50a87c1f |
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 | */ |
c4f2d992 |
431 | |
50a87c1f |
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 --- * |
c4f2d992 |
518 | * |
50a87c1f |
519 | * It may have lost a load of elements. Maybe it can be represented |
520 | * better as an immediate or even as @class_none@. |
c4f2d992 |
521 | */ |
522 | |
50a87c1f |
523 | { |
524 | sym_iter i; |
525 | sym_base *b; |
c4f2d992 |
526 | |
50a87c1f |
527 | class_dec(r); |
c4f2d992 |
528 | |
50a87c1f |
529 | sym_createIter(&i, &l->v.t); |
530 | if ((b = sym_next(&i)) == 0) { |
531 | class_dec(l); |
532 | return (class_none); |
03f996bd |
533 | } |
50a87c1f |
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); |
c4f2d992 |
605 | |
50a87c1f |
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 | } |
c4f2d992 |
635 | |
50a87c1f |
636 | if (r == class_none) |
637 | return (l); |
c4f2d992 |
638 | |
50a87c1f |
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)) |
c4f2d992 |
751 | return (1); |
50a87c1f |
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; |
607937a4 |
784 | |
50a87c1f |
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; |
607937a4 |
838 | |
50a87c1f |
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)) |
607937a4 |
851 | return (1); |
607937a4 |
852 | } |
50a87c1f |
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; |
c4f2d992 |
879 | } |
50a87c1f |
880 | } |
03f996bd |
881 | |
50a87c1f |
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; |
c4f2d992 |
898 | } |
50a87c1f |
899 | |
900 | return (class__doMatchHost(c, ip, name, aliases)); |
c4f2d992 |
901 | } |
902 | |
50a87c1f |
903 | /*----- Debugging code ----------------------------------------------------*/ |
904 | |
c4f2d992 |
905 | /* --- @class_dump@ --- * |
906 | * |
50a87c1f |
907 | * Argumemnts: @class_node *c@ = pointer to root node |
908 | * @int indent@ = indent depth |
c4f2d992 |
909 | * |
910 | * Returns: --- |
911 | * |
50a87c1f |
912 | * Use: Dumps a class to the trace output. |
c4f2d992 |
913 | */ |
914 | |
50a87c1f |
915 | void class_dump(class_node *c, int indent) |
c4f2d992 |
916 | { |
51cf22e0 |
917 | #ifdef TRACING |
50a87c1f |
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); |
c4f2d992 |
976 | } |
50a87c1f |
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; |
c4f2d992 |
984 | } |
50a87c1f |
985 | |
51cf22e0 |
986 | #endif |
c4f2d992 |
987 | } |
988 | |
989 | /*----- That's all, folks -------------------------------------------------*/ |