/*++ /* NAME /* qmqpd 8 /* SUMMARY /* Postfix QMQP server /* SYNOPSIS /* \fBqmqpd\fR [generic Postfix daemon options] /* DESCRIPTION /* The Postfix QMQP server receives one message per connection. /* Each message is piped through the \fBcleanup\fR(8) /* daemon, and is placed into the \fBincoming\fR queue as one /* single queue file. The program expects to be run from the /* \fBmaster\fR(8) process manager. /* /* The QMQP server implements one access policy: only explicitly /* authorized client hosts are allowed to use the service. /* SECURITY /* .ad /* .fi /* The QMQP server is moderately security-sensitive. It talks to QMQP /* clients and to DNS servers on the network. The QMQP server can be /* run chrooted at fixed low privilege. /* DIAGNOSTICS /* Problems and transactions are logged to \fBsyslogd\fR(8) /* or \fBpostlogd\fR(8). /* BUGS /* The QMQP protocol provides only one server reply per message /* delivery. It is therefore not possible to reject individual /* recipients. /* /* The QMQP protocol requires the server to receive the entire /* message before replying. If a message is malformed, or if any /* netstring component is longer than acceptable, Postfix replies /* immediately and closes the connection. It is left up to the /* client to handle the situation. /* CONFIGURATION PARAMETERS /* .ad /* .fi /* Changes to \fBmain.cf\fR are picked up automatically, as \fBqmqpd\fR(8) /* processes run for only a limited amount of time. Use the command /* "\fBpostfix reload\fR" to speed up a change. /* /* The text below provides only a parameter summary. See /* \fBpostconf\fR(5) for more details including examples. /* CONTENT INSPECTION CONTROLS /* .ad /* .fi /* .IP "\fBcontent_filter (empty)\fR" /* After the message is queued, send the entire message to the /* specified \fItransport:destination\fR. /* .IP "\fBreceive_override_options (empty)\fR" /* Enable or disable recipient validation, built-in content /* filtering, or address mapping. /* SMTPUTF8 CONTROLS /* .ad /* .fi /* Preliminary SMTPUTF8 support is introduced with Postfix 3.0. /* .IP "\fBsmtputf8_enable (yes)\fR" /* Enable preliminary SMTPUTF8 support for the protocols described /* in RFC 6531..6533. /* .IP "\fBsmtputf8_autodetect_classes (sendmail, verify)\fR" /* Detect that a message requires SMTPUTF8 support for the specified /* mail origin classes. /* .PP /* Available in Postfix version 3.2 and later: /* .IP "\fBenable_idna2003_compatibility (no)\fR" /* Enable 'transitional' compatibility between IDNA2003 and IDNA2008, /* when converting UTF-8 domain names to/from the ASCII form that is /* used for DNS lookups. /* RESOURCE AND RATE CONTROLS /* .ad /* .fi /* .IP "\fBline_length_limit (2048)\fR" /* Upon input, long lines are chopped up into pieces of at most /* this length; upon delivery, long lines are reconstructed. /* .IP "\fBhopcount_limit (50)\fR" /* The maximal number of Received: message headers that is allowed /* in the primary message headers. /* .IP "\fBmessage_size_limit (10240000)\fR" /* The maximal size in bytes of a message, including envelope information. /* .IP "\fBqmqpd_timeout (300s)\fR" /* The time limit for sending or receiving information over the network. /* TROUBLE SHOOTING CONTROLS /* .ad /* .fi /* .IP "\fBdebug_peer_level (2)\fR" /* The increment in verbose logging level when a nexthop destination, /* remote client or server name or network address matches a pattern /* given with the debug_peer_list parameter. /* .IP "\fBdebug_peer_list (empty)\fR" /* Optional list of nexthop destination, remote client or server /* name or network address patterns that, if matched, cause the verbose /* logging level to increase by the amount specified in $debug_peer_level. /* .IP "\fBsoft_bounce (no)\fR" /* Safety net to keep mail queued that would otherwise be returned to /* the sender. /* TARPIT CONTROLS /* .ad /* .fi /* .IP "\fBqmqpd_error_delay (1s)\fR" /* How long the Postfix QMQP server will pause before sending a negative /* reply to the remote QMQP client. /* MISCELLANEOUS CONTROLS /* .ad /* .fi /* .IP "\fBconfig_directory (see 'postconf -d' output)\fR" /* The default location of the Postfix main.cf and master.cf /* configuration files. /* .IP "\fBdaemon_timeout (18000s)\fR" /* How much time a Postfix daemon process may take to handle a /* request before it is terminated by a built-in watchdog timer. /* .IP "\fBipc_timeout (3600s)\fR" /* The time limit for sending or receiving information over an internal /* communication channel. /* .IP "\fBmax_idle (100s)\fR" /* The maximum amount of time that an idle Postfix daemon process waits /* for an incoming connection before terminating voluntarily. /* .IP "\fBmax_use (100)\fR" /* The maximal number of incoming connections that a Postfix daemon /* process will service before terminating voluntarily. /* .IP "\fBprocess_id (read-only)\fR" /* The process ID of a Postfix command or daemon process. /* .IP "\fBprocess_name (read-only)\fR" /* The process name of a Postfix command or daemon process. /* .IP "\fBqmqpd_authorized_clients (empty)\fR" /* What remote QMQP clients are allowed to connect to the Postfix QMQP /* server port. /* .IP "\fBqueue_directory (see 'postconf -d' output)\fR" /* The location of the Postfix top-level queue directory. /* .IP "\fBsyslog_facility (mail)\fR" /* The syslog facility of Postfix logging. /* .IP "\fBsyslog_name (see 'postconf -d' output)\fR" /* A prefix that is prepended to the process name in syslog /* records, so that, for example, "smtpd" becomes "prefix/smtpd". /* .IP "\fBverp_delimiter_filter (-=+)\fR" /* The characters Postfix accepts as VERP delimiter characters on the /* Postfix \fBsendmail\fR(1) command line and in SMTP commands. /* .PP /* Available in Postfix version 2.5 and later: /* .IP "\fBqmqpd_client_port_logging (no)\fR" /* Enable logging of the remote QMQP client port in addition to /* the hostname and IP address. /* .PP /* Available in Postfix 3.3 and later: /* .IP "\fBservice_name (read-only)\fR" /* The master.cf service name of a Postfix daemon process. /* SEE ALSO /* http://cr.yp.to/proto/qmqp.html, QMQP protocol /* cleanup(8), message canonicalization /* master(8), process manager /* postlogd(8), Postfix logging /* syslogd(8), system logging /* README FILES /* .ad /* .fi /* Use "\fBpostconf readme_directory\fR" or /* "\fBpostconf html_directory\fR" to locate this information. /* .na /* .nf /* QMQP_README, Postfix ezmlm-idx howto. /* LICENSE /* .ad /* .fi /* The Secure Mailer license must be distributed with this software. /* HISTORY /* .ad /* .fi /* The qmqpd service was introduced with Postfix version 1.1. /* AUTHOR(S) /* Wietse Venema /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA /* /* Wietse Venema /* Google, Inc. /* 111 8th Avenue /* New York, NY 10011, USA /*--*/ /* System library. */ #include #include #include #include #include #include /* Utility library. */ #include #include #include #include #include #include #include /* Global library. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Single-threaded server skeleton. */ #include /* Application-specific */ #include /* * Tunable parameters. Make sure that there is some bound on the length of a * netstring, so that the mail system stays in control even when a malicious * client sends netstrings of unreasonable length. The recipient count limit * is enforced by the message size limit. */ int var_qmqpd_timeout; int var_qmqpd_err_sleep; char *var_filter_xport; char *var_qmqpd_clients; char *var_input_transp; bool var_qmqpd_client_port_log; /* * Silly little macros. */ #define STR(x) vstring_str(x) #define LEN(x) VSTRING_LEN(x) #define DO_LOG 1 #define DONT_LOG 0 /* * Access control. This service should be exposed only to explicitly * authorized clients. There is no default authorization. */ static NAMADR_LIST *qmqpd_clients; /* * Transparency: before mail is queued, do we allow address mapping, * automatic bcc, header/body checks? */ int qmqpd_input_transp_mask; /* qmqpd_open_file - open a queue file */ static void qmqpd_open_file(QMQPD_STATE *state) { int cleanup_flags; /* * Connect to the cleanup server. Log client name/address with queue ID. */ cleanup_flags = input_transp_cleanup(CLEANUP_FLAG_MASK_EXTERNAL, qmqpd_input_transp_mask); cleanup_flags |= smtputf8_autodetect(MAIL_SRC_MASK_QMQPD); state->dest = mail_stream_service(MAIL_CLASS_PUBLIC, var_cleanup_service); if (state->dest == 0 || attr_print(state->dest->stream, ATTR_FLAG_NONE, SEND_ATTR_INT(MAIL_ATTR_FLAGS, cleanup_flags), ATTR_TYPE_END) != 0) msg_fatal("unable to connect to the %s %s service", MAIL_CLASS_PUBLIC, var_cleanup_service); state->cleanup = state->dest->stream; state->queue_id = mystrdup(state->dest->id); msg_info("%s: client=%s", state->queue_id, state->namaddr); /* * Record the time of arrival. Optionally, enable content filtering (not * bloody likely, but present for the sake of consistency with all other * Postfix points of entrance). */ rec_fprintf(state->cleanup, REC_TYPE_TIME, REC_TYPE_TIME_FORMAT, REC_TYPE_TIME_ARG(state->arrival_time)); if (*var_filter_xport) rec_fprintf(state->cleanup, REC_TYPE_FILT, "%s", var_filter_xport); } /* qmqpd_read_content - receive message content */ static void qmqpd_read_content(QMQPD_STATE *state) { state->where = "receiving message content"; netstring_get(state->client, state->message, var_message_limit); } /* qmqpd_copy_sender - copy envelope sender */ static void qmqpd_copy_sender(QMQPD_STATE *state) { char *end_prefix; char *end_origin; int verp_requested; static char verp_delims[] = "-="; /* * If the sender address looks like prefix@origin-@[], then request * variable envelope return path delivery, with an envelope sender * address of prefi@origin, and with VERP delimiters of x and =. This * way, the recipients will see envelope sender addresses that look like: * prefixuser=domain@origin. */ state->where = "receiving sender address"; netstring_get(state->client, state->buf, var_line_limit); VSTRING_TERMINATE(state->buf); verp_requested = ((end_origin = vstring_end(state->buf) - 4) > STR(state->buf) && strcmp(end_origin, "-@[]") == 0 && (end_prefix = strchr(STR(state->buf), '@')) != 0 /* XXX */ && --end_prefix < end_origin - 2 /* non-null origin */ && end_prefix > STR(state->buf)); /* non-null prefix */ if (verp_requested) { verp_delims[0] = end_prefix[0]; if (verp_delims_verify(verp_delims) != 0) { state->err |= CLEANUP_STAT_CONT; /* XXX */ vstring_sprintf(state->why_rejected, "Invalid VERP delimiters: \"%s\". Need two characters from \"%s\"", verp_delims, var_verp_filter); } memmove(end_prefix, end_prefix + 1, end_origin - end_prefix - 1); vstring_truncate(state->buf, end_origin - STR(state->buf) - 1); } if (state->err == CLEANUP_STAT_OK && REC_PUT_BUF(state->cleanup, REC_TYPE_FROM, state->buf) < 0) state->err = CLEANUP_STAT_WRITE; if (verp_requested) if (state->err == CLEANUP_STAT_OK && rec_put(state->cleanup, REC_TYPE_VERP, verp_delims, 2) < 0) state->err = CLEANUP_STAT_WRITE; state->sender = mystrndup(STR(state->buf), LEN(state->buf)); } /* qmqpd_write_attributes - save session attributes */ static void qmqpd_write_attributes(QMQPD_STATE *state) { /* * Logging attributes, also used for XFORWARD. */ rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s", MAIL_ATTR_LOG_CLIENT_NAME, state->name); rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s", MAIL_ATTR_LOG_CLIENT_ADDR, state->rfc_addr); rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s", MAIL_ATTR_LOG_CLIENT_PORT, state->port); rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s", MAIL_ATTR_LOG_ORIGIN, state->namaddr); rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s", MAIL_ATTR_LOG_PROTO_NAME, state->protocol); /* * For consistency with the smtpd Milter client, we need to provide the * real client attributes to the cleanup Milter client. This does not * matter much with qmqpd which speaks to trusted clients only, but we * want to be sure that the cleanup input protocol is ready when a new * type of network daemon is added to receive mail from the Internet. * * See also the comments in smtpd.c. */ rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s", MAIL_ATTR_ACT_CLIENT_NAME, state->name); rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s", MAIL_ATTR_ACT_CLIENT_ADDR, state->addr); rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s", MAIL_ATTR_ACT_CLIENT_PORT, state->port); rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%u", MAIL_ATTR_ACT_CLIENT_AF, state->addr_family); rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s", MAIL_ATTR_ACT_PROTO_NAME, state->protocol); /* XXX What about the address rewriting context? */ } /* qmqpd_copy_recipients - copy message recipients */ static void qmqpd_copy_recipients(QMQPD_STATE *state) { int ch; /* * Remember the first recipient. We are done when we read the over-all * netstring terminator. * * XXX This approach violates abstractions, but it is a heck of a lot more * convenient than counting the over-all byte count down to zero, like * qmail does. */ state->where = "receiving recipient address"; while ((ch = VSTREAM_GETC(state->client)) != ',') { vstream_ungetc(state->client, ch); netstring_get(state->client, state->buf, var_line_limit); if (state->err == CLEANUP_STAT_OK && REC_PUT_BUF(state->cleanup, REC_TYPE_RCPT, state->buf) < 0) state->err = CLEANUP_STAT_WRITE; state->rcpt_count++; if (state->recipient == 0) state->recipient = mystrndup(STR(state->buf), LEN(state->buf)); } } /* qmqpd_next_line - get line from buffer, return last char, newline, or -1 */ static int qmqpd_next_line(VSTRING *message, char **start, int *len, char **next) { char *beyond = STR(message) + LEN(message); char *enough = *next + var_line_limit; char *cp; /* * Stop at newline or at some limit. Don't look beyond the end of the * buffer. */ #define UCHARPTR(x) ((unsigned char *) (x)) for (cp = *start = *next; /* void */ ; cp++) { if (cp >= beyond) return ((*len = (*next = cp) - *start) > 0 ? UCHARPTR(cp)[-1] : -1); if (*cp == '\n') return ((*len = cp - *start), (*next = cp + 1), '\n'); if (cp >= enough) return ((*len = cp - *start), (*next = cp), UCHARPTR(cp)[-1]); } } /* qmqpd_write_content - write the message content segment */ static void qmqpd_write_content(QMQPD_STATE *state) { char *start; char *next; int len; int rec_type; int first = 1; int ch; /* * Start the message content segment. Prepend our own Received: header to * the message content. List the recipient only when a message has one * recipient. Otherwise, don't list the recipient to avoid revealing Bcc: * recipients that are supposed to be invisible. */ rec_fputs(state->cleanup, REC_TYPE_MESG, ""); rec_fprintf(state->cleanup, REC_TYPE_NORM, "Received: from %s (%s [%s])", state->name, state->name, state->rfc_addr); if (state->rcpt_count == 1 && state->recipient) { rec_fprintf(state->cleanup, REC_TYPE_NORM, "\tby %s (%s) with %s id %s", var_myhostname, var_mail_name, state->protocol, state->queue_id); quote_822_local(state->buf, state->recipient); rec_fprintf(state->cleanup, REC_TYPE_NORM, "\tfor <%s>; %s", STR(state->buf), mail_date(state->arrival_time.tv_sec)); } else { rec_fprintf(state->cleanup, REC_TYPE_NORM, "\tby %s (%s) with %s", var_myhostname, var_mail_name, state->protocol); rec_fprintf(state->cleanup, REC_TYPE_NORM, "\tid %s; %s", state->queue_id, mail_date(state->arrival_time.tv_sec)); } #ifdef RECEIVED_ENVELOPE_FROM quote_822_local(state->buf, state->sender); rec_fprintf(state->cleanup, REC_TYPE_NORM, "\t(envelope-from <%s>)", STR(state->buf)); #endif /* * Write the message content. * * XXX Force an empty record when the queue file content begins with * whitespace, so that it won't be considered as being part of our own * Received: header. What an ugly Kluge. * * XXX Deal with UNIX-style From_ lines at the start of message content just * in case. */ for (next = STR(state->message); /* void */ ; /* void */ ) { if ((ch = qmqpd_next_line(state->message, &start, &len, &next)) < 0) break; if (ch == '\n') rec_type = REC_TYPE_NORM; else rec_type = REC_TYPE_CONT; if (first) { if (strncmp(start + strspn(start, ">"), "From ", 5) == 0) { rec_fprintf(state->cleanup, rec_type, "X-Mailbox-Line: %.*s", len, start); continue; } first = 0; if (len > 0 && IS_SPACE_TAB(start[0])) rec_put(state->cleanup, REC_TYPE_NORM, "", 0); } if (rec_put(state->cleanup, rec_type, start, len) < 0) { state->err = CLEANUP_STAT_WRITE; return; } } } /* qmqpd_close_file - close queue file */ static void qmqpd_close_file(QMQPD_STATE *state) { /* * Send the end-of-segment markers. */ if (state->err == CLEANUP_STAT_OK) if (rec_fputs(state->cleanup, REC_TYPE_XTRA, "") < 0 || rec_fputs(state->cleanup, REC_TYPE_END, "") < 0 || vstream_fflush(state->cleanup)) state->err = CLEANUP_STAT_WRITE; /* * Finish the queue file or finish the cleanup conversation. */ if (state->err == 0) state->err = mail_stream_finish(state->dest, state->why_rejected); else mail_stream_cleanup(state->dest); state->dest = 0; } /* qmqpd_reply - send status to client and optionally log message */ static void qmqpd_reply(QMQPD_STATE *state, int log_message, int status_code, const char *fmt,...) { va_list ap; /* * Optionally change hard errors into retryable ones. Send the reply and * optionally log it. Always insert a delay before reporting a problem. * This slows down software run-away conditions. */ if (status_code == QMQPD_STAT_HARD && var_soft_bounce) status_code = QMQPD_STAT_RETRY; VSTRING_RESET(state->buf); VSTRING_ADDCH(state->buf, status_code); va_start(ap, fmt); vstring_vsprintf_append(state->buf, fmt, ap); va_end(ap); NETSTRING_PUT_BUF(state->client, state->buf); if (log_message) (status_code == QMQPD_STAT_OK ? msg_info : msg_warn) ("%s: %s: %s", state->queue_id, state->namaddr, STR(state->buf) + 1); if (status_code != QMQPD_STAT_OK) sleep(var_qmqpd_err_sleep); netstring_fflush(state->client); } /* qmqpd_send_status - send mail transaction completion status */ static void qmqpd_send_status(QMQPD_STATE *state) { /* * One message may suffer from multiple errors, so complain only about * the most severe error. * * See also: smtpd.c */ state->where = "sending completion status"; if (state->err == CLEANUP_STAT_OK) { qmqpd_reply(state, DONT_LOG, QMQPD_STAT_OK, "Ok: queued as %s", state->queue_id); } else if ((state->err & CLEANUP_STAT_DEFER) != 0) { qmqpd_reply(state, DO_LOG, QMQPD_STAT_RETRY, "Error: %s", STR(state->why_rejected)); } else if ((state->err & CLEANUP_STAT_BAD) != 0) { qmqpd_reply(state, DO_LOG, QMQPD_STAT_RETRY, "Error: internal error %d", state->err); } else if ((state->err & CLEANUP_STAT_SIZE) != 0) { qmqpd_reply(state, DO_LOG, QMQPD_STAT_HARD, "Error: message too large"); } else if ((state->err & CLEANUP_STAT_HOPS) != 0) { qmqpd_reply(state, DO_LOG, QMQPD_STAT_HARD, "Error: too many hops"); } else if ((state->err & CLEANUP_STAT_CONT) != 0) { qmqpd_reply(state, DO_LOG, STR(state->why_rejected)[0] == '4' ? QMQPD_STAT_RETRY : QMQPD_STAT_HARD, "Error: %s", STR(state->why_rejected)); } else if ((state->err & CLEANUP_STAT_WRITE) != 0) { qmqpd_reply(state, DO_LOG, QMQPD_STAT_RETRY, "Error: queue file write error"); } else if ((state->err & CLEANUP_STAT_RCPT) != 0) { qmqpd_reply(state, DO_LOG, QMQPD_STAT_HARD, "Error: no recipients specified"); } else { qmqpd_reply(state, DO_LOG, QMQPD_STAT_RETRY, "Error: internal error %d", state->err); } } /* qmqpd_receive - receive QMQP message+sender+recipients */ static void qmqpd_receive(QMQPD_STATE *state) { /* * Open a queue file. This must be first so that we can simplify the * error logging and always include the queue ID information. */ qmqpd_open_file(state); /* * Read and ignore the over-all netstring length indicator. */ state->where = "receiving QMQP packet header"; (void) netstring_get_length(state->client); /* * XXX Read the message content into memory, because Postfix expects to * store the sender before storing the message content. Fixing that * requires changes to pickup, cleanup, qmgr, and perhaps elsewhere, so * that will have to happen later when I have more time. However, QMQP is * used for mailing list distribution, so the bulk of the volume is * expected to be not message content but recipients, and recipients are * not accumulated in memory. */ qmqpd_read_content(state); /* * Read and write the envelope sender. */ qmqpd_copy_sender(state); /* * Record some session attributes. */ qmqpd_write_attributes(state); /* * Read and write the envelope recipients, including the optional big * brother recipient. */ qmqpd_copy_recipients(state); /* * Start the message content segment, prepend our own Received: header, * and write the message content. */ if (state->err == 0) qmqpd_write_content(state); /* * Close the queue file. */ qmqpd_close_file(state); /* * Report the completion status to the client. */ qmqpd_send_status(state); } /* qmqpd_proto - speak the QMQP "protocol" */ static void qmqpd_proto(QMQPD_STATE *state) { int status; netstring_setup(state->client, var_qmqpd_timeout); switch (status = vstream_setjmp(state->client)) { default: msg_panic("qmqpd_proto: unknown status %d", status); case NETSTRING_ERR_EOF: state->reason = "lost connection"; break; case NETSTRING_ERR_TIME: state->reason = "read/write timeout"; break; case NETSTRING_ERR_FORMAT: state->reason = "netstring format error"; if (vstream_setjmp(state->client) == 0) if (state->reason && state->where) qmqpd_reply(state, DONT_LOG, QMQPD_STAT_HARD, "%s while %s", state->reason, state->where); break; case NETSTRING_ERR_SIZE: state->reason = "netstring length exceeds storage limit"; if (vstream_setjmp(state->client) == 0) if (state->reason && state->where) qmqpd_reply(state, DONT_LOG, QMQPD_STAT_HARD, "%s while %s", state->reason, state->where); break; case 0: /* * See if we want to talk to this client at all. */ if (namadr_list_match(qmqpd_clients, state->name, state->addr) != 0) { qmqpd_receive(state); } else if (qmqpd_clients->error == 0) { qmqpd_reply(state, DONT_LOG, QMQPD_STAT_HARD, "Error: %s is not authorized to use this service", state->namaddr); } else { qmqpd_reply(state, DONT_LOG, QMQPD_STAT_RETRY, "Error: server configuration error"); } break; } /* * Log abnormal session termination. Indicate the last recognized state * before things went wrong. */ if (state->reason && state->where) msg_info("%s: %s: %s while %s", state->queue_id ? state->queue_id : "NOQUEUE", state->namaddr, state->reason, state->where); } /* qmqpd_service - service one client */ static void qmqpd_service(VSTREAM *stream, char *unused_service, char **argv) { QMQPD_STATE *state; /* * Sanity check. This service takes no command-line arguments. */ if (argv[0]) msg_fatal("unexpected command-line argument: %s", argv[0]); /* * For sanity, require that at least one of INET or INET6 is enabled. * Otherwise, we can't look up interface information, and we can't * convert names or addresses. */ if (inet_proto_info()->ai_family_list[0] == 0) msg_fatal("all network protocols are disabled (%s = %s)", VAR_INET_PROTOCOLS, var_inet_protocols); /* * This routine runs when a client has connected to our network port. * Look up and sanitize the peer name and initialize some connection- * specific state. */ state = qmqpd_state_alloc(stream); /* * See if we need to turn on verbose logging for this client. */ debug_peer_check(state->name, state->addr); /* * Provide the QMQP service. */ msg_info("connect from %s", state->namaddr); qmqpd_proto(state); msg_info("disconnect from %s", state->namaddr); /* * After the client has gone away, clean up whatever we have set up at * connection time. */ debug_peer_restore(); qmqpd_state_free(state); } /* pre_accept - see if tables have changed */ static void pre_accept(char *unused_name, char **unused_argv) { const char *table; if ((table = dict_changed_name()) != 0) { msg_info("table %s has changed -- restarting", table); exit(0); } } /* pre_jail_init - pre-jail initialization */ static void pre_jail_init(char *unused_name, char **unused_argv) { debug_peer_init(); qmqpd_clients = namadr_list_init(VAR_QMQPD_CLIENTS, MATCH_FLAG_RETURN | match_parent_style(VAR_QMQPD_CLIENTS), var_qmqpd_clients); } /* post_jail_init - post-jail initialization */ static void post_jail_init(char *unused_name, char **unused_argv) { /* * Initialize the receive transparency options: do we want unknown * recipient checks, do we want address mapping. */ qmqpd_input_transp_mask = input_transp_mask(VAR_INPUT_TRANSP, var_input_transp); } MAIL_VERSION_STAMP_DECLARE; /* main - the main program */ int main(int argc, char **argv) { static const CONFIG_TIME_TABLE time_table[] = { VAR_QMTPD_TMOUT, DEF_QMTPD_TMOUT, &var_qmqpd_timeout, 1, 0, VAR_QMTPD_ERR_SLEEP, DEF_QMTPD_ERR_SLEEP, &var_qmqpd_err_sleep, 0, 0, 0, }; static const CONFIG_STR_TABLE str_table[] = { VAR_FILTER_XPORT, DEF_FILTER_XPORT, &var_filter_xport, 0, 0, VAR_QMQPD_CLIENTS, DEF_QMQPD_CLIENTS, &var_qmqpd_clients, 0, 0, VAR_INPUT_TRANSP, DEF_INPUT_TRANSP, &var_input_transp, 0, 0, 0, }; static const CONFIG_BOOL_TABLE bool_table[] = { VAR_QMQPD_CLIENT_PORT_LOG, DEF_QMQPD_CLIENT_PORT_LOG, &var_qmqpd_client_port_log, 0, }; /* * Fingerprint executables and core dumps. */ MAIL_VERSION_STAMP_ALLOCATE; /* * Pass control to the single-threaded service skeleton. */ single_server_main(argc, argv, qmqpd_service, CA_MAIL_SERVER_TIME_TABLE(time_table), CA_MAIL_SERVER_STR_TABLE(str_table), CA_MAIL_SERVER_BOOL_TABLE(bool_table), CA_MAIL_SERVER_PRE_INIT(pre_jail_init), CA_MAIL_SERVER_PRE_ACCEPT(pre_accept), CA_MAIL_SERVER_POST_INIT(post_jail_init), 0); }