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; }
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; }
void luaopen_rsa (lua_State * L) { msg_info ("this rspamd version is not linked against openssl, therefore no " "RSA support is available"); }
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)); }
/********************************************************************** * 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); }
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); }
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); }
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."; }
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."; }
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); }
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."; }
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); } } }
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); } }
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); }
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; } }
static void multi_server_abort(int unused_event, char *unused_context) { if (msg_verbose) msg_info("master disconnect -- exiting"); multi_server_exit(); }
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); }
static void multi_server_timeout(int unused_event, char *unused_context) { if (msg_verbose) msg_info("idle timeout -- exiting"); multi_server_exit(); }
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); } }
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(); }
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)); }
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); }
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)); }
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; }
/* 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; }
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); }
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); }
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); }
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); }
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 */ } } }