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
clnt_stream_event(int unused_event,void * context)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
clnt_stream_ttl_event(int event,void * context)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
clnt_stream_open(CLNT_STREAM * clnt_stream)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
clnt_stream_close(CLNT_STREAM * clnt_stream)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
clnt_stream_recover(CLNT_STREAM * clnt_stream)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
clnt_stream_access(CLNT_STREAM * clnt_stream)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
clnt_stream_create(const char * class,const char * service,int timeout,int ttl,CLNT_STREAM_HANDSHAKE_FN handshake)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
clnt_stream_free(CLNT_STREAM * clnt_stream)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