Пример #1
0
void    cleanup_extracted_process(CLEANUP_STATE *state, int type,
				          const char *buf, ssize_t len)
{
    const char *myname = "cleanup_extracted_process";
    const char *encoding;
    char   *attr_name;
    char   *attr_value;
    const char *error_text;
    int     extra_opts;
    int     junk;

#ifdef DELAY_ACTION
    int     defer_delay;

#endif

    if (msg_verbose)
	msg_info("extracted envelope %c %.*s", type, (int) len, buf);

    if (type == REC_TYPE_FLGS) {
	/* Not part of queue file format. */
	extra_opts = atoi(buf);
	if (extra_opts & ~CLEANUP_FLAG_MASK_EXTRA)
	    msg_warn("%s: ignoring bad extra flags: 0x%x",
		     state->queue_id, extra_opts);
	else
	    state->flags |= extra_opts;
	return;
    }
#ifdef DELAY_ACTION
    if (type == REC_TYPE_DELAY) {
	/* Not part of queue file format. */
	defer_delay = atoi(buf);
	if (defer_delay <= 0)
	    msg_warn("%s: ignoring bad delay time: %s", state->queue_id, buf);
	else
	    state->defer_delay = defer_delay;
	return;
    }
#endif

    if (strchr(REC_TYPE_EXTRACT, type) == 0) {
	msg_warn("%s: message rejected: "
		 "unexpected record type %d in extracted envelope",
		 state->queue_id, type);
	state->errs |= CLEANUP_STAT_BAD;
	return;
    }

    /*
     * Map DSN attribute name to pseudo record type so that we don't have to
     * pollute the queue file with records that are incompatible with past
     * Postfix versions. Preferably, people should be able to back out from
     * an upgrade without losing mail.
     */
    if (type == REC_TYPE_ATTR) {
	vstring_strcpy(state->attr_buf, buf);
	error_text = split_nameval(STR(state->attr_buf), &attr_name, &attr_value);
	if (error_text != 0) {
	    msg_warn("%s: message rejected: malformed attribute: %s: %.100s",
		     state->queue_id, error_text, buf);
	    state->errs |= CLEANUP_STAT_BAD;
	    return;
	}
	/* Zero-length values are place holders for unavailable values. */
	if (*attr_value == 0) {
	    msg_warn("%s: spurious null attribute value for \"%s\" -- ignored",
		     state->queue_id, attr_name);
	    return;
	}
	if ((junk = rec_attr_map(attr_name)) != 0) {
	    buf = attr_value;
	    type = junk;
	}
    }

    /*
     * On the transition from non-recipient records to recipient records,
     * emit optional information from header/body content.
     */
    if ((state->flags & CLEANUP_FLAG_INRCPT) == 0
	&& strchr(REC_TYPE_EXT_RECIPIENT, type) != 0) {
	if (state->filter != 0)
	    cleanup_out_string(state, REC_TYPE_FILT, state->filter);
	if (state->redirect != 0)
	    cleanup_out_string(state, REC_TYPE_RDR, state->redirect);
	if ((encoding = nvtable_find(state->attr, MAIL_ATTR_ENCODING)) != 0)
	    cleanup_out_format(state, REC_TYPE_ATTR, "%s=%s",
			       MAIL_ATTR_ENCODING, encoding);
	state->flags |= CLEANUP_FLAG_INRCPT;
	/* Make room to append more meta records. */
	if (state->milters || cleanup_milters) {
	    if ((state->append_meta_pt_offset = vstream_ftell(state->dst)) < 0)
		msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path);
	    cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L);
	    if ((state->append_meta_pt_target = vstream_ftell(state->dst)) < 0)
		msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path);
	}
    }

    /*
     * Extracted envelope recipient record processing.
     */
    if (type == REC_TYPE_RCPT) {
	if (state->sender == 0) {		/* protect showq */
	    msg_warn("%s: message rejected: envelope recipient precedes sender",
		     state->queue_id);
	    state->errs |= CLEANUP_STAT_BAD;
	    return;
	}
	if (state->orig_rcpt == 0)
	    state->orig_rcpt = mystrdup(buf);
	cleanup_addr_recipient(state, buf);
	if (cleanup_milters != 0
	    && state->milters == 0
	    && CLEANUP_MILTER_OK(state))
	    cleanup_milter_emul_rcpt(state, cleanup_milters, state->recip);
	myfree(state->orig_rcpt);
	state->orig_rcpt = 0;
	if (state->dsn_orcpt != 0) {
	    myfree(state->dsn_orcpt);
	    state->dsn_orcpt = 0;
	}
	state->dsn_notify = 0;
	return;
    }
    if (type == REC_TYPE_DONE || type == REC_TYPE_DRCP) {
	if (state->orig_rcpt != 0) {
	    myfree(state->orig_rcpt);
	    state->orig_rcpt = 0;
	}
	if (state->dsn_orcpt != 0) {
	    myfree(state->dsn_orcpt);
	    state->dsn_orcpt = 0;
	}
	state->dsn_notify = 0;
	return;
    }
    if (type == REC_TYPE_DSN_ORCPT) {
	if (state->dsn_orcpt) {
	    msg_warn("%s: ignoring out-of-order DSN original recipient record <%.200s>",
		     state->queue_id, state->dsn_orcpt);
	    myfree(state->dsn_orcpt);
	}
	state->dsn_orcpt = mystrdup(buf);
	return;
    }
    if (type == REC_TYPE_DSN_NOTIFY) {
	if (state->dsn_notify) {
	    msg_warn("%s: ignoring out-of-order DSN notify record <%d>",
		     state->queue_id, state->dsn_notify);
	    state->dsn_notify = 0;
	}
	if (!alldig(buf) || (junk = atoi(buf)) == 0 || DSN_NOTIFY_OK(junk) == 0)
	    msg_warn("%s: ignoring malformed dsn notify record <%.200s>",
		     state->queue_id, buf);
	else
	    state->qmgr_opts |=
		QMGR_READ_FLAG_FROM_DSN(state->dsn_notify = junk);
	return;
    }
    if (type == REC_TYPE_ORCP) {
	if (state->orig_rcpt != 0) {
	    msg_warn("%s: ignoring out-of-order original recipient record <%.200s>",
		     state->queue_id, buf);
	    myfree(state->orig_rcpt);
	}
	state->orig_rcpt = mystrdup(buf);
	return;
    }
    if (type == REC_TYPE_END) {
	/* Make room to append recipient. */
	if ((state->milters || cleanup_milters)
	    && state->append_rcpt_pt_offset < 0) {
	    if ((state->append_rcpt_pt_offset = vstream_ftell(state->dst)) < 0)
		msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path);
	    cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L);
	    if ((state->append_rcpt_pt_target = vstream_ftell(state->dst)) < 0)
		msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path);
	}
	state->flags &= ~CLEANUP_FLAG_INRCPT;
	state->flags |= CLEANUP_FLAG_END_SEEN;
	cleanup_extracted_finish(state);
	return;
    }

    /*
     * Extracted envelope non-recipient record processing.
     */
    if (state->flags & CLEANUP_FLAG_INRCPT)
	/* Tell qmgr that recipient records are mixed with other information. */
	state->qmgr_opts |= QMGR_READ_FLAG_MIXED_RCPT_OTHER;
    cleanup_out(state, type, buf, len);
    return;
}
Пример #2
0
gboolean
write_smtp_reply (struct smtp_session *session)
{
	gchar logbuf[1024], *new_subject;
	const gchar *old_subject;
	struct smtp_metric_callback_data cd;
	GMimeStream *stream;
	gint old_fd, sublen;

	/* Check metrics */
	cd.session = session;
	cd.action = METRIC_ACTION_NOACTION;
	cd.res = NULL;
	cd.log_buf = logbuf;
	cd.log_offset = rspamd_snprintf (logbuf,
			sizeof (logbuf),
			"id: <%s>, qid: <%s>, ",
			session->task->message_id,
			session->task->queue_id);
	cd.log_size = sizeof (logbuf);
	if (session->task->user) {
		cd.log_offset += rspamd_snprintf (logbuf + cd.log_offset,
				sizeof (logbuf) - cd.log_offset,
				"user: %s, ",
				session->task->user);
	}

	g_hash_table_foreach (session->task->results, smtp_metric_callback, &cd);

	msg_info ("%s", logbuf);

	if (cd.action <= METRIC_ACTION_REJECT) {
		if (!rspamd_dispatcher_write (session->dispatcher,
			session->ctx->reject_message, 0, FALSE, TRUE)) {
			return FALSE;
		}
		if (!rspamd_dispatcher_write (session->dispatcher, CRLF, sizeof (CRLF) -
			1, FALSE, TRUE)) {
			return FALSE;
		}
		rspamd_session_destroy (session->s);
		return FALSE;
	}
	else if (cd.action <= METRIC_ACTION_ADD_HEADER || cd.action <=
		METRIC_ACTION_REWRITE_SUBJECT) {
		old_fd = session->temp_fd;
		if (!make_smtp_tempfile (session)) {
			session->error = SMTP_ERROR_FILE;
			session->state = SMTP_STATE_CRITICAL_ERROR;
			rspamd_dispatcher_restore (session->dispatcher);
			if (!rspamd_dispatcher_write (session->dispatcher, session->error,
				0, FALSE, TRUE)) {
				goto err;
			}
			rspamd_session_destroy (session->s);
			return FALSE;
		}

		if (cd.action <= METRIC_ACTION_REWRITE_SUBJECT) {
			/* XXX: add this action */
			old_subject = g_mime_message_get_subject (session->task->message);
			if (old_subject != NULL) {
				sublen = strlen (old_subject) + sizeof (SPAM_SUBJECT);
				new_subject = rspamd_mempool_alloc (session->pool, sublen);
				rspamd_snprintf (new_subject,
					sublen,
					"%s%s",
					SPAM_SUBJECT,
					old_subject);
			}
			else {
				new_subject = SPAM_SUBJECT;
			}
			g_mime_message_set_subject (session->task->message, new_subject);
		}
		else if (cd.action <= METRIC_ACTION_ADD_HEADER) {
#ifndef GMIME24
			g_mime_message_add_header (session->task->message, "X-Spam",
				"true");
#else
			g_mime_object_append_header (GMIME_OBJECT (
					session->task->message), "X-Spam", "true");
#endif
		}
		stream = g_mime_stream_fs_new (session->temp_fd);
		g_mime_stream_fs_set_owner (GMIME_STREAM_FS (stream), FALSE);
		close (old_fd);

		if (g_mime_object_write_to_stream (GMIME_OBJECT (session->task->message),
			stream) == -1) {
			msg_err ("cannot write MIME object to stream: %s",
				strerror (errno));
			session->error = SMTP_ERROR_FILE;
			session->state = SMTP_STATE_CRITICAL_ERROR;
			rspamd_dispatcher_restore (session->dispatcher);
			if (!rspamd_dispatcher_write (session->dispatcher, session->error,
				0, FALSE, TRUE)) {
				goto err;
			}
			rspamd_session_destroy (session->s);
			return FALSE;
		}
		g_object_unref (stream);
	}
	/* XXX: Add other actions */
	return smtp_send_upstream_message (session);
err:
	session->error = SMTP_ERROR_FILE;
	session->state = SMTP_STATE_CRITICAL_ERROR;
	if (!rspamd_dispatcher_write (session->dispatcher, session->error, 0, FALSE,
		TRUE)) {
		return FALSE;
	}
	rspamd_session_destroy (session->s);
	return FALSE;
}
Пример #3
0
void
luaopen_rsa (lua_State * L)
{
	msg_info ("this rspamd version is not linked against openssl, therefore no "
		"RSA support is available");
}
Пример #4
0
static int bounce_verp_proto(char *service_name, VSTREAM *client)
{
    const char *myname = "bounce_verp_proto";
    int     flags;
    int     dsn_ret;

    /*
     * Read and validate the client request.
     */
    if (mail_command_server(client,
			    ATTR_TYPE_INT, MAIL_ATTR_FLAGS, &flags,
			    ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue_name,
			    ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
			    ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
			    ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
			    ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid,
			    ATTR_TYPE_INT, MAIL_ATTR_DSN_RET, &dsn_ret,
			    ATTR_TYPE_STR, MAIL_ATTR_VERPDL, verp_delims,
			    ATTR_TYPE_END) != 8) {
	msg_warn("malformed request");
	return (-1);
    }

    /*
     * Sanitize input.
     */
    if (mail_queue_name_ok(STR(queue_name)) == 0) {
	msg_warn("malformed queue name: %s", printable(STR(queue_name), '?'));
	return (-1);
    }
    if (mail_queue_id_ok(STR(queue_id)) == 0) {
	msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
	return (-1);
    }
    printable(STR(dsn_envid), '?');
    if (strlen(STR(verp_delims)) != 2) {
	msg_warn("malformed verp delimiter string: %s",
		 printable(STR(verp_delims), '?'));
	return (-1);
    }
    if (msg_verbose)
	msg_info("%s: flags=0x%x service=%s queue=%s id=%s encoding=%s sender=%s envid=%s ret=0x%x delim=%s",
		 myname, flags, service_name, STR(queue_name),
		 STR(queue_id), STR(encoding), STR(sender),
		 STR(dsn_envid), dsn_ret, STR(verp_delims));

    /*
     * On request by the client, set up a trap to delete the log file in case
     * of errors.
     */
    if (flags & BOUNCE_FLAG_CLEAN)
	bounce_cleanup_register(service_name, STR(queue_id));

    /*
     * Execute the request. Fall back to traditional notification if a bounce
     * was returned as undeliverable, because we don't want to VERPify those.
     */
    if (!*STR(sender) || !strcasecmp(STR(sender), mail_addr_double_bounce())) {
	msg_warn("request to send VERP-style notification of bounced mail");
	return (bounce_notify_service(flags, service_name, STR(queue_name),
				      STR(queue_id), STR(encoding),
				      STR(sender), STR(dsn_envid), dsn_ret,
				      bounce_templates));
    } else
	return (bounce_notify_verp(flags, service_name, STR(queue_name),
				   STR(queue_id), STR(encoding),
				   STR(sender), STR(dsn_envid), dsn_ret,
				   STR(verp_delims), bounce_templates));
}
Пример #5
0
/**********************************************************************
 * public interface dict_mysql_lookup
 * find database entry return 0 if no alias found, set dict_errno
 * on errors to DICT_ERRBO_RETRY and set dict_errno to 0 on success
 *********************************************************************/
