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