7 /* Postfix shared connection cache server
9 /* \fBscache\fR [generic Postfix daemon options]
11 /* The \fBscache\fR(8) server maintains a shared multi-connection
12 /* cache. This information can be used by, for example, Postfix
13 /* SMTP clients or other Postfix delivery agents.
15 /* The connection cache is organized into logical destination
16 /* names, physical endpoint names, and connections.
18 /* As a specific example, logical SMTP destinations specify
19 /* (transport, domain, port), and physical SMTP endpoints
20 /* specify (transport, IP address, port). An SMTP connection
21 /* may be saved after a successful mail transaction.
23 /* In the general case, one logical destination may refer to
24 /* zero or more physical endpoints, one physical endpoint may
25 /* be referenced by zero or more logical destinations, and
26 /* one endpoint may refer to zero or more connections.
28 /* The exact syntax of a logical destination or endpoint name
29 /* is application dependent; the \fBscache\fR(8) server does
30 /* not care. A connection is stored as a file descriptor together
31 /* with application-dependent information that is needed to
32 /* re-activate a connection object. Again, the \fBscache\fR(8)
33 /* server is completely unaware of the details of that
36 /* All information is stored with a finite time to live (ttl).
37 /* The connection cache daemon terminates when no client is
38 /* connected for \fBmax_idle\fR time units.
40 /* This server implements the following requests:
41 /* .IP "\fBsave_endp\fI ttl endpoint endpoint_properties file_descriptor\fR"
42 /* Save the specified file descriptor and connection property data
43 /* under the specified endpoint name. The endpoint properties
44 /* are used by the client to re-activate a passivated connection
46 /* .IP "\fBfind_endp\fI endpoint\fR"
47 /* Look up cached properties and a cached file descriptor for the
48 /* specified endpoint.
49 /* .IP "\fBsave_dest\fI ttl destination destination_properties endpoint\fR"
50 /* Save the binding between a logical destination and an
51 /* endpoint under the destination name, together with destination
52 /* specific connection properties. The destination properties
53 /* are used by the client to re-activate a passivated connection
55 /* .IP "\fBfind_dest\fI destination\fR"
56 /* Look up cached destination properties, cached endpoint properties,
57 /* and a cached file descriptor for the specified logical destination.
61 /* The \fBscache\fR(8) server is not security-sensitive. It does not
62 /* talk to the network, and it does not talk to local users.
63 /* The \fBscache\fR(8) server can run chrooted at fixed low privilege.
65 /* The \fBscache\fR(8) server is not a trusted process. It must
66 /* not be used to store information that is security sensitive.
68 /* Problems and transactions are logged to \fBsyslogd\fR(8).
70 /* The session cache cannot be shared among multiple machines.
72 /* When a connection expires from the cache, it is closed without
73 /* the appropriate protocol specific handshake.
74 /* CONFIGURATION PARAMETERS
77 /* Changes to \fBmain.cf\fR are picked up automatically as \fBscache\fR(8)
78 /* processes run for only a limited amount of time. Use the command
79 /* "\fBpostfix reload\fR" to speed up a change.
81 /* The text below provides only a parameter summary. See
82 /* \fBpostconf\fR(5) for more details including examples.
86 /* .IP "\fBconnection_cache_ttl_limit (2s)\fR"
87 /* The maximal time-to-live value that the \fBscache\fR(8) connection
90 /* .IP "\fBconnection_cache_status_update_time (600s)\fR"
91 /* How frequently the \fBscache\fR(8) server logs usage statistics with
92 /* connection cache hit and miss rates for logical destinations and for
93 /* physical endpoints.
94 /* MISCELLANEOUS CONTROLS
97 /* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
98 /* The default location of the Postfix main.cf and master.cf
99 /* configuration files.
100 /* .IP "\fBdaemon_timeout (18000s)\fR"
101 /* How much time a Postfix daemon process may take to handle a
102 /* request before it is terminated by a built-in watchdog timer.
103 /* .IP "\fBipc_timeout (3600s)\fR"
104 /* The time limit for sending or receiving information over an internal
105 /* communication channel.
106 /* .IP "\fBmax_idle (100s)\fR"
107 /* The maximum amount of time that an idle Postfix daemon process waits
108 /* for an incoming connection before terminating voluntarily.
109 /* .IP "\fBprocess_id (read-only)\fR"
110 /* The process ID of a Postfix command or daemon process.
111 /* .IP "\fBprocess_name (read-only)\fR"
112 /* The process name of a Postfix command or daemon process.
113 /* .IP "\fBsyslog_facility (mail)\fR"
114 /* The syslog facility of Postfix logging.
115 /* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
116 /* The mail system name that is prepended to the process name in syslog
117 /* records, so that "smtpd" becomes, for example, "postfix/smtpd".
119 /* smtp(8), SMTP client
120 /* postconf(5), configuration parameters
121 /* master(8), process manager
122 /* syslogd(8), system logging
126 /* Use "\fBpostconf readme_directory\fR" or
127 /* "\fBpostconf html_directory\fR" to locate this information.
130 /* CONNECTION_CACHE_README, Postfix connection cache
134 /* The Secure Mailer license must be distributed with this software.
136 /* This service was introduced with Postfix version 2.2.
139 /* IBM T.J. Watson Research
141 /* Yorktown Heights, NY 10598, USA
144 /* System library. */
146 #include <sys_defs.h>
149 /* Utility library. */
157 /* Global library. */
159 #include <mail_params.h>
160 #include <mail_version.h>
161 #include <mail_proto.h>
164 /* Single server skeleton. */
166 #include <mail_server.h>
167 #include <mail_conf.h>
169 /* Application-specific. */
172 * Tunable parameters.
174 int var_scache_ttl_lim
;
175 int var_scache_stat_time
;
178 * Request parameters.
180 static VSTRING
*scache_request
;
181 static VSTRING
*scache_dest_label
;
182 static VSTRING
*scache_dest_prop
;
183 static VSTRING
*scache_endp_label
;
184 static VSTRING
*scache_endp_prop
;
186 #ifdef CANT_WRITE_BEFORE_SENDING_FD
187 static VSTRING
*scache_dummy
;
192 * Session cache instance.
194 static SCACHE
*scache
;
199 static int scache_dest_hits
;
200 static int scache_dest_miss
;
201 static int scache_dest_count
;
202 static int scache_endp_hits
;
203 static int scache_endp_miss
;
204 static int scache_endp_count
;
205 static int scache_sess_count
;
206 time_t scache_start_time
;
209 * Silly little macros.
211 #define STR(x) vstring_str(x)
212 #define VSTREQ(x,y) (strcmp(STR(x),y) == 0)
214 /* scache_save_endp_service - protocol to save endpoint->stream binding */
216 static void scache_save_endp_service(VSTREAM
*client_stream
)
218 const char *myname
= "scache_save_endp_service";
223 if (attr_scan(client_stream
,
225 ATTR_TYPE_INT
, MAIL_ATTR_TTL
, &ttl
,
226 ATTR_TYPE_STR
, MAIL_ATTR_LABEL
, scache_endp_label
,
227 ATTR_TYPE_STR
, MAIL_ATTR_PROP
, scache_endp_prop
,
230 msg_warn("%s: bad or missing request parameter", myname
);
231 attr_print(client_stream
, ATTR_FLAG_NONE
,
232 ATTR_TYPE_INT
, MAIL_ATTR_STATUS
, SCACHE_STAT_BAD
,
236 #ifdef CANT_WRITE_BEFORE_SENDING_FD
237 attr_print(client_stream
, ATTR_FLAG_NONE
,
238 ATTR_TYPE_STR
, MAIL_ATTR_DUMMY
, "",
240 || vstream_fflush(client_stream
) != 0
241 || read_wait(vstream_fileno(client_stream
),
242 client_stream
->timeout
) < 0 /* XXX */
245 (fd
= LOCAL_RECV_FD(vstream_fileno(client_stream
))) < 0) {
246 msg_warn("%s: unable to receive file descriptor: %m", myname
);
247 (void) attr_print(client_stream
, ATTR_FLAG_NONE
,
248 ATTR_TYPE_INT
, MAIL_ATTR_STATUS
, SCACHE_STAT_FAIL
,
252 scache_save_endp(scache
,
253 ttl
> var_scache_ttl_lim
? var_scache_ttl_lim
: ttl
,
254 STR(scache_endp_label
), STR(scache_endp_prop
), fd
);
255 (void) attr_print(client_stream
, ATTR_FLAG_NONE
,
256 ATTR_TYPE_INT
, MAIL_ATTR_STATUS
, SCACHE_STAT_OK
,
258 scache_size(scache
, &size
);
259 if (size
.endp_count
> scache_endp_count
)
260 scache_endp_count
= size
.endp_count
;
261 if (size
.sess_count
> scache_sess_count
)
262 scache_sess_count
= size
.sess_count
;
267 /* scache_find_endp_service - protocol to find connection for endpoint */
269 static void scache_find_endp_service(VSTREAM
*client_stream
)
271 const char *myname
= "scache_find_endp_service";
274 if (attr_scan(client_stream
,
276 ATTR_TYPE_STR
, MAIL_ATTR_LABEL
, scache_endp_label
,
277 ATTR_TYPE_END
) != 1) {
278 msg_warn("%s: bad or missing request parameter", myname
);
279 attr_print(client_stream
, ATTR_FLAG_NONE
,
280 ATTR_TYPE_INT
, MAIL_ATTR_STATUS
, SCACHE_STAT_BAD
,
281 ATTR_TYPE_STR
, MAIL_ATTR_PROP
, "",
284 } else if ((fd
= scache_find_endp(scache
, STR(scache_endp_label
),
285 scache_endp_prop
)) < 0) {
286 attr_print(client_stream
, ATTR_FLAG_NONE
,
287 ATTR_TYPE_INT
, MAIL_ATTR_STATUS
, SCACHE_STAT_FAIL
,
288 ATTR_TYPE_STR
, MAIL_ATTR_PROP
, "",
293 attr_print(client_stream
, ATTR_FLAG_NONE
,
294 ATTR_TYPE_INT
, MAIL_ATTR_STATUS
, SCACHE_STAT_OK
,
295 ATTR_TYPE_STR
, MAIL_ATTR_PROP
, STR(scache_endp_prop
),
297 if (vstream_fflush(client_stream
) != 0
298 #ifdef CANT_WRITE_BEFORE_SENDING_FD
299 || attr_scan(client_stream
, ATTR_FLAG_STRICT
,
300 ATTR_TYPE_STR
, MAIL_ATTR_DUMMY
, scache_dummy
,
303 || LOCAL_SEND_FD(vstream_fileno(client_stream
), fd
) < 0
304 #ifdef MUST_READ_AFTER_SENDING_FD
305 || attr_scan(client_stream
, ATTR_FLAG_STRICT
,
306 ATTR_TYPE_STR
, MAIL_ATTR_DUMMY
, scache_dummy
,
310 msg_warn("%s: cannot send file descriptor: %m", myname
);
312 msg_warn("close(%d): %m", fd
);
318 /* scache_save_dest_service - protocol to save destination->endpoint binding */
320 static void scache_save_dest_service(VSTREAM
*client_stream
)
322 const char *myname
= "scache_save_dest_service";
326 if (attr_scan(client_stream
,
328 ATTR_TYPE_INT
, MAIL_ATTR_TTL
, &ttl
,
329 ATTR_TYPE_STR
, MAIL_ATTR_LABEL
, scache_dest_label
,
330 ATTR_TYPE_STR
, MAIL_ATTR_PROP
, scache_dest_prop
,
331 ATTR_TYPE_STR
, MAIL_ATTR_LABEL
, scache_endp_label
,
334 msg_warn("%s: bad or missing request parameter", myname
);
335 attr_print(client_stream
, ATTR_FLAG_NONE
,
336 ATTR_TYPE_INT
, MAIL_ATTR_STATUS
, SCACHE_STAT_BAD
,
340 scache_save_dest(scache
,
341 ttl
> var_scache_ttl_lim
? var_scache_ttl_lim
: ttl
,
342 STR(scache_dest_label
), STR(scache_dest_prop
),
343 STR(scache_endp_label
));
344 attr_print(client_stream
, ATTR_FLAG_NONE
,
345 ATTR_TYPE_INT
, MAIL_ATTR_STATUS
, SCACHE_STAT_OK
,
347 scache_size(scache
, &size
);
348 if (size
.dest_count
> scache_dest_count
)
349 scache_dest_count
= size
.dest_count
;
350 if (size
.endp_count
> scache_endp_count
)
351 scache_endp_count
= size
.endp_count
;
356 /* scache_find_dest_service - protocol to find connection for destination */
358 static void scache_find_dest_service(VSTREAM
*client_stream
)
360 const char *myname
= "scache_find_dest_service";
363 if (attr_scan(client_stream
,
365 ATTR_TYPE_STR
, MAIL_ATTR_LABEL
, scache_dest_label
,
366 ATTR_TYPE_END
) != 1) {
367 msg_warn("%s: bad or missing request parameter", myname
);
368 attr_print(client_stream
, ATTR_FLAG_NONE
,
369 ATTR_TYPE_INT
, MAIL_ATTR_STATUS
, SCACHE_STAT_BAD
,
370 ATTR_TYPE_STR
, MAIL_ATTR_PROP
, "",
371 ATTR_TYPE_STR
, MAIL_ATTR_PROP
, "",
374 } else if ((fd
= scache_find_dest(scache
, STR(scache_dest_label
),
376 scache_endp_prop
)) < 0) {
377 attr_print(client_stream
, ATTR_FLAG_NONE
,
378 ATTR_TYPE_INT
, MAIL_ATTR_STATUS
, SCACHE_STAT_FAIL
,
379 ATTR_TYPE_STR
, MAIL_ATTR_PROP
, "",
380 ATTR_TYPE_STR
, MAIL_ATTR_PROP
, "",
385 attr_print(client_stream
, ATTR_FLAG_NONE
,
386 ATTR_TYPE_INT
, MAIL_ATTR_STATUS
, SCACHE_STAT_OK
,
387 ATTR_TYPE_STR
, MAIL_ATTR_PROP
, STR(scache_dest_prop
),
388 ATTR_TYPE_STR
, MAIL_ATTR_PROP
, STR(scache_endp_prop
),
390 if (vstream_fflush(client_stream
) != 0
391 #ifdef CANT_WRITE_BEFORE_SENDING_FD
392 || attr_scan(client_stream
, ATTR_FLAG_STRICT
,
393 ATTR_TYPE_STR
, MAIL_ATTR_DUMMY
, scache_dummy
,
396 || LOCAL_SEND_FD(vstream_fileno(client_stream
), fd
) < 0
397 #ifdef MUST_READ_AFTER_SENDING_FD
398 || attr_scan(client_stream
, ATTR_FLAG_STRICT
,
399 ATTR_TYPE_STR
, MAIL_ATTR_DUMMY
, scache_dummy
,
403 msg_warn("%s: cannot send file descriptor: %m", myname
);
405 msg_warn("close(%d): %m", fd
);
411 /* scache_service - perform service for client */
413 static void scache_service(VSTREAM
*client_stream
, char *unused_service
,
418 * Sanity check. This service takes no command-line arguments.
421 msg_fatal("unexpected command-line argument: %s", argv
[0]);
424 * This routine runs whenever a client connects to the UNIX-domain socket
425 * dedicated to the scache service. All connection-management stuff is
426 * handled by the common code in multi_server.c.
428 * XXX Workaround: with some requests, the client sends a dummy message
429 * after the server replies (yes that's a botch). When the scache server
430 * is slow, this dummy message may become concatenated with the next
431 * request from the same client. The do-while loop below will repeat
432 * instead of discarding the client request. We must process it now
433 * because there will be no select() notification.
436 if (attr_scan(client_stream
,
437 ATTR_FLAG_MORE
| ATTR_FLAG_STRICT
,
438 ATTR_TYPE_STR
, MAIL_ATTR_REQ
, scache_request
,
439 ATTR_TYPE_END
) == 1) {
440 if (VSTREQ(scache_request
, SCACHE_REQ_SAVE_DEST
)) {
441 scache_save_dest_service(client_stream
);
442 } else if (VSTREQ(scache_request
, SCACHE_REQ_FIND_DEST
)) {
443 scache_find_dest_service(client_stream
);
444 } else if (VSTREQ(scache_request
, SCACHE_REQ_SAVE_ENDP
)) {
445 scache_save_endp_service(client_stream
);
446 } else if (VSTREQ(scache_request
, SCACHE_REQ_FIND_ENDP
)) {
447 scache_find_endp_service(client_stream
);
449 msg_warn("unrecognized request: \"%s\", ignored",
450 STR(scache_request
));
451 attr_print(client_stream
, ATTR_FLAG_NONE
,
452 ATTR_TYPE_INT
, MAIL_ATTR_STATUS
, SCACHE_STAT_BAD
,
456 } while (vstream_peek(client_stream
) > 0);
457 vstream_fflush(client_stream
);
460 /* scache_status_dump - log and reset cache statistics */
462 static void scache_status_dump(char *unused_name
, char **unused_argv
)
464 if (scache_dest_hits
|| scache_dest_miss
465 || scache_endp_hits
|| scache_endp_miss
466 || scache_dest_count
|| scache_endp_count
467 || scache_sess_count
)
468 msg_info("statistics: start interval %.15s",
469 ctime(&scache_start_time
) + 4);
471 if (scache_dest_hits
|| scache_dest_miss
) {
472 msg_info("statistics: domain lookup hits=%d miss=%d success=%d%%",
473 scache_dest_hits
, scache_dest_miss
,
474 scache_dest_hits
* 100
475 / (scache_dest_hits
+ scache_dest_miss
));
476 scache_dest_hits
= scache_dest_miss
= 0;
478 if (scache_endp_hits
|| scache_endp_miss
) {
479 msg_info("statistics: address lookup hits=%d miss=%d success=%d%%",
480 scache_endp_hits
, scache_endp_miss
,
481 scache_endp_hits
* 100
482 / (scache_endp_hits
+ scache_endp_miss
));
483 scache_endp_hits
= scache_endp_miss
= 0;
485 if (scache_dest_count
|| scache_endp_count
|| scache_sess_count
) {
486 msg_info("statistics: max simultaneous domains=%d addresses=%d connection=%d",
487 scache_dest_count
, scache_endp_count
, scache_sess_count
);
488 scache_dest_count
= 0;
489 scache_endp_count
= 0;
490 scache_sess_count
= 0;
492 scache_start_time
= event_time();
495 /* scache_status_update - log and reset cache statistics periodically */
497 static void scache_status_update(int unused_event
, char *context
)
499 scache_status_dump((char *) 0, (char **) 0);
500 event_request_timer(scache_status_update
, context
, var_scache_stat_time
);
503 /* post_jail_init - initialization after privilege drop */
505 static void post_jail_init(char *unused_name
, char **unused_argv
)
509 * Pre-allocate the cache instance.
511 scache
= scache_multi_create();
514 * Pre-allocate buffers.
516 scache_request
= vstring_alloc(10);
517 scache_dest_label
= vstring_alloc(10);
518 scache_dest_prop
= vstring_alloc(10);
519 scache_endp_label
= vstring_alloc(10);
520 scache_endp_prop
= vstring_alloc(10);
521 #ifdef CANT_WRITE_BEFORE_SENDING_FD
522 scache_dummy
= vstring_alloc(10);
526 * Disable the max_use limit. We still terminate when no client is
527 * connected for $idle_limit time units.
532 * Dump and reset cache statistics every so often.
534 event_request_timer(scache_status_update
, (char *) 0, var_scache_stat_time
);
535 scache_start_time
= event_time();
538 MAIL_VERSION_STAMP_DECLARE
;
540 /* main - pass control to the multi-threaded skeleton */
542 int main(int argc
, char **argv
)
544 static const CONFIG_TIME_TABLE time_table
[] = {
545 VAR_SCACHE_TTL_LIM
, DEF_SCACHE_TTL_LIM
, &var_scache_ttl_lim
, 1, 0,
546 VAR_SCACHE_STAT_TIME
, DEF_SCACHE_STAT_TIME
, &var_scache_stat_time
, 1, 0,
551 * Fingerprint executables and core dumps.
553 MAIL_VERSION_STAMP_ALLOCATE
;
555 multi_server_main(argc
, argv
, scache_service
,
556 MAIL_SERVER_TIME_TABLE
, time_table
,
557 MAIL_SERVER_POST_INIT
, post_jail_init
,
558 MAIL_SERVER_EXIT
, scache_status_dump
,
559 MAIL_SERVER_SOLITARY
,