static const char *dict_mysql_lookup(DICT *dict, const char *name)
{
    MYSQL_RES *query_res;
    MYSQL_ROW row;
    DICT_MYSQL *dict_mysql;
    PLMYSQL *pldb;
    static VSTRING *result;
    static VSTRING *query = 0;
    int     i,
            j,
            numrows;
    char   *name_escaped = 0;

    dict_mysql = (DICT_MYSQL *) dict;
    pldb = dict_mysql->pldb;
    /* initialization  for query */
    query = vstring_alloc(24);
    vstring_strcpy(query, "");
    if ((name_escaped = (char *) mymalloc((sizeof(char) * (strlen(name) * 2) +1))) == NULL) {
        msg_fatal("dict_mysql_lookup: out of memory.");
    }
    /* prepare the query */
    mysql_escape_string(name_escaped, name, (unsigned int) strlen(name));
    vstring_sprintf(query, "select %s from %s where %s = '%s' %s", dict_mysql->name->select_field,
                    dict_mysql->name->table, dict_mysql->name->where_field, name_escaped,
                    dict_mysql->name->additional_conditions);
    if (msg_verbose)
        msg_info("dict_mysql_lookup using sql query: %s", vstring_str(query));
    /* free mem associated with preparing the query */
    myfree(name_escaped);
    /* do the query - set dict_errno & cleanup if there's an error */
    if ((query_res = plmysql_query(pldb,
                                   vstring_str(query),
                                   dict_mysql->name->dbname,
                                   dict_mysql->name->username,
                                   dict_mysql->name->password)) == 0) {
        dict_errno = DICT_ERR_RETRY;
        vstring_free(query);
        return 0;
    }
    dict_errno = 0;
    /* free the vstring query */
    vstring_free(query);
    numrows = mysql_num_rows(query_res);
    if (msg_verbose)
        msg_info("dict_mysql_lookup: retrieved %d rows", numrows);
    if (numrows == 0) {
        mysql_free_result(query_res);
        return 0;
    }
    if (result == 0)
        result = vstring_alloc(10);
    vstring_strcpy(result, "");
    for (i = 0; i < numrows; i++) {
        row = mysql_fetch_row(query_res);
        if (i > 0)
            vstring_strcat(result, ",");
        for (j = 0; j < mysql_num_fields(query_res); j++) {
            if (row[j] == 0) {
                if (msg_verbose > 1)
                    msg_info("dict_mysql_lookup: null field #%d row #%d", j, i);
                mysql_free_result(query_res);
                return (0);
            }
            if (j > 0)
                vstring_strcat(result, ",");
            vstring_strcat(result, row[j]);
            if (msg_verbose > 1)
                msg_info("dict_mysql_lookup: retrieved field: %d: %s", j, row[j]);
        }
    }
    mysql_free_result(query_res);
    return vstring_str(result);
}
Пример #6
0
int     xsasl_dovecot_server_first(XSASL_SERVER *xp, const char *sasl_method,
			          const char *init_response, VSTRING *reply)
{
    const char *myname = "xsasl_dovecot_server_first";
    XSASL_DOVECOT_SERVER *server = (XSASL_DOVECOT_SERVER *) xp;
    int     i;
    char  **cpp;

#define IFELSE(e1,e2,e3) ((e1) ? (e2) : (e3))

    if (msg_verbose)
	msg_info("%s: sasl_method %s%s%s", myname, sasl_method,
		 IFELSE(init_response, ", init_response ", ""),
		 IFELSE(init_response, init_response, ""));

    if (server->mechanism_argv == 0)
	msg_panic("%s: no mechanism list", myname);

    for (cpp = server->mechanism_argv->argv; /* see below */ ; cpp++) {
	if (*cpp == 0) {
	    vstring_strcpy(reply, "Invalid authentication mechanism");
	    return XSASL_AUTH_FAIL;
	}
	if (strcasecmp(sasl_method, *cpp) == 0)
	    break;
    }
    if (init_response)
	if (!is_valid_base64(init_response)) {
	    vstring_strcpy(reply, "Invalid base64 data in initial response");
	    return XSASL_AUTH_FAIL;
	}
    for (i = 0; i < 2; i++) {
	if (!server->impl->sasl_stream) {
	    if (xsasl_dovecot_server_connect(server->impl) < 0)
		return XSASL_AUTH_TEMP;
	}
	/* send the request */
	server->last_request_id = ++server->impl->request_id_counter;
	/* XXX Encapsulate for logging. */
	vstream_fprintf(server->impl->sasl_stream,
			"AUTH\t%u\t%s\tservice=%s\tnologin\tlip=%s\trip=%s",
			server->last_request_id, sasl_method,
			server->service, server->server_addr,
			server->client_addr);
	if (server->tls_flag)
	    /* XXX Encapsulate for logging. */
	    vstream_fputs("\tsecured", server->impl->sasl_stream);
	if (init_response) {

	    /*
	     * initial response is already base64 encoded, so we can send it
	     * directly.
	     */
	    /* XXX Encapsulate for logging. */
	    vstream_fprintf(server->impl->sasl_stream,
			    "\tresp=%s", init_response);
	}
	/* XXX Encapsulate for logging. */
	VSTREAM_PUTC('\n', server->impl->sasl_stream);

	if (vstream_fflush(server->impl->sasl_stream) != VSTREAM_EOF)
	    break;

	if (i == 1) {
	    vstring_strcpy(reply, "Can't connect to authentication server");
	    return XSASL_AUTH_TEMP;
	}

	/*
	 * Reconnect and try again.
	 */
	xsasl_dovecot_server_disconnect(server->impl);
    }

    return xsasl_dovecot_handle_reply(server, reply);
}
Пример #7
0
static const char *dict_pcre_lookup(DICT *dict, const char *lookup_string)
{
    DICT_PCRE *dict_pcre = (DICT_PCRE *) dict;
    DICT_PCRE_RULE *rule;
    DICT_PCRE_IF_RULE *if_rule;
    DICT_PCRE_MATCH_RULE *match_rule;
    int     lookup_len = strlen(lookup_string);
    DICT_PCRE_EXPAND_CONTEXT ctxt;
    int     nesting = 0;

    dict->error = 0;

    if (msg_verbose)
	msg_info("dict_pcre_lookup: %s: %s", dict->name, lookup_string);

    /*
     * Optionally fold the key.
     */
    if (dict->flags & DICT_FLAG_FOLD_MUL) {
	if (dict->fold_buf == 0)
	    dict->fold_buf = vstring_alloc(10);
	vstring_strcpy(dict->fold_buf, lookup_string);
	lookup_string = lowercase(vstring_str(dict->fold_buf));
    }
    for (rule = dict_pcre->head; rule; rule = rule->next) {

	/*
	 * Skip rules inside failed IF/ENDIF.
	 */
	if (nesting < rule->nesting)
	    continue;

	switch (rule->op) {

	    /*
	     * Search for a matching expression.
	     */
	case DICT_PCRE_OP_MATCH:
	    match_rule = (DICT_PCRE_MATCH_RULE *) rule;
	    ctxt.matches = pcre_exec(match_rule->pattern, match_rule->hints,
				     lookup_string, lookup_len,
				     NULL_STARTOFFSET, NULL_EXEC_OPTIONS,
				     ctxt.offsets, PCRE_MAX_CAPTURE * 3);

	    if (ctxt.matches > 0) {
		if (!match_rule->match)
		    continue;			/* Negative rule matched */
	    } else if (ctxt.matches == PCRE_ERROR_NOMATCH) {
		if (match_rule->match)
		    continue;			/* Positive rule did not
						 * match */
	    } else {
		dict_pcre_exec_error(dict->name, rule->lineno, ctxt.matches);
		continue;			/* pcre_exec failed */
	    }

	    /*
	     * Skip $number substitutions when the replacement text contains
	     * no $number strings, as learned during the compile time
	     * pre-scan. The pre-scan already replaced $$ by $.
	     */
	    if (match_rule->max_sub == 0)
		return match_rule->replacement;

	    /*
	     * We've got a match. Perform substitution on replacement string.
	     */
	    if (dict_pcre->expansion_buf == 0)
		dict_pcre->expansion_buf = vstring_alloc(10);
	    VSTRING_RESET(dict_pcre->expansion_buf);
	    ctxt.dict_pcre = dict_pcre;
	    ctxt.match_rule = match_rule;
	    ctxt.lookup_string = lookup_string;

	    if (mac_parse(match_rule->replacement, dict_pcre_expand,
			  (char *) &ctxt) & MAC_PARSE_ERROR)
		msg_fatal("pcre map %s, line %d: bad replacement syntax",
			  dict->name, rule->lineno);

	    VSTRING_TERMINATE(dict_pcre->expansion_buf);
	    return (vstring_str(dict_pcre->expansion_buf));

	    /*
	     * Conditional. XXX We provide space for matched substring info
	     * because PCRE uses part of it as workspace for backtracking.
	     * PCRE will allocate memory if it runs out of backtracking
	     * storage.
	     */
	case DICT_PCRE_OP_IF:
	    if_rule = (DICT_PCRE_IF_RULE *) rule;
	    ctxt.matches = pcre_exec(if_rule->pattern, if_rule->hints,
				     lookup_string, lookup_len,
				     NULL_STARTOFFSET, NULL_EXEC_OPTIONS,
				     ctxt.offsets, PCRE_MAX_CAPTURE * 3);

	    if (ctxt.matches > 0) {
		if (!if_rule->match)
		    continue;			/* Negative rule matched */
	    } else if (ctxt.matches == PCRE_ERROR_NOMATCH) {
		if (if_rule->match)
		    continue;			/* Positive rule did not
						 * match */
	    } else {
		dict_pcre_exec_error(dict->name, rule->lineno, ctxt.matches);
		continue;			/* pcre_exec failed */
	    }
	    nesting++;
	    continue;

	    /*
	     * ENDIF after successful IF.
	     */
	case DICT_PCRE_OP_ENDIF:
	    nesting--;
	    continue;

	default:
	    msg_panic("dict_pcre_lookup: impossible operation %d", rule->op);
	}
    }
    return (0);
}
Пример #8
0
void VTKExporter::writeVTKXML()
{
    std::string filename = vtkFilename.getFullPath();

    std::ostringstream oss;
    oss << nbFiles;

    if ( filename.size() > 3 && filename.substr(filename.size()-4)==".vtu")
    {
        if (!overwrite.getValue())
            filename = filename.substr(0,filename.size()-4) + oss.str() + ".vtu";
    }
    else
    {
        if (!overwrite.getValue())
            filename += oss.str();
        filename += ".vtu";
    }

    outfile = new std::ofstream(filename.c_str());
    if( !outfile->is_open() )
    {
        msg_error() << "Error creating file "<<filename;
        delete outfile;
        outfile = NULL;
        return;
    }
    const helper::vector<std::string>& pointsData = dPointsDataFields.getValue();
    const helper::vector<std::string>& cellsData = dCellsDataFields.getValue();

    helper::ReadAccessor<Data<defaulttype::Vec3Types::VecCoord> > pointsPos = position;

    const int nbp = (!pointsPos.empty()) ? pointsPos.size() : topology->getNbPoints();

    unsigned int numberOfCells;
    numberOfCells = ( (writeEdges.getValue()) ? topology->getNbEdges() : 0 )
            +( (writeTriangles.getValue()) ? topology->getNbTriangles() : 0 )
            +( (writeQuads.getValue()) ? topology->getNbQuads() : 0 )
            +( (writeTetras.getValue()) ? topology->getNbTetras() : 0 )
            +( (writeHexas.getValue()) ? topology->getNbHexas() : 0 );

    msg_info() << "### VTKExporter[" << this->getName() << "] ###" << msgendl
               << "Nb points: " << nbp << msgendl
               << "Nb edges: " << ( (writeEdges.getValue()) ? topology->getNbEdges() : 0 ) << msgendl
               << "Nb triangles: " << ( (writeTriangles.getValue()) ? topology->getNbTriangles() : 0 ) << msgendl
               << "Nb quads: " << ( (writeQuads.getValue()) ? topology->getNbQuads() : 0 ) << msgendl
               << "Nb tetras: " << ( (writeTetras.getValue()) ? topology->getNbTetras() : 0 ) << msgendl
               << "Nb hexas: " << ( (writeHexas.getValue()) ? topology->getNbHexas() : 0 ) << msgendl
               << "### ###" << msgendl
               << "Total nb cells: " << numberOfCells << msgendl;

    //write header
    *outfile << "<VTKFile type=\"UnstructuredGrid\" version=\"0.1\" byte_order=\"BigEndian\">" << std::endl;
    *outfile << "  <UnstructuredGrid>" << std::endl;

    //write piece
    *outfile << "    <Piece NumberOfPoints=\"" << nbp << "\" NumberOfCells=\""<< numberOfCells << "\">" << std::endl;

    //write point data
    if (!pointsData.empty())
    {
        *outfile << "      <PointData>" << std::endl;
        writeDataArray(pointsDataObject, pointsDataField, pointsDataName);
        *outfile << "      </PointData>" << std::endl;
    }
    //write cell data
    if (!cellsData.empty())
    {
        *outfile << "      <CellData>" << std::endl;
        writeDataArray(cellsDataObject, cellsDataField, cellsDataName);
        *outfile << "      </CellData>" << std::endl;
    }



    //write points
    *outfile << "      <Points>" << std::endl;
    *outfile << "        <DataArray type=\"Float32\" NumberOfComponents=\"3\" format=\"ascii\">" << std::endl;
    if (!pointsPos.empty())
    {
        for (int i = 0 ; i < nbp; i++)
        {
            *outfile << "\t" << pointsPos[i] << std::endl;
        }
    }
    else if (mstate && mstate->getSize() == (size_t)nbp)
    {
        for (size_t i = 0; i < mstate->getSize(); i++)
            *outfile << "          " << mstate->getPX(i) << " " << mstate->getPY(i) << " " << mstate->getPZ(i) << std::endl;
    }
    else
    {
        for (int i = 0; i < nbp; i++)
            *outfile << "          " << topology->getPX(i) << " " << topology->getPY(i) << " " << topology->getPZ(i) << std::endl;
    }
    *outfile << "        </DataArray>" << std::endl;
    *outfile << "      </Points>" << std::endl;

    //write cells
    *outfile << "      <Cells>" << std::endl;
    //write connectivity
    *outfile << "        <DataArray type=\"Int32\" Name=\"connectivity\" format=\"ascii\">" << std::endl;
    if (writeEdges.getValue())
    {
        for (int i=0 ; i<topology->getNbEdges() ; i++)
            *outfile << "          " << topology->getEdge(i) << std::endl;
    }

    if (writeTriangles.getValue())
    {
        for (int i=0 ; i<topology->getNbTriangles() ; i++)
            *outfile << "          " <<  topology->getTriangle(i) << std::endl;
    }
    if (writeQuads.getValue())
    {
        for (int i=0 ; i<topology->getNbQuads() ; i++)
            *outfile << "          " << topology->getQuad(i) << std::endl;
    }
    if (writeTetras.getValue())
    {
        for (int i=0 ; i<topology->getNbTetras() ; i++)
            *outfile << "          " <<  topology->getTetra(i) << std::endl;
    }
    if (writeHexas.getValue())
    {
        for (int i=0 ; i<topology->getNbHexas() ; i++)
            *outfile << "          " <<  topology->getHexa(i) << std::endl;
    }
    *outfile << "        </DataArray>" << std::endl;
    //write offsets
    int num = 0;
    *outfile << "        <DataArray type=\"Int32\" Name=\"offsets\" format=\"ascii\">" << std::endl;
    *outfile << "          ";
    if (writeEdges.getValue())
    {
        for (int i=0 ; i<topology->getNbEdges() ; i++)
        {
            num += 2;
            *outfile << num << " ";
        }
    }
    if (writeTriangles.getValue())
    {
        for (int i=0 ; i<topology->getNbTriangles() ; i++)
        {
            num += 3;
            *outfile << num << " ";
        }
    }
    if (writeQuads.getValue())
    {
        for (int i=0 ; i<topology->getNbQuads() ; i++)
        {
            num += 4;
            *outfile << num << " ";
        }
    }
    if (writeTetras.getValue())
    {
        for (int i=0 ; i<topology->getNbTetras() ; i++)
        {
            num += 4;
            *outfile << num << " ";
        }
    }
    if (writeHexas.getValue())
    {
        for (int i=0 ; i<topology->getNbHexas() ; i++)
        {
            num += 8;
            *outfile << num << " ";
        }
    }
    *outfile << std::endl;
    *outfile << "        </DataArray>" << std::endl;
    //write types
    *outfile << "        <DataArray type=\"UInt8\" Name=\"types\" format=\"ascii\">" << std::endl;
    *outfile << "          ";
    if (writeEdges.getValue())
    {
        for (int i=0 ; i<topology->getNbEdges() ; i++)
            *outfile << 3 << " ";
    }
    if (writeTriangles.getValue())
    {
        for (int i=0 ; i<topology->getNbTriangles() ; i++)
            *outfile << 5 << " ";
    }
    if (writeQuads.getValue())
    {
        for (int i=0 ; i<topology->getNbQuads() ; i++)
            *outfile << 9 << " ";
    }
    if (writeTetras.getValue())
    {
        for (int i=0 ; i<topology->getNbTetras() ; i++)
            *outfile << 10 << " ";
    }
    if (writeHexas.getValue())
    {
        for (int i=0 ; i<topology->getNbHexas() ; i++)
            *outfile << 12 << " ";
    }
    *outfile << std::endl;
    *outfile << "        </DataArray>" << std::endl;
    *outfile << "      </Cells>" << std::endl;

    //write end
    *outfile << "    </Piece>" << std::endl;
    *outfile << "  </UnstructuredGrid>" << std::endl;
    *outfile << "</VTKFile>" << std::endl;
    outfile->close();
    ++nbFiles;

    msg_info() << "Export VTK XML in file " << filename << "  done.";
}
Пример #9
0
void VTKExporter::writeParallelFile()
{
    std::string filename = vtkFilename.getFullPath();
    filename.insert(0, "P_");
    filename += ".vtk";

    outfile = new std::ofstream(filename.c_str());
    if( !outfile->is_open() )
    {
        msg_error() << "Error creating file "<<filename;
        delete outfile;
        outfile = NULL;
        return;
    }

    *outfile << "<VTKFile type=\"PUnstructuredGrid\" version=\"0.1\" byte_order=\"BigEndian\">" << std::endl;
    *outfile << "  <PUnstructuredGrid GhostLevel=\"0\">" << std::endl;

    const helper::vector<std::string>& pointsData = dPointsDataFields.getValue();
    const helper::vector<std::string>& cellsData = dCellsDataFields.getValue();

    //write type of the data
    sofa::core::objectmodel::BaseContext* context = this->getContext();
    if (!pointsData.empty())
    {
        for (unsigned int i=0 ; i<pointsDataObject.size() ; i++)
        {
            core::objectmodel::BaseObject* obj = context->get<core::objectmodel::BaseObject> (pointsDataObject[i]);
            core::objectmodel::BaseData* field = NULL;
            if (obj)
            {
                field = obj->findData(pointsDataField[i]);
            }

            if (!obj || !field)
            {
                if (!obj)
                    msg_error() << "VTKExporter : error while fetching data field '" << msgendl
                                << pointsDataField[i] << "' of object '" << pointsDataObject[i] << msgendl
                                << "', check object name" << msgendl;
                else if (!field)
                    msg_error() << "VTKExporter : error while fetching data field '" << msgendl
                                << pointsDataField[i] << "' of object '" << pointsDataObject[i] << msgendl
                                << "', check field name " << msgendl;
            }
            else
            {
                //Scalars
                std::string type;
                unsigned int sizeSeg=0;
                if (dynamic_cast<sofa::core::objectmodel::TData< helper::vector<int> >* >(field))
                {
                    type = "Int32";
                    sizeSeg = 1;
                }
                if (dynamic_cast<sofa::core::objectmodel::TData< helper::vector<unsigned int> >* >(field))
                {
                    type = "UInt32";
                    sizeSeg = 1;
                }
                if (dynamic_cast<sofa::core::objectmodel::TData< helper::vector<float> >* >(field))
                {
                    type = "Float32";
                    sizeSeg = 1;
                }
                if (dynamic_cast<sofa::core::objectmodel::TData<helper::vector<double> >* >(field))
                {
                    type = "Float64";
                    sizeSeg = 1;
                }

                //Vectors
                if (type.empty())
                {
                    if (dynamic_cast<sofa::core::objectmodel::TData<helper::vector< defaulttype::Vec3f > >* > (field))
                    {
                        type = "Float32";
                        sizeSeg = 3;
                    }
                    if (dynamic_cast<sofa::core::objectmodel::TData<helper::vector< defaulttype::Vec3d > >* >(field))
                    {
                        type = "Float64";
                        sizeSeg = 3;
                    }
                }

                *outfile << "    <PPointData>" << std::endl;
                *outfile << "      <PDataArray type=\""<< type << "\" Name=\"" << pointsDataName[i];
                if(sizeSeg > 1)
                    *outfile << "\" NumberOfComponents=\"" << sizeSeg;
                *outfile << "\"/>" << std::endl;
                *outfile << "    </PPointData>" << std::endl;
            }
        }
    }

    if (!cellsData.empty())
    {
        for (unsigned int i=0 ; i<cellsDataObject.size() ; i++)
        {
            core::objectmodel::BaseObject* obj = context->get<core::objectmodel::BaseObject> (cellsDataObject[i]);
            core::objectmodel::BaseData* field = NULL;
            if (obj)
            {
                field = obj->findData(cellsDataField[i]);
            }

            if (!obj || !field)
            {
                if (!obj)
                    msg_error() << "VTKExporter : error while fetching data field '"
                         << cellsDataField[i] << "' of object '" << cellsDataObject[i]
                         << "', check object name" << sendl;
                else if (!field)
                    msg_error() << "VTKExporter : error while fetching data field '" << msgendl
                                << cellsDataField[i] << "' of object '" << cellsDataObject[i] << msgendl
                                << "', check field name " << msgendl;
            }
            else
            {
                //Scalars
                std::string type;
                unsigned int sizeSeg=0;
                if (dynamic_cast<sofa::core::objectmodel::TData< helper::vector<int> >* >(field))
                {
                    type = "Int32";
                    sizeSeg = 1;
                }
                if (dynamic_cast<sofa::core::objectmodel::TData< helper::vector<unsigned int> >* >(field))
                {
                    type = "UInt32";
                    sizeSeg = 1;
                }
                if (dynamic_cast<sofa::core::objectmodel::TData< helper::vector<float> >* >(field))
                {
                    type = "Float32";
                    sizeSeg = 1;
                }
                if (dynamic_cast<sofa::core::objectmodel::TData<helper::vector<double> >* >(field))
                {
                    type = "Float64";
                    sizeSeg = 1;
                }

                //Vectors
                if (type.empty())
                {
                    if (dynamic_cast<sofa::core::objectmodel::TData<helper::vector< defaulttype::Vec3f > >* > (field))
                    {
                        type = "Float32";
                        sizeSeg = 3;
                    }
                    if (dynamic_cast<sofa::core::objectmodel::TData<helper::vector< defaulttype::Vec3d > >* >(field))
                    {
                        type = "Float64";
                        sizeSeg = 3;
                    }
                }

                *outfile << "    <PCellData>" << std::endl;
                *outfile << "      <PDataArray type=\""<< type << "\" Name=\"" << cellsDataName[i];
                if(sizeSeg > 1)
                    *outfile << "\" NumberOfComponents=\"" << sizeSeg;
                *outfile << "\"/>" << std::endl;
                *outfile << "    </PCellData>" << std::endl;
            }
        }
    }

    *outfile << "    <PPoints>" << std::endl;
    *outfile << "      <PDataArray type=\"Float32\" NumberOfComponents=\"3\"/>" << std::endl;
    *outfile << "    </PPoints>" << std::endl;

    //write piece
    for(int i = 1; i < nbFiles; ++i)
    {
        std::ostringstream oss;
        oss << i;
        *outfile << "    <Piece Source=\"" << vtkFilename.getFullPath() << oss.str() << ".vtu" << "\"/>" << std::endl;
    }

    //write end
    *outfile << "  </PUnstructuredGrid>" << std::endl;
    *outfile << "</VTKFile>" << std::endl;
    outfile->close();

    msg_info() << "Export VTK in file " << filename << "  done.";
}
Пример #10
0
void    smtpd_peer_init(SMTPD_STATE *state)
{
    const char *myname = "smtpd_peer_init";
    SOCKADDR_SIZE sa_length;
    struct sockaddr *sa;
    INET_PROTO_INFO *proto_info = inet_proto_info();

    sa = (struct sockaddr *) & (state->sockaddr);
    sa_length = sizeof(state->sockaddr);

    /*
     * Look up the peer address information.
     * 
     * XXX If we make local endpoint (getsockname) information available to
     * Milter applications as {if_name} and {if_addr}, then we also must be
     * able to provide this via the XCLIENT command for Milter testing.
     * 
     * XXX If we make local or remote port information available to policy
     * servers or Milter applications, then we must also make this testable
     * with the XCLIENT command, otherwise there will be confusion.
     * 
     * XXX If we make local or remote port information available via logging,
     * then we must also support these attributes with the XFORWARD command.
     * 
     * XXX If support were to be added for Milter applications in down-stream
     * MTAs, then consistency demands that we propagate a lot of Sendmail
     * macro information via the XFORWARD command. Otherwise we could end up
     * with a very confusing situation.
     */
    if (getpeername(vstream_fileno(state->client), sa, &sa_length) >= 0) {
	errno = 0;
    }

    /*
     * If peer went away, give up.
     */
    if (errno != 0 && errno != ENOTSOCK) {
	state->name = mystrdup(CLIENT_NAME_UNKNOWN);
	state->reverse_name = mystrdup(CLIENT_NAME_UNKNOWN);
	state->addr = mystrdup(CLIENT_ADDR_UNKNOWN);
	state->rfc_addr = mystrdup(CLIENT_ADDR_UNKNOWN);
	state->addr_family = AF_UNSPEC;
	state->name_status = SMTPD_PEER_CODE_PERM;
	state->reverse_name_status = SMTPD_PEER_CODE_PERM;
	state->port = mystrdup(CLIENT_PORT_UNKNOWN);
    }

    /*
     * Convert the client address to printable address and hostname.
     * 
     * XXX If we're given an IPv6 (or IPv4) connection from, e.g., inetd, while
     * Postfix IPv6 (or IPv4) support is turned off, don't (skip to the final
     * else clause, pretend the origin is localhost[127.0.0.1], and become an
     * open relay).
     */
    else if (errno == 0
	     && (sa->sa_family == AF_INET
#ifdef AF_INET6
		 || sa->sa_family == AF_INET6
#endif
		 )) {
	MAI_HOSTNAME_STR client_name;
	MAI_HOSTADDR_STR client_addr;
	MAI_SERVPORT_STR client_port;
	int     aierr;
	char   *colonp;

	/*
	 * Sanity check: we can't use sockets that we're not configured for.
	 */
	if (strchr((char *) proto_info->sa_family_list, sa->sa_family) == 0)
	    msg_fatal("cannot handle socket type %s with \"%s = %s\"",
#ifdef AF_INET6
		      sa->sa_family == AF_INET6 ? "AF_INET6" :
#endif
		      sa->sa_family == AF_INET ? "AF_INET" :
		      "other", VAR_INET_PROTOCOLS, var_inet_protocols);

	/*
	 * Sorry, but there are some things that we just cannot do while
	 * connected to the network.
	 */
	if (geteuid() != var_owner_uid || getuid() != var_owner_uid) {
	    msg_error("incorrect SMTP server privileges: uid=%lu euid=%lu",
		      (unsigned long) getuid(), (unsigned long) geteuid());
	    msg_fatal("the Postfix SMTP server must run with $%s privileges",
		      VAR_MAIL_OWNER);
	}

	/*
	 * Convert the client address to printable form.
	 */
	if ((aierr = sockaddr_to_hostaddr(sa, sa_length, &client_addr,
					  &client_port, 0)) != 0)
	    msg_fatal("%s: cannot convert client address/port to string: %s",
		      myname, MAI_STRERROR(aierr));
	state->port = mystrdup(client_port.buf);

	/*
	 * XXX Strip off the IPv6 datalink suffix to avoid false alarms with
	 * strict address syntax checks.
	 */
#ifdef HAS_IPV6
	(void) split_at(client_addr.buf, '%');
#endif

	/*
	 * We convert IPv4-in-IPv6 address to 'true' IPv4 address early on,
	 * but only if IPv4 support is enabled (why would anyone want to turn
	 * it off)? With IPv4 support enabled we have no need for the IPv6
	 * form in logging, hostname verification and access checks.
	 */
#ifdef HAS_IPV6
	if (sa->sa_family == AF_INET6) {
	    if (strchr((char *) proto_info->sa_family_list, AF_INET) != 0
		&& IN6_IS_ADDR_V4MAPPED(&SOCK_ADDR_IN6_ADDR(sa))
		&& (colonp = strrchr(client_addr.buf, ':')) != 0) {
		struct addrinfo *res0;

		if (msg_verbose > 1)
		    msg_info("%s: rewriting V4-mapped address \"%s\" to \"%s\"",
			     myname, client_addr.buf, colonp + 1);

		state->addr = mystrdup(colonp + 1);
		state->rfc_addr = mystrdup(colonp + 1);
		state->addr_family = AF_INET;
		aierr = hostaddr_to_sockaddr(state->addr, (char *) 0, 0, &res0);
		if (aierr)
		    msg_fatal("%s: cannot convert %s from string to binary: %s",
			      myname, state->addr, MAI_STRERROR(aierr));
		sa_length = res0->ai_addrlen;
		if (sa_length > sizeof(state->sockaddr))
		    sa_length = sizeof(state->sockaddr);
		memcpy((char *) sa, res0->ai_addr, sa_length);
		freeaddrinfo(res0);		/* 200412 */
	    }

	    /*
	     * Following RFC 2821 section 4.1.3, an IPv6 address literal gets
	     * a prefix of 'IPv6:'. We do this consistently for all IPv6
	     * addresses that that appear in headers or envelopes. The fact
	     * that valid_mailhost_addr() enforces the form helps of course.
	     * We use the form without IPV6: prefix when doing access
	     * control, or when accessing the connection cache.
	     */
	    else {
		state->addr = mystrdup(client_addr.buf);
		state->rfc_addr =
		    concatenate(IPV6_COL, client_addr.buf, (char *) 0);
		state->addr_family = sa->sa_family;
	    }
	}

	/*
	 * An IPv4 address is in dotted quad decimal form.
	 */
	else
#endif
	{
	    state->addr = mystrdup(client_addr.buf);
	    state->rfc_addr = mystrdup(client_addr.buf);
	    state->addr_family = sa->sa_family;
	}

	/*
	 * Look up and sanity check the client hostname.
	 * 
	 * It is unsafe to allow numeric hostnames, especially because there
	 * exists pressure to turn off the name->addr double check. In that
	 * case an attacker could trivally bypass access restrictions.
	 * 
	 * sockaddr_to_hostname() already rejects malformed or numeric names.
	 */
#define TEMP_AI_ERROR(e) \
	((e) == EAI_AGAIN || (e) == EAI_MEMORY || (e) == EAI_SYSTEM)

#define REJECT_PEER_NAME(state, code) { \
	myfree(state->name); \
	state->name = mystrdup(CLIENT_NAME_UNKNOWN); \
	state->name_status = code; \
    }

	if (var_smtpd_peername_lookup == 0) {
	    state->name = mystrdup(CLIENT_NAME_UNKNOWN);
	    state->reverse_name = mystrdup(CLIENT_NAME_UNKNOWN);
	    state->name_status = SMTPD_PEER_CODE_PERM;
	    state->reverse_name_status = SMTPD_PEER_CODE_PERM;
	} else if ((aierr = sockaddr_to_hostname(sa, sa_length, &client_name,
					 (MAI_SERVNAME_STR *) 0, 0)) != 0) {
	    state->name = mystrdup(CLIENT_NAME_UNKNOWN);
	    state->reverse_name = mystrdup(CLIENT_NAME_UNKNOWN);
	    state->name_status = (TEMP_AI_ERROR(aierr) ?
			       SMTPD_PEER_CODE_TEMP : SMTPD_PEER_CODE_PERM);
	    state->reverse_name_status = (TEMP_AI_ERROR(aierr) ?
			       SMTPD_PEER_CODE_TEMP : SMTPD_PEER_CODE_PERM);
	} else {
	    struct addrinfo *res0;
	    struct addrinfo *res;

	    state->name = mystrdup(client_name.buf);
	    state->reverse_name = mystrdup(client_name.buf);
	    state->name_status = SMTPD_PEER_CODE_OK;
	    state->reverse_name_status = SMTPD_PEER_CODE_OK;

	    /*
	     * Reject the hostname if it does not list the peer address.
	     * Without further validation or qualification, such information
	     * must not be allowed to enter the audit trail, as people would
	     * draw false conclusions.
	     */
	    aierr = hostname_to_sockaddr_pf(state->name, state->addr_family,
					    (char *) 0, 0, &res0);
	    if (aierr) {
		msg_warn("hostname %s does not resolve to address %s: %s",
			 state->name, state->addr, MAI_STRERROR(aierr));
		REJECT_PEER_NAME(state, (TEMP_AI_ERROR(aierr) ?
			    SMTPD_PEER_CODE_TEMP : SMTPD_PEER_CODE_FORGED));
	    } else {
		for (res = res0; /* void */ ; res = res->ai_next) {
		    if (res == 0) {
			msg_warn("hostname %s does not resolve to address %s",
				 state->name, state->addr);
			REJECT_PEER_NAME(state, SMTPD_PEER_CODE_FORGED);
			break;
		    }
		    if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) {
			msg_info("skipping address family %d for host %s",
				 res->ai_family, state->name);
			continue;
		    }
		    if (sock_addr_cmp_addr(res->ai_addr, sa) == 0)
			break;			/* keep peer name */
		}
		freeaddrinfo(res0);
	    }
	}
    }

    /*
     * If it's not Internet, assume the client is local, and avoid using the
     * naming service because that can hang when the machine is disconnected.
     */
    else {
	state->name = mystrdup("localhost");
	state->reverse_name = mystrdup("localhost");
	if (proto_info->sa_family_list[0] == PF_INET6) {
	    state->addr = mystrdup("::1");	/* XXX bogus. */
	    state->rfc_addr = mystrdup(IPV6_COL "::1");	/* XXX bogus. */
	} else {
	    state->addr = mystrdup("127.0.0.1");/* XXX bogus. */
	    state->rfc_addr = mystrdup("127.0.0.1");	/* XXX bogus. */
	}
	state->addr_family = AF_UNSPEC;
	state->name_status = SMTPD_PEER_CODE_OK;
	state->reverse_name_status = SMTPD_PEER_CODE_OK;
	state->port = mystrdup("0");		/* XXX bogus. */
    }

    /*
     * Do the name[addr]:port formatting for pretty reports.
     */
    state->namaddr = SMTPD_BUILD_NAMADDRPORT(state->name, state->addr,
					     state->port);
}
Пример #11
0
void VTKExporter::writeVTKSimple()
{
    std::string filename = vtkFilename.getFullPath();

    std::ostringstream oss;
    oss << "_" << nbFiles;

    if (filename.size() > 3) {
        std::string ext;
        std::string baseName;
        if (filename.substr(filename.size()-4)==".vtu") {
            ext = ".vtu";
            baseName = filename.substr(0, filename.size()-4);
        }

        if (filename.substr(filename.size()-4)==".vtk") {
            ext = ".vtk";
            baseName = filename.substr(0, filename.size()-4);
        }

        /// no extension given => default "vtu"
        if (ext == "") {
            ext = ".vtu";
            baseName = filename;
        }

        if (overwrite.getValue())
            filename = baseName + ext;
        else
            filename = baseName + oss.str() + ext;

    }

    /*if ( filename.size() > 3 && filename.substr(filename.size()-4)==".vtu")
    {
        if (!overwrite.getValue())
            filename = filename.substr(0,filename.size()-4) + oss.str() + ".vtu";
    }
    else
    {
        if (!overwrite.getValue())
            filename += oss.str();
        filename += ".vtu";
    }*/

    outfile = new std::ofstream(filename.c_str());
    if( !outfile->is_open() )
    {
        msg_error() << "Error creating file "<<filename;
        delete outfile;
        outfile = NULL;
        return;
    }

    const helper::vector<std::string>& pointsData = dPointsDataFields.getValue();
    const helper::vector<std::string>& cellsData = dCellsDataFields.getValue();

    helper::ReadAccessor<Data<defaulttype::Vec3Types::VecCoord> > pointsPos = position;

    const int nbp = (!pointsPos.empty()) ? pointsPos.size() : topology->getNbPoints();

    //Write header
    *outfile << "# vtk DataFile Version 2.0" << std::endl;

    //write Title
    *outfile << "Exported VTK file" << std::endl;

    //write Data type
    *outfile << "ASCII" << std::endl;

    *outfile << std::endl;

    //write dataset (geometry, unstructured grid)
    *outfile << "DATASET " << "UNSTRUCTURED_GRID" << std::endl;

    *outfile << "POINTS " << nbp << " float" << std::endl;
    //write Points
    if (!pointsPos.empty())
    {
        for (int i=0 ; i<nbp; i++)
        {
            *outfile << pointsPos[i] << std::endl;
        }
    }
    else if (mstate && mstate->getSize() == (size_t)nbp)
    {
        for (size_t i=0 ; i<mstate->getSize() ; i++)
        {
            *outfile << mstate->getPX(i) << " " << mstate->getPY(i) << " " << mstate->getPZ(i) << std::endl;
        }
    }
    else
    {
        for (int i=0 ; i<nbp ; i++)
        {
            *outfile << topology->getPX(i) << " " << topology->getPY(i) << " " << topology->getPZ(i) << std::endl;
        }
    }

    *outfile << std::endl;

    //Write Cells
    unsigned int numberOfCells, totalSize;
    numberOfCells = ( (writeEdges.getValue()) ? topology->getNbEdges() : 0 )
            +( (writeTriangles.getValue()) ? topology->getNbTriangles() : 0 )
            +( (writeQuads.getValue()) ? topology->getNbQuads() : 0 )
            +( (writeTetras.getValue()) ? topology->getNbTetras() : 0 )
            +( (writeHexas.getValue()) ? topology->getNbHexas() : 0 );
    totalSize =     ( (writeEdges.getValue()) ? 3 * topology->getNbEdges() : 0 )
            +( (writeTriangles.getValue()) ? 4 *topology->getNbTriangles() : 0 )
            +( (writeQuads.getValue()) ? 5 *topology->getNbQuads() : 0 )
            +( (writeTetras.getValue()) ? 5 *topology->getNbTetras() : 0 )
            +( (writeHexas.getValue()) ? 9 *topology->getNbHexas() : 0 );


    *outfile << "CELLS " << numberOfCells << " " << totalSize << std::endl;

    if (writeEdges.getValue())
    {
        for (int i=0 ; i<topology->getNbEdges() ; i++)
            *outfile << 2 << " " << topology->getEdge(i) << std::endl;
    }

    if (writeTriangles.getValue())
    {
        for (int i=0 ; i<topology->getNbTriangles() ; i++)
            *outfile << 3 << " " <<  topology->getTriangle(i) << std::endl;
    }
    if (writeQuads.getValue())
    {
        for (int i=0 ; i<topology->getNbQuads() ; i++)
            *outfile << 4 << " " << topology->getQuad(i) << std::endl;
    }

    if (writeTetras.getValue())
    {
        for (int i=0 ; i<topology->getNbTetras() ; i++)
            *outfile << 4 << " " <<  topology->getTetra(i) << std::endl;
    }
    if (writeHexas.getValue())
    {
        for (int i=0 ; i<topology->getNbHexas() ; i++)
            *outfile << 8 << " " <<  topology->getHexa(i) << std::endl;
    }

    *outfile << std::endl;

    *outfile << "CELL_TYPES " << numberOfCells << std::endl;

    if (writeEdges.getValue())
    {
        for (int i=0 ; i<topology->getNbEdges() ; i++)
            *outfile << 3 << std::endl;
    }

    if (writeTriangles.getValue())
    {
        for (int i=0 ; i<topology->getNbTriangles() ; i++)
            *outfile << 5 << std::endl;
    }
    if (writeQuads.getValue())
    {
        for (int i=0 ; i<topology->getNbQuads() ; i++)
            *outfile << 9 << std::endl;
    }

    if (writeTetras.getValue())
    {
        for (int i=0 ; i<topology->getNbTetras() ; i++)
            *outfile << 10 << std::endl;
    }
    if (writeHexas.getValue())
    {
        for (int i=0 ; i<topology->getNbHexas() ; i++)
            *outfile << 12 << std::endl;
    }

    *outfile << std::endl;

    //write dataset attributes
    if (!pointsData.empty())
    {
        *outfile << "POINT_DATA " << nbp << std::endl;
        writeData(pointsDataObject, pointsDataField, pointsDataName);
    }

    if (!cellsData.empty())
    {
        *outfile << "CELL_DATA " << numberOfCells << std::endl;
        writeData(cellsDataObject, cellsDataField, cellsDataName);
    }

    outfile->close();

    ++nbFiles;

    msg_info() << "Export VTK in file " << filename << "  done.";
}
Пример #12
0
static void smtpd_peer_sockaddr_to_hostname(SMTPD_STATE *state)
{
    struct sockaddr *sa = (struct sockaddr *) &(state->sockaddr);
    SOCKADDR_SIZE sa_length = state->sockaddr_len;
    MAI_HOSTNAME_STR client_name;
    int     aierr;

    /*
     * Look up and sanity check the client hostname.
     * 
     * It is unsafe to allow numeric hostnames, especially because there exists
     * pressure to turn off the name->addr double check. In that case an
     * attacker could trivally bypass access restrictions.
     * 
     * sockaddr_to_hostname() already rejects malformed or numeric names.
     */
#define TEMP_AI_ERROR(e) \
	((e) == EAI_AGAIN || (e) == EAI_MEMORY || (e) == EAI_SYSTEM)

#define REJECT_PEER_NAME(state, code) { \
	myfree(state->name); \
	state->name = mystrdup(CLIENT_NAME_UNKNOWN); \
	state->name_status = code; \
    }

    if (var_smtpd_peername_lookup == 0) {
	state->name = mystrdup(CLIENT_NAME_UNKNOWN);
	state->reverse_name = mystrdup(CLIENT_NAME_UNKNOWN);
	state->name_status = SMTPD_PEER_CODE_PERM;
	state->reverse_name_status = SMTPD_PEER_CODE_PERM;
    } else if ((aierr = sockaddr_to_hostname(sa, sa_length, &client_name,
					 (MAI_SERVNAME_STR *) 0, 0)) != 0) {
	state->name = mystrdup(CLIENT_NAME_UNKNOWN);
	state->reverse_name = mystrdup(CLIENT_NAME_UNKNOWN);
	state->name_status = (TEMP_AI_ERROR(aierr) ?
			      SMTPD_PEER_CODE_TEMP : SMTPD_PEER_CODE_PERM);
	state->reverse_name_status = (TEMP_AI_ERROR(aierr) ?
			       SMTPD_PEER_CODE_TEMP : SMTPD_PEER_CODE_PERM);
    } else {
	struct addrinfo *res0;
	struct addrinfo *res;

	state->name = mystrdup(client_name.buf);
	state->reverse_name = mystrdup(client_name.buf);
	state->name_status = SMTPD_PEER_CODE_OK;
	state->reverse_name_status = SMTPD_PEER_CODE_OK;

	/*
	 * Reject the hostname if it does not list the peer address. Without
	 * further validation or qualification, such information must not be
	 * allowed to enter the audit trail, as people would draw false
	 * conclusions.
	 */
	aierr = hostname_to_sockaddr_pf(state->name, state->addr_family,
					(char *) 0, 0, &res0);
	if (aierr) {
	    msg_warn("hostname %s does not resolve to address %s: %s",
		     state->name, state->addr, MAI_STRERROR(aierr));
	    REJECT_PEER_NAME(state, (TEMP_AI_ERROR(aierr) ?
			    SMTPD_PEER_CODE_TEMP : SMTPD_PEER_CODE_FORGED));
	} else {
	    for (res = res0; /* void */ ; res = res->ai_next) {
		if (res == 0) {
		    msg_warn("hostname %s does not resolve to address %s",
			     state->name, state->addr);
		    REJECT_PEER_NAME(state, SMTPD_PEER_CODE_FORGED);
		    break;
		}
		if (strchr((char *) proto_info->sa_family_list, res->ai_family) == 0) {
		    msg_info("skipping address family %d for host %s",
			     res->ai_family, state->name);
		    continue;
		}
		if (sock_addr_cmp_addr(res->ai_addr, sa) == 0)
		    break;			/* keep peer name */
	    }
	    freeaddrinfo(res0);
	}
    }
}
Пример #13
0
static int smtpd_peer_sockaddr_to_hostaddr(SMTPD_STATE *state)
{
    const char *myname = "smtpd_peer_sockaddr_to_hostaddr";
    struct sockaddr *sa = (struct sockaddr *) &(state->sockaddr);
    SOCKADDR_SIZE sa_length = state->sockaddr_len;

    /*
     * XXX If we're given an IPv6 (or IPv4) connection from, e.g., inetd,
     * while Postfix IPv6 (or IPv4) support is turned off, don't (skip to the
     * final else clause, pretend the origin is localhost[127.0.0.1], and
     * become an open relay).
     */
    if (sa->sa_family == AF_INET
#ifdef AF_INET6
	|| sa->sa_family == AF_INET6
#endif
	) {
	MAI_HOSTADDR_STR client_addr;
	MAI_SERVPORT_STR client_port;
	int     aierr;
	char   *colonp;

	/*
	 * Sanity check: we can't use sockets that we're not configured for.
	 */
	if (strchr((char *) proto_info->sa_family_list, sa->sa_family) == 0)
	    msg_fatal("cannot handle socket type %s with \"%s = %s\"",
#ifdef AF_INET6
		      sa->sa_family == AF_INET6 ? "AF_INET6" :
#endif
		      sa->sa_family == AF_INET ? "AF_INET" :
		      "other", VAR_INET_PROTOCOLS, var_inet_protocols);

	/*
	 * Sorry, but there are some things that we just cannot do while
	 * connected to the network.
	 */
	if (geteuid() != var_owner_uid || getuid() != var_owner_uid) {
	    msg_error("incorrect SMTP server privileges: uid=%lu euid=%lu",
		      (unsigned long) getuid(), (unsigned long) geteuid());
	    msg_fatal("the Postfix SMTP server must run with $%s privileges",
		      VAR_MAIL_OWNER);
	}

	/*
	 * Convert the client address to printable form.
	 */
	if ((aierr = sockaddr_to_hostaddr(sa, sa_length, &client_addr,
					  &client_port, 0)) != 0)
	    msg_fatal("%s: cannot convert client address/port to string: %s",
		      myname, MAI_STRERROR(aierr));
	state->port = mystrdup(client_port.buf);

	/*
	 * XXX Require that the infrastructure strips off the IPv6 datalink
	 * suffix to avoid false alarms with strict address syntax checks.
	 */
#ifdef HAS_IPV6
	if (strchr(client_addr.buf, '%') != 0)
	    msg_panic("%s: address %s has datalink suffix",
		      myname, client_addr.buf);
#endif

	/*
	 * We convert IPv4-in-IPv6 address to 'true' IPv4 address early on,
	 * but only if IPv4 support is enabled (why would anyone want to turn
	 * it off)? With IPv4 support enabled we have no need for the IPv6
	 * form in logging, hostname verification and access checks.
	 */
#ifdef HAS_IPV6
	if (sa->sa_family == AF_INET6) {
	    if (strchr((char *) proto_info->sa_family_list, AF_INET) != 0
		&& IN6_IS_ADDR_V4MAPPED(&SOCK_ADDR_IN6_ADDR(sa))
		&& (colonp = strrchr(client_addr.buf, ':')) != 0) {
		struct addrinfo *res0;

		if (msg_verbose > 1)
		    msg_info("%s: rewriting V4-mapped address \"%s\" to \"%s\"",
			     myname, client_addr.buf, colonp + 1);

		state->addr = mystrdup(colonp + 1);
		state->rfc_addr = mystrdup(colonp + 1);
		state->addr_family = AF_INET;
		aierr = hostaddr_to_sockaddr(state->addr, (char *) 0, 0, &res0);
		if (aierr)
		    msg_fatal("%s: cannot convert %s from string to binary: %s",
			      myname, state->addr, MAI_STRERROR(aierr));
		sa_length = res0->ai_addrlen;
		if (sa_length > sizeof(state->sockaddr))
		    sa_length = sizeof(state->sockaddr);
		memcpy((void *) sa, res0->ai_addr, sa_length);
		freeaddrinfo(res0);		/* 200412 */
	    }

	    /*
	     * Following RFC 2821 section 4.1.3, an IPv6 address literal gets
	     * a prefix of 'IPv6:'. We do this consistently for all IPv6
	     * addresses that that appear in headers or envelopes. The fact
	     * that valid_mailhost_addr() enforces the form helps of course.
	     * We use the form without IPV6: prefix when doing access
	     * control, or when accessing the connection cache.
	     */
	    else {
		state->addr = mystrdup(client_addr.buf);
		state->rfc_addr =
		    concatenate(IPV6_COL, client_addr.buf, (char *) 0);
		state->addr_family = sa->sa_family;
	    }
	}

	/*
	 * An IPv4 address is in dotted quad decimal form.
	 */
	else
#endif
	{
	    state->addr = mystrdup(client_addr.buf);
	    state->rfc_addr = mystrdup(client_addr.buf);
	    state->addr_family = sa->sa_family;
	}
	return (0);
    }

    /*
     * It's not Internet.
     */
    else {
	return (-1);
    }
}
Пример #14
0
static int xsasl_dovecot_server_connect(XSASL_DOVECOT_SERVER_IMPL *xp)
{
    const char *myname = "xsasl_dovecot_server_connect";
    VSTRING *line_str;
    VSTREAM *sasl_stream;
    char   *line, *cmd, *mech_name;
    unsigned int major_version, minor_version;
    int     fd, success;
    int     sec_props;
    const char *path;

    if (msg_verbose)
	msg_info("%s: Connecting", myname);

    /*
     * Not documented, but necessary for testing.
     */
    path = xp->socket_path;
    if (strncmp(path, "inet:", 5) == 0) {
	fd = inet_connect(path + 5, BLOCKING, AUTH_TIMEOUT);
    } else {
	if (strncmp(path, "unix:", 5) == 0)
	    path += 5;
	fd = unix_connect(path, BLOCKING, AUTH_TIMEOUT);
    }
    if (fd < 0) {
	msg_warn("SASL: Connect to %s failed: %m", xp->socket_path);
	return (-1);
    }
    sasl_stream = vstream_fdopen(fd, O_RDWR);
    vstream_control(sasl_stream,
		    VSTREAM_CTL_PATH, xp->socket_path,
		    VSTREAM_CTL_TIMEOUT, AUTH_TIMEOUT,
		    VSTREAM_CTL_END);

    /* XXX Encapsulate for logging. */
    vstream_fprintf(sasl_stream,
		    "VERSION\t%u\t%u\n"
		    "CPID\t%u\n",
		    AUTH_PROTOCOL_MAJOR_VERSION,
		    AUTH_PROTOCOL_MINOR_VERSION,
		    (unsigned int) getpid());
    if (vstream_fflush(sasl_stream) == VSTREAM_EOF) {
	msg_warn("SASL: Couldn't send handshake: %m");
	return (-1);
    }
    success = 0;
    line_str = vstring_alloc(256);
    /* XXX Encapsulate for logging. */
    while (vstring_get_nonl(line_str, sasl_stream) != VSTREAM_EOF) {
	line = vstring_str(line_str);

	if (msg_verbose)
	    msg_info("%s: auth reply: %s", myname, line);

	cmd = line;
	line = split_at(line, '\t');

	if (strcmp(cmd, "VERSION") == 0) {
	    if (sscanf(line, "%u\t%u", &major_version, &minor_version) != 2) {
		msg_warn("SASL: Protocol version error");
		break;
	    }
	    if (major_version != AUTH_PROTOCOL_MAJOR_VERSION) {
		/* Major version is different from ours. */
		msg_warn("SASL: Protocol version mismatch (%d vs. %d)",
			 major_version, AUTH_PROTOCOL_MAJOR_VERSION);
		break;
	    }
	} else if (strcmp(cmd, "MECH") == 0 && line != NULL) {
	    mech_name = line;
	    line = split_at(line, '\t');
	    if (line != 0) {
		sec_props =
		    name_mask_delim_opt(myname,
					xsasl_dovecot_serv_sec_props,
					line, "\t",
				     NAME_MASK_ANY_CASE | NAME_MASK_IGNORE);
		if ((sec_props & SEC_PROPS_PRIVATE) != 0)
		    continue;
	    } else
		sec_props = 0;
	    xsasl_dovecot_server_mech_append(&xp->mechanism_list, mech_name,
					     sec_props);
	} else if (strcmp(cmd, "DONE") == 0) {
	    /* Handshake finished. */
	    success = 1;
	    break;
	} else {
	    /* ignore any unknown commands */
	}
    }
    vstring_free(line_str);

    if (!success) {
	/* handshake failed */
	(void) vstream_fclose(sasl_stream);
	return (-1);
    }
    xp->sasl_stream = sasl_stream;
    return (0);
}
Пример #15
0
static void psc_early_event(int event, char *context)
{
    const char *myname = "psc_early_event";
    PSC_STATE *state = (PSC_STATE *) context;
    char    read_buf[PSC_READ_BUF_SIZE];
    int     read_count;
    DELTA_TIME elapsed;

    if (msg_verbose > 1)
	msg_info("%s: sq=%d cq=%d event %d on smtp socket %d from [%s]:%s flags=%s",
		 myname, psc_post_queue_length, psc_check_queue_length,
		 event, vstream_fileno(state->smtp_client_stream),
		 state->smtp_client_addr, state->smtp_client_port,
		 psc_print_state_flags(state->flags, myname));

    PSC_CLEAR_EVENT_REQUEST(vstream_fileno(state->smtp_client_stream),
			    psc_early_event, context);

    /*
     * XXX Be sure to empty the DNSBL lookup buffer otherwise we have a
     * memory leak.
     * 
     * XXX We can avoid "forgetting" to do this by keeping a pointer to the
     * DNSBL lookup buffer in the PSC_STATE structure. This also allows us to
     * shave off a hash table lookup when retrieving the DNSBL result.
     * 
     * A direct pointer increases the odds of dangling pointers. Hash-table
     * lookup is safer, and that is why it's done that way.
     */
    switch (event) {

	/*
	 * We either reached the end of the early tests time limit, or all
	 * early tests completed before the pregreet timer would go off.
	 */
    case EVENT_TIME:

	/*
	 * Check if the SMTP client spoke before its turn.
	 */
	if ((state->flags & PSC_STATE_FLAG_PREGR_TODO) != 0
	    && (state->flags & PSC_STATE_MASK_PREGR_FAIL_DONE) == 0) {
	    state->pregr_stamp = event_time() + var_psc_pregr_ttl;
	    PSC_PASS_SESSION_STATE(state, "pregreet test",
				   PSC_STATE_FLAG_PREGR_PASS);
	}
	if ((state->flags & PSC_STATE_FLAG_PREGR_FAIL)
	    && psc_pregr_action == PSC_ACT_IGNORE) {
	    PSC_UNFAIL_SESSION_STATE(state, PSC_STATE_FLAG_PREGR_FAIL);
	    /* Not: PSC_PASS_SESSION_STATE. Repeat this test the next time. */
	}

	/*
	 * Collect the DNSBL score, and whitelist other tests if applicable.
	 * Note: this score will be partial when some DNS lookup did not
	 * complete before the pregreet timer expired.
	 * 
	 * If the client is DNS blocklisted, drop the connection, send the
	 * client to a dummy protocol engine, or continue to the next test.
	 */
#define PSC_DNSBL_FORMAT \
	"%s 5.7.1 Service unavailable; client [%s] blocked using %s\r\n"
#define NO_DNSBL_SCORE	INT_MAX

	if (state->flags & PSC_STATE_FLAG_DNSBL_TODO) {
	    if (state->dnsbl_score == NO_DNSBL_SCORE) {
		state->dnsbl_score =
		    psc_dnsbl_retrieve(state->smtp_client_addr,
				       &state->dnsbl_name,
				       state->dnsbl_index);
		if (var_psc_dnsbl_wthresh < 0)
		    psc_whitelist_non_dnsbl(state);
	    }
	    if (state->dnsbl_score < var_psc_dnsbl_thresh) {
		state->dnsbl_stamp = event_time() + var_psc_dnsbl_ttl;
		PSC_PASS_SESSION_STATE(state, "dnsbl test",
				       PSC_STATE_FLAG_DNSBL_PASS);
	    } else {
		msg_info("DNSBL rank %d for [%s]:%s",
			 state->dnsbl_score, PSC_CLIENT_ADDR_PORT(state));
		PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_DNSBL_FAIL);
		switch (psc_dnsbl_action) {
		case PSC_ACT_DROP:
		    state->dnsbl_reply = vstring_sprintf(vstring_alloc(100),
						    PSC_DNSBL_FORMAT, "521",
						    state->smtp_client_addr,
							 state->dnsbl_name);
		    PSC_DROP_SESSION_STATE(state, STR(state->dnsbl_reply));
		    return;
		case PSC_ACT_ENFORCE:
		    state->dnsbl_reply = vstring_sprintf(vstring_alloc(100),
						    PSC_DNSBL_FORMAT, "550",
						    state->smtp_client_addr,
							 state->dnsbl_name);
		    PSC_ENFORCE_SESSION_STATE(state, STR(state->dnsbl_reply));
		    break;
		case PSC_ACT_IGNORE:
		    PSC_UNFAIL_SESSION_STATE(state, PSC_STATE_FLAG_DNSBL_FAIL);
		    /* Not: PSC_PASS_SESSION_STATE. Repeat this test. */
		    break;
		default:
		    msg_panic("%s: unknown dnsbl action value %d",
			      myname, psc_dnsbl_action);

		}
	    }
	}

	/*
	 * Pass the connection to a real SMTP server, or enter the dummy
	 * engine for deep tests.
	 */
	if ((state->flags & PSC_STATE_FLAG_NOFORWARD) != 0
	    || ((state->flags & PSC_STATE_MASK_SMTPD_PASS)
		!= PSC_STATE_FLAGS_TODO_TO_PASS(state->flags & PSC_STATE_MASK_SMTPD_TODO)))
	    psc_smtpd_tests(state);
	else
	    psc_conclude(state);
	return;

	/*
	 * EOF, or the client spoke before its turn. We simply drop the
	 * connection, or we continue waiting and allow DNS replies to
	 * trickle in.
	 */
    default:
	if ((read_count = recv(vstream_fileno(state->smtp_client_stream),
			  read_buf, sizeof(read_buf) - 1, MSG_PEEK)) <= 0) {
	    /* Avoid memory leak. */
	    if (state->dnsbl_score == NO_DNSBL_SCORE
		&& (state->flags & PSC_STATE_FLAG_DNSBL_TODO))
		(void) psc_dnsbl_retrieve(state->smtp_client_addr,
					  &state->dnsbl_name,
					  state->dnsbl_index);
	    /* XXX Wait for DNS replies to come in. */
	    psc_hangup_event(state);
	    return;
	}
	read_buf[read_count] = 0;
	escape(psc_escape_buf, read_buf, read_count);
	msg_info("PREGREET %d after %s from [%s]:%s: %.100s", read_count,
	       psc_format_delta_time(psc_temp, state->start_time, &elapsed),
		 PSC_CLIENT_ADDR_PORT(state), STR(psc_escape_buf));
	PSC_FAIL_SESSION_STATE(state, PSC_STATE_FLAG_PREGR_FAIL);
	switch (psc_pregr_action) {
	case PSC_ACT_DROP:
	    /* Avoid memory leak. */
	    if (state->dnsbl_score == NO_DNSBL_SCORE
		&& (state->flags & PSC_STATE_FLAG_DNSBL_TODO))
		(void) psc_dnsbl_retrieve(state->smtp_client_addr,
					  &state->dnsbl_name,
					  state->dnsbl_index);
	    PSC_DROP_SESSION_STATE(state, "521 5.5.1 Protocol error\r\n");
	    return;
	case PSC_ACT_ENFORCE:
	    /* We call psc_dnsbl_retrieve() when the timer expires. */
	    PSC_ENFORCE_SESSION_STATE(state, "550 5.5.1 Protocol error\r\n");
	    break;
	case PSC_ACT_IGNORE:
	    /* We call psc_dnsbl_retrieve() when the timer expires. */
	    /* We must handle this case after the timer expires. */
	    break;
	default:
	    msg_panic("%s: unknown pregreet action value %d",
		      myname, psc_pregr_action);
	}

	/*
	 * Terminate the greet delay if we're just waiting for the pregreet
	 * test to complete. It is safe to call psc_early_event directly,
	 * since we are already in that function.
	 * 
	 * XXX After this code passes all tests, swap around the two blocks in
	 * this switch statement and fall through from EVENT_READ into
	 * EVENT_TIME, instead of calling psc_early_event recursively.
	 */
	state->flags |= PSC_STATE_FLAG_PREGR_DONE;
	if (elapsed.dt_sec >= PSC_EFF_GREET_WAIT
	    || ((state->flags & PSC_STATE_MASK_EARLY_DONE)
		== PSC_STATE_FLAGS_TODO_TO_DONE(state->flags & PSC_STATE_MASK_EARLY_TODO)))
	    psc_early_event(EVENT_TIME, context);
	else
	    event_request_timer(psc_early_event, context,
				PSC_EFF_GREET_WAIT - elapsed.dt_sec);
	return;
    }
}
Пример #16
0
static void multi_server_abort(int unused_event, char *unused_context)
{
    if (msg_verbose)
	msg_info("master disconnect -- exiting");
    multi_server_exit();
}
Пример #17
0
static int xsasl_dovecot_server_connect(XSASL_DOVECOT_SERVER_IMPL *xp)
{
    const char *myname = "xsasl_dovecot_server_connect";
    VSTRING *line_str;
    VSTREAM *sasl_stream;
    char   *line, *cmd, *mech_name;
    unsigned int major_version, minor_version;
    int     fd, success, have_mech_line;
    int     sec_props;
    const char *path;

    if (msg_verbose)
	msg_info("%s: Connecting", myname);

    /*
     * Not documented, but necessary for testing.
     */
    path = xp->socket_path;
    if (strncmp(path, "inet:", 5) == 0) {
	fd = inet_connect(path + 5, BLOCKING, AUTH_TIMEOUT);
    } else {
	if (strncmp(path, "unix:", 5) == 0)
	    path += 5;
	fd = unix_connect(path, BLOCKING, AUTH_TIMEOUT);
    }
    if (fd < 0) {
	msg_warn("SASL: Connect to %s failed: %m", xp->socket_path);
	return (-1);
    }
    sasl_stream = vstream_fdopen(fd, O_RDWR);
    vstream_control(sasl_stream,
		    CA_VSTREAM_CTL_PATH(xp->socket_path),
		    CA_VSTREAM_CTL_TIMEOUT(AUTH_TIMEOUT),
		    CA_VSTREAM_CTL_END);

    /* XXX Encapsulate for logging. */
    vstream_fprintf(sasl_stream,
		    "VERSION\t%u\t%u\n"
		    "CPID\t%u\n",
		    AUTH_PROTOCOL_MAJOR_VERSION,
		    AUTH_PROTOCOL_MINOR_VERSION,
		    (unsigned int) getpid());
    if (vstream_fflush(sasl_stream) == VSTREAM_EOF) {
	msg_warn("SASL: Couldn't send handshake: %m");
	return (-1);
    }
    success = 0;
    have_mech_line = 0;
    line_str = vstring_alloc(256);
    /* XXX Encapsulate for logging. */
    while (vstring_get_nonl(line_str, sasl_stream) != VSTREAM_EOF) {
	line = vstring_str(line_str);

	if (msg_verbose)
	    msg_info("%s: auth reply: %s", myname, line);

	cmd = line;
	line = split_at(line, '\t');

	if (strcmp(cmd, "VERSION") == 0) {
	    if (sscanf(line, "%u\t%u", &major_version, &minor_version) != 2) {
		msg_warn("SASL: Protocol version error");
		break;
	    }
	    if (major_version != AUTH_PROTOCOL_MAJOR_VERSION) {
		/* Major version is different from ours. */
		msg_warn("SASL: Protocol version mismatch (%d vs. %d)",
			 major_version, AUTH_PROTOCOL_MAJOR_VERSION);
		break;
	    }
	} else if (strcmp(cmd, "MECH") == 0 && line != NULL) {
	    mech_name = line;
	    have_mech_line = 1;
	    line = split_at(line, '\t');
	    if (line != 0) {
		sec_props =
		    name_mask_delim_opt(myname,
					xsasl_dovecot_serv_sec_props,
					line, "\t",
				     NAME_MASK_ANY_CASE | NAME_MASK_IGNORE);
		if ((sec_props & SEC_PROPS_PRIVATE) != 0)
		    continue;
	    } else
		sec_props = 0;
	    xsasl_dovecot_server_mech_append(&xp->mechanism_list, mech_name,
					     sec_props);
	} else if (strcmp(cmd, "SPID") == 0) {

	    /*
	     * Unfortunately the auth protocol handshake wasn't designed well
	     * to differentiate between auth-client/userdb/master.
	     * auth-userdb and auth-master send VERSION + SPID lines only and
	     * nothing afterwards, while auth-client sends VERSION + MECH +
	     * SPID + CUID + more. The simplest way that we can determine if
	     * we've connected to the correct socket is to see if MECH line
	     * exists or not (alternatively we'd have to have a small timeout
	     * after SPID to see if CUID is sent or not).
	     */
	    if (!have_mech_line) {
		msg_warn("SASL: Connected to wrong auth socket (auth-master instead of auth-client)");
		break;
	    }
	} else if (strcmp(cmd, "DONE") == 0) {
	    /* Handshake finished. */
	    success = 1;
	    break;
	} else {
	    /* ignore any unknown commands */
	}
    }
    vstring_free(line_str);

    if (!success) {
	/* handshake failed */
	(void) vstream_fclose(sasl_stream);
	return (-1);
    }
    xp->sasl_stream = sasl_stream;
    return (0);
}
Пример #18
0
static void multi_server_timeout(int unused_event, char *unused_context)
{
    if (msg_verbose)
	msg_info("idle timeout -- exiting");
    multi_server_exit();
}
Пример #19
0
static int smtpd_proxy_cmd(SMTPD_STATE *state, int expect, const char *fmt,...)
{
    SMTPD_PROXY *proxy = state->proxy;
    va_list ap;
    char   *cp;
    int     last_char;
    int     err = 0;
    static VSTRING *buffer = 0;

    /*
     * Errors first. Be prepared for delayed errors from the DATA phase.
     */
    if (vstream_ferror(proxy->service_stream)
	|| vstream_feof(proxy->service_stream)
	|| (err = vstream_setjmp(proxy->service_stream)) != 0) {
	return (smtpd_proxy_rdwr_error(state, err));
    }

    /*
     * The command can be omitted at the start of an SMTP session. This is
     * not documented as part of the official interface because it is used
     * only internally to this module.
     */
    if (fmt != SMTPD_PROXY_CONN_FMT) {

	/*
	 * Format the command.
	 */
	va_start(ap, fmt);
	vstring_vsprintf(proxy->request, fmt, ap);
	va_end(ap);

	/*
	 * Optionally log the command first, so that we can see in the log
	 * what the program is trying to do.
	 */
	if (msg_verbose)
	    msg_info("> %s: %s", proxy->service_name, STR(proxy->request));

	/*
	 * Send the command to the proxy server. Since we're going to read a
	 * reply immediately, there is no need to flush buffers.
	 */
	smtp_fputs(STR(proxy->request), LEN(proxy->request),
		   proxy->service_stream);
    }

    /*
     * Early return if we don't want to wait for a server reply (such as
     * after sending QUIT).
     */
    if (expect == SMTPD_PROX_WANT_NONE)
	return (0);

    /*
     * Censor out non-printable characters in server responses and save
     * complete multi-line responses if possible.
     * 
     * We can't parse or store input that exceeds var_line_limit, so we just
     * skip over it to simplify the remainder of the code below.
     */
    VSTRING_RESET(proxy->reply);
    if (buffer == 0)
	buffer = vstring_alloc(10);
    for (;;) {
	last_char = smtp_get(buffer, proxy->service_stream, var_line_limit,
			     SMTP_GET_FLAG_SKIP);
	printable(STR(buffer), '?');
	if (last_char != '\n')
	    msg_warn("%s: response longer than %d: %.30s...",
		     proxy->service_name, var_line_limit,
		     STR(buffer));
	if (msg_verbose)
	    msg_info("< %s: %.100s", proxy->service_name, STR(buffer));

	/*
	 * Defend against a denial of service attack by limiting the amount
	 * of multi-line text that we are willing to store.
	 */
	if (LEN(proxy->reply) < var_line_limit) {
	    if (VSTRING_LEN(proxy->reply))
		vstring_strcat(proxy->reply, "\r\n");
	    vstring_strcat(proxy->reply, STR(buffer));
	}

	/*
	 * Parse the response into code and text. Ignore unrecognized
	 * garbage. This means that any character except space (or end of
	 * line) will have the same effect as the '-' line continuation
	 * character.
	 */
	for (cp = STR(buffer); *cp && ISDIGIT(*cp); cp++)
	     /* void */ ;
	if (cp - STR(buffer) == 3) {
	    if (*cp == '-')
		continue;
	    if (*cp == ' ' || *cp == 0)
		break;
	}
	msg_warn("received garbage from proxy %s: %.100s",
		 proxy->service_name, STR(buffer));
    }

    /*
     * Log a warning in case the proxy does not send the expected response.
     * Silently accept any response when the client expressed no expectation.
     * 
     * Starting with Postfix 2.6 we don't pass through unexpected 2xx or 3xx
     * proxy replies. They are a source of support problems, so we replace
     * them by generic server error replies.
     */
    if (expect != SMTPD_PROX_WANT_ANY && expect != *STR(proxy->reply)) {
	msg_warn("proxy %s rejected \"%s\": \"%s\"",
		 proxy->service_name, fmt == SMTPD_PROXY_CONN_FMT ?
		 "connection request" : STR(proxy->request),
		 STR(proxy->reply));
	if (*STR(proxy->reply) == SMTPD_PROX_WANT_OK
	    || *STR(proxy->reply) == SMTPD_PROX_WANT_MORE) {
	    smtpd_proxy_rdwr_error(state, 0);
	}
	return (-1);
    } else {
	return (0);
    }
}
Пример #20
0
NORETURN multi_server_main(int argc, char **argv, MULTI_SERVER_FN service,...)
{
    const char *myname = "multi_server_main";
    VSTREAM *stream = 0;
    char   *root_dir = 0;
    char   *user_name = 0;
    int     debug_me = 0;
    int     daemon_mode = 1;
    char   *service_name = basename(argv[0]);
    int     delay;
    int     c;
    int     fd;
    va_list ap;
    MAIL_SERVER_INIT_FN pre_init = 0;
    MAIL_SERVER_INIT_FN post_init = 0;
    MAIL_SERVER_LOOP_FN loop = 0;
    int     key;
    char   *transport = 0;

#if 0
    char   *lock_path;
    VSTRING *why;

#endif
    int     alone = 0;
    int     zerolimit = 0;
    WATCHDOG *watchdog;
    char   *oname_val;
    char   *oname;
    char   *oval;
    const char *err;
    char   *generation;
    int     msg_vstream_needed = 0;
    int     redo_syslog_init = 0;

    /*
     * Process environment options as early as we can.
     */
    if (getenv(CONF_ENV_VERB))
	msg_verbose = 1;
    if (getenv(CONF_ENV_DEBUG))
	debug_me = 1;

    /*
     * Don't die when a process goes away unexpectedly.
     */
    signal(SIGPIPE, SIG_IGN);

    /*
     * Don't die for frivolous reasons.
     */
#ifdef SIGXFSZ
    signal(SIGXFSZ, SIG_IGN);
#endif

    /*
     * May need this every now and then.
     */
    var_procname = mystrdup(basename(argv[0]));
    set_mail_conf_str(VAR_PROCNAME, var_procname);

    /*
     * Initialize logging and exit handler. Do the syslog first, so that its
     * initialization completes before we enter the optional chroot jail.
     */
    msg_syslog_init(mail_task(var_procname), LOG_PID, LOG_FACILITY);
    if (msg_verbose)
	msg_info("daemon started");

    /*
     * Check the Postfix library version as soon as we enable logging.
     */
    MAIL_VERSION_CHECK;

    /*
     * Initialize from the configuration file. Allow command-line options to
     * override compiled-in defaults or configured parameter values.
     */
    mail_conf_suck();

    /*
     * Register dictionaries that use higher-level interfaces and protocols.
     */
    mail_dict_init();
 
    /*
     * After database open error, continue execution with reduced
     * functionality.
     */
    dict_allow_surrogate = 1;

    /*
     * Pick up policy settings from master process. Shut up error messages to
     * stderr, because no-one is going to see them.
     */
    opterr = 0;
    while ((c = GETOPT(argc, argv, "cdDi:lm:n:o:s:St:uvVz")) > 0) {
	switch (c) {
	case 'c':
	    root_dir = "setme";
	    break;
	case 'd':
	    daemon_mode = 0;
	    break;
	case 'D':
	    debug_me = 1;
	    break;
	case 'i':
	    mail_conf_update(VAR_MAX_IDLE, optarg);
	    break;
	case 'l':
	    alone = 1;
	    break;
	case 'm':
	    mail_conf_update(VAR_MAX_USE, optarg);
	    break;
	case 'n':
	    service_name = optarg;
	    break;
	case 'o':
	    oname_val = mystrdup(optarg);
	    if ((err = split_nameval(oname_val, &oname, &oval)) != 0)
		msg_fatal("invalid \"-o %s\" option value: %s", optarg, err);
	    mail_conf_update(oname, oval);
	    if (strcmp(oname, VAR_SYSLOG_NAME) == 0)
		redo_syslog_init = 1;
	    myfree(oname_val);
	    break;
	case 's':
	    if ((socket_count = atoi(optarg)) <= 0)
		msg_fatal("invalid socket_count: %s", optarg);
	    break;
	case 'S':
	    stream = VSTREAM_IN;
	    break;
	case 'u':
	    user_name = "setme";
	    break;
	case 't':
	    transport = optarg;
	    break;
	case 'v':
	    msg_verbose++;
	    break;
	case 'V':
	    if (++msg_vstream_needed == 1)
		msg_vstream_init(mail_task(var_procname), VSTREAM_ERR);
	    break;
	case 'z':
	    zerolimit = 1;
	    break;
	default:
	    msg_fatal("invalid option: %c", c);
	    break;
	}
    }

    /*
     * Initialize generic parameters.
     */
    mail_params_init();
    if (redo_syslog_init)
	msg_syslog_init(mail_task(var_procname), LOG_PID, LOG_FACILITY);

    /*
     * If not connected to stdin, stdin must not be a terminal.
     */
    if (daemon_mode && stream == 0 && isatty(STDIN_FILENO)) {
	msg_vstream_init(var_procname, VSTREAM_ERR);
	msg_fatal("do not run this command by hand");
    }

    /*
     * Application-specific initialization.
     */
    va_start(ap, service);
    while ((key = va_arg(ap, int)) != 0) {
	switch (key) {
	case MAIL_SERVER_INT_TABLE:
	    get_mail_conf_int_table(va_arg(ap, CONFIG_INT_TABLE *));
	    break;
	case MAIL_SERVER_LONG_TABLE:
	    get_mail_conf_long_table(va_arg(ap, CONFIG_LONG_TABLE *));
	    break;
	case MAIL_SERVER_STR_TABLE:
	    get_mail_conf_str_table(va_arg(ap, CONFIG_STR_TABLE *));
	    break;
	case MAIL_SERVER_BOOL_TABLE:
	    get_mail_conf_bool_table(va_arg(ap, CONFIG_BOOL_TABLE *));
	    break;
	case MAIL_SERVER_TIME_TABLE:
	    get_mail_conf_time_table(va_arg(ap, CONFIG_TIME_TABLE *));
	    break;
	case MAIL_SERVER_RAW_TABLE:
	    get_mail_conf_raw_table(va_arg(ap, CONFIG_RAW_TABLE *));
	    break;
	case MAIL_SERVER_NINT_TABLE:
	    get_mail_conf_nint_table(va_arg(ap, CONFIG_NINT_TABLE *));
	    break;
	case MAIL_SERVER_NBOOL_TABLE:
	    get_mail_conf_nbool_table(va_arg(ap, CONFIG_NBOOL_TABLE *));
	    break;
	case MAIL_SERVER_PRE_INIT:
	    pre_init = va_arg(ap, MAIL_SERVER_INIT_FN);
	    break;
	case MAIL_SERVER_POST_INIT:
	    post_init = va_arg(ap, MAIL_SERVER_INIT_FN);
	    break;
	case MAIL_SERVER_LOOP:
	    loop = va_arg(ap, MAIL_SERVER_LOOP_FN);
	    break;
	case MAIL_SERVER_EXIT:
	    multi_server_onexit = va_arg(ap, MAIL_SERVER_EXIT_FN);
	    break;
	case MAIL_SERVER_PRE_ACCEPT:
	    multi_server_pre_accept = va_arg(ap, MAIL_SERVER_ACCEPT_FN);
	    break;
	case MAIL_SERVER_PRE_DISCONN:
	    multi_server_pre_disconn = va_arg(ap, MAIL_SERVER_DISCONN_FN);
	    break;
	case MAIL_SERVER_IN_FLOW_DELAY:
	    multi_server_in_flow_delay = 1;
	    break;
	case MAIL_SERVER_SOLITARY:
	    if (stream == 0 && !alone)
		msg_fatal("service %s requires a process limit of 1",
			  service_name);
	    break;
	case MAIL_SERVER_UNLIMITED:
	    if (stream == 0 && !zerolimit)
		msg_fatal("service %s requires a process limit of 0",
			  service_name);
	    break;
	case MAIL_SERVER_PRIVILEGED:
	    if (user_name)
		msg_fatal("service %s requires privileged operation",
			  service_name);
	    break;
	default:
	    msg_panic("%s: unknown argument type: %d", myname, key);
	}
    }
    va_end(ap);

    if (root_dir)
	root_dir = var_queue_dir;
    if (user_name)
	user_name = var_mail_owner;

    /*
     * Can options be required?
     */
    if (stream == 0) {
	if (transport == 0)
	    msg_fatal("no transport type specified");
	if (strcasecmp(transport, MASTER_XPORT_NAME_INET) == 0)
	    multi_server_accept = multi_server_accept_inet;
	else if (strcasecmp(transport, MASTER_XPORT_NAME_UNIX) == 0)
	    multi_server_accept = multi_server_accept_local;
#ifdef MASTER_XPORT_NAME_PASS
	else if (strcasecmp(transport, MASTER_XPORT_NAME_PASS) == 0)
	    multi_server_accept = multi_server_accept_pass;
#endif
	else
	    msg_fatal("unsupported transport type: %s", transport);
    }

    /*
     * Retrieve process generation from environment.
     */
    if ((generation = getenv(MASTER_GEN_NAME)) != 0) {
	if (!alldig(generation))
	    msg_fatal("bad generation: %s", generation);
	OCTAL_TO_UNSIGNED(multi_server_generation, generation);
	if (msg_verbose)
	    msg_info("process generation: %s (%o)",
		     generation, multi_server_generation);
    }

    /*
     * Optionally start the debugger on ourself.
     */
    if (debug_me)
	debug_process();

    /*
     * Traditionally, BSD select() can't handle multiple processes selecting
     * on the same socket, and wakes up every process in select(). See TCP/IP
     * Illustrated volume 2 page 532. We avoid select() collisions with an
     * external lock file.
     */

    /*
     * XXX Can't compete for exclusive access to the listen socket because we
     * also have to monitor existing client connections for service requests.
     */
#if 0
    if (stream == 0 && !alone) {
	lock_path = concatenate(DEF_PID_DIR, "/", transport,
				".", service_name, (char *) 0);
	why = vstring_alloc(1);
	if ((multi_server_lock = safe_open(lock_path, O_CREAT | O_RDWR, 0600,
				      (struct stat *) 0, -1, -1, why)) == 0)
	    msg_fatal("open lock file %s: %s", lock_path, vstring_str(why));
	close_on_exec(vstream_fileno(multi_server_lock), CLOSE_ON_EXEC);
	myfree(lock_path);
	vstring_free(why);
    }
#endif

    /*
     * Set up call-back info.
     */
    multi_server_service = service;
    multi_server_name = service_name;
    multi_server_argv = argv + optind;

    /*
     * Run pre-jail initialization.
     */
    if (chdir(var_queue_dir) < 0)
	msg_fatal("chdir(\"%s\"): %m", var_queue_dir);
    if (pre_init)
	pre_init(multi_server_name, multi_server_argv);

    /*
     * Optionally, restrict the damage that this process can do.
     */
    resolve_local_init();
    tzset();
    chroot_uid(root_dir, user_name);

    /*
     * Run post-jail initialization.
     */
    if (post_init)
	post_init(multi_server_name, multi_server_argv);

    /*
     * Are we running as a one-shot server with the client connection on
     * standard input? If so, make sure the output is written to stdout so as
     * to satisfy common expectation.
     */
    if (stream != 0) {
	vstream_control(stream,
			VSTREAM_CTL_DOUBLE,
			VSTREAM_CTL_WRITE_FD, STDOUT_FILENO,
			VSTREAM_CTL_END);
	service(stream, multi_server_name, multi_server_argv);
	vstream_fflush(stream);
	multi_server_exit();
    }

    /*
     * Running as a semi-resident server. Service connection requests.
     * Terminate when we have serviced a sufficient number of clients, when
     * no-one has been talking to us for a configurable amount of time, or
     * when the master process terminated abnormally.
     */
    if (var_idle_limit > 0)
	event_request_timer(multi_server_timeout, (char *) 0, var_idle_limit);
    for (fd = MASTER_LISTEN_FD; fd < MASTER_LISTEN_FD + socket_count; fd++) {
	event_enable_read(fd, multi_server_accept, CAST_INT_TO_CHAR_PTR(fd));
	close_on_exec(fd, CLOSE_ON_EXEC);
    }
    event_enable_read(MASTER_STATUS_FD, multi_server_abort, (char *) 0);
    close_on_exec(MASTER_STATUS_FD, CLOSE_ON_EXEC);
    close_on_exec(MASTER_FLOW_READ, CLOSE_ON_EXEC);
    close_on_exec(MASTER_FLOW_WRITE, CLOSE_ON_EXEC);
    watchdog = watchdog_create(var_daemon_timeout, (WATCHDOG_FN) 0, (char *) 0);

    /*
     * The event loop, at last.
     */
    while (var_use_limit == 0 || use_count < var_use_limit || client_count > 0) {
	if (multi_server_lock != 0) {
	    watchdog_stop(watchdog);
	    if (myflock(vstream_fileno(multi_server_lock), INTERNAL_LOCK,
			MYFLOCK_OP_EXCLUSIVE) < 0)
		msg_fatal("select lock: %m");
	}
	watchdog_start(watchdog);
	delay = loop ? loop(multi_server_name, multi_server_argv) : -1;
	event_loop(delay);
    }
    multi_server_exit();
}
Пример #21
0
static int bounce_append_proto(char *service_name, VSTREAM *client)
{
    const char *myname = "bounce_append_proto";
    int     flags;

    /*
     * Read and validate the client request.
     */
    if (mail_command_server(client,
			    ATTR_TYPE_INT, MAIL_ATTR_FLAGS, &flags,
			    ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
			    ATTR_TYPE_FUNC, rcpb_scan, (void *) rcpt_buf,
			    ATTR_TYPE_FUNC, dsb_scan, (void *) dsn_buf,
			    ATTR_TYPE_END) != 4) {
	msg_warn("malformed request");
	return (-1);
    }

    /*
     * Sanitize input.
     */
    if (mail_queue_id_ok(STR(queue_id)) == 0) {
	msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
	return (-1);
    }
    VS_NEUTER(rcpt_buf->address);
    VS_NEUTER(rcpt_buf->orig_addr);
    VS_NEUTER(rcpt_buf->dsn_orcpt);
    VS_NEUTER(dsn_buf->status);
    VS_NEUTER(dsn_buf->action);
    VS_NEUTER(dsn_buf->reason);
    VS_NEUTER(dsn_buf->dtype);
    VS_NEUTER(dsn_buf->dtext);
    VS_NEUTER(dsn_buf->mtype);
    VS_NEUTER(dsn_buf->mname);
    (void) RECIPIENT_FROM_RCPT_BUF(rcpt_buf);
    (void) DSN_FROM_DSN_BUF(dsn_buf);

    /*
     * Beware: some DSN or RECIPIENT fields may be null; access dsn_buf and
     * rcpt_buf buffers instead. See DSN_FROM_DSN_BUF() and
     * RECIPIENT_FROM_RCPT_BUF().
     */
    if (msg_verbose)
	msg_info("%s: flags=0x%x service=%s id=%s org_to=%s to=%s off=%ld dsn_org=%s, notif=0x%x stat=%s act=%s why=%s",
		 myname, flags, service_name, STR(queue_id),
		 STR(rcpt_buf->orig_addr), STR(rcpt_buf->address),
		 rcpt_buf->offset, STR(rcpt_buf->dsn_orcpt),
		 rcpt_buf->dsn_notify, STR(dsn_buf->status),
		 STR(dsn_buf->action), STR(dsn_buf->reason));

    /*
     * On request by the client, set up a trap to delete the log file in case
     * of errors.
     */
    if (flags & BOUNCE_FLAG_CLEAN)
	bounce_cleanup_register(service_name, STR(queue_id));

    /*
     * Execute the request.
     */
    return (bounce_append_service(flags, service_name, STR(queue_id),
				  &rcpt_buf->rcpt, &dsn_buf->dsn));
}
Пример #22
0
int     server_acl_eval(const char *client_addr, SERVER_ACL * intern_acl,
			        const char *origin)
{
    const char *myname = "server_acl_eval";
    char  **cpp;
    DICT   *dict;
    SERVER_ACL *argv;
    const char *acl;
    const char *dict_val;
    int     ret;

    for (cpp = intern_acl->argv; (acl = *cpp) != 0; cpp++) {
	if (msg_verbose)
	    msg_info("source=%s address=%s acl=%s",
		     origin, client_addr, acl);
	if (STREQ(acl, SERVER_ACL_NAME_REJECT)) {
	    return (SERVER_ACL_ACT_REJECT);
	} else if (STREQ(acl, SERVER_ACL_NAME_PERMIT)) {
	    return (SERVER_ACL_ACT_PERMIT);
	} else if (STREQ(acl, SERVER_ACL_NAME_WL_MYNETWORKS)) {
	    if (addr_match_list_match(server_acl_mynetworks, client_addr))
		return (SERVER_ACL_ACT_PERMIT);
	    if (server_acl_mynetworks->error != 0) {
		msg_warn("%s: %s: mynetworks lookup error -- ignoring the "
			 "remainder of this access list", origin, acl);
		return (SERVER_ACL_ACT_ERROR);
	    }
	} else if (strchr(acl, ':') != 0) {
	    if ((dict = dict_handle(acl)) == 0)
		msg_panic("%s: unexpected dictionary: %s", myname, acl);
	    if ((dict_val = dict_get(dict, client_addr)) != 0) {
		/* Fake up an ARGV to avoid lots of mallocs and frees. */
		if (dict_val[strcspn(dict_val, ":" SERVER_ACL_SEPARATORS)] == 0) {
		    ARGV_FAKE_BEGIN(fake_argv, dict_val);
		    ret = server_acl_eval(client_addr, &fake_argv, acl);
		    ARGV_FAKE_END;
		} else {
		    argv = server_acl_parse(dict_val, acl);
		    ret = server_acl_eval(client_addr, argv, acl);
		    argv_free(argv);
		}
		if (ret != SERVER_ACL_ACT_DUNNO)
		    return (ret);
	    } else if (dict->error != 0) {
		msg_warn("%s: %s: table lookup error -- ignoring the remainder "
			 "of this access list", origin, acl);
		return (SERVER_ACL_ACT_ERROR);
	    }
	} else if (STREQ(acl, SERVER_ACL_NAME_DUNNO)) {
	    return (SERVER_ACL_ACT_DUNNO);
	} else {
	    msg_warn("%s: unknown command: %s -- ignoring the remainder "
		     "of this access list", origin, acl);
	    return (SERVER_ACL_ACT_ERROR);
	}
    }
    if (msg_verbose)
	msg_info("source=%s address=%s - no match",
		 origin, client_addr);
    return (SERVER_ACL_ACT_DUNNO);
}
Пример #23
0
static int bounce_one_proto(char *service_name, VSTREAM *client)
{
    const char *myname = "bounce_one_proto";
    int     flags;
    int     dsn_ret;

    /*
     * Read and validate the client request.
     */
    if (mail_command_server(client,
			    ATTR_TYPE_INT, MAIL_ATTR_FLAGS, &flags,
			    ATTR_TYPE_STR, MAIL_ATTR_QUEUE, queue_name,
			    ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, queue_id,
			    ATTR_TYPE_STR, MAIL_ATTR_ENCODING, encoding,
			    ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
			    ATTR_TYPE_STR, MAIL_ATTR_DSN_ENVID, dsn_envid,
			    ATTR_TYPE_INT, MAIL_ATTR_DSN_RET, &dsn_ret,
			    ATTR_TYPE_FUNC, rcpb_scan, (void *) rcpt_buf,
			    ATTR_TYPE_FUNC, dsb_scan, (void *) dsn_buf,
			    ATTR_TYPE_END) != 9) {
	msg_warn("malformed request");
	return (-1);
    }

    /*
     * Sanitize input.
     */
    if (strcmp(service_name, MAIL_SERVICE_BOUNCE) != 0) {
	msg_warn("wrong service name \"%s\" for one-recipient bouncing",
		 service_name);
	return (-1);
    }
    if (mail_queue_name_ok(STR(queue_name)) == 0) {
	msg_warn("malformed queue name: %s", printable(STR(queue_name), '?'));
	return (-1);
    }
    if (mail_queue_id_ok(STR(queue_id)) == 0) {
	msg_warn("malformed queue id: %s", printable(STR(queue_id), '?'));
	return (-1);
    }
    printable(STR(dsn_envid), '?');
    VS_NEUTER(rcpt_buf->address);
    VS_NEUTER(rcpt_buf->orig_addr);
    VS_NEUTER(rcpt_buf->dsn_orcpt);
    VS_NEUTER(dsn_buf->status);
    VS_NEUTER(dsn_buf->action);
    VS_NEUTER(dsn_buf->reason);
    VS_NEUTER(dsn_buf->dtype);
    VS_NEUTER(dsn_buf->dtext);
    VS_NEUTER(dsn_buf->mtype);
    VS_NEUTER(dsn_buf->mname);
    (void) RECIPIENT_FROM_RCPT_BUF(rcpt_buf);
    (void) DSN_FROM_DSN_BUF(dsn_buf);

    /*
     * Beware: some DSN or RECIPIENT fields may be null; access dsn_buf and
     * rcpt_buf buffers instead. See DSN_FROM_DSN_BUF() and
     * RECIPIENT_FROM_RCPT_BUF().
     */
    if (msg_verbose)
	msg_info("%s: flags=0x%x queue=%s id=%s encoding=%s sender=%s envid=%s dsn_ret=0x%x orig_to=%s to=%s off=%ld dsn_orig=%s notif=0x%x stat=%s act=%s why=%s",
		 myname, flags, STR(queue_name), STR(queue_id),
		 STR(encoding), STR(sender), STR(dsn_envid), dsn_ret,
		 STR(rcpt_buf->orig_addr), STR(rcpt_buf->address),
		 rcpt_buf->offset, STR(rcpt_buf->dsn_orcpt),
		 rcpt_buf->dsn_notify, STR(dsn_buf->status),
		 STR(dsn_buf->action), STR(dsn_buf->reason));

    /*
     * Execute the request.
     */
    return (bounce_one_service(flags, STR(queue_name), STR(queue_id),
			       STR(encoding), STR(sender), STR(dsn_envid),
			     dsn_ret, rcpt_buf, dsn_buf, bounce_templates));
}
Пример #24
0
static int 
rspamdscan_socket(SMFICTX *ctx, struct mlfi_priv *priv, const struct spamd_server *srv,
		struct config_file *cfg, rspamd_result_t *res, char **mid)
{
	char buf[16384];
	char *c, *p, *err_str;
	struct sockaddr_un server_un;
	struct sockaddr_in server_in;
	int s, r, fd, ofl, size = 0, to_write, written, state, next_state, toklen;
	int remain;
	struct stat sb;
	struct rspamd_metric_result *cur = NULL;
	struct rcpt *rcpt;
	struct rspamd_symbol *cur_symbol;

