c4f2d992 |
1 | /* -*-c-*- |
2 | * |
c758e654 |
3 | * $Id: class.c,v 1.7 1998/01/12 16:45:50 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 | |
29 | /*----- Revision history --------------------------------------------------* |
30 | * |
31 | * $Log: class.c,v $ |
c758e654 |
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 |
50a87c1f |
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 |
51cf22e0 |
40 | * Patch memory leak. Don't try to trace when tracing's turned off. |
41 | * |
4f67acfa |
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. |
607937a4 |
45 | * |
03f996bd |
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 |
c4f2d992 |
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 | |
03f996bd |
75 | #include "become.h" |
c4f2d992 |
76 | #include "class.h" |
c4f2d992 |
77 | #include "sym.h" |
78 | #include "utils.h" |
79 | |
80 | /*----- Global variables --------------------------------------------------*/ |
81 | |
50a87c1f |
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; |
c4f2d992 |
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); |
50a87c1f |
113 | return (pat[1] == 0); |
c4f2d992 |
114 | } else if (*pat == '?' || *pat == *p) |
115 | p++, pat++; |
116 | else |
117 | return (0); |
118 | } |
119 | } |
120 | |
50a87c1f |
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 | } |
c4f2d992 |
146 | |
50a87c1f |
147 | /* --- @class_fromUser@ --- * |
c4f2d992 |
148 | * |
50a87c1f |
149 | * Arguments: @unsigned type@ = a type field |
150 | * @uid_t u@ = a user-id number |
c4f2d992 |
151 | * |
50a87c1f |
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@. |
c4f2d992 |
155 | * |
50a87c1f |
156 | * Use: Given a uid, wrap a class node around it. |
c4f2d992 |
157 | */ |
158 | |
50a87c1f |
159 | class_node *class_fromUser(unsigned type, uid_t u) |
c4f2d992 |
160 | { |
50a87c1f |
161 | class_node *c = xmalloc(sizeof(*c)); |
162 | c->type = type | clNode_immed | clFlag_friendly; |
c4f2d992 |
163 | c->ref = 1; |
50a87c1f |
164 | c->v.u = u; |
c4f2d992 |
165 | return (c); |
166 | } |
167 | |
50a87c1f |
168 | /*----- Reference counter tricks ------------------------------------------*/ |
169 | |
c4f2d992 |
170 | /* --- @class_inc@ --- * |
171 | * |
50a87c1f |
172 | * Arguments: @class_node *c@ = pointer to a class block |
c4f2d992 |
173 | * |
174 | * Returns: --- |
175 | * |
176 | * Use: Adds a reference to the class definition. |
177 | */ |
178 | |
50a87c1f |
179 | void class_inc(class_node *c) |
c4f2d992 |
180 | { |
50a87c1f |
181 | if (c != class_all && c != class_none) |
c4f2d992 |
182 | c->ref++; |
183 | } |
184 | |
185 | /* --- @class_dec@ --- * |
186 | * |
50a87c1f |
187 | * Arguments: @class_node *c@ = pointer to a class block |
c4f2d992 |
188 | * |
189 | * Returns: --- |
190 | * |
191 | * Use: Removes a reference to a class block. |
192 | */ |
193 | |
50a87c1f |
194 | void class_dec(class_node *c) |
c4f2d992 |
195 | { |
50a87c1f |
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 | } |
c4f2d992 |
219 | free(c); |
220 | } |
221 | } |
222 | |
50a87c1f |
223 | /* --- @class_mod@ --- * |
c4f2d992 |
224 | * |
50a87c1f |
225 | * Arguments: @class_node *c@ = pointer to a class node |
c4f2d992 |
226 | * |
50a87c1f |
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. |
c4f2d992 |
229 | * |
50a87c1f |
230 | * Use: Gives you a node which you can modify. Don't call this |
231 | * for @class_all@ or @class_none@. |
c4f2d992 |
232 | */ |
233 | |
50a87c1f |
234 | class_node *class_mod(class_node *c) |
c4f2d992 |
235 | { |
50a87c1f |
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; |
03f996bd |
273 | } |
50a87c1f |
274 | |
275 | class_dec(c); |
276 | return (cc); |
c4f2d992 |
277 | } |
278 | |
50a87c1f |
279 | /*----- Some weirder operations on classes --------------------------------*/ |
280 | |
281 | /* --- @class__hashify@ --- * |
c4f2d992 |
282 | * |
50a87c1f |
283 | * Arguments: @class_node *c@ = pointer to a node |
c4f2d992 |
284 | * |
50a87c1f |
285 | * Returns: A pointer to a hash node containing the node's value. |
c4f2d992 |
286 | * |
50a87c1f |
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. |
c4f2d992 |
290 | */ |
291 | |
50a87c1f |
292 | static class_node *class__hashify(class_node *c) |
c4f2d992 |
293 | { |
50a87c1f |
294 | /* --- Some sanity checking --- */ |
c4f2d992 |
295 | |
50a87c1f |
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); |
03f996bd |
311 | } else { |
50a87c1f |
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); |
c4f2d992 |
316 | } |
50a87c1f |
317 | |
318 | return (c); |
c4f2d992 |
319 | } |
320 | |
50a87c1f |
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 |
c4f2d992 |
328 | * |
50a87c1f |
329 | * Returns: A class node representing the result of a binary operation |
330 | * upon two classes. |
c4f2d992 |
331 | * |
50a87c1f |
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. |
c4f2d992 |
337 | * |
50a87c1f |
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. |
c4f2d992 |
350 | */ |
351 | |
50a87c1f |
352 | class_node *class__binop(class_node *l, class_node *r, int op) |
c4f2d992 |
353 | { |
50a87c1f |
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 --- */ |
c4f2d992 |
358 | |
50a87c1f |
359 | if (!type) { |
360 | class_dec(l); |
361 | class_dec(r); |
c4f2d992 |
362 | return (0); |
50a87c1f |
363 | } |
c4f2d992 |
364 | |
50a87c1f |
365 | /* --- Handle `friendly' nodes --- */ |
c4f2d992 |
366 | |
50a87c1f |
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; |
03f996bd |
427 | } |
607937a4 |
428 | |
50a87c1f |
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 | */ |
c4f2d992 |
434 | |
50a87c1f |
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 --- * |
c4f2d992 |
521 | * |
50a87c1f |
522 | * It may have lost a load of elements. Maybe it can be represented |
523 | * better as an immediate or even as @class_none@. |
c4f2d992 |
524 | */ |
525 | |
50a87c1f |
526 | { |
527 | sym_iter i; |
528 | sym_base *b; |
c4f2d992 |
529 | |
50a87c1f |
530 | class_dec(r); |
c4f2d992 |
531 | |
50a87c1f |
532 | sym_createIter(&i, &l->v.t); |
533 | if ((b = sym_next(&i)) == 0) { |
534 | class_dec(l); |
535 | return (class_none); |
03f996bd |
536 | } |
50a87c1f |
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); |
c4f2d992 |
608 | |
50a87c1f |
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 | } |
c4f2d992 |
638 | |
50a87c1f |
639 | if (r == class_none) |
640 | return (l); |
c4f2d992 |
641 | |
50a87c1f |
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)) |
c4f2d992 |
754 | return (1); |
50a87c1f |
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; |
607937a4 |
787 | |
50a87c1f |
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; |
607937a4 |
841 | |
50a87c1f |
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)) |
607937a4 |
854 | return (1); |
607937a4 |
855 | } |
50a87c1f |
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; |
c4f2d992 |
882 | } |
50a87c1f |
883 | } |
03f996bd |
884 | |
50a87c1f |
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; |
c4f2d992 |
901 | } |
50a87c1f |
902 | |
903 | return (class__doMatchHost(c, ip, name, aliases)); |
c4f2d992 |
904 | } |
905 | |
50a87c1f |
906 | /*----- Debugging code ----------------------------------------------------*/ |
907 | |
c4f2d992 |
908 | /* --- @class_dump@ --- * |
909 | * |
50a87c1f |
910 | * Argumemnts: @class_node *c@ = pointer to root node |
911 | * @int indent@ = indent depth |
c4f2d992 |
912 | * |
913 | * Returns: --- |
914 | * |
50a87c1f |
915 | * Use: Dumps a class to the trace output. |
c4f2d992 |
916 | */ |
917 | |
50a87c1f |
918 | void class_dump(class_node *c, int indent) |
c4f2d992 |
919 | { |
51cf22e0 |
920 | #ifdef TRACING |
50a87c1f |
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); |
c4f2d992 |
979 | } |
50a87c1f |
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; |
c4f2d992 |
987 | } |
50a87c1f |
988 | |
51cf22e0 |
989 | #endif |
c4f2d992 |
990 | } |
991 | |
992 | /*----- That's all, folks -------------------------------------------------*/ |