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