	/* somebody doesn't need reply... */
	if (!srv)
		return 0;

	if (srv->sock_type == AF_LOCAL) {

		memset(&server_un, 0, sizeof(server_un));
		server_un.sun_family = AF_UNIX;
		strncpy(server_un.sun_path, srv->sock.unix_path, sizeof(server_un.sun_path));

		if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
			msg_warn("rspamd: socket %s, %s", srv->sock.unix_path,
					strerror (errno));
			return -1;
		}
		if (connect_t(s, (struct sockaddr *) & server_un, sizeof(server_un), cfg->spamd_connect_timeout) < 0) {
			msg_warn("rspamd: connect %s, %s", srv->sock.unix_path,
					strerror (errno));
			close(s);
			return -1;
		}
	} else {
		/* inet hostname, send stream over tcp/ip */

		memset(&server_in, 0, sizeof(server_in));
		server_in.sin_family = AF_INET;
		server_in.sin_port = srv->sock.inet.port;
		memcpy((char *)&server_in.sin_addr, &srv->sock.inet.addr, sizeof(struct in_addr));

		if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
			msg_warn("rspamd: socket %s", strerror (errno));
			return -1;
		}
		if (connect_t(s, (struct sockaddr *) & server_in, sizeof(server_in), cfg->spamd_connect_timeout) < 0) {
			msg_warn("rspamd: connect %s: %s", srv->name, strerror (errno));
			close(s);
			return -1;
		}
	}
	/* Get file size */
	fd = open(priv->file, O_RDONLY);
	if (fstat (fd, &sb) == -1) {
		msg_warn ("rspamd: stat failed: %s", strerror (errno));
		close(s);
		return -1;
	}
	
	if (poll_fd(s, cfg->spamd_connect_timeout, POLLOUT) < 1) {
		msg_warn ("rspamd: timeout waiting writing, %s", srv->name);
		close (s);
		return -1;
	}
	/* Set blocking again */
	ofl = fcntl(s, F_GETFL, 0);
	fcntl(s, F_SETFL, ofl & (~O_NONBLOCK));
	
	r = 0;
	to_write = sizeof (buf) - r;
	written = snprintf (buf + r, to_write, "SYMBOLS RSPAMC/1.2\r\nContent-length: %ld\r\n", (long int)sb.st_size);
	if (written > to_write) {
		msg_warn("rspamd: buffer overflow while filling buffer (%s)", srv->name);
		close(fd);
		close(s);
		return -1;
	}
	r += written;

	for (rcpt = priv->rcpts.lh_first; rcpt != NULL; rcpt = rcpt->r_list.le_next) {
		to_write = sizeof (buf) - r;
		written = snprintf (buf + r, to_write, "Rcpt: %s\r\n", rcpt->r_addr);
		if (written > to_write) {
			msg_warn("rspamd: buffer overflow while filling buffer (%s)", srv->name);
			close(fd);
			close(s);
			return -1;
		}
		r += written;
	}

	if (priv->priv_from[0] != '\0') {
		to_write = sizeof (buf) - r;
		written = snprintf (buf + r, to_write, "From: %s\r\n", priv->priv_from);
		if (written > to_write) {
			msg_warn("rspamd: buffer overflow while filling buffer (%s)", srv->name);
			close(fd);
			close(s);
			return -1;
		}
		r += written;
	}
	if (priv->priv_helo[0] != '\0') {
		to_write = sizeof (buf) - r;
		written = snprintf (buf + r, to_write, "Helo: %s\r\n", priv->priv_helo);
		if (written > to_write) {
			msg_warn("rspamd: buffer overflow while filling buffer (%s)", srv->name);
			close(fd);
			close(s);
			return -1;
		}
		r += written;
	}
	if (priv->priv_hostname[0] != '\0' && memcmp (priv->priv_hostname, "unknown", 8) != 0) {
		to_write = sizeof (buf) - r;
		written = snprintf (buf + r, to_write, "Hostname: %s\r\n", priv->priv_hostname);
		if (written > to_write) {
			msg_warn("rspamd: buffer overflow while filling buffer (%s)", srv->name);
			close(fd);
			close(s);
			return -1;
		}
		r += written;
	}
	if (priv->priv_ip[0] != '\0') {
		to_write = sizeof (buf) - r;
		written = snprintf (buf + r, to_write, "IP: %s\r\n", priv->priv_ip);
		if (written > to_write) {
			msg_warn("rspamd: buffer overflow while filling buffer (%s)", srv->name);
			close(fd);
			close(s);
			return -1;
		}
		r += written;
	}
	if (priv->priv_user[0] != '\0') {
		to_write = sizeof (buf) - r;
		written = snprintf (buf + r, to_write, "User: %s\r\n", priv->priv_user);
		if (written > to_write) {
			msg_warn("rspamd: buffer overflow while filling buffer (%s)", srv->name);
			close(fd);
			close(s);
			return -1;
		}
		r += written;
	}
	to_write = sizeof (buf) - r;
	written = snprintf (buf + r, to_write, "Queue-ID: %s\r\n\r\n", priv->mlfi_id);
	if (written > to_write) {
		msg_warn("rspamd: buffer overflow while filling buffer (%s)", srv->name);
		close(fd);
		close(s);
		return -1;
	}
	r += written;



	if (write (s, buf, r) == -1) {
		msg_warn("rspamd: write (%s), %s", srv->name, strerror (errno));
		close(fd);
		close(s);
		return -1;
	}

