1 /* $NetBSD: scache_clnt.c,v 1.3 2022/10/08 16:12:45 christos Exp $ */
2
3 /*++
4 /* NAME
5 /* scache_clnt 3
6 /* SUMMARY
7 /* session cache manager client
8 /* SYNOPSIS
9 /* #include <scache.h>
10 /* DESCRIPTION
11 /* SCACHE *scache_clnt_create(server, timeout, idle_limit, ttl_limit)
12 /* const char *server;
13 /* int timeout;
14 /* int idle_limit;
15 /* int ttl_limit;
16 /* DESCRIPTION
17 /* This module implements the client-side protocol of the
18 /* session cache service.
19 /*
20 /* scache_clnt_create() creates a session cache service client.
21 /*
22 /* Arguments:
23 /* .IP server
24 /* The session cache service name.
25 /* .IP timeout
26 /* Time limit for connect, send or receive operations.
27 /* .IP idle_limit
28 /* Idle time after which the client disconnects.
29 /* .IP ttl_limit
30 /* Upper bound on the time that a connection is allowed to persist.
31 /* DIAGNOSTICS
32 /* Fatal error: memory allocation problem;
33 /* warning: communication error;
34 /* panic: internal consistency failure.
35 /* SEE ALSO
36 /* scache(3), generic session cache API
37 /* LICENSE
38 /* .ad
39 /* .fi
40 /* The Secure Mailer license must be distributed with this software.
41 /* AUTHOR(S)
42 /* Wietse Venema
43 /* IBM T.J. Watson Research
44 /* P.O. Box 704
45 /* Yorktown Heights, NY 10598, USA
46 /*
47 /* Wietse Venema
48 /* Google, Inc.
49 /* 111 8th Avenue
50 /* New York, NY 10011, USA
51 /*--*/
52
53 /* System library. */
54
55 #include <sys_defs.h>
56 #include <errno.h>
57
58 /* Utility library. */
59
60 #include <msg.h>
61 #include <mymalloc.h>
62 #include <auto_clnt.h>
63 #include <stringops.h>
64
65 /*#define msg_verbose 1*/
66
67 /* Global library. */
68
69 #include <mail_proto.h>
70 #include <mail_params.h>
71 #include <scache.h>
72
73 /* Application-specific. */
74
75 /*
76 * SCACHE_CLNT is a derived type from the SCACHE super-class.
77 */
78 typedef struct {
79 SCACHE scache[1]; /* super-class */
80 AUTO_CLNT *auto_clnt; /* client endpoint */
81 #ifdef CANT_WRITE_BEFORE_SENDING_FD
82 VSTRING *dummy; /* dummy buffer */
83 #endif
84 } SCACHE_CLNT;
85
86 #define STR(x) vstring_str(x)
87
88 #define SCACHE_MAX_TRIES 2
89
90 /* scache_clnt_handshake - receive server protocol announcement */
91
scache_clnt_handshake(VSTREAM * stream)92 static int scache_clnt_handshake(VSTREAM *stream)
93 {
94 return (attr_scan(stream, ATTR_FLAG_STRICT,
95 RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_SCACHE),
96 ATTR_TYPE_END));
97 }
98
99 /* scache_clnt_save_endp - save endpoint */
100
scache_clnt_save_endp(SCACHE * scache,int endp_ttl,const char * endp_label,const char * endp_prop,int fd)101 static void scache_clnt_save_endp(SCACHE *scache, int endp_ttl,
102 const char *endp_label,
103 const char *endp_prop, int fd)
104 {
105 SCACHE_CLNT *sp = (SCACHE_CLNT *) scache;
106 const char *myname = "scache_clnt_save_endp";
107 VSTREAM *stream;
108 int status;
109 int tries;
110 int count = 0;
111
112 if (msg_verbose)
113 msg_info("%s: endp=%s prop=%s fd=%d",
114 myname, endp_label, endp_prop, fd);
115
116 /*
117 * Sanity check.
118 */
119 if (endp_ttl <= 0)
120 msg_panic("%s: bad endp_ttl: %d", myname, endp_ttl);
121
122 /*
123 * Try a few times before disabling the cache. We use synchronous calls;
124 * the session cache service is CPU bound and making the client
125 * asynchronous would just complicate the code.
126 */
127 for (tries = 0; sp->auto_clnt != 0; tries++) {
128 if ((stream = auto_clnt_access(sp->auto_clnt)) != 0) {
129 errno = 0;
130 count += 1;
131 if (attr_print(stream, ATTR_FLAG_NONE,
132 SEND_ATTR_STR(MAIL_ATTR_REQ, SCACHE_REQ_SAVE_ENDP),
133 SEND_ATTR_INT(MAIL_ATTR_TTL, endp_ttl),
134 SEND_ATTR_STR(MAIL_ATTR_LABEL, endp_label),
135 SEND_ATTR_STR(MAIL_ATTR_PROP, endp_prop),
136 ATTR_TYPE_END) != 0
137 || vstream_fflush(stream)
138 #ifdef CANT_WRITE_BEFORE_SENDING_FD
139 || attr_scan(stream, ATTR_FLAG_STRICT,
140 RECV_ATTR_STR(MAIL_ATTR_DUMMY, sp->dummy),
141 ATTR_TYPE_END) != 1
142 #endif
143 || LOCAL_SEND_FD(vstream_fileno(stream), fd) < 0
144 || attr_scan(stream, ATTR_FLAG_STRICT,
145 RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
146 ATTR_TYPE_END) != 1) {
147 if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT))
148 msg_warn("problem talking to service %s: %m",
149 VSTREAM_PATH(stream));
150 /* Give up or recover. */
151 } else {
152 if (msg_verbose && status != 0)
153 msg_warn("%s: descriptor save failed with status %d",
154 myname, status);
155 break;
156 }
157 }
158 /* Give up or recover. */
159 if (tries >= SCACHE_MAX_TRIES - 1) {
160 msg_warn("disabling connection caching");
161 auto_clnt_free(sp->auto_clnt);
162 sp->auto_clnt = 0;
163 break;
164 }
165 sleep(1); /* XXX make configurable */
166 auto_clnt_recover(sp->auto_clnt);
167 }
168 /* Always close the descriptor before returning. */
169 if (close(fd) < 0)
170 msg_warn("%s: close(%d): %m", myname, fd);
171 }
172
173 /* scache_clnt_find_endp - look up cached session */
174
scache_clnt_find_endp(SCACHE * scache,const char * endp_label,VSTRING * endp_prop)175 static int scache_clnt_find_endp(SCACHE *scache, const char *endp_label,
176 VSTRING *endp_prop)
177 {
178 SCACHE_CLNT *sp = (SCACHE_CLNT *) scache;
179 const char *myname = "scache_clnt_find_endp";
180 VSTREAM *stream;
181 int status;
182 int tries;
183 int fd;
184
185 /*
186 * Try a few times before disabling the cache. We use synchronous calls;
187 * the session cache service is CPU bound and making the client
188 * asynchronous would just complicate the code.
189 */
190 for (tries = 0; sp->auto_clnt != 0; tries++) {
191 if ((stream = auto_clnt_access(sp->auto_clnt)) != 0) {
192 errno = 0;
193 if (attr_print(stream, ATTR_FLAG_NONE,
194 SEND_ATTR_STR(MAIL_ATTR_REQ, SCACHE_REQ_FIND_ENDP),
195 SEND_ATTR_STR(MAIL_ATTR_LABEL, endp_label),
196 ATTR_TYPE_END) != 0
197 || vstream_fflush(stream)
198 || attr_scan(stream, ATTR_FLAG_STRICT,
199 RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
200 RECV_ATTR_STR(MAIL_ATTR_PROP, endp_prop),
201 ATTR_TYPE_END) != 2) {
202 if (msg_verbose || (errno != EPIPE && errno != ENOENT))
203 msg_warn("problem talking to service %s: %m",
204 VSTREAM_PATH(stream));
205 /* Give up or recover. */
206 } else if (status != 0) {
207 if (msg_verbose)
208 msg_info("%s: not found: %s", myname, endp_label);
209 return (-1);
210 } else if (
211 #ifdef CANT_WRITE_BEFORE_SENDING_FD
212 attr_print(stream, ATTR_FLAG_NONE,
213 SEND_ATTR_STR(MAIL_ATTR_DUMMY, ""),
214 ATTR_TYPE_END) != 0
215 || vstream_fflush(stream) != 0
216 || read_wait(vstream_fileno(stream),
217 stream->timeout) < 0 || /* XXX */
218 #endif
219 (fd = LOCAL_RECV_FD(vstream_fileno(stream))) < 0) {
220 if (msg_verbose || (errno != EPIPE && errno != ENOENT))
221 msg_warn("problem talking to service %s: %m",
222 VSTREAM_PATH(stream));
223 /* Give up or recover. */
224 } else {
225 #ifdef MUST_READ_AFTER_SENDING_FD
226 (void) attr_print(stream, ATTR_FLAG_NONE,
227 SEND_ATTR_STR(MAIL_ATTR_DUMMY, ""),
228 ATTR_TYPE_END);
229 (void) vstream_fflush(stream);
230 #endif
231 if (msg_verbose)
232 msg_info("%s: endp=%s prop=%s fd=%d",
233 myname, endp_label, STR(endp_prop), fd);
234 return (fd);
235 }
236 }
237 /* Give up or recover. */
238 if (tries >= SCACHE_MAX_TRIES - 1) {
239 msg_warn("disabling connection caching");
240 auto_clnt_free(sp->auto_clnt);
241 sp->auto_clnt = 0;
242 return (-1);
243 }
244 sleep(1); /* XXX make configurable */
245 auto_clnt_recover(sp->auto_clnt);
246 }
247 return (-1);
248 }
249
250 /* scache_clnt_save_dest - create destination/endpoint association */
251
scache_clnt_save_dest(SCACHE * scache,int dest_ttl,const char * dest_label,const char * dest_prop,const char * endp_label)252 static void scache_clnt_save_dest(SCACHE *scache, int dest_ttl,
253 const char *dest_label,
254 const char *dest_prop,
255 const char *endp_label)
256 {
257 SCACHE_CLNT *sp = (SCACHE_CLNT *) scache;
258 const char *myname = "scache_clnt_save_dest";
259 VSTREAM *stream;
260 int status;
261 int tries;
262
263 if (msg_verbose)
264 msg_info("%s: dest_label=%s dest_prop=%s endp_label=%s",
265 myname, dest_label, dest_prop, endp_label);
266
267 /*
268 * Sanity check.
269 */
270 if (dest_ttl <= 0)
271 msg_panic("%s: bad dest_ttl: %d", myname, dest_ttl);
272
273 /*
274 * Try a few times before disabling the cache. We use synchronous calls;
275 * the session cache service is CPU bound and making the client
276 * asynchronous would just complicate the code.
277 */
278 for (tries = 0; sp->auto_clnt != 0; tries++) {
279 if ((stream = auto_clnt_access(sp->auto_clnt)) != 0) {
280 errno = 0;
281 if (attr_print(stream, ATTR_FLAG_NONE,
282 SEND_ATTR_STR(MAIL_ATTR_REQ, SCACHE_REQ_SAVE_DEST),
283 SEND_ATTR_INT(MAIL_ATTR_TTL, dest_ttl),
284 SEND_ATTR_STR(MAIL_ATTR_LABEL, dest_label),
285 SEND_ATTR_STR(MAIL_ATTR_PROP, dest_prop),
286 SEND_ATTR_STR(MAIL_ATTR_LABEL, endp_label),
287 ATTR_TYPE_END) != 0
288 || vstream_fflush(stream)
289 || attr_scan(stream, ATTR_FLAG_STRICT,
290 RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
291 ATTR_TYPE_END) != 1) {
292 if (msg_verbose || (errno != EPIPE && errno != ENOENT))
293 msg_warn("problem talking to service %s: %m",
294 VSTREAM_PATH(stream));
295 /* Give up or recover. */
296 } else {
297 if (msg_verbose && status != 0)
298 msg_warn("%s: destination save failed with status %d",
299 myname, status);
300 break;
301 }
302 }
303 /* Give up or recover. */
304 if (tries >= SCACHE_MAX_TRIES - 1) {
305 msg_warn("disabling connection caching");
306 auto_clnt_free(sp->auto_clnt);
307 sp->auto_clnt = 0;
308 break;
309 }
310 sleep(1); /* XXX make configurable */
311 auto_clnt_recover(sp->auto_clnt);
312 }
313 }
314
315 /* scache_clnt_find_dest - look up cached session */
316
scache_clnt_find_dest(SCACHE * scache,const char * dest_label,VSTRING * dest_prop,VSTRING * endp_prop)317 static int scache_clnt_find_dest(SCACHE *scache, const char *dest_label,
318 VSTRING *dest_prop,
319 VSTRING *endp_prop)
320 {
321 SCACHE_CLNT *sp = (SCACHE_CLNT *) scache;
322 const char *myname = "scache_clnt_find_dest";
323 VSTREAM *stream;
324 int status;
325 int tries;
326 int fd;
327
328 /*
329 * Try a few times before disabling the cache. We use synchronous calls;
330 * the session cache service is CPU bound and making the client
331 * asynchronous would just complicate the code.
332 */
333 for (tries = 0; sp->auto_clnt != 0; tries++) {
334 if ((stream = auto_clnt_access(sp->auto_clnt)) != 0) {
335 errno = 0;
336 if (attr_print(stream, ATTR_FLAG_NONE,
337 SEND_ATTR_STR(MAIL_ATTR_REQ, SCACHE_REQ_FIND_DEST),
338 SEND_ATTR_STR(MAIL_ATTR_LABEL, dest_label),
339 ATTR_TYPE_END) != 0
340 || vstream_fflush(stream)
341 || attr_scan(stream, ATTR_FLAG_STRICT,
342 RECV_ATTR_INT(MAIL_ATTR_STATUS, &status),
343 RECV_ATTR_STR(MAIL_ATTR_PROP, dest_prop),
344 RECV_ATTR_STR(MAIL_ATTR_PROP, endp_prop),
345 ATTR_TYPE_END) != 3) {
346 if (msg_verbose || (errno != EPIPE && errno != ENOENT))
347 msg_warn("problem talking to service %s: %m",
348 VSTREAM_PATH(stream));
349 /* Give up or recover. */
350 } else if (status != 0) {
351 if (msg_verbose)
352 msg_info("%s: not found: %s", myname, dest_label);
353 return (-1);
354 } else if (
355 #ifdef CANT_WRITE_BEFORE_SENDING_FD
356 attr_print(stream, ATTR_FLAG_NONE,
357 SEND_ATTR_STR(MAIL_ATTR_DUMMY, ""),
358 ATTR_TYPE_END) != 0
359 || vstream_fflush(stream) != 0
360 || read_wait(vstream_fileno(stream),
361 stream->timeout) < 0 || /* XXX */
362 #endif
363 (fd = LOCAL_RECV_FD(vstream_fileno(stream))) < 0) {
364 if (msg_verbose || (errno != EPIPE && errno != ENOENT))
365 msg_warn("problem talking to service %s: %m",
366 VSTREAM_PATH(stream));
367 /* Give up or recover. */
368 } else {
369 #ifdef MUST_READ_AFTER_SENDING_FD
370 (void) attr_print(stream, ATTR_FLAG_NONE,
371 SEND_ATTR_STR(MAIL_ATTR_DUMMY, ""),
372 ATTR_TYPE_END);
373 (void) vstream_fflush(stream);
374 #endif
375 if (msg_verbose)
376 msg_info("%s: dest=%s dest_prop=%s endp_prop=%s fd=%d",
377 myname, dest_label, STR(dest_prop), STR(endp_prop), fd);
378 return (fd);
379 }
380 }
381 /* Give up or recover. */
382 if (tries >= SCACHE_MAX_TRIES - 1) {
383 msg_warn("disabling connection caching");
384 auto_clnt_free(sp->auto_clnt);
385 sp->auto_clnt = 0;
386 return (-1);
387 }
388 sleep(1); /* XXX make configurable */
389 auto_clnt_recover(sp->auto_clnt);
390 }
391 return (-1);
392 }
393
394 /* scache_clnt_size - dummy */
395
scache_clnt_size(SCACHE * unused_scache,SCACHE_SIZE * size)396 static void scache_clnt_size(SCACHE *unused_scache, SCACHE_SIZE *size)
397 {
398 /* XXX Crap in a hurry. */
399 size->dest_count = 0;
400 size->endp_count = 0;
401 size->sess_count = 0;
402 }
403
404 /* scache_clnt_free - destroy cache */
405
scache_clnt_free(SCACHE * scache)406 static void scache_clnt_free(SCACHE *scache)
407 {
408 SCACHE_CLNT *sp = (SCACHE_CLNT *) scache;
409
410 if (sp->auto_clnt)
411 auto_clnt_free(sp->auto_clnt);
412 #ifdef CANT_WRITE_BEFORE_SENDING_FD
413 vstring_free(sp->dummy);
414 #endif
415 myfree((void *) sp);
416 }
417
418 /* scache_clnt_create - initialize */
419
scache_clnt_create(const char * server,int timeout,int idle_limit,int ttl_limit)420 SCACHE *scache_clnt_create(const char *server, int timeout,
421 int idle_limit, int ttl_limit)
422 {
423 SCACHE_CLNT *sp = (SCACHE_CLNT *) mymalloc(sizeof(*sp));
424 char *service;
425
426 sp->scache->save_endp = scache_clnt_save_endp;
427 sp->scache->find_endp = scache_clnt_find_endp;
428 sp->scache->save_dest = scache_clnt_save_dest;
429 sp->scache->find_dest = scache_clnt_find_dest;
430 sp->scache->size = scache_clnt_size;
431 sp->scache->free = scache_clnt_free;
432
433 service = concatenate("local:" MAIL_CLASS_PRIVATE "/", server, (char *) 0);
434 sp->auto_clnt = auto_clnt_create(service, timeout, idle_limit, ttl_limit);
435 auto_clnt_control(sp->auto_clnt,
436 AUTO_CLNT_CTL_HANDSHAKE, scache_clnt_handshake,
437 AUTO_CLNT_CTL_END);
438 myfree(service);
439
440 #ifdef CANT_WRITE_BEFORE_SENDING_FD
441 sp->dummy = vstring_alloc(1);
442 #endif
443
444 return (sp->scache);
445 }
446