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 */ 109 struct 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 118 static void clnt_stream_close(CLNT_STREAM *); 119 120 /* clnt_stream_event - server-initiated disconnect or client-side timeout */ 121 122 static 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 138 static 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 158 static 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 189 static 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 212 void 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 224 VSTREAM *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 252 CLNT_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 273 void 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