@Override @SuppressWarnings("unchecked") public HookResult doRcpt(SMTPSession session, MaybeSender sender, MailAddress rcpt) { Collection<MailAddress> rcptList = (Collection<MailAddress>) session.getAttachment(SMTPSession.RCPT_LIST, State.Transaction); // Check if the recipient is already in the rcpt list if (rcptList != null && rcptList.contains(rcpt)) { StringBuilder responseBuffer = new StringBuilder(); responseBuffer.append(DSNStatus.getStatus(DSNStatus.SUCCESS, DSNStatus.ADDRESS_VALID)) .append(" Recipient <") .append(rcpt.toString()) .append("> OK"); LOGGER.debug("Duplicate recipient not add to recipient list: {}", rcpt); return HookResult.builder() .hookReturnCode(HookReturnCode.ok()) .smtpReturnCode(SMTPRetCode.MAIL_OK) .smtpDescription(responseBuffer.toString()) .build(); } return HookResult.DECLINED; } }
public static HookReturnCode disconnected(Action action) { return new HookReturnCode(action, ConnectionStatus.Disconnected); }
public static boolean canBeConvertedToSmtpAnswer(HookReturnCode returnCode) { return HookReturnCode.Action.ACTIVE_ACTIONS .contains(returnCode.getAction()) || returnCode.isDisconnected(); }
@Override public HookResult onHookResult(SMTPSession session, HookResult hResult, long executionTime, Hook hook) { HookReturnCode result = hResult.getResult(); boolean requiresInfoLogging = result.getAction() == HookReturnCode.Action.DENY || result.getAction() == HookReturnCode.Action.DENYSOFT || result.isDisconnected(); if (requiresInfoLogging) { LOGGER.info("{}: result= ({} {})", hook.getClass().getName(), result.getAction(), result.getConnectionStatus()); } else { LOGGER.debug("{}: result= ({} {})", hook.getClass().getName(), result.getAction(), result.getConnectionStatus()); } return hResult; }
public HookResult reject(MailAddress rcpt) { LOGGER.info("Rejected message. Unknown user: {}", rcpt); return HookResult.builder() .hookReturnCode(HookReturnCode.deny()) .smtpReturnCode(SMTPRetCode.MAILBOX_PERM_UNAVAILABLE) .smtpDescription(DSNStatus.getStatus(DSNStatus.PERMANENT,DSNStatus.ADDRESS_MAILBOX) + " Unknown user: " + rcpt.asString()) .build(); }
@Test public void testSPFfail() throws Exception { MailAddress sender = new MailAddress("test@spf2.james.apache.org"); MailAddress rcpt = new MailAddress("test@localhost"); setupMockedSMTPSession("192.168.100.1", "spf2.james.apache.org"); SPFHandler spf = new SPFHandler(); spf.setDNSService(mockedDnsService); assertThat(spf.doMail(mockedSMTPSession, sender).getResult()).describedAs("declined").isEqualTo(HookReturnCode.declined()); assertThat(spf.doRcpt(mockedSMTPSession, sender, rcpt).getResult()).describedAs("fail").isEqualTo(HookReturnCode.deny()); }
@Test public void doRcptShouldDeclineValidUsersWhenRelay() throws Exception { SMTPSession session = setupMockedSMTPSession(RELAYING_ALLOWED); HookReturnCode rCode = handler.doRcpt(session, SENDER, validUserEmail).getResult(); assertThat(rCode).isEqualTo(HookReturnCode.declined()); }
/** * Adds header to the message * */ @Override public HookResult onMessage(SMTPSession session, Mail mail) { LOGGER.debug("sending mail"); try { queue.enQueue(mail); LOGGER.info("Successfully spooled mail {} from {} on {} for {}", mail.getName(), mail.getMaybeSender(), session.getRemoteAddress().getAddress(), mail.getRecipients()); } catch (MessagingException me) { LOGGER.error("Unknown error occurred while processing DATA.", me); return HookResult.builder() .hookReturnCode(HookReturnCode.denySoft()) .smtpDescription(DSNStatus.getStatus(DSNStatus.TRANSIENT, DSNStatus.UNDEFINED_STATUS) + " Error processing message.") .build(); } return HookResult.builder() .hookReturnCode(HookReturnCode.ok()) .smtpDescription(DSNStatus.getStatus(DSNStatus.SUCCESS, DSNStatus.CONTENT_OTHER) + " Message received") .build(); }
@Override public HookResult doRcpt(SMTPSession session, MailAddress sender, MailAddress rcpt) { if (!session.isRelayingAllowed()) { // Check if session is blocklisted if (session.getAttachment(SPF_BLOCKLISTED, State.Transaction) != null) { return HookResult.builder() .hookReturnCode(HookReturnCode.deny()) .smtpDescription(DSNStatus.getStatus(DSNStatus.PERMANENT, DSNStatus.SECURITY_AUTH) + " " + session.getAttachment(SPF_TEMPBLOCKLISTED, State.Transaction)) .build(); } else if (session.getAttachment(SPF_TEMPBLOCKLISTED, State.Transaction) != null) { return HookResult.builder() .hookReturnCode(HookReturnCode.denySoft()) .smtpReturnCode(SMTPRetCode.LOCAL_ERROR) .smtpDescription(DSNStatus.getStatus(DSNStatus.TRANSIENT, DSNStatus.NETWORK_DIR_SERVER) + " Temporarily rejected: Problem on SPF lookup") .build(); } } return HookResult.DECLINED; }
handler.setMaxUnknownCmdCount(2); HookReturnCode resp = handler.doUnknown(session, "what").getResult(); assertThat(resp).isEqualTo(HookReturnCode.declined()); assertThat(resp).isEqualTo(HookReturnCode.declined()); assertThat(resp).isEqualTo(new HookReturnCode(HookReturnCode.Action.DENY, HookReturnCode.ConnectionStatus.Disconnected));
@Test public void testSPFtempError() throws Exception { MailAddress sender = new MailAddress("test@spf5.james.apache.org"); MailAddress rcpt = new MailAddress("test@localhost"); setupMockedSMTPSession("192.168.100.1", "spf5.james.apache.org"); SPFHandler spf = new SPFHandler(); spf.setDNSService(mockedDnsService); assertThat(spf.doMail(mockedSMTPSession, sender).getResult()).describedAs("declined").isEqualTo(HookReturnCode.declined()); assertThat(spf.doRcpt(mockedSMTPSession, sender, rcpt).getResult()).describedAs("temperror denysoft").isEqualTo(HookReturnCode.denySoft()); }
private Optional<String> retrieveDefaultSmtpDescription(HookReturnCode returnCode) { switch (returnCode.getAction()) { case DENY: return Optional.of("Authentication Failed"); case DENYSOFT: return Optional.of("Temporary problem. Please try again later"); case OK: return Optional.of("Authentication Succesfull"); case DECLINED: case NONE: break; } return Optional.empty(); }
public static HookReturnCode ok() { return connected(Action.OK); }
@Test public void doRcptShouldRejectNotExistingLocalUsersWhenNoRelay() { SMTPSession session = setupMockedSMTPSession(!RELAYING_ALLOWED); HookReturnCode rCode = handler.doRcpt(session, SENDER, invalidUserEmail).getResult(); assertThat(rCode).isEqualTo(HookReturnCode.deny()); }
@Test public void testSPFsoftFailRejectEnabled() throws Exception { MailAddress sender = new MailAddress("test@spf3.james.apache.org"); MailAddress rcpt = new MailAddress("test@localhost"); setupMockedSMTPSession("192.168.100.1", "spf3.james.apache.org"); SPFHandler spf = new SPFHandler(); spf.setDNSService(mockedDnsService); spf.setBlockSoftFail(true); assertThat(spf.doMail(mockedSMTPSession, sender).getResult()).describedAs("declined").isEqualTo(HookReturnCode.declined()); assertThat(spf.doRcpt(mockedSMTPSession, sender, rcpt).getResult()).describedAs("softfail reject").isEqualTo(HookReturnCode.deny()); }
@Test public void doRcptShouldDeclineValidUsersWhenNoRelay() throws Exception { SMTPSession session = setupMockedSMTPSession(!RELAYING_ALLOWED); HookReturnCode rCode = handler.doRcpt(session, SENDER, validUserEmail).getResult(); assertThat(rCode).isEqualTo(HookReturnCode.declined()); }
mailboxManager.endProcessingRequest(mailboxSession); return HookResult.builder() .hookReturnCode(HookReturnCode.ok()) .smtpReturnCode(SMTPRetCode.MAIL_OK) .smtpDescription(DSNStatus.getStatus(DSNStatus.SUCCESS, DSNStatus.CONTENT_OTHER) + " Message received") LOGGER.error("Unexpected error handling DATA stream", e); return HookResult.builder() .hookReturnCode(HookReturnCode.denySoft()) .smtpDescription(" Temporary error deliver message to " + recipient) .build();
handler.setMaxUnknownCmdCount(2); HookReturnCode resp = handler.doUnknown(session, "what").getResult(); assertThat(resp).isEqualTo(HookReturnCode.declined()); assertThat(resp).isEqualTo(HookReturnCode.declined()); assertThat(resp).isEqualTo(new HookReturnCode(HookReturnCode.Action.DENY, HookReturnCode.ConnectionStatus.Disconnected));
private Optional<String> retrieveDefaultSmtpReturnCode(HookReturnCode returnCode) { switch (returnCode.getAction()) { case DENY: return Optional.of(SMTPRetCode.AUTH_FAILED); case DENYSOFT: return Optional.of(SMTPRetCode.LOCAL_ERROR); case OK: return Optional.of(SMTPRetCode.AUTH_OK); case DECLINED: case NONE: break; } return Optional.empty(); }
public static HookReturnCode declined() { return connected(Action.DECLINED); }