9 /* \fBqmqpd\fR [generic Postfix daemon options]
11 /* The Postfix QMQP server receives one message per connection.
12 /* Each message is piped through the \fBcleanup\fR(8)
13 /* daemon, and is placed into the \fBincoming\fR queue as one
14 /* single queue file. The program expects to be run from the
15 /* \fBmaster\fR(8) process manager.
17 /* The QMQP server implements one access policy: only explicitly
18 /* authorized client hosts are allowed to use the service.
22 /* The QMQP server is moderately security-sensitive. It talks to QMQP
23 /* clients and to DNS servers on the network. The QMQP server can be
24 /* run chrooted at fixed low privilege.
26 /* Problems and transactions are logged to \fBsyslogd\fR(8).
28 /* The QMQP protocol provides only one server reply per message
29 /* delivery. It is therefore not possible to reject individual
32 /* The QMQP protocol requires the server to receive the entire
33 /* message before replying. If a message is malformed, or if any
34 /* netstring component is longer than acceptable, Postfix replies
35 /* immediately and closes the connection. It is left up to the
36 /* client to handle the situation.
37 /* CONFIGURATION PARAMETERS
40 /* Changes to \fBmain.cf\fR are picked up automatically, as \fBqmqpd\fR(8)
41 /* processes run for only a limited amount of time. Use the command
42 /* "\fBpostfix reload\fR" to speed up a change.
44 /* The text below provides only a parameter summary. See
45 /* \fBpostconf\fR(5) for more details including examples.
46 /* CONTENT INSPECTION CONTROLS
49 /* .IP "\fBcontent_filter (empty)\fR"
50 /* The name of a mail delivery transport that filters mail after
52 /* .IP "\fBreceive_override_options (empty)\fR"
53 /* Enable or disable recipient validation, built-in content
54 /* filtering, or address mapping.
55 /* RESOURCE AND RATE CONTROLS
58 /* .IP "\fBline_length_limit (2048)\fR"
59 /* Upon input, long lines are chopped up into pieces of at most
60 /* this length; upon delivery, long lines are reconstructed.
61 /* .IP "\fBhopcount_limit (50)\fR"
62 /* The maximal number of Received: message headers that is allowed
63 /* in the primary message headers.
64 /* .IP "\fBmessage_size_limit (10240000)\fR"
65 /* The maximal size in bytes of a message, including envelope information.
66 /* .IP "\fBqmqpd_timeout (300s)\fR"
67 /* The time limit for sending or receiving information over the network.
68 /* TROUBLE SHOOTING CONTROLS
71 /* .IP "\fBdebug_peer_level (2)\fR"
72 /* The increment in verbose logging level when a remote client or
73 /* server matches a pattern in the debug_peer_list parameter.
74 /* .IP "\fBdebug_peer_list (empty)\fR"
75 /* Optional list of remote client or server hostname or network
76 /* address patterns that cause the verbose logging level to increase
77 /* by the amount specified in $debug_peer_level.
78 /* .IP "\fBsoft_bounce (no)\fR"
79 /* Safety net to keep mail queued that would otherwise be returned to
84 /* .IP "\fBqmqpd_error_delay (1s)\fR"
85 /* How long the QMQP server will pause before sending a negative reply
87 /* MISCELLANEOUS CONTROLS
90 /* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
91 /* The default location of the Postfix main.cf and master.cf
92 /* configuration files.
93 /* .IP "\fBdaemon_timeout (18000s)\fR"
94 /* How much time a Postfix daemon process may take to handle a
95 /* request before it is terminated by a built-in watchdog timer.
96 /* .IP "\fBipc_timeout (3600s)\fR"
97 /* The time limit for sending or receiving information over an internal
98 /* communication channel.
99 /* .IP "\fBmax_idle (100s)\fR"
100 /* The maximum amount of time that an idle Postfix daemon process waits
101 /* for an incoming connection before terminating voluntarily.
102 /* .IP "\fBmax_use (100)\fR"
103 /* The maximal number of incoming connections that a Postfix daemon
104 /* process will service before terminating voluntarily.
105 /* .IP "\fBprocess_id (read-only)\fR"
106 /* The process ID of a Postfix command or daemon process.
107 /* .IP "\fBprocess_name (read-only)\fR"
108 /* The process name of a Postfix command or daemon process.
109 /* .IP "\fBqmqpd_authorized_clients (empty)\fR"
110 /* What clients are allowed to connect to the QMQP server port.
111 /* .IP "\fBqueue_directory (see 'postconf -d' output)\fR"
112 /* The location of the Postfix top-level queue directory.
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 /* .IP "\fBverp_delimiter_filter (-=+)\fR"
119 /* The characters Postfix accepts as VERP delimiter characters on the
120 /* Postfix \fBsendmail\fR(1) command line and in SMTP commands.
122 /* Available in Postfix version 2.5 and later:
123 /* .IP "\fBqmqpd_client_port_logging (no)\fR"
124 /* Enable logging of the remote QMQP client port in addition to
125 /* the hostname and IP address.
127 /* http://cr.yp.to/proto/qmqp.html, QMQP protocol
128 /* cleanup(8), message canonicalization
129 /* master(8), process manager
130 /* syslogd(8), system logging
134 /* Use "\fBpostconf readme_directory\fR" or
135 /* "\fBpostconf html_directory\fR" to locate this information.
138 /* QMQP_README, Postfix ezmlm-idx howto.
142 /* The Secure Mailer license must be distributed with this software.
146 /* The qmqpd service was introduced with Postfix version 1.1.
149 /* IBM T.J. Watson Research
151 /* Yorktown Heights, NY 10598, USA
154 /* System library. */
156 #include <sys_defs.h>
163 /* Utility library. */
166 #include <mymalloc.h>
169 #include <netstring.h>
172 /* Global library. */
174 #include <mail_params.h>
175 #include <mail_version.h>
177 #include <rec_type.h>
178 #include <mail_proto.h>
179 #include <cleanup_user.h>
180 #include <mail_date.h>
181 #include <mail_conf.h>
182 #include <debug_peer.h>
183 #include <mail_stream.h>
184 #include <namadr_list.h>
185 #include <quote_822_local.h>
186 #include <match_parent_style.h>
188 #include <verp_sender.h>
189 #include <input_transp.h>
191 /* Single-threaded server skeleton. */
193 #include <mail_server.h>
195 /* Application-specific */
200 * Tunable parameters. Make sure that there is some bound on the length of a
201 * netstring, so that the mail system stays in control even when a malicious
202 * client sends netstrings of unreasonable length. The recipient count limit
203 * is enforced by the message size limit.
205 int var_qmqpd_timeout
;
206 int var_qmqpd_err_sleep
;
207 char *var_filter_xport
;
208 char *var_qmqpd_clients
;
209 char *var_input_transp
;
210 bool var_qmqpd_client_port_log
;
213 * Silly little macros.
215 #define STR(x) vstring_str(x)
216 #define LEN(x) VSTRING_LEN(x)
222 * Access control. This service should be exposed only to explicitly
223 * authorized clients. There is no default authorization.
225 static NAMADR_LIST
*qmqpd_clients
;
228 * Transparency: before mail is queued, do we allow address mapping,
229 * automatic bcc, header/body checks?
231 int qmqpd_input_transp_mask
;
233 /* qmqpd_open_file - open a queue file */
235 static void qmqpd_open_file(QMQPD_STATE
*state
)
240 * Connect to the cleanup server. Log client name/address with queue ID.
242 cleanup_flags
= input_transp_cleanup(CLEANUP_FLAG_MASK_EXTERNAL
,
243 qmqpd_input_transp_mask
);
244 state
->dest
= mail_stream_service(MAIL_CLASS_PUBLIC
, var_cleanup_service
);
246 || attr_print(state
->dest
->stream
, ATTR_FLAG_NONE
,
247 ATTR_TYPE_INT
, MAIL_ATTR_FLAGS
, cleanup_flags
,
249 msg_fatal("unable to connect to the %s %s service",
250 MAIL_CLASS_PUBLIC
, var_cleanup_service
);
251 state
->cleanup
= state
->dest
->stream
;
252 state
->queue_id
= mystrdup(state
->dest
->id
);
253 msg_info("%s: client=%s", state
->queue_id
, state
->namaddr
);
256 * Record the time of arrival. Optionally, enable content filtering (not
257 * bloody likely, but present for the sake of consistency with all other
258 * Postfix points of entrance).
260 rec_fprintf(state
->cleanup
, REC_TYPE_TIME
, REC_TYPE_TIME_FORMAT
,
261 REC_TYPE_TIME_ARG(state
->arrival_time
));
262 if (*var_filter_xport
)
263 rec_fprintf(state
->cleanup
, REC_TYPE_FILT
, "%s", var_filter_xport
);
266 /* qmqpd_read_content - receive message content */
268 static void qmqpd_read_content(QMQPD_STATE
*state
)
270 state
->where
= "receiving message content";
271 netstring_get(state
->client
, state
->message
, var_message_limit
);
274 /* qmqpd_copy_sender - copy envelope sender */
276 static void qmqpd_copy_sender(QMQPD_STATE
*state
)
281 static char verp_delims
[] = "-=";
284 * If the sender address looks like prefix@origin-@[], then request
285 * variable envelope return path delivery, with an envelope sender
286 * address of prefi@origin, and with VERP delimiters of x and =. This
287 * way, the recipients will see envelope sender addresses that look like:
288 * prefixuser=domain@origin.
290 state
->where
= "receiving sender address";
291 netstring_get(state
->client
, state
->buf
, var_line_limit
);
292 VSTRING_TERMINATE(state
->buf
);
294 ((end_origin
= vstring_end(state
->buf
) - 4) > STR(state
->buf
)
295 && strcmp(end_origin
, "-@[]") == 0
296 && (end_prefix
= strchr(STR(state
->buf
), '@')) != 0 /* XXX */
297 && --end_prefix
< end_origin
- 2 /* non-null origin */
298 && end_prefix
> STR(state
->buf
)); /* non-null prefix */
299 if (verp_requested
) {
300 verp_delims
[0] = end_prefix
[0];
301 if (verp_delims_verify(verp_delims
) != 0) {
302 state
->err
|= CLEANUP_STAT_CONT
; /* XXX */
303 vstring_sprintf(state
->why_rejected
, "Invalid VERP delimiters: \"%s\". Need two characters from \"%s\"",
304 verp_delims
, var_verp_filter
);
306 memmove(end_prefix
, end_prefix
+ 1, end_origin
- end_prefix
- 1);
307 vstring_truncate(state
->buf
, end_origin
- STR(state
->buf
) - 1);
309 if (state
->err
== CLEANUP_STAT_OK
310 && REC_PUT_BUF(state
->cleanup
, REC_TYPE_FROM
, state
->buf
) < 0)
311 state
->err
= CLEANUP_STAT_WRITE
;
313 if (state
->err
== CLEANUP_STAT_OK
314 && rec_put(state
->cleanup
, REC_TYPE_VERP
, verp_delims
, 2) < 0)
315 state
->err
= CLEANUP_STAT_WRITE
;
316 state
->sender
= mystrndup(STR(state
->buf
), LEN(state
->buf
));
319 /* qmqpd_write_attributes - save session attributes */
321 static void qmqpd_write_attributes(QMQPD_STATE
*state
)
325 * Logging attributes, also used for XFORWARD.
327 rec_fprintf(state
->cleanup
, REC_TYPE_ATTR
, "%s=%s",
328 MAIL_ATTR_LOG_CLIENT_NAME
, state
->name
);
329 rec_fprintf(state
->cleanup
, REC_TYPE_ATTR
, "%s=%s",
330 MAIL_ATTR_LOG_CLIENT_ADDR
, state
->rfc_addr
);
331 rec_fprintf(state
->cleanup
, REC_TYPE_ATTR
, "%s=%s",
332 MAIL_ATTR_LOG_CLIENT_PORT
, state
->port
);
333 rec_fprintf(state
->cleanup
, REC_TYPE_ATTR
, "%s=%s",
334 MAIL_ATTR_LOG_ORIGIN
, state
->namaddr
);
335 rec_fprintf(state
->cleanup
, REC_TYPE_ATTR
, "%s=%s",
336 MAIL_ATTR_LOG_PROTO_NAME
, state
->protocol
);
339 * For consistency with the smtpd Milter client, we need to provide the
340 * real client attributes to the cleanup Milter client. This does not
341 * matter much with qmqpd which speaks to trusted clients only, but we
342 * want to be sure that the cleanup input protocol is ready when a new
343 * type of network daemon is added to receive mail from the Internet.
345 * See also the comments in smtpd.c.
347 rec_fprintf(state
->cleanup
, REC_TYPE_ATTR
, "%s=%s",
348 MAIL_ATTR_ACT_CLIENT_NAME
, state
->name
);
349 rec_fprintf(state
->cleanup
, REC_TYPE_ATTR
, "%s=%s",
350 MAIL_ATTR_ACT_CLIENT_ADDR
, state
->addr
);
351 rec_fprintf(state
->cleanup
, REC_TYPE_ATTR
, "%s=%s",
352 MAIL_ATTR_ACT_CLIENT_PORT
, state
->port
);
353 rec_fprintf(state
->cleanup
, REC_TYPE_ATTR
, "%s=%u",
354 MAIL_ATTR_ACT_CLIENT_AF
, state
->addr_family
);
355 rec_fprintf(state
->cleanup
, REC_TYPE_ATTR
, "%s=%s",
356 MAIL_ATTR_ACT_PROTO_NAME
, state
->protocol
);
358 /* XXX What about the address rewriting context? */
361 /* qmqpd_copy_recipients - copy message recipients */
363 static void qmqpd_copy_recipients(QMQPD_STATE
*state
)
368 * Remember the first recipient. We are done when we read the over-all
369 * netstring terminator.
371 * XXX This approach violates abstractions, but it is a heck of a lot more
372 * convenient than counting the over-all byte count down to zero, like
375 state
->where
= "receiving recipient address";
376 while ((ch
= VSTREAM_GETC(state
->client
)) != ',') {
377 vstream_ungetc(state
->client
, ch
);
378 netstring_get(state
->client
, state
->buf
, var_line_limit
);
379 if (state
->err
== CLEANUP_STAT_OK
380 && REC_PUT_BUF(state
->cleanup
, REC_TYPE_RCPT
, state
->buf
) < 0)
381 state
->err
= CLEANUP_STAT_WRITE
;
383 if (state
->recipient
== 0)
384 state
->recipient
= mystrndup(STR(state
->buf
), LEN(state
->buf
));
388 /* qmqpd_next_line - get line from buffer, return last char, newline, or -1 */
390 static int qmqpd_next_line(VSTRING
*message
, char **start
, int *len
,
393 char *beyond
= STR(message
) + LEN(message
);
394 char *enough
= *next
+ var_line_limit
;
398 * Stop at newline or at some limit. Don't look beyond the end of the
401 #define UCHARPTR(x) ((unsigned char *) (x))
403 for (cp
= *start
= *next
; /* void */ ; cp
++) {
405 return ((*len
= (*next
= cp
) - *start
) > 0 ? UCHARPTR(cp
)[-1] : -1);
407 return ((*len
= cp
- *start
), (*next
= cp
+ 1), '\n');
409 return ((*len
= cp
- *start
), (*next
= cp
), UCHARPTR(cp
)[-1]);
413 /* qmqpd_write_content - write the message content segment */
415 static void qmqpd_write_content(QMQPD_STATE
*state
)
425 * Start the message content segment. Prepend our own Received: header to
426 * the message content. List the recipient only when a message has one
427 * recipient. Otherwise, don't list the recipient to avoid revealing Bcc:
428 * recipients that are supposed to be invisible.
430 rec_fputs(state
->cleanup
, REC_TYPE_MESG
, "");
431 rec_fprintf(state
->cleanup
, REC_TYPE_NORM
, "Received: from %s (%s [%s])",
432 state
->name
, state
->name
, state
->rfc_addr
);
433 if (state
->rcpt_count
== 1 && state
->recipient
) {
434 rec_fprintf(state
->cleanup
, REC_TYPE_NORM
,
435 "\tby %s (%s) with %s id %s",
436 var_myhostname
, var_mail_name
,
437 state
->protocol
, state
->queue_id
);
438 quote_822_local(state
->buf
, state
->recipient
);
439 rec_fprintf(state
->cleanup
, REC_TYPE_NORM
,
440 "\tfor <%s>; %s", STR(state
->buf
),
441 mail_date(state
->arrival_time
.tv_sec
));
443 rec_fprintf(state
->cleanup
, REC_TYPE_NORM
,
444 "\tby %s (%s) with %s",
445 var_myhostname
, var_mail_name
, state
->protocol
);
446 rec_fprintf(state
->cleanup
, REC_TYPE_NORM
,
447 "\tid %s; %s", state
->queue_id
,
448 mail_date(state
->arrival_time
.tv_sec
));
450 #ifdef RECEIVED_ENVELOPE_FROM
451 quote_822_local(state
->buf
, state
->sender
);
452 rec_fprintf(state
->cleanup
, REC_TYPE_NORM
,
453 "\t(envelope-from <%s>)", STR(state
->buf
));
457 * Write the message content.
459 * XXX Force an empty record when the queue file content begins with
460 * whitespace, so that it won't be considered as being part of our own
461 * Received: header. What an ugly Kluge.
463 * XXX Deal with UNIX-style From_ lines at the start of message content just
466 for (next
= STR(state
->message
); /* void */ ; /* void */ ) {
467 if ((ch
= qmqpd_next_line(state
->message
, &start
, &len
, &next
)) < 0)
470 rec_type
= REC_TYPE_NORM
;
472 rec_type
= REC_TYPE_CONT
;
474 if (strncmp(start
+ strspn(start
, ">"), "From ", 5) == 0) {
475 rec_fprintf(state
->cleanup
, rec_type
,
476 "X-Mailbox-Line: %*s", len
, start
);
480 if (len
> 0 && IS_SPACE_TAB(start
[0]))
481 rec_put(state
->cleanup
, REC_TYPE_NORM
, "", 0);
483 if (rec_put(state
->cleanup
, rec_type
, start
, len
) < 0) {
484 state
->err
= CLEANUP_STAT_WRITE
;
490 /* qmqpd_close_file - close queue file */
492 static void qmqpd_close_file(QMQPD_STATE
*state
)
496 * Send the end-of-segment markers.
498 if (state
->err
== CLEANUP_STAT_OK
)
499 if (rec_fputs(state
->cleanup
, REC_TYPE_XTRA
, "") < 0
500 || rec_fputs(state
->cleanup
, REC_TYPE_END
, "") < 0
501 || vstream_fflush(state
->cleanup
))
502 state
->err
= CLEANUP_STAT_WRITE
;
505 * Finish the queue file or finish the cleanup conversation.
508 state
->err
= mail_stream_finish(state
->dest
, state
->why_rejected
);
510 mail_stream_cleanup(state
->dest
);
514 /* qmqpd_reply - send status to client and optionally log message */
516 static void qmqpd_reply(QMQPD_STATE
*state
, int log_message
,
517 int status_code
, const char *fmt
,...)
522 * Optionally change hard errors into retryable ones. Send the reply and
523 * optionally log it. Always insert a delay before reporting a problem.
524 * This slows down software run-away conditions.
526 if (status_code
== QMQPD_STAT_HARD
&& var_soft_bounce
)
527 status_code
= QMQPD_STAT_RETRY
;
528 VSTRING_RESET(state
->buf
);
529 VSTRING_ADDCH(state
->buf
, status_code
);
531 vstring_vsprintf_append(state
->buf
, fmt
, ap
);
533 NETSTRING_PUT_BUF(state
->client
, state
->buf
);
535 (status_code
== QMQPD_STAT_OK
? msg_info
: msg_warn
) ("%s: %s: %s",
536 state
->queue_id
, state
->namaddr
, STR(state
->buf
) + 1);
537 if (status_code
!= QMQPD_STAT_OK
)
538 sleep(var_qmqpd_err_sleep
);
539 netstring_fflush(state
->client
);
542 /* qmqpd_send_status - send mail transaction completion status */
544 static void qmqpd_send_status(QMQPD_STATE
*state
)
548 * One message may suffer from multiple errors, so complain only about
549 * the most severe error.
553 state
->where
= "sending completion status";
555 if (state
->err
== CLEANUP_STAT_OK
) {
556 qmqpd_reply(state
, DONT_LOG
, QMQPD_STAT_OK
,
557 "Ok: queued as %s", state
->queue_id
);
558 } else if ((state
->err
& CLEANUP_STAT_DEFER
) != 0) {
559 qmqpd_reply(state
, DO_LOG
, QMQPD_STAT_RETRY
,
560 "Error: %s", STR(state
->why_rejected
));
561 } else if ((state
->err
& CLEANUP_STAT_BAD
) != 0) {
562 qmqpd_reply(state
, DO_LOG
, QMQPD_STAT_RETRY
,
563 "Error: internal error %d", state
->err
);
564 } else if ((state
->err
& CLEANUP_STAT_SIZE
) != 0) {
565 qmqpd_reply(state
, DO_LOG
, QMQPD_STAT_HARD
,
566 "Error: message too large");
567 } else if ((state
->err
& CLEANUP_STAT_HOPS
) != 0) {
568 qmqpd_reply(state
, DO_LOG
, QMQPD_STAT_HARD
,
569 "Error: too many hops");
570 } else if ((state
->err
& CLEANUP_STAT_CONT
) != 0) {
571 qmqpd_reply(state
, DO_LOG
, STR(state
->why_rejected
)[0] == '4' ?
572 QMQPD_STAT_RETRY
: QMQPD_STAT_HARD
,
573 "Error: %s", STR(state
->why_rejected
));
574 } else if ((state
->err
& CLEANUP_STAT_WRITE
) != 0) {
575 qmqpd_reply(state
, DO_LOG
, QMQPD_STAT_RETRY
,
576 "Error: queue file write error");
577 } else if ((state
->err
& CLEANUP_STAT_RCPT
) != 0) {
578 qmqpd_reply(state
, DO_LOG
, QMQPD_STAT_HARD
,
579 "Error: no recipients specified");
581 qmqpd_reply(state
, DO_LOG
, QMQPD_STAT_RETRY
,
582 "Error: internal error %d", state
->err
);
586 /* qmqpd_receive - receive QMQP message+sender+recipients */
588 static void qmqpd_receive(QMQPD_STATE
*state
)
592 * Open a queue file. This must be first so that we can simplify the
593 * error logging and always include the queue ID information.
595 qmqpd_open_file(state
);
598 * Read and ignore the over-all netstring length indicator.
600 state
->where
= "receiving QMQP packet header";
601 (void) netstring_get_length(state
->client
);
604 * XXX Read the message content into memory, because Postfix expects to
605 * store the sender before storing the message content. Fixing that
606 * requires changes to pickup, cleanup, qmgr, and perhaps elsewhere, so
607 * that will have to happen later when I have more time. However, QMQP is
608 * used for mailing list distribution, so the bulk of the volume is
609 * expected to be not message content but recipients, and recipients are
610 * not accumulated in memory.
612 qmqpd_read_content(state
);
615 * Read and write the envelope sender.
617 qmqpd_copy_sender(state
);
620 * Record some session attributes.
622 qmqpd_write_attributes(state
);
625 * Read and write the envelope recipients, including the optional big
628 qmqpd_copy_recipients(state
);
631 * Start the message content segment, prepend our own Received: header,
632 * and write the message content.
635 qmqpd_write_content(state
);
638 * Close the queue file.
640 qmqpd_close_file(state
);
643 * Report the completion status to the client.
645 qmqpd_send_status(state
);
648 /* qmqpd_proto - speak the QMQP "protocol" */
650 static void qmqpd_proto(QMQPD_STATE
*state
)
654 netstring_setup(state
->client
, var_qmqpd_timeout
);
656 switch (status
= vstream_setjmp(state
->client
)) {
659 msg_panic("qmqpd_proto: unknown status %d", status
);
661 case NETSTRING_ERR_EOF
:
662 state
->reason
= "lost connection";
665 case NETSTRING_ERR_TIME
:
666 state
->reason
= "read/write timeout";
669 case NETSTRING_ERR_FORMAT
:
670 state
->reason
= "netstring format error";
671 if (vstream_setjmp(state
->client
) == 0)
672 if (state
->reason
&& state
->where
)
673 qmqpd_reply(state
, DONT_LOG
, QMQPD_STAT_HARD
, "%s while %s",
674 state
->reason
, state
->where
);
677 case NETSTRING_ERR_SIZE
:
678 state
->reason
= "netstring length exceeds storage limit";
679 if (vstream_setjmp(state
->client
) == 0)
680 if (state
->reason
&& state
->where
)
681 qmqpd_reply(state
, DONT_LOG
, QMQPD_STAT_HARD
, "%s while %s",
682 state
->reason
, state
->where
);
688 * See if we want to talk to this client at all.
690 if (namadr_list_match(qmqpd_clients
, state
->name
, state
->addr
) == 0) {
691 qmqpd_reply(state
, DONT_LOG
, QMQPD_STAT_HARD
,
692 "Error: %s is not authorized to use this service",
695 qmqpd_receive(state
);
700 * Log abnormal session termination. Indicate the last recognized state
701 * before things went wrong.
703 if (state
->reason
&& state
->where
)
704 msg_info("%s: %s: %s while %s",
705 state
->queue_id
, state
->namaddr
, state
->reason
, state
->where
);
708 /* qmqpd_service - service one client */
710 static void qmqpd_service(VSTREAM
*stream
, char *unused_service
, char **argv
)
715 * Sanity check. This service takes no command-line arguments.
718 msg_fatal("unexpected command-line argument: %s", argv
[0]);
721 * This routine runs when a client has connected to our network port.
722 * Look up and sanitize the peer name and initialize some connection-
725 state
= qmqpd_state_alloc(stream
);
728 * See if we need to turn on verbose logging for this client.
730 debug_peer_check(state
->name
, state
->addr
);
733 * Provide the QMQP service.
735 msg_info("connect from %s", state
->namaddr
);
737 msg_info("disconnect from %s", state
->namaddr
);
740 * After the client has gone away, clean up whatever we have set up at
743 debug_peer_restore();
744 qmqpd_state_free(state
);
747 /* pre_accept - see if tables have changed */
749 static void pre_accept(char *unused_name
, char **unused_argv
)
753 if ((table
= dict_changed_name()) != 0) {
754 msg_info("table %s has changed -- restarting", table
);
759 /* pre_jail_init - pre-jail initialization */
761 static void pre_jail_init(char *unused_name
, char **unused_argv
)
765 namadr_list_init(match_parent_style(VAR_QMQPD_CLIENTS
),
769 /* post_jail_init - post-jail initialization */
771 static void post_jail_init(char *unused_name
, char **unused_argv
)
775 * Initialize the receive transparency options: do we want unknown
776 * recipient checks, do we want address mapping.
778 qmqpd_input_transp_mask
=
779 input_transp_mask(VAR_INPUT_TRANSP
, var_input_transp
);
782 MAIL_VERSION_STAMP_DECLARE
;
784 /* main - the main program */
786 int main(int argc
, char **argv
)
788 static const CONFIG_TIME_TABLE time_table
[] = {
789 VAR_QMTPD_TMOUT
, DEF_QMTPD_TMOUT
, &var_qmqpd_timeout
, 1, 0,
790 VAR_QMTPD_ERR_SLEEP
, DEF_QMTPD_ERR_SLEEP
, &var_qmqpd_err_sleep
, 0, 0,
793 static const CONFIG_STR_TABLE str_table
[] = {
794 VAR_FILTER_XPORT
, DEF_FILTER_XPORT
, &var_filter_xport
, 0, 0,
795 VAR_QMQPD_CLIENTS
, DEF_QMQPD_CLIENTS
, &var_qmqpd_clients
, 0, 0,
796 VAR_INPUT_TRANSP
, DEF_INPUT_TRANSP
, &var_input_transp
, 0, 0,
799 static const CONFIG_BOOL_TABLE bool_table
[] = {
800 VAR_QMQPD_CLIENT_PORT_LOG
, DEF_QMQPD_CLIENT_PORT_LOG
, &var_qmqpd_client_port_log
,
805 * Fingerprint executables and core dumps.
807 MAIL_VERSION_STAMP_ALLOCATE
;
810 * Pass control to the single-threaded service skeleton.
812 single_server_main(argc
, argv
, qmqpd_service
,
813 MAIL_SERVER_TIME_TABLE
, time_table
,
814 MAIL_SERVER_STR_TABLE
, str_table
,
815 MAIL_SERVER_BOOL_TABLE
, bool_table
,
816 MAIL_SERVER_PRE_INIT
, pre_jail_init
,
817 MAIL_SERVER_PRE_ACCEPT
, pre_accept
,
818 MAIL_SERVER_POST_INIT
, post_jail_init
,