#ifdef HAVE_SENDFILE
#if defined(FREEBSD)
	if (sendfile(fd, s, 0, 0, 0, 0, 0) != 0) {
		msg_warn("rspamd: sendfile (%s), %s", srv->name, strerror (errno));
		close(fd);
		close(s);
		return -1;
	}
#elif defined(LINUX)
	off_t off = 0;
	if (sendfile(s, fd, &off, sb.st_size) == -1) {
		msg_warn("rspamd: sendfile (%s), %s", srv->name, strerror (errno));
		close(fd);
		close(s);
		return -1;		
	}
#endif
#else 
	while ((r = read (fd, buf, sizeof (buf))) > 0) {
		write (s, buf, r);
	}
#endif

	fcntl(s, F_SETFL, ofl);
	close(fd);

	/* wait for reply */

	if (poll_fd(s, cfg->spamd_results_timeout, POLLIN) < 1) {
		msg_warn("rspamd: timeout waiting results %s", srv->name);
		close(s);
		return -1;
	}
	
	/*
	 * read results
	 */

	buf[0] = 0;
	size = 0;
	
	/* XXX: in fact here should be some FSM to parse reply and this one just skip long replies */
	while ((r = read(s, buf + size, sizeof (buf) - size - 1)) > 0 && size < sizeof (buf) - 1) {
		size += r;
	}

	if (r < 0) {
		msg_warn("rspamd: read, %s, %s", srv->name, strerror (errno));
		close(s);
		return -1;
	}
	buf[size] = '\0';
	close(s);

