1/* $NetBSD: clnt_stream.c,v 1.4 2022/10/08 16:12:45 christos Exp $ */ 2 3/*++ 4/* NAME 5/* clnt_stream 3 6/* SUMMARY 7/* client endpoint maintenance 8/* SYNOPSIS 9/* #include <clnt_stream.h> 10/* 11/* typedef void (*CLNT_STREAM_HANDSHAKE_FN)(VSTREAM *) 12/* 13/* CLNT_STREAM *clnt_stream_create(class, service, timeout, ttl, 14/* handshake) 15/* const char *class; 16/* const char *service; 17/* int timeout; 18/* int ttl; 19/* CLNT_STREAM_HANDSHAKE_FN *handshake; 20/* 21/* VSTREAM *clnt_stream_access(clnt_stream) 22/* CLNT_STREAM *clnt_stream; 23/* 24/* void clnt_stream_recover(clnt_stream) 25/* CLNT_STREAM *clnt_stream; 26/* 27/* void clnt_stream_free(clnt_stream) 28/* CLNT_STREAM *clnt_stream; 29/* DESCRIPTION 30/* This module maintains local IPC client endpoints that automatically 31/* disconnect after a being idle for a configurable amount of time, 32/* that disconnect after a configurable time to live, 33/* and that transparently handle most server-initiated disconnects. 34/* Server disconnect is detected by read-selecting the client endpoint. 35/* The code assumes that the server has disconnected when the endpoint 36/* becomes readable. 37/* 38/* clnt_stream_create() instantiates a client endpoint. 39/* 40/* clnt_stream_access() returns an open stream to the service specified 41/* to clnt_stream_create(). The stream instance may change between calls. 42/* This function returns null when the handshake function returned an 43/* error. 44/* 45/* clnt_stream_recover() recovers from a server-initiated disconnect 46/* that happened in the middle of an I/O operation. 47/* 48/* clnt_stream_free() destroys of the specified client endpoint. 49/* 50/* Arguments: 51/* .IP class 52/* The service class, private or public. 53/* .IP service 54/* The service endpoint name. The name is limited to local IPC 55/* over sockets or equivalent. 56/* .IP timeout 57/* Idle time after which the client disconnects. 58/* .IP ttl 59/* Upper bound on the time that a connection is allowed to persist. 60/* .IP handshake 61/* Null pointer, or pointer to function that will be called 62/* at the start of a new connection and that returns 0 in case 63/* of success. 64/* DIAGNOSTICS 65/* Warnings: communication failure. Fatal error: mail system is down, 66/* out of memory. 67/* SEE ALSO 68/* mail_proto(3h) low-level mail component glue. 69/* LICENSE 70/* .ad 71/* .fi 72/* The Secure Mailer license must be distributed with this software. 73/* AUTHOR(S) 74/* Wietse Venema 75/* IBM T.J. Watson Research 76/* P.O. Box 704 77/* Yorktown Heights, NY 10598, USA 78/* 79/* Wietse Venema 80/* Google, Inc. 81/* 111 8th Avenue 82/* New York, NY 10011, USA 83/*--*/ 84 85/* System library. */ 86 87#include <sys_defs.h> 88 89/* Utility library. */ 90 91#include <msg.h> 92#include <mymalloc.h> 93#include <vstream.h> 94#include <events.h> 95#include <iostuff.h> 96 97/* Global library. */ 98 99#include "mail_proto.h" 100#include "mail_params.h" 101#include "clnt_stream.h" 102 103/* Application-specific. */ 104 105 /* 106 * CLNT_STREAM is an opaque structure. None of the access methods can easily 107 * be implemented as a macro, and access is not performance critical anyway. 108 */ 109struct CLNT_STREAM { 110 VSTREAM *vstream; /* buffered I/O */ 111 int timeout; /* time before client disconnect */ 112 int ttl; /* time before client disconnect */ 113 CLNT_STREAM_HANDSHAKE_FN handshake; 114 char *class; /* server class */ 115 char *service; /* server name */ 116}; 117 118static void clnt_stream_close(CLNT_STREAM *); 119 120/* clnt_stream_event - server-initiated disconnect or client-side timeout */ 121 122static void clnt_stream_event(int unused_event, void *context) 123{ 124 CLNT_STREAM *clnt_stream = (CLNT_STREAM *) context; 125 126 /* 127 * Sanity check. This routine causes the stream to be closed, so it 128 * cannot be called when the stream is already closed. 129 */ 130 if (clnt_stream->vstream == 0) 131 msg_panic("clnt_stream_event: stream is closed"); 132 133 clnt_stream_close(clnt_stream); 134} 135 136/* clnt_stream_ttl_event - client-side expiration */ 137 138static void clnt_stream_ttl_event(int event, void *context) 139{ 140 141 /* 142 * XXX This function is needed only because event_request_timer() cannot 143 * distinguish between requests that specify the same call-back routine 144 * and call-back context. The fix is obvious: specify a request ID along 145 * with the call-back routine, but there is too much code that would have 146 * to be changed. 147 * 148 * XXX Should we be concerned that an overly aggressive optimizer will 149 * eliminate this function and replace calls to clnt_stream_ttl_event() 150 * by direct calls to clnt_stream_event()? It should not, because there 151 * exists code that takes the address of both functions. 152 */ 153 clnt_stream_event(event, context); 154} 155 156/* clnt_stream_open - connect to service */ 157 158static void clnt_stream_open(CLNT_STREAM *clnt_stream) 159{ 160 161 /* 162 * Sanity check. 163 */ 164 if (clnt_stream->vstream) 165 msg_panic("clnt_stream_open: stream is open"); 166 167 /* 168 * Schedule a read event so that we can clean up when the remote side 169 * disconnects, and schedule a timer event so that we can cleanup an idle 170 * connection. Note that both events are handled by the same routine. 171 * 172 * Finally, schedule an event to force disconnection even when the 173 * connection is not idle. This is to prevent one client from clinging on 174 * to a server forever. 175 */ 176 clnt_stream->vstream = mail_connect_wait(clnt_stream->class, 177 clnt_stream->service); 178 close_on_exec(vstream_fileno(clnt_stream->vstream), CLOSE_ON_EXEC); 179 event_enable_read(vstream_fileno(clnt_stream->vstream), clnt_stream_event, 180 (void *) clnt_stream); 181 event_request_timer(clnt_stream_event, (void *) clnt_stream, 182 clnt_stream->timeout); 183 event_request_timer(clnt_stream_ttl_event, (void *) clnt_stream, 184 clnt_stream->ttl); 185} 186 187/* clnt_stream_close - disconnect from service */ 188 189static void clnt_stream_close(CLNT_STREAM *clnt_stream) 190{ 191 192 /* 193 * Sanity check. 194 */ 195 if (clnt_stream->vstream == 0) 196 msg_panic("clnt_stream_close: stream is closed"); 197 198 /* 199 * Be sure to disable read and timer events. 200 */ 201 if (msg_verbose) 202 msg_info("%s stream disconnect", clnt_stream->service); 203 event_disable_readwrite(vstream_fileno(clnt_stream->vstream)); 204 event_cancel_timer(clnt_stream_event, (void *) clnt_stream); 205 event_cancel_timer(clnt_stream_ttl_event, (void *) clnt_stream); 206 (void) vstream_fclose(clnt_stream->vstream); 207 clnt_stream->vstream = 0; 208} 209 210/* clnt_stream_recover - recover from server-initiated disconnect */ 211 212void clnt_stream_recover(CLNT_STREAM *clnt_stream) 213{ 214 215 /* 216 * Clean up. Don't re-connect until the caller needs it. 217 */ 218 if (clnt_stream->vstream) 219 clnt_stream_close(clnt_stream); 220} 221 222/* clnt_stream_access - access a client stream */ 223 224VSTREAM *clnt_stream_access(CLNT_STREAM *clnt_stream) 225{ 226 CLNT_STREAM_HANDSHAKE_FN handshake; 227 228 /* 229 * Open a stream or restart the idle timer. 230 * 231 * Important! Do not restart the TTL timer! 232 */ 233 if (clnt_stream->vstream == 0) { 234 clnt_stream_open(clnt_stream); 235 handshake = clnt_stream->handshake; 236 } else if (readable(vstream_fileno(clnt_stream->vstream))) { 237 clnt_stream_close(clnt_stream); 238 clnt_stream_open(clnt_stream); 239 handshake = clnt_stream->handshake; 240 } else { 241 event_request_timer(clnt_stream_event, (void *) clnt_stream, 242 clnt_stream->timeout); 243 handshake = 0; 244 } 245 if (handshake != 0 && handshake(clnt_stream->vstream) != 0) 246 return (0); 247 return (clnt_stream->vstream); 248} 249 250/* clnt_stream_create - create client stream connection */ 251 252CLNT_STREAM *clnt_stream_create(const char *class, const char *service, 253 int timeout, int ttl, 254 CLNT_STREAM_HANDSHAKE_FN handshake) 255{ 256 CLNT_STREAM *clnt_stream; 257 258 /* 259 * Don't open the stream until the caller needs it. 260 */ 261 clnt_stream = (CLNT_STREAM *) mymalloc(sizeof(*clnt_stream)); 262 clnt_stream->vstream = 0; 263 clnt_stream->timeout = timeout; 264 clnt_stream->ttl = ttl; 265 clnt_stream->handshake = handshake; 266 clnt_stream->class = mystrdup(class); 267 clnt_stream->service = mystrdup(service); 268 return (clnt_stream); 269} 270 271/* clnt_stream_free - destroy client stream instance */ 272 273void clnt_stream_free(CLNT_STREAM *clnt_stream) 274{ 275 if (clnt_stream->vstream) 276 clnt_stream_close(clnt_stream); 277 myfree(clnt_stream->class); 278 myfree(clnt_stream->service); 279 myfree((void *) clnt_stream); 280} 281