Home | History | Annotate | Line # | Download | only in global
      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 
     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 
    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 
    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 
    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 
    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 
    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 
    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 
    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