#define TEST_WORD(x)																\
do {																				\
	if (remain < sizeof ((x)) - 1 || memcmp (p, (x), sizeof ((x)) - 1) != 0) {		\
		msg_warn ("invalid reply from server %s at state %d, expected: %s, got %*s", srv->name, state, ((x)), (int)sizeof((x)), p);				\
		return -1;																	\
	}																				\
	p += sizeof((x)) - 1;															\
	remain -= sizeof((x)) - 1;														\
} while (0)


	c = buf;
	p = buf;
	remain = size - 1;
	state = 0;
	next_state = 100;

	while (remain > 0) {
		switch (state) {
			case 0:
				/*
				 * Expect first reply line:
				 * RSPAMD/{VERSION} {ERROR_CODE} {DESCR} CRLF
				 */
				TEST_WORD("RSPAMD/");
				if ((c = strchr (p, ' ')) == NULL) {
					msg_warn ("invalid reply from server %s on state %d", srv->name, state);
					return -1;
				}
				/* Well now in c we have space symbol, skip all */
				while (remain > 0 && isspace (*c)) {
					c ++;
				}
				/* Now check code */
				if (*c != '0') {
					msg_warn ("invalid reply from server %s on state %d, code: %c", srv->name, state, *c);
					return -1;
				}
				/* Now skip everything till \n */
				if ((c = strchr (c, '\n')) == NULL) {
					msg_warn ("invalid reply from server %s on state %d", srv->name, state);
					return -1;
				}
				c ++;
				remain -= c - p;
				p = c;
				next_state = 2;
				state = 99;
				break;
			case 2:
				/*
				 * In this state we compare begin of line with Metric:
				 */
				TEST_WORD("Metric:");
				cur = malloc (sizeof (struct rspamd_metric_result));
				if (cur == NULL) {
					msg_err ("malloc failed: %s", strerror (errno));
					return -1;
				}
				cur->subject = NULL;
				TAILQ_INIT(&cur->symbols);
				next_state = 3;
				state = 99;
				break;
			case 3:
				/* 
				 * In this state we parse metric line 
				 * Typical line looks as name; result; score1 / score2[ / score3] and we are interested in:
				 * name, result, score1 and score2
				 */
				if ((c = strchr (p, ';')) == NULL) {
					msg_warn ("invalid reply from server %s on state %d, at position: %s", srv->name, state, p);
					return -1;
				}
				/* Now in c we have end of name and in p - begin of name, so copy this data to temp buffer */
				cur->metric_name = malloc (c - p + 1);
				if (cur->metric_name == NULL) {
					msg_err ("malloc failed: %s", strerror (errno));
					return -1;
				}
				rmilter_strlcpy (cur->metric_name, p, c - p + 1);
				remain -= c - p + 1;
				p = c + 1;
				/* Now skip result from rspamd, just extract 2 numbers */
				if ((c = strchr (p, ';')) == NULL) {
					msg_warn ("invalid reply from server %s on state %d, at position: %s", srv->name, state, p);
					return -1;
				}
				remain -= c - p + 1;
				p = c + 1;
				/* Now skip spaces */
				while (isspace (*p) && remain > 0) {
					p ++;
					remain --;
				}
				/* Try to read first mark */
				cur->score = strtod (p, &err_str);
				if (err_str != NULL && (*err_str != ' ' && *err_str != '/')) {
					msg_warn ("invalid reply from server %s on state %d, error converting score number: %s", srv->name, state, err_str);
					return -1;
				}
				remain -= err_str - p;
				p = err_str;
				while (remain > 0 && (*p == ' ' || *p == '/')) {
					remain --;
					p ++;
				}
				/* Try to read second mark */
				cur->required_score = strtod (p, &err_str);
				if (err_str != NULL && (*err_str != ' ' && *err_str != '/' && *err_str != '\r')) {
					msg_warn ("invalid reply from server %s on state %d, error converting required score number: %s", srv->name, state, err_str);
					return -1;
				}
				remain -= err_str - p;
				p = err_str;
				while (remain > 0 && *p != '\n') {
					remain --;
					p ++;
				}
				state = 99;
				next_state = 4;
				break;
			case 4:
				/* Symbol/Action */
				if (remain >= sizeof ("Symbol:") && memcmp (p, "Symbol:", sizeof ("Symbol:") - 1) == 0) {
					state = 99;
					next_state = 5;
					p += sizeof("Symbol:") - 1;															\
					remain -= sizeof("Symbol:") - 1;
				}
				else if (remain >= sizeof ("Action:") && memcmp (p, "Action:", sizeof ("Action:") - 1) == 0) {
					state = 99;
					next_state = 6;
					p += sizeof("Action:") - 1;															\
					remain -= sizeof("Action:") - 1;
				}
				else if (remain >= sizeof ("Metric:") && memcmp (p, "Metric:", sizeof ("Metric:") - 1) == 0) {
					state = 99;
					next_state = 3;
					p += sizeof("Metric:") - 1;															\
					remain -= sizeof("Metric:") - 1;
					TAILQ_INSERT_HEAD(res, cur, entry);
					cur = malloc (sizeof (struct rspamd_metric_result));
					if (cur == NULL) {
						msg_err ("malloc failed: %s", strerror (errno));
						return -1;
					}
					TAILQ_INIT(&cur->symbols);
				}
				else if (remain >= sizeof ("Message-ID:") && memcmp (p, "Message-ID:", sizeof ("Message-ID:") - 1) == 0) {
					state = 99;
					next_state = 7;
					p += sizeof("Message-ID:") - 1;															\
					remain -= sizeof("Message-ID:") - 1;
				}
				else if (remain >= sizeof ("Subject:") && memcmp (p, "Subject:", sizeof ("Subject:") - 1) == 0) {
					state = 99;
					next_state = 8;
					p += sizeof("Subject:") - 1;															\
					remain -= sizeof("Subject:") - 1;
				}
				else {
					toklen = strcspn (p, "\r\n");
					if (toklen > remain) {
						msg_info ("bad symbol name detected");
						return -1;
					}
					remain -= toklen;
					p += toklen;
					next_state = 4;
					state = 99;
				}
				break;
			case 5:
				/* Parse symbol line */
				toklen = strcspn (p, ";\r\n");
				if (toklen == 0 || toklen > remain) {
					/* Bad symbol name */
					msg_info ("bad symbol name detected");
					return -1;
				}
				cur_symbol = malloc (sizeof (struct rspamd_symbol));
				if (cur_symbol == NULL) {
					msg_err ("malloc failed: %s", strerror (errno));
					return -1;
				}
				cur_symbol->symbol = malloc (toklen + 1);
				if (cur_symbol->symbol == NULL) {
					msg_err ("malloc failed: %s", strerror (errno));
					return -1;
				}
				rmilter_strlcpy (cur_symbol->symbol, p, toklen + 1);
				TAILQ_INSERT_HEAD (&cur->symbols, cur_symbol, entry);
				/* Skip to the end of line */
				toklen = strcspn (p, "\r\n");
				if (toklen > remain) {
					msg_info ("bad symbol name detected");
					return -1;
				}
				remain -= toklen;
				p += toklen;
				next_state = 4;
				state = 99;
				break;
			case 6:
				/* Parse action */
				if (memcmp (p, "reject", sizeof ("reject") - 1) == 0) {
					cur->action = METRIC_ACTION_REJECT;
				}
				else if (memcmp (p, "greylist", sizeof ("greylist") - 1) == 0) {
					cur->action = METRIC_ACTION_GREYLIST;
				}
				else if (memcmp (p, "add header", sizeof ("add header") - 1) == 0) {
					cur->action = METRIC_ACTION_ADD_HEADER;
				}
				else if (memcmp (p, "rewrite subject", sizeof ("rewrite subject") - 1) == 0) {
					cur->action = METRIC_ACTION_REWRITE_SUBJECT;
				}
				else {
					cur->action = METRIC_ACTION_NOACTION;
				}
				/* Skip to the end of line */
				toklen = strcspn (p, "\r\n");
				if (toklen > remain) {
					msg_info ("bad symbol name detected");
					return -1;
				}
				remain -= toklen;
				p += toklen;
				next_state = 4;
				state = 99;
				break;
			case 7:
				/* Parse message id */
				toklen = strcspn (p, "\r\n");
				*mid = malloc (toklen + 1);
				rmilter_strlcpy (*mid, p, toklen + 1);
				remain -= toklen;
				p += toklen;
				next_state = 4;
				state = 99;
				break;
			case 8:
				/* Parse subject line */
				toklen = strcspn (p, "\r\n");
				if (cur) {
					cur->subject = malloc (toklen + 1);
					rmilter_strlcpy (cur->subject, p, toklen + 1);
				}
				remain -= toklen;
				p += toklen;
				next_state = 4;
				state = 99;
				break;
			case 99:
				/* Skip spaces */
				if (isspace (*p)) {
					p ++;
					remain --;
				}
				else {
					state = next_state;
				}
				break;
			default:
				msg_err ("state machine breakage detected, state = %d, p = %s", state, p);
				return -1;
		}
	}

	if (cur != NULL) {
		TAILQ_INSERT_HEAD(res, cur, entry);
	}
	return 0;
}
Пример #25
0
/* mysqlname_parse - parse mysql configuration file */
static MYSQL_NAME *mysqlname_parse(const char *mysqlcf)
{
    const char *myname = "mysqlname_parse";
    int     i;
    char   *hosts;
    MYSQL_NAME *name = (MYSQL_NAME *) mymalloc(sizeof(MYSQL_NAME));
    ARGV   *hosts_argv;

    /* parser */
    name->parser = cfg_parser_alloc(mysqlcf);

    /* username */
    name->username = cfg_get_str(name->parser, "user", "", 0, 0);

    /* password */
    name->password = cfg_get_str(name->parser, "password", "", 0, 0);

    /* database name */
    name->dbname = cfg_get_str(name->parser, "dbname", "", 1, 0);

    /* table name */
    name->table = cfg_get_str(name->parser, "table", "", 1, 0);

    /* select field */
    name->select_field = cfg_get_str(name->parser, "select_field", "", 1, 0);

    /* where field */
    name->where_field = cfg_get_str(name->parser, "where_field", "", 1, 0);

    /* additional conditions */
    name->additional_conditions = cfg_get_str(name->parser,
                                  "additional_conditions",
                                  "", 0, 0);

    /* mysql server hosts */
    hosts = cfg_get_str(name->parser, "hosts", "", 0, 0);

    /* coo argv interface */
    hosts_argv = argv_split(hosts, " ,\t\r\n");
    if (hosts_argv->argc == 0) {
        /* no hosts specified,
        					 * default to 'localhost' */
        if (msg_verbose)
            msg_info("%s: %s: no hostnames specified, defaulting to 'localhost'",
                     myname, mysqlcf);
        argv_add(hosts_argv, "localhost", ARGV_END);
        argv_terminate(hosts_argv);
    }
    name->len_hosts = hosts_argv->argc;
    name->hostnames = (char **) mymalloc((sizeof(char *)) * name->len_hosts);
    i = 0;
    for (i = 0; hosts_argv->argv[i] != NULL; i++) {
        name->hostnames[i] = mystrdup(hosts_argv->argv[i]);
        if (msg_verbose)
            msg_info("%s: %s: adding host '%s' to list of mysql server hosts",
                     myname, mysqlcf, name->hostnames[i]);
    }
    myfree(hosts);
    argv_free(hosts_argv);
    return name;
}
Пример #26
0
int 
spamdscan(SMFICTX *ctx, struct mlfi_priv *priv, struct config_file *cfg, char **subject, int extra)
{
	int retry, r = -2, hr = 0, to_trace = 0;
	struct timeval t;
	double ts, tf;
	struct spamd_server *selected = NULL;
	char rbuf[BUFSIZ], hdrbuf[BUFSIZ];
	char *prefix = "s", *mid = NULL, *c;
	rspamd_result_t res;
	struct rspamd_metric_result *cur = NULL, *tmp;
	struct rspamd_symbol *cur_symbol, *tmp_symbol;
	enum rspamd_metric_action res_action = METRIC_ACTION_NOACTION;
	struct timespec sleep_ts;
	

	gettimeofday(&t, NULL);
	ts = t.tv_sec + t.tv_usec / 1000000.0;
	retry = cfg->spamd_retry_count;
	sleep_ts.tv_sec = cfg->spamd_retry_timeout / 1000;
	sleep_ts.tv_nsec = (cfg->spamd_retry_timeout % 1000) * 1000000ULL;

	TAILQ_INIT(&res);

	/* try to scan with available servers */
	while (1) {
		if (extra) {
			selected = (struct spamd_server *) get_random_upstream ((void *)cfg->extra_spamd_servers,
					cfg->extra_spamd_servers_num, sizeof (struct spamd_server),
					t.tv_sec, cfg->spamd_error_time, cfg->spamd_dead_time, cfg->spamd_maxerrors);
		}
		else {
			selected = (struct spamd_server *) get_random_upstream ((void *)cfg->spamd_servers,
					cfg->spamd_servers_num, sizeof (struct spamd_server),
					t.tv_sec, cfg->spamd_error_time, cfg->spamd_dead_time, cfg->spamd_maxerrors);
		}
		if (selected == NULL) {
			msg_err ("spamdscan: upstream get error, %s", priv->file);
			return -1;
		}
		
		if (selected->type == SPAMD_SPAMASSASSIN) {
			prefix = "s";
			r = spamdscan_socket (priv->file, selected, cfg, &res);
		}
		else {
			prefix = "rs";
			r = rspamdscan_socket (ctx, priv, selected, cfg, &res, &mid);
		}
		if (r == 0 || r == 1) {
			upstream_ok (&selected->up, t.tv_sec);
			break;
		}
		upstream_fail (&selected->up, t.tv_sec);
		if (r == -2) {
			msg_warn("%spamdscan: unexpected problem, %s, %s", prefix, selected->name, priv->file);
			break;
		}
		if (--retry < 1) {
			msg_warn("%spamdscan: retry limit exceeded, %s, %s", prefix, selected->name, priv->file);
			break;
		}

		msg_warn("%spamdscan: failed to scan, retry, %s, %s", prefix, selected->name, priv->file);
		nanosleep (&sleep_ts, NULL);
	}

	/*
	 * print scanning time, server and result
	 */
	gettimeofday(&t, NULL);
	tf = t.tv_sec + t.tv_usec / 1000000.0;
	
	/* Parse res tailq */
	cur = TAILQ_FIRST(&res);
	while (cur) {
		if (cur->metric_name) {
			if (cfg->extended_spam_headers) {
				hr = snprintf (hdrbuf, sizeof (hdrbuf), "%s: %s [%.2f / %.2f]%c",
						cur->metric_name,
						cur->score > cur->required_score ? "True" : "False",
						cur->score,
						cur->required_score,
						TAILQ_FIRST(&cur->symbols) != NULL ? '\n' : ' ');
			}
			r = snprintf (rbuf, sizeof (rbuf), "spamdscan: scan qid: <%s>, mid: <%s>, %f, %s, metric: %s: [%f / %f], symbols: ",
					priv->mlfi_id,
					(mid != NULL) ? mid : "undef",
					tf - ts,
					selected->name,
					cur->metric_name,
					cur->score,
					cur->required_score);
			free (cur->metric_name);
		}
		else {
			if (cfg->extended_spam_headers) {
				hr = snprintf (hdrbuf, sizeof (hdrbuf), "%s: %s [%.2f / %.2f]%c",
						"default",
						cur->score > cur->required_score ? "True" : "False",
						cur->score,
						cur->required_score,
						TAILQ_FIRST(&cur->symbols) != NULL ? '\n' : ' ');
			}
			r = snprintf (rbuf, sizeof (rbuf), "spamdscan: scan <%s>, %f, %s, metric: default: [%f / %f], symbols: ",
					priv->mlfi_id,
					tf - ts,
					selected->name,
					cur->score,
					cur->required_score);
		
		}
		if (cur->action > res_action) {
			res_action = cur->action;
			if (res_action == METRIC_ACTION_REWRITE_SUBJECT && cur->subject != NULL) {
				/* Copy subject as it would be freed further */
				if (*subject != NULL) {
					free (*subject);
				}
				*subject = strdup (cur->subject);
			}
		}
		/* Write symbols */
		cur_symbol = TAILQ_FIRST(&cur->symbols);
		if (cur_symbol == NULL) {
			r += snprintf (rbuf + r, sizeof (rbuf) - r, "no symbols");
		}
		else {
			while (cur_symbol) {
				if (cur_symbol->symbol) {
					if (TAILQ_NEXT (cur_symbol, entry)) {
						r += snprintf (rbuf + r, sizeof (rbuf) - r, "%s, ", cur_symbol->symbol);
					}
					else {
						r += snprintf (rbuf + r, sizeof (rbuf) - r, "%s", cur_symbol->symbol);
					}
					if (cfg->trace_symbol) {
						c = strchr (cur_symbol->symbol, '(');
						if (c != NULL) {
							*c = '\0';
						}
						if ( !strcmp (cfg->trace_symbol, cur_symbol->symbol)) {
							to_trace ++;
						}
					}
					if (cfg->extended_spam_headers) {
						if (TAILQ_NEXT (cur_symbol, entry)) {
							hr += snprintf (hdrbuf + hr, sizeof (hdrbuf) - hr, " %s\n",
									cur_symbol->symbol);
						}
						else {
							hr += snprintf (hdrbuf + hr, sizeof (hdrbuf) - hr, " %s",
									cur_symbol->symbol);
						}
					}
					free (cur_symbol->symbol);
				}
				tmp_symbol = cur_symbol;
				cur_symbol = TAILQ_NEXT(cur_symbol, entry);
				free (tmp_symbol);
			}
		}
		msg_info ("%s", rbuf);
		if (cur->subject != NULL) {
			free (cur->subject);
		}

		tmp = cur;
		cur = TAILQ_NEXT(cur, entry);

		free (tmp);
		if (cfg->extended_spam_headers) {
			if (extra) {
				smfi_addheader (ctx, "X-Spamd-Extra-Result", hdrbuf);
			}
			else {
				smfi_addheader (ctx, "X-Spamd-Result", hdrbuf);
			}
		}
	}
	/* All other statistic headers */
	if (cfg->extended_spam_headers) {
		if (extra) {
			smfi_addheader (ctx, "X-Spamd-Extra-Server", selected->name);
			snprintf (hdrbuf, sizeof (hdrbuf), "%.2f", tf - ts);
			smfi_addheader (ctx, "X-Spamd-Extra-Scan-Time", hdrbuf);
		}
		else {
			smfi_addheader (ctx, "X-Spamd-Server", selected->name);
			snprintf (hdrbuf, sizeof (hdrbuf), "%.2f", tf - ts);
			smfi_addheader (ctx, "X-Spamd-Scan-Time", hdrbuf);
			smfi_addheader (ctx, "X-Spamd-Queue-ID", priv->mlfi_id);
		}
	}
	/* Trace spam messages to specific addr */
	if (!extra && to_trace && cfg->trace_addr) {
		smfi_addrcpt (ctx, cfg->trace_addr);
		smfi_setpriv (ctx, priv);
	}


	return (r > 0 ? res_action : r);
}
Пример #27
0
int     bounce_one_service(int flags, char *queue_name, char *queue_id,
			           char *encoding, char *orig_sender,
			           char *dsn_envid, int dsn_ret,
			           RCPT_BUF *rcpt_buf, DSN_BUF *dsn_buf,
			           BOUNCE_TEMPLATES *ts)
{
    BOUNCE_INFO *bounce_info;
    int     bounce_status = 1;
    int     postmaster_status = 1;
    VSTREAM *bounce;
    int     notify_mask = name_mask(VAR_NOTIFY_CLASSES, mail_error_masks,
				    var_notify_classes);
    VSTRING *new_id = vstring_alloc(10);

    /*
     * Initialize. Open queue file, bounce log, etc.
     */
    bounce_info = bounce_mail_one_init(queue_name, queue_id, encoding,
				       dsn_envid, rcpt_buf, dsn_buf,
				       ts->failure);

#define NULL_SENDER		MAIL_ADDR_EMPTY	/* special address */
#define NULL_TRACE_FLAGS	0

    /*
     * The choice of bounce sender address depends on the original sender
     * address. For a single bounce (a non-delivery notification to the
     * message originator), the sender address is the empty string. For a
     * double bounce (typically a failed single bounce, or a postmaster
     * notification that was produced by any of the mail processes) the
     * sender address is defined by the var_double_bounce_sender
     * configuration variable. When a double bounce cannot be delivered, the
     * queue manager blackholes the resulting triple bounce message.
     */

    /*
     * Double bounce failed. Never send a triple bounce.
     * 
     * However, this does not prevent double bounces from bouncing on other
     * systems. In order to cope with this, either the queue manager must
     * recognize the double-bounce original sender address and discard mail,
     * or every delivery agent must recognize the double-bounce sender
     * address and substitute something else so mail does not come back at
     * us.
     */
    if (strcasecmp(orig_sender, mail_addr_double_bounce()) == 0) {
	msg_warn("%s: undeliverable postmaster notification discarded",
		 queue_id);
	bounce_status = 0;
    }

    /*
     * Single bounce failed. Optionally send a double bounce to postmaster,
     * subject to notify_classes restrictions.
     */
#define ANY_BOUNCE (MAIL_ERROR_2BOUNCE | MAIL_ERROR_BOUNCE)
#define SEND_POSTMASTER_ANY_BOUNCE_NOTICE (notify_mask & ANY_BOUNCE)

    else if (*orig_sender == 0) {
	if (!SEND_POSTMASTER_ANY_BOUNCE_NOTICE) {
	    bounce_status = 0;
	} else {
	    if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
						 var_2bounce_rcpt,
						 INT_FILT_MASK_BOUNCE,
						 NULL_TRACE_FLAGS,
						 new_id)) != 0) {

		/*
		 * Double bounce to Postmaster. This is the last opportunity
		 * for this message to be delivered. Send the text with
		 * reason for the bounce, and the headers of the original
		 * message. Don't bother sending the boiler-plate text.
		 */
		if (!bounce_header(bounce, bounce_info, var_2bounce_rcpt,
				   POSTMASTER_COPY)
		    && bounce_recipient_log(bounce, bounce_info) == 0
		    && bounce_header_dsn(bounce, bounce_info) == 0
		    && bounce_recipient_dsn(bounce, bounce_info) == 0)
		    bounce_original(bounce, bounce_info, DSN_RET_FULL);
		bounce_status = post_mail_fclose(bounce);
		if (bounce_status == 0)
		    msg_info("%s: postmaster non-delivery notification: %s",
			     queue_id, STR(new_id));
	    }
	}
    }

    /*
     * Non-bounce failed. Send a single bounce, subject to DSN NOTIFY
     * restrictions.
     */
    else {
	RECIPIENT *rcpt = &bounce_info->rcpt_buf->rcpt;

	if (rcpt->dsn_notify != 0		/* compat */
	    && (rcpt->dsn_notify & DSN_NOTIFY_FAILURE) == 0) {
	    bounce_status = 0;
	} else {
	    if ((bounce = post_mail_fopen_nowait(NULL_SENDER, orig_sender,
						 INT_FILT_MASK_BOUNCE,
						 NULL_TRACE_FLAGS,
						 new_id)) != 0) {

		/*
		 * Send the bounce message header, some boilerplate text that
		 * pretends that we are a polite mail system, the text with
		 * reason for the bounce, and a copy of the original message.
		 */
		if (bounce_header(bounce, bounce_info, orig_sender,
				  NO_POSTMASTER_COPY) == 0
		    && bounce_boilerplate(bounce, bounce_info) == 0
		    && bounce_recipient_log(bounce, bounce_info) == 0
		    && bounce_header_dsn(bounce, bounce_info) == 0
		    && bounce_recipient_dsn(bounce, bounce_info) == 0)
		    bounce_original(bounce, bounce_info, dsn_ret ?
				    dsn_ret : DSN_RET_FULL);
		bounce_status = post_mail_fclose(bounce);
		if (bounce_status == 0)
		    msg_info("%s: sender non-delivery notification: %s",
			     queue_id, STR(new_id));
	    }
	}

	/*
	 * Optionally send a postmaster notice, subject to notify_classes
	 * restrictions.
	 * 
	 * This postmaster notice is not critical, so if it fails don't
	 * retransmit the bounce that we just generated, just log a warning.
	 */
#define SEND_POSTMASTER_SINGLE_BOUNCE_NOTICE (notify_mask & MAIL_ERROR_BOUNCE)

	if (bounce_status == 0 && SEND_POSTMASTER_SINGLE_BOUNCE_NOTICE
	    && strcasecmp(orig_sender, mail_addr_double_bounce()) != 0) {

	    /*
	     * Send the text with reason for the bounce, and the headers of
	     * the original message. Don't bother sending the boiler-plate
	     * text. This postmaster notice is not critical, so if it fails
	     * don't retransmit the bounce that we just generated, just log a
	     * warning.
	     */
	    if ((bounce = post_mail_fopen_nowait(mail_addr_double_bounce(),
						 var_bounce_rcpt,
						 INT_FILT_MASK_BOUNCE,
						 NULL_TRACE_FLAGS,
						 new_id)) != 0) {
		if (bounce_header(bounce, bounce_info, var_bounce_rcpt,
				  POSTMASTER_COPY) == 0
		    && bounce_recipient_log(bounce, bounce_info) == 0
		    && bounce_header_dsn(bounce, bounce_info) == 0
		    && bounce_recipient_dsn(bounce, bounce_info) == 0)
		    bounce_original(bounce, bounce_info, DSN_RET_HDRS);
		postmaster_status = post_mail_fclose(bounce);
		if (postmaster_status == 0)
		    msg_info("%s: postmaster non-delivery notification: %s",
			     queue_id, STR(new_id));
	    }
	    if (postmaster_status)
		msg_warn("%s: postmaster notice failed while bouncing to %s",
			 queue_id, orig_sender);
	}
    }

    /*
     * Optionally, delete the recipient from the queue file.
     */
    if (bounce_status == 0 && (flags & BOUNCE_FLAG_DELRCPT))
	bounce_delrcpt_one(bounce_info);

    /*
     * Cleanup.
     */
    bounce_mail_free(bounce_info);
    vstring_free(new_id);

    return (bounce_status);
}
Пример #28
0
static void post_init(char *unused_name, char **unused_argv)
{
    static const NAME_MASK lookup_masks[] = {
	SMTP_HOST_LOOKUP_DNS, SMTP_HOST_FLAG_DNS,
	SMTP_HOST_LOOKUP_NATIVE, SMTP_HOST_FLAG_NATIVE,
	0,
    };
    static const NAME_MASK dns_res_opt_masks[] = {
	SMTP_DNS_RES_OPT_DEFNAMES, RES_DEFNAMES,
	SMTP_DNS_RES_OPT_DNSRCH, RES_DNSRCH,
	0,
    };
    static const NAME_CODE dns_support[] = {
	SMTP_DNS_SUPPORT_DISABLED, SMTP_DNS_DISABLED,
	SMTP_DNS_SUPPORT_ENABLED, SMTP_DNS_ENABLED,
#if (RES_USE_DNSSEC != 0) && (RES_USE_EDNS0 != 0)
	SMTP_DNS_SUPPORT_DNSSEC, SMTP_DNS_DNSSEC,
#endif
	0, SMTP_DNS_INVALID,
    };

    if (*var_smtp_dns_support == 0) {
	/* Backwards compatible empty setting */
	smtp_dns_support =
	    var_disable_dns ? SMTP_DNS_DISABLED : SMTP_DNS_ENABLED;
    } else {
	smtp_dns_support =
	    name_code(dns_support, NAME_CODE_FLAG_NONE, var_smtp_dns_support);
	if (smtp_dns_support == SMTP_DNS_INVALID)
	    msg_fatal("invalid %s: \"%s\"", SMTP_X(DNS_SUPPORT),
		      var_smtp_dns_support);
	var_disable_dns = (smtp_dns_support == SMTP_DNS_DISABLED);
    }

    /*
     * Select hostname lookup mechanisms.
     */
    if (smtp_dns_support == SMTP_DNS_DISABLED)
	smtp_host_lookup_mask = SMTP_HOST_FLAG_NATIVE;
    else
	smtp_host_lookup_mask =
	    name_mask(SMTP_X(HOST_LOOKUP), lookup_masks, var_smtp_host_lookup);
    if (msg_verbose)
	msg_info("host name lookup methods: %s",
		 str_name_mask(SMTP_X(HOST_LOOKUP), lookup_masks,
			       smtp_host_lookup_mask));

    /*
     * Session cache instance.
     */
    if (*var_smtp_cache_dest || var_smtp_cache_demand)
#if 0
	smtp_scache = scache_multi_create();
#else
	smtp_scache = scache_clnt_create(var_scache_service,
					 var_scache_proto_tmout,
					 var_ipc_idle_limit,
					 var_ipc_ttl_limit);
#endif

    /*
     * Select DNS query flags.
     */
    smtp_dns_res_opt = name_mask(SMTP_X(DNS_RES_OPT), dns_res_opt_masks,
				 var_smtp_dns_res_opt);
}
Пример #29
0
static void tlsp_service(VSTREAM *stream, char *unused_service,
			         char **unused_argv)
{
    msg_info("TLS support is not compiled in -- exiting");
    event_server_disconnect(stream);
}
Пример #30
0
int     main(int argc, char **argv)
{
    static VSTREAM *lock_fp;
    static VSTREAM *data_lock_fp;
    VSTRING *lock_path;
    VSTRING *data_lock_path;
    off_t   inherited_limit;
    int     debug_me = 0;
    int     keep_stdout = 0;
    int     ch;
    int     fd;
    int     n;
    int     test_lock = 0;
    VSTRING *why;
    WATCHDOG *watchdog;
    ARGV   *import_env;
    int     wait_flag = 0;
    int     monitor_fd = -1;

    /*
     * Fingerprint executables and core dumps.
     */
    MAIL_VERSION_STAMP_ALLOCATE;

    /*
     * Initialize.
     */
    umask(077);					/* never fails! */

    /*
     * Process environment options as early as we can.
     */
    if (getenv(CONF_ENV_VERB))
	msg_verbose = 1;
    if (getenv(CONF_ENV_DEBUG))
	debug_me = 1;

    /*
     * Don't die when a process goes away unexpectedly.
     */
    signal(SIGPIPE, SIG_IGN);

    /*
     * Strip and save the process name for diagnostics etc.
     */
    var_procname = mystrdup(basename(argv[0]));

    /*
     * When running a child process, don't leak any open files that were
     * leaked to us by our own (privileged) parent process. Descriptors 0-2
     * are taken care of after we have initialized error logging.
     * 
     * Some systems such as AIX have a huge per-process open file limit. In
     * those cases, limit the search for potential file descriptor leaks to
     * just the first couple hundred.
     * 
     * The Debian post-installation script passes an open file descriptor into
     * the master process and waits forever for someone to close it. Because
     * of this we have to close descriptors > 2, and pray that doing so does
     * not break things.
     */
    closefrom(3);

    /*
     * Initialize logging and exit handler.
     */
    maillog_client_init(mail_task(var_procname),
			MAILLOG_CLIENT_FLAG_LOGWRITER_FALLBACK);

    /*
     * Check the Postfix library version as soon as we enable logging.
     */
    MAIL_VERSION_CHECK;

    /*
     * The mail system must be run by the superuser so it can revoke
     * privileges for selected operations. That's right - it takes privileges
     * to toss privileges.
     */
    if (getuid() != 0)
	msg_fatal("the master command is reserved for the superuser");
    if (unsafe() != 0)
	msg_fatal("the master command must not run as a set-uid process");

    /*
     * Process JCL.
     */
    while ((ch = GETOPT(argc, argv, "c:Dde:istvw")) > 0) {
	switch (ch) {
	case 'c':
	    if (setenv(CONF_ENV_PATH, optarg, 1) < 0)
		msg_fatal("out of memory");
	    break;
	case 'd':
	    master_detach = 0;
	    break;
	case 'e':
	    event_request_timer(master_exit_event, (void *) 0, atoi(optarg));
	    break;
	case 'i':
	    if (getpid() != 1)
		msg_fatal("-i is allowed only for PID 1 process");
	    init_mode = 1;
	    keep_stdout = 1;
	    break;
	case 'D':
	    debug_me = 1;
	    break;
	case 's':
	    keep_stdout = 1;
	    break;
	case 't':
	    test_lock = 1;
	    break;
	case 'v':
	    msg_verbose++;
	    break;
	case 'w':
	    wait_flag = 1;
	    break;
	default:
	    usage(argv[0]);
	    /* NOTREACHED */
	}
    }

    /*
     * This program takes no other arguments.
     */
    if (argc > optind)
	usage(argv[0]);

    /*
     * Sanity check.
     */
    if (test_lock && wait_flag)
	msg_fatal("the -t and -w options cannot be used together");
    if (init_mode && (debug_me || !master_detach || wait_flag))
	msg_fatal("the -i option cannot be used with -D, -d, or -w");

    /*
     * Run a foreground monitor process that returns an exit status of 0 when
     * the child background process reports successful initialization as a
     * daemon process. We use a generous limit in case main/master.cf specify
     * symbolic hosts/ports and the naming service is slow.
     */
#define MASTER_INIT_TIMEOUT	100		/* keep this limit generous */

    if (wait_flag)
	monitor_fd = master_monitor(MASTER_INIT_TIMEOUT);

    /*
     * If started from a terminal, get rid of any tty association. This also
     * means that all errors and warnings must go to the syslog daemon.
     * Some new world has no terminals and prefers logging to stdout.
     */
    if (master_detach)
	for (fd = 0; fd < 3; fd++) {
	    if (fd == STDOUT_FILENO && keep_stdout)
		continue;
	    (void) close(fd);
	    if (open("/dev/null", O_RDWR, 0) != fd)
		msg_fatal("open /dev/null: %m");
	}

    /*
     * Run in a separate process group, so that "postfix stop" can terminate
     * all MTA processes cleanly. Give up if we can't separate from our
     * parent process. We're not supposed to blow away the parent.
     */
    if (init_mode == 0 && debug_me == 0 && master_detach != 0
	&& setsid() == -1 && getsid(0) != getpid())
	msg_fatal("unable to set session and process group ID: %m");

    /*
     * Make some room for plumbing with file descriptors. XXX This breaks
     * when a service listens on many ports. In order to do this right we
     * must change the master-child interface so that descriptors do not need
     * to have fixed numbers.
     * 
     * In a child we need two descriptors for the flow control pipe, one for
     * child->master status updates and at least one for listening.
     */
    for (n = 0; n < 5; n++) {
	if (close_on_exec(dup(0), CLOSE_ON_EXEC) < 0)
	    msg_fatal("dup(0): %m");
    }

    /*
     * Final initializations. Unfortunately, we must read the global Postfix
     * configuration file after doing command-line processing, so that we get
     * consistent results when we SIGHUP the server to reload configuration
     * files.
     */
    master_vars_init();

    /*
     * In case of multi-protocol support. This needs to be done because
     * master does not invoke mail_params_init() (it was written before that
     * code existed).
     */
    (void) inet_proto_init(VAR_INET_PROTOCOLS, var_inet_protocols);

    /*
     * Environment import filter, to enforce consistent behavior whether
     * Postfix is started by hand, or at system boot time.
     */
    import_env = mail_parm_split(VAR_IMPORT_ENVIRON, var_import_environ);
    clean_env(import_env->argv);
    argv_free(import_env);

    if ((inherited_limit = get_file_limit()) < 0)
	set_file_limit(OFF_T_MAX);

    if (chdir(var_queue_dir))
	msg_fatal("chdir %s: %m", var_queue_dir);

    /*
     * Lock down the master.pid file. In test mode, no file means that it
     * isn't locked.
     */
    lock_path = vstring_alloc(10);
    data_lock_path = vstring_alloc(10);
    why = vstring_alloc(10);

    vstring_sprintf(lock_path, "%s/%s.pid", DEF_PID_DIR, var_procname);
    if (test_lock && access(vstring_str(lock_path), F_OK) < 0)
	exit(0);
    lock_fp = open_lock(vstring_str(lock_path), O_RDWR | O_CREAT, 0644, why);
    if (test_lock)
	exit(lock_fp ? 0 : 1);
    if (lock_fp == 0)
	msg_fatal("open lock file %s: %s",
		  vstring_str(lock_path), vstring_str(why));
    vstream_fprintf(lock_fp, "%*lu\n", (int) sizeof(unsigned long) * 4,
		    (unsigned long) var_pid);
    if (vstream_fflush(lock_fp))
	msg_fatal("cannot update lock file %s: %m", vstring_str(lock_path));
    close_on_exec(vstream_fileno(lock_fp), CLOSE_ON_EXEC);

    /*
     * Lock down the Postfix-writable data directory.
     */
    vstring_sprintf(data_lock_path, "%s/%s.lock", var_data_dir, var_procname);
    set_eugid(var_owner_uid, var_owner_gid);
    data_lock_fp =
	open_lock(vstring_str(data_lock_path), O_RDWR | O_CREAT, 0644, why);
    set_ugid(getuid(), getgid());
    if (data_lock_fp == 0)
	msg_fatal("open lock file %s: %s",
		  vstring_str(data_lock_path), vstring_str(why));
    vstream_fprintf(data_lock_fp, "%*lu\n", (int) sizeof(unsigned long) * 4,
		    (unsigned long) var_pid);
    if (vstream_fflush(data_lock_fp))
	msg_fatal("cannot update lock file %s: %m", vstring_str(data_lock_path));
    close_on_exec(vstream_fileno(data_lock_fp), CLOSE_ON_EXEC);

    /*
     * Clean up.
     */
    vstring_free(why);
    vstring_free(lock_path);
    vstring_free(data_lock_path);

    /*
     * Optionally start the debugger on ourself.
     */
    if (debug_me)
	debug_process();

    /*
     * Finish initialization, last part. We must process configuration files
     * after processing command-line parameters, so that we get consistent
     * results when we SIGHUP the server to reload configuration files.
     */
    master_config();
    master_sigsetup();
    master_flow_init();
    maillog_client_init(mail_task(var_procname),
			MAILLOG_CLIENT_FLAG_LOGWRITER_FALLBACK);
    msg_info("daemon started -- version %s, configuration %s",
	     var_mail_version, var_config_dir);

    /*
     * Report successful initialization to the foreground monitor process.
     */
    if (monitor_fd >= 0) {
	write(monitor_fd, "", 1);
	(void) close(monitor_fd);
    }

    /*
     * Process events. The event handler will execute the read/write/timer
     * action routines. Whenever something has happened, see if we received
     * any signal in the mean time. Although the master process appears to do
     * multiple things at the same time, it really is all a single thread, so
     * that there are no concurrency conflicts within the master process.
     */
#define MASTER_WATCHDOG_TIME	1000

    watchdog = watchdog_create(MASTER_WATCHDOG_TIME, (WATCHDOG_FN) 0, (void *) 0);
    for (;;) {
#ifdef HAS_VOLATILE_LOCKS
	if (myflock(vstream_fileno(lock_fp), INTERNAL_LOCK,
		    MYFLOCK_OP_EXCLUSIVE) < 0)
	    msg_fatal("refresh exclusive lock: %m");
	if (myflock(vstream_fileno(data_lock_fp), INTERNAL_LOCK,
		    MYFLOCK_OP_EXCLUSIVE) < 0)
	    msg_fatal("refresh exclusive lock: %m");
#endif
	watchdog_start(watchdog);		/* same as trigger servers */
	event_loop(MASTER_WATCHDOG_TIME / 2);
	if (master_gotsighup) {
	    msg_info("reload -- version %s, configuration %s",
		     var_mail_version, var_config_dir);
	    master_gotsighup = 0;		/* this first */
	    master_vars_init();			/* then this */
	    master_refresh();			/* then this */
	    maillog_client_init(mail_task(var_procname),
				MAILLOG_CLIENT_FLAG_LOGWRITER_FALLBACK);
	}
	if (master_gotsigchld) {
	    if (msg_verbose)
		msg_info("got sigchld");
	    master_gotsigchld = 0;		/* this first */
	    master_reap_child();		/* then this */
	}
    }
}