xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/clnt_stream.c (revision 67b9b338a7386232ac596b5fd0cd5a9cc8a03c71)
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