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