xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/scache.c (revision e89934bbf778a6d6d6894877c4da59d0c7835b0f)
1 /*	$NetBSD: scache.c,v 1.2 2017/02/14 01:16:45 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	scache 3
6 /* SUMMARY
7 /*	generic session cache API
8 /* SYNOPSIS
9 /*	#include <scache.h>
10 /* DESCRIPTION
11 /*	typedef struct {
12 /* .in +4
13 /*		int	dest_count;
14 /*		int	endp_count;
15 /*		int	sess_count;
16 /* .in -4
17 /*	} SCACHE_SIZE;
18 /*
19 /*	unsigned scache_size(scache, size)
20 /*	SCACHE	*scache;
21 /*	SCACHE_SIZE *size;
22 /*
23 /*	void	scache_free(scache)
24 /*	SCACHE	*scache;
25 /*
26 /*	void    scache_save_endp(scache, endp_ttl, endp_label, endp_prop, fd)
27 /*	SCACHE	*scache;
28 /*	int	endp_ttl;
29 /*	const char *endp_label;
30 /*	const char *endp_prop;
31 /*	int	fd;
32 /*
33 /*	int     scache_find_endp(scache, endp_label, endp_prop)
34 /*	SCACHE	*scache;
35 /*	const char *endp_label;
36 /*	VSTRING	*endp_prop;
37 /*
38 /*	void	scache_save_dest(scache, dest_ttl, dest_label,
39 /*				dest_prop, endp_label)
40 /*	SCACHE	*scache;
41 /*	int	dest_ttl;
42 /*	const char *dest_label;
43 /*	const char *dest_prop;
44 /*	const char *endp_label;
45 /*
46 /*	int	scache_find_dest(dest_label, dest_prop, endp_prop)
47 /*	SCACHE	*scache;
48 /*	const char *dest_label;
49 /*	VSTRING	*dest_prop;
50 /*	VSTRING	*endp_prop;
51 /* DESCRIPTION
52 /*	This module implements a generic session cache interface.
53 /*	Specific cache types are described in scache_single(3),
54 /*	scache_clnt(3) and scache_multi(3). These documents also
55 /*	describe now to instantiate a specific session cache type.
56 /*
57 /*	The code maintains two types of association: a) physical
58 /*	endpoint to file descriptor, and b) logical endpoint
59 /*	to physical endpoint. Physical endpoints are stored and
60 /*	looked up under their low-level session details such as
61 /*	numerical addresses, while logical endpoints are stored
62 /*	and looked up by the domain name that humans use. One logical
63 /*	endpoint can refer to multiple physical endpoints, one
64 /*	physical endpoint may be referenced by multiple logical
65 /*	endpoints, and one physical endpoint may have multiple
66 /*	sessions.
67 /*
68 /*	scache_size() returns the number of logical destination
69 /*	names, physical endpoint addresses, and cached sessions.
70 /*
71 /*	scache_free() destroys the specified session cache.
72 /*
73 /*	scache_save_endp() stores an open session under the specified
74 /*	physical endpoint name.
75 /*
76 /*	scache_find_endp() looks up a saved session under the
77 /*	specified physical endpoint name.
78 /*
79 /*	scache_save_dest() associates the specified physical endpoint
80 /*	with the specified logical endpoint name.
81 /*
82 /*	scache_find_dest() looks up a saved session under the
83 /*	specified physical endpoint name.
84 /*
85 /*	Arguments:
86 /* .IP endp_ttl
87 /*	How long the session should be cached.  When information
88 /*	expires it is purged automatically.
89 /* .IP endp_label
90 /*	The transport name and the physical endpoint name under
91 /*	which the session is stored and looked up.
92 /*
93 /*	In the case of SMTP, the physical endpoint includes the numerical
94 /*	IP address, address family information, and the numerical TCP port.
95 /* .IP endp_prop
96 /*	Application-specific data with endpoint attributes.  It is up to
97 /*	the application to passivate (flatten) and re-activate this content
98 /*	upon storage and retrieval, respectively.
99 /* .sp
100 /*	In the case of SMTP, the endpoint attributes specify the
101 /*	server hostname, IP address, numerical TCP port, as well
102 /*	as ESMTP features advertised by the server, and when information
103 /*	expires. All this in some application-specific format that is easy
104 /*	to unravel when re-activating a cached session.
105 /* .IP dest_ttl
106 /*	How long the destination-to-endpoint binding should be
107 /*	cached. When information expires it is purged automatically.
108 /* .IP dest_label
109 /*	The transport name and the logical destination under which the
110 /*	destination-to-endpoint binding is stored and looked up.
111 /*
112 /*	In the case of SMTP, the logical destination is the DNS
113 /*	host or domain name with or without [], plus the numerical TCP port.
114 /* .IP dest_prop
115 /*	Application-specific attributes that describe features of
116 /*	this logical to physical binding. It is up to the application
117 /*	to passivate (flatten) and re-activate this content.
118 /*	upon storage and retrieval, respectively
119 /* .sp
120 /*	In case the of an SMTP logical destination to physical
121 /*	endpoint binding, the attributes specify the logical
122 /*	destination name, numerical port, whether the physical
123 /*	endpoint is best mx host with respect to a logical or
124 /*	fall-back destination, and when information expires.
125 /* .IP fd
126 /*	File descriptor with session to be cached.
127 /* DIAGNOSTICS
128 /*	scache_find_endp() and scache_find_dest() return -1 when
129 /*	the lookup fails, and a file descriptor upon success.
130 /*
131 /*	Other diagnostics: fatal error: memory allocation problem;
132 /*	panic: internal consistency failure.
133 /* SEE ALSO
134 /*	scache_single(3), single-session, in-memory cache
135 /*	scache_clnt(3), session cache client
136 /*	scache_multi(3), multi-session, in-memory cache
137 /* LICENSE
138 /* .ad
139 /* .fi
140 /*	The Secure Mailer license must be distributed with this software.
141 /* AUTHOR(S)
142 /*	Wietse Venema
143 /*	IBM T.J. Watson Research
144 /*	P.O. Box 704
145 /*	Yorktown Heights, NY 10598, USA
146 /*--*/
147 
148 /* System library. */
149 
150 #include <sys_defs.h>
151 #include <stdlib.h>
152 #include <unistd.h>
153 #include <string.h>
154 
155 /* Utility library. */
156 
157 #include <msg.h>
158 #include <vstream.h>
159 #include <vstring.h>
160 #include <vstring_vstream.h>
161 #include <argv.h>
162 #include <events.h>
163 
164 /* Global library. */
165 
166 #include <scache.h>
167 
168 #ifdef TEST
169 
170  /*
171   * Driver program for cache regression tests. Although all variants are
172   * relatively simple to verify by hand for single session storage, more
173   * sophisticated instrumentation is needed to demonstrate that the
174   * multi-session cache manager properly handles collisions in the time
175   * domain and in all the name space domains.
176   */
177 static SCACHE *scache;
178 static VSTRING *endp_prop;
179 static VSTRING *dest_prop;
180 static int verbose_level = 3;
181 
182  /*
183   * Cache mode lookup table. We don't do the client-server variant because
184   * that drags in a ton of configuration junk; the client-server protocol is
185   * relatively easy to verify manually.
186   */
187 struct cache_type {
188     char   *mode;
189     SCACHE *(*create) (void);
190 };
191 
192 static struct cache_type cache_types[] = {
193     "single", scache_single_create,
194     "multi", scache_multi_create,
195     0,
196 };
197 
198 #define STR(x) vstring_str(x)
199 
200 /* cache_type - select session cache type */
201 
cache_type(ARGV * argv)202 static void cache_type(ARGV *argv)
203 {
204     struct cache_type *cp;
205 
206     if (argv->argc != 2) {
207 	msg_error("usage: %s mode", argv->argv[0]);
208 	return;
209     }
210     if (scache != 0)
211 	scache_free(scache);
212     for (cp = cache_types; cp->mode != 0; cp++) {
213 	if (strcmp(cp->mode, argv->argv[1]) == 0) {
214 	    scache = cp->create();
215 	    return;
216 	}
217     }
218     msg_error("unknown cache type: %s", argv->argv[1]);
219 }
220 
221 /* handle_events - handle events while time advances */
222 
handle_events(ARGV * argv)223 static void handle_events(ARGV *argv)
224 {
225     int     delay;
226     time_t  before;
227     time_t  after;
228 
229     if (argv->argc != 2 || (delay = atoi(argv->argv[1])) <= 0) {
230 	msg_error("usage: %s time", argv->argv[0]);
231 	return;
232     }
233     before = event_time();
234     event_drain(delay);
235     after = event_time();
236     if (after < before + delay)
237 	sleep(before + delay - after);
238 }
239 
240 /* save_endp - save endpoint->session binding */
241 
save_endp(ARGV * argv)242 static void save_endp(ARGV *argv)
243 {
244     int     ttl;
245     int     fd;
246 
247     if (argv->argc != 5
248 	|| (ttl = atoi(argv->argv[1])) <= 0
249 	|| (fd = atoi(argv->argv[4])) <= 0) {
250 	msg_error("usage: save_endp ttl endpoint endp_props fd");
251 	return;
252     }
253     if (DUP2(0, fd) < 0)
254 	msg_fatal("dup2(0, %d): %m", fd);
255     scache_save_endp(scache, ttl, argv->argv[2], argv->argv[3], fd);
256 }
257 
258 /* find_endp - find endpoint->session binding */
259 
find_endp(ARGV * argv)260 static void find_endp(ARGV *argv)
261 {
262     int     fd;
263 
264     if (argv->argc != 2) {
265 	msg_error("usage: find_endp endpoint");
266 	return;
267     }
268     if ((fd = scache_find_endp(scache, argv->argv[1], endp_prop)) >= 0)
269 	close(fd);
270 }
271 
272 /* save_dest - save destination->endpoint binding */
273 
save_dest(ARGV * argv)274 static void save_dest(ARGV *argv)
275 {
276     int     ttl;
277 
278     if (argv->argc != 5 || (ttl = atoi(argv->argv[1])) <= 0) {
279 	msg_error("usage: save_dest ttl destination dest_props endpoint");
280 	return;
281     }
282     scache_save_dest(scache, ttl, argv->argv[2], argv->argv[3], argv->argv[4]);
283 }
284 
285 /* find_dest - find destination->endpoint->session binding */
286 
find_dest(ARGV * argv)287 static void find_dest(ARGV *argv)
288 {
289     int     fd;
290 
291     if (argv->argc != 2) {
292 	msg_error("usage: find_dest destination");
293 	return;
294     }
295     if ((fd = scache_find_dest(scache, argv->argv[1], dest_prop, endp_prop)) >= 0)
296 	close(fd);
297 }
298 
299 /* verbose - adjust noise level during cache manipulation */
300 
verbose(ARGV * argv)301 static void verbose(ARGV *argv)
302 {
303     int     level;
304 
305     if (argv->argc != 2 || (level = atoi(argv->argv[1])) < 0) {
306 	msg_error("usage: verbose level");
307 	return;
308     }
309     verbose_level = level;
310 }
311 
312  /*
313   * The command lookup table.
314   */
315 struct action {
316     char   *command;
317     void    (*action) (ARGV *);
318     int     flags;
319 };
320 
321 #define FLAG_NEED_CACHE	(1<<0)
322 
323 static void help(ARGV *);
324 
325 static struct action actions[] = {
326     "cache_type", cache_type, 0,
327     "save_endp", save_endp, FLAG_NEED_CACHE,
328     "find_endp", find_endp, FLAG_NEED_CACHE,
329     "save_dest", save_dest, FLAG_NEED_CACHE,
330     "find_dest", find_dest, FLAG_NEED_CACHE,
331     "sleep", handle_events, 0,
332     "verbose", verbose, 0,
333     "?", help, 0,
334     0,
335 };
336 
337 /* help - list commands */
338 
help(ARGV * argv)339 static void help(ARGV *argv)
340 {
341     struct action *ap;
342 
343     vstream_printf("commands:");
344     for (ap = actions; ap->command != 0; ap++)
345 	vstream_printf(" %s", ap->command);
346     vstream_printf("\n");
347     vstream_fflush(VSTREAM_OUT);
348 }
349 
350 /* get_buffer - prompt for input or log input */
351 
get_buffer(VSTRING * buf,VSTREAM * fp,int interactive)352 static int get_buffer(VSTRING *buf, VSTREAM *fp, int interactive)
353 {
354     int     status;
355 
356     if (interactive) {
357 	vstream_printf("> ");
358 	vstream_fflush(VSTREAM_OUT);
359     }
360     if ((status = vstring_get_nonl(buf, fp)) != VSTREAM_EOF) {
361 	if (!interactive) {
362 	    vstream_printf(">>> %s\n", STR(buf));
363 	    vstream_fflush(VSTREAM_OUT);
364 	}
365     }
366     return (status);
367 }
368 
369 /* at last, the main program */
370 
main(int unused_argc,char ** unused_argv)371 int     main(int unused_argc, char **unused_argv)
372 {
373     VSTRING *buf = vstring_alloc(1);
374     ARGV   *argv;
375     struct action *ap;
376     int     interactive = isatty(0);
377 
378     endp_prop = vstring_alloc(1);
379     dest_prop = vstring_alloc(1);
380 
381     vstream_fileno(VSTREAM_ERR) = 1;
382 
383     while (get_buffer(buf, VSTREAM_IN, interactive) != VSTREAM_EOF) {
384 	argv = argv_split(STR(buf), CHARS_SPACE);
385 	if (argv->argc > 0 && argv->argv[0][0] != '#') {
386 	    msg_verbose = verbose_level;
387 	    for (ap = actions; ap->command != 0; ap++) {
388 		if (strcmp(ap->command, argv->argv[0]) == 0) {
389 		    if ((ap->flags & FLAG_NEED_CACHE) != 0 && scache == 0)
390 			msg_error("no session cache");
391 		    else
392 			ap->action(argv);
393 		    break;
394 		}
395 	    }
396 	    msg_verbose = 0;
397 	    if (ap->command == 0)
398 		msg_error("bad command: %s", argv->argv[0]);
399 	}
400 	argv_free(argv);
401     }
402     scache_free(scache);
403     vstring_free(endp_prop);
404     vstring_free(dest_prop);
405     vstring_free(buf);
406     exit(0);
407 }
408 
409 #endif
410