1 /* $NetBSD: scache.c,v 1.2 2017/02/14 01:16:47 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* scache 8 6 /* SUMMARY 7 /* Postfix shared connection cache server 8 /* SYNOPSIS 9 /* \fBscache\fR [generic Postfix daemon options] 10 /* DESCRIPTION 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. 14 /* 15 /* The connection cache is organized into logical destination 16 /* names, physical endpoint names, and connections. 17 /* 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. 22 /* 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. 27 /* 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 34 /* information. 35 /* 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. 39 /* 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 45 /* object. 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 54 /* object. 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. 58 /* SECURITY 59 /* .ad 60 /* .fi 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. 64 /* 65 /* The \fBscache\fR(8) server is not a trusted process. It must 66 /* not be used to store information that is security sensitive. 67 /* DIAGNOSTICS 68 /* Problems and transactions are logged to \fBsyslogd\fR(8). 69 /* BUGS 70 /* The session cache cannot be shared among multiple machines. 71 /* 72 /* When a connection expires from the cache, it is closed without 73 /* the appropriate protocol specific handshake. 74 /* CONFIGURATION PARAMETERS 75 /* .ad 76 /* .fi 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. 80 /* 81 /* The text below provides only a parameter summary. See 82 /* \fBpostconf\fR(5) for more details including examples. 83 /* RESOURCE CONTROLS 84 /* .ad 85 /* .fi 86 /* .IP "\fBconnection_cache_ttl_limit (2s)\fR" 87 /* The maximal time-to-live value that the \fBscache\fR(8) connection 88 /* cache server 89 /* allows. 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 95 /* .ad 96 /* .fi 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". 118 /* SEE ALSO 119 /* smtp(8), SMTP client 120 /* postconf(5), configuration parameters 121 /* master(8), process manager 122 /* syslogd(8), system logging 123 /* README FILES 124 /* .ad 125 /* .fi 126 /* Use "\fBpostconf readme_directory\fR" or 127 /* "\fBpostconf html_directory\fR" to locate this information. 128 /* .na 129 /* .nf 130 /* CONNECTION_CACHE_README, Postfix connection cache 131 /* LICENSE 132 /* .ad 133 /* .fi 134 /* The Secure Mailer license must be distributed with this software. 135 /* HISTORY 136 /* This service was introduced with Postfix version 2.2. 137 /* AUTHOR(S) 138 /* Wietse Venema 139 /* IBM T.J. Watson Research 140 /* P.O. Box 704 141 /* Yorktown Heights, NY 10598, USA 142 /* 143 /* Wietse Venema 144 /* Google, Inc. 145 /* 111 8th Avenue 146 /* New York, NY 10011, USA 147 /*--*/ 148 149 /* System library. */ 150 151 #include <sys_defs.h> 152 #include <time.h> 153 154 /* Utility library. */ 155 156 #include <msg.h> 157 #include <iostuff.h> 158 #include <htable.h> 159 #include <ring.h> 160 #include <events.h> 161 162 /* Global library. */ 163 164 #include <mail_params.h> 165 #include <mail_version.h> 166 #include <mail_proto.h> 167 #include <scache.h> 168 169 /* Single server skeleton. */ 170 171 #include <mail_server.h> 172 #include <mail_conf.h> 173 174 /* Application-specific. */ 175 176 /* 177 * Tunable parameters. 178 */ 179 int var_scache_ttl_lim; 180 int var_scache_stat_time; 181 182 /* 183 * Request parameters. 184 */ 185 static VSTRING *scache_request; 186 static VSTRING *scache_dest_label; 187 static VSTRING *scache_dest_prop; 188 static VSTRING *scache_endp_label; 189 static VSTRING *scache_endp_prop; 190 191 #ifdef CANT_WRITE_BEFORE_SENDING_FD 192 static VSTRING *scache_dummy; 193 194 #endif 195 196 /* 197 * Session cache instance. 198 */ 199 static SCACHE *scache; 200 201 /* 202 * Statistics. 203 */ 204 static int scache_dest_hits; 205 static int scache_dest_miss; 206 static int scache_dest_count; 207 static int scache_endp_hits; 208 static int scache_endp_miss; 209 static int scache_endp_count; 210 static int scache_sess_count; 211 time_t scache_start_time; 212 213 /* 214 * Silly little macros. 215 */ 216 #define STR(x) vstring_str(x) 217 #define VSTREQ(x,y) (strcmp(STR(x),y) == 0) 218 219 /* scache_save_endp_service - protocol to save endpoint->stream binding */ 220 221 static void scache_save_endp_service(VSTREAM *client_stream) 222 { 223 const char *myname = "scache_save_endp_service"; 224 int ttl; 225 int fd; 226 SCACHE_SIZE size; 227 228 if (attr_scan(client_stream, 229 ATTR_FLAG_STRICT, 230 RECV_ATTR_INT(MAIL_ATTR_TTL, &ttl), 231 RECV_ATTR_STR(MAIL_ATTR_LABEL, scache_endp_label), 232 RECV_ATTR_STR(MAIL_ATTR_PROP, scache_endp_prop), 233 ATTR_TYPE_END) != 3 234 || ttl <= 0) { 235 msg_warn("%s: bad or missing request parameter", myname); 236 attr_print(client_stream, ATTR_FLAG_NONE, 237 SEND_ATTR_INT(MAIL_ATTR_STATUS, SCACHE_STAT_BAD), 238 ATTR_TYPE_END); 239 return; 240 } else if ( 241 #ifdef CANT_WRITE_BEFORE_SENDING_FD 242 attr_print(client_stream, ATTR_FLAG_NONE, 243 SEND_ATTR_STR(MAIL_ATTR_DUMMY, ""), 244 ATTR_TYPE_END) != 0 245 || vstream_fflush(client_stream) != 0 246 || read_wait(vstream_fileno(client_stream), 247 client_stream->timeout) < 0 /* XXX */ 248 || 249 #endif 250 (fd = LOCAL_RECV_FD(vstream_fileno(client_stream))) < 0) { 251 msg_warn("%s: unable to receive file descriptor: %m", myname); 252 (void) attr_print(client_stream, ATTR_FLAG_NONE, 253 SEND_ATTR_INT(MAIL_ATTR_STATUS, SCACHE_STAT_FAIL), 254 ATTR_TYPE_END); 255 return; 256 } else { 257 scache_save_endp(scache, 258 ttl > var_scache_ttl_lim ? var_scache_ttl_lim : ttl, 259 STR(scache_endp_label), STR(scache_endp_prop), fd); 260 (void) attr_print(client_stream, ATTR_FLAG_NONE, 261 SEND_ATTR_INT(MAIL_ATTR_STATUS, SCACHE_STAT_OK), 262 ATTR_TYPE_END); 263 scache_size(scache, &size); 264 if (size.endp_count > scache_endp_count) 265 scache_endp_count = size.endp_count; 266 if (size.sess_count > scache_sess_count) 267 scache_sess_count = size.sess_count; 268 return; 269 } 270 } 271 272 /* scache_find_endp_service - protocol to find connection for endpoint */ 273 274 static void scache_find_endp_service(VSTREAM *client_stream) 275 { 276 const char *myname = "scache_find_endp_service"; 277 int fd; 278 279 if (attr_scan(client_stream, 280 ATTR_FLAG_STRICT, 281 RECV_ATTR_STR(MAIL_ATTR_LABEL, scache_endp_label), 282 ATTR_TYPE_END) != 1) { 283 msg_warn("%s: bad or missing request parameter", myname); 284 attr_print(client_stream, ATTR_FLAG_NONE, 285 SEND_ATTR_INT(MAIL_ATTR_STATUS, SCACHE_STAT_BAD), 286 SEND_ATTR_STR(MAIL_ATTR_PROP, ""), 287 ATTR_TYPE_END); 288 return; 289 } else if ((fd = scache_find_endp(scache, STR(scache_endp_label), 290 scache_endp_prop)) < 0) { 291 attr_print(client_stream, ATTR_FLAG_NONE, 292 SEND_ATTR_INT(MAIL_ATTR_STATUS, SCACHE_STAT_FAIL), 293 SEND_ATTR_STR(MAIL_ATTR_PROP, ""), 294 ATTR_TYPE_END); 295 scache_endp_miss++; 296 return; 297 } else { 298 attr_print(client_stream, ATTR_FLAG_NONE, 299 SEND_ATTR_INT(MAIL_ATTR_STATUS, SCACHE_STAT_OK), 300 SEND_ATTR_STR(MAIL_ATTR_PROP, STR(scache_endp_prop)), 301 ATTR_TYPE_END); 302 if (vstream_fflush(client_stream) != 0 303 #ifdef CANT_WRITE_BEFORE_SENDING_FD 304 || attr_scan(client_stream, ATTR_FLAG_STRICT, 305 RECV_ATTR_STR(MAIL_ATTR_DUMMY, scache_dummy), 306 ATTR_TYPE_END) != 1 307 #endif 308 || LOCAL_SEND_FD(vstream_fileno(client_stream), fd) < 0 309 #ifdef MUST_READ_AFTER_SENDING_FD 310 || attr_scan(client_stream, ATTR_FLAG_STRICT, 311 RECV_ATTR_STR(MAIL_ATTR_DUMMY, scache_dummy), 312 ATTR_TYPE_END) != 1 313 #endif 314 ) 315 msg_warn("%s: cannot send file descriptor: %m", myname); 316 if (close(fd) < 0) 317 msg_warn("close(%d): %m", fd); 318 scache_endp_hits++; 319 return; 320 } 321 } 322 323 /* scache_save_dest_service - protocol to save destination->endpoint binding */ 324 325 static void scache_save_dest_service(VSTREAM *client_stream) 326 { 327 const char *myname = "scache_save_dest_service"; 328 int ttl; 329 SCACHE_SIZE size; 330 331 if (attr_scan(client_stream, 332 ATTR_FLAG_STRICT, 333 RECV_ATTR_INT(MAIL_ATTR_TTL, &ttl), 334 RECV_ATTR_STR(MAIL_ATTR_LABEL, scache_dest_label), 335 RECV_ATTR_STR(MAIL_ATTR_PROP, scache_dest_prop), 336 RECV_ATTR_STR(MAIL_ATTR_LABEL, scache_endp_label), 337 ATTR_TYPE_END) != 4 338 || ttl <= 0) { 339 msg_warn("%s: bad or missing request parameter", myname); 340 attr_print(client_stream, ATTR_FLAG_NONE, 341 SEND_ATTR_INT(MAIL_ATTR_STATUS, SCACHE_STAT_BAD), 342 ATTR_TYPE_END); 343 return; 344 } else { 345 scache_save_dest(scache, 346 ttl > var_scache_ttl_lim ? var_scache_ttl_lim : ttl, 347 STR(scache_dest_label), STR(scache_dest_prop), 348 STR(scache_endp_label)); 349 attr_print(client_stream, ATTR_FLAG_NONE, 350 SEND_ATTR_INT(MAIL_ATTR_STATUS, SCACHE_STAT_OK), 351 ATTR_TYPE_END); 352 scache_size(scache, &size); 353 if (size.dest_count > scache_dest_count) 354 scache_dest_count = size.dest_count; 355 if (size.endp_count > scache_endp_count) 356 scache_endp_count = size.endp_count; 357 return; 358 } 359 } 360 361 /* scache_find_dest_service - protocol to find connection for destination */ 362 363 static void scache_find_dest_service(VSTREAM *client_stream) 364 { 365 const char *myname = "scache_find_dest_service"; 366 int fd; 367 368 if (attr_scan(client_stream, 369 ATTR_FLAG_STRICT, 370 RECV_ATTR_STR(MAIL_ATTR_LABEL, scache_dest_label), 371 ATTR_TYPE_END) != 1) { 372 msg_warn("%s: bad or missing request parameter", myname); 373 attr_print(client_stream, ATTR_FLAG_NONE, 374 SEND_ATTR_INT(MAIL_ATTR_STATUS, SCACHE_STAT_BAD), 375 SEND_ATTR_STR(MAIL_ATTR_PROP, ""), 376 SEND_ATTR_STR(MAIL_ATTR_PROP, ""), 377 ATTR_TYPE_END); 378 return; 379 } else if ((fd = scache_find_dest(scache, STR(scache_dest_label), 380 scache_dest_prop, 381 scache_endp_prop)) < 0) { 382 attr_print(client_stream, ATTR_FLAG_NONE, 383 SEND_ATTR_INT(MAIL_ATTR_STATUS, SCACHE_STAT_FAIL), 384 SEND_ATTR_STR(MAIL_ATTR_PROP, ""), 385 SEND_ATTR_STR(MAIL_ATTR_PROP, ""), 386 ATTR_TYPE_END); 387 scache_dest_miss++; 388 return; 389 } else { 390 attr_print(client_stream, ATTR_FLAG_NONE, 391 SEND_ATTR_INT(MAIL_ATTR_STATUS, SCACHE_STAT_OK), 392 SEND_ATTR_STR(MAIL_ATTR_PROP, STR(scache_dest_prop)), 393 SEND_ATTR_STR(MAIL_ATTR_PROP, STR(scache_endp_prop)), 394 ATTR_TYPE_END); 395 if (vstream_fflush(client_stream) != 0 396 #ifdef CANT_WRITE_BEFORE_SENDING_FD 397 || attr_scan(client_stream, ATTR_FLAG_STRICT, 398 RECV_ATTR_STR(MAIL_ATTR_DUMMY, scache_dummy), 399 ATTR_TYPE_END) != 1 400 #endif 401 || LOCAL_SEND_FD(vstream_fileno(client_stream), fd) < 0 402 #ifdef MUST_READ_AFTER_SENDING_FD 403 || attr_scan(client_stream, ATTR_FLAG_STRICT, 404 RECV_ATTR_STR(MAIL_ATTR_DUMMY, scache_dummy), 405 ATTR_TYPE_END) != 1 406 #endif 407 ) 408 msg_warn("%s: cannot send file descriptor: %m", myname); 409 if (close(fd) < 0) 410 msg_warn("close(%d): %m", fd); 411 scache_dest_hits++; 412 return; 413 } 414 } 415 416 /* scache_service - perform service for client */ 417 418 static void scache_service(VSTREAM *client_stream, char *unused_service, 419 char **argv) 420 { 421 422 /* 423 * Sanity check. This service takes no command-line arguments. 424 */ 425 if (argv[0]) 426 msg_fatal("unexpected command-line argument: %s", argv[0]); 427 428 /* 429 * This routine runs whenever a client connects to the UNIX-domain socket 430 * dedicated to the scache service. All connection-management stuff is 431 * handled by the common code in multi_server.c. 432 * 433 * XXX Workaround: with some requests, the client sends a dummy message 434 * after the server replies (yes that's a botch). When the scache server 435 * is slow, this dummy message may become concatenated with the next 436 * request from the same client. The do-while loop below will repeat 437 * instead of discarding the client request. We must process it now 438 * because there will be no select() notification. 439 */ 440 do { 441 if (attr_scan(client_stream, 442 ATTR_FLAG_MORE | ATTR_FLAG_STRICT, 443 RECV_ATTR_STR(MAIL_ATTR_REQ, scache_request), 444 ATTR_TYPE_END) == 1) { 445 if (VSTREQ(scache_request, SCACHE_REQ_SAVE_DEST)) { 446 scache_save_dest_service(client_stream); 447 } else if (VSTREQ(scache_request, SCACHE_REQ_FIND_DEST)) { 448 scache_find_dest_service(client_stream); 449 } else if (VSTREQ(scache_request, SCACHE_REQ_SAVE_ENDP)) { 450 scache_save_endp_service(client_stream); 451 } else if (VSTREQ(scache_request, SCACHE_REQ_FIND_ENDP)) { 452 scache_find_endp_service(client_stream); 453 } else { 454 msg_warn("unrecognized request: \"%s\", ignored", 455 STR(scache_request)); 456 attr_print(client_stream, ATTR_FLAG_NONE, 457 SEND_ATTR_INT(MAIL_ATTR_STATUS, SCACHE_STAT_BAD), 458 ATTR_TYPE_END); 459 } 460 } 461 } while (vstream_peek(client_stream) > 0); 462 vstream_fflush(client_stream); 463 } 464 465 /* scache_status_dump - log and reset cache statistics */ 466 467 static void scache_status_dump(char *unused_name, char **unused_argv) 468 { 469 if (scache_dest_hits || scache_dest_miss 470 || scache_endp_hits || scache_endp_miss 471 || scache_dest_count || scache_endp_count 472 || scache_sess_count) 473 msg_info("statistics: start interval %.15s", 474 ctime(&scache_start_time) + 4); 475 476 if (scache_dest_hits || scache_dest_miss) { 477 msg_info("statistics: domain lookup hits=%d miss=%d success=%d%%", 478 scache_dest_hits, scache_dest_miss, 479 scache_dest_hits * 100 480 / (scache_dest_hits + scache_dest_miss)); 481 scache_dest_hits = scache_dest_miss = 0; 482 } 483 if (scache_endp_hits || scache_endp_miss) { 484 msg_info("statistics: address lookup hits=%d miss=%d success=%d%%", 485 scache_endp_hits, scache_endp_miss, 486 scache_endp_hits * 100 487 / (scache_endp_hits + scache_endp_miss)); 488 scache_endp_hits = scache_endp_miss = 0; 489 } 490 if (scache_dest_count || scache_endp_count || scache_sess_count) { 491 msg_info("statistics: max simultaneous domains=%d addresses=%d connection=%d", 492 scache_dest_count, scache_endp_count, scache_sess_count); 493 scache_dest_count = 0; 494 scache_endp_count = 0; 495 scache_sess_count = 0; 496 } 497 scache_start_time = event_time(); 498 } 499 500 /* scache_status_update - log and reset cache statistics periodically */ 501 502 static void scache_status_update(int unused_event, void *context) 503 { 504 scache_status_dump((char *) 0, (char **) 0); 505 event_request_timer(scache_status_update, context, var_scache_stat_time); 506 } 507 508 /* post_jail_init - initialization after privilege drop */ 509 510 static void post_jail_init(char *unused_name, char **unused_argv) 511 { 512 513 /* 514 * Pre-allocate the cache instance. 515 */ 516 scache = scache_multi_create(); 517 518 /* 519 * Pre-allocate buffers. 520 */ 521 scache_request = vstring_alloc(10); 522 scache_dest_label = vstring_alloc(10); 523 scache_dest_prop = vstring_alloc(10); 524 scache_endp_label = vstring_alloc(10); 525 scache_endp_prop = vstring_alloc(10); 526 #ifdef CANT_WRITE_BEFORE_SENDING_FD 527 scache_dummy = vstring_alloc(10); 528 #endif 529 530 /* 531 * Disable the max_use limit. We still terminate when no client is 532 * connected for $idle_limit time units. 533 */ 534 var_use_limit = 0; 535 536 /* 537 * Dump and reset cache statistics every so often. 538 */ 539 event_request_timer(scache_status_update, (void *) 0, var_scache_stat_time); 540 scache_start_time = event_time(); 541 } 542 543 MAIL_VERSION_STAMP_DECLARE; 544 545 /* main - pass control to the multi-threaded skeleton */ 546 547 int main(int argc, char **argv) 548 { 549 static const CONFIG_TIME_TABLE time_table[] = { 550 VAR_SCACHE_TTL_LIM, DEF_SCACHE_TTL_LIM, &var_scache_ttl_lim, 1, 0, 551 VAR_SCACHE_STAT_TIME, DEF_SCACHE_STAT_TIME, &var_scache_stat_time, 1, 0, 552 0, 553 }; 554 555 /* 556 * Fingerprint executables and core dumps. 557 */ 558 MAIL_VERSION_STAMP_ALLOCATE; 559 560 multi_server_main(argc, argv, scache_service, 561 CA_MAIL_SERVER_TIME_TABLE(time_table), 562 CA_MAIL_SERVER_POST_INIT(post_jail_init), 563 CA_MAIL_SERVER_EXIT(scache_status_dump), 564 CA_MAIL_SERVER_SOLITARY, 565 0); 566 } 567