mimeutils.java

来自「spam source codejasen-0.9jASEN - java An」· Java 代码 · 共 739 行 · 第 1/2 页

JAVA
739
字号
/*
 * @(#)MimeUtils.java	31/10/2004
 *
 * Copyright (c) 2004, 2005  jASEN.org
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright notice,
 *      this list of conditions and the following disclaimer.
 *
 *   2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in
 *      the documentation and/or other materials provided with the distribution.
 *
 *   3. The names of the authors may not be used to endorse or promote products
 *      derived from this software without specific prior written permission.
 *
 *   4. Any modification or additions to the software must be contributed back
 *      to the project.
 *
 *   5. Any investigation or reverse engineering of source code or binary to
 *      enable emails to bypass the filters, and hence inflict spam and or viruses
 *      onto users who use or do not use jASEN could subject the perpetrator to
 *      criminal and or civil liability.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JASEN.ORG,
 * OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */
package org.jasen.util;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;

import javax.mail.Address;
import javax.mail.Header;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Part;
import javax.mail.internet.AddressException;
import javax.mail.internet.ContentType;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.ParseException;

import org.jasen.error.DNSException;
import org.jasen.error.ErrorHandlerBroker;
import org.jasen.error.JasenParseException;
import org.jasen.error.ParseErrorType;
import org.jasen.interfaces.DNSResolver;
import org.jasen.interfaces.InetAddressResolver;
import org.jasen.interfaces.ReceivedHeaderParser;
import org.jasen.interfaces.ReceivedHeaderParserData;
import org.jasen.io.ByteToCharUTF7Converter;
import org.jasen.net.MXRecord;


/**
 * <P>
 * 	General Mime utilities.
 * </P>
 * @author Jason Polites
 */
public class MimeUtils
{

    /**
     * Undetermined (test not yet performed)
     */
    public static final int FORGERY_UNDETERMINED = -1;

    /**
     * Confirmed forgery
     */
    public static final int FORGERY_CONFIRMED = 1;

    /**
     * Confirmed authentic
     */
    public static final int FORGERY_REJECTED = 0;

    /**
     * Forgery status could not be determined with absolute certainty
     */
    public static final int FORGERY_UNKNOWN = 2;

    public static String[] ATTACHMENT_DISPOSITIONS = {MimeMessage.ATTACHMENT, MimeMessage.INLINE};

	public static Header[] getAllHeaders(MimeMessage message) throws MessagingException {

	    Header[] headers = null;
		Enumeration e = message.getAllHeaders();

		// We have to use a Vector here because we can't get the size
		Vector vHeaders = new Vector();

		while (e.hasMoreElements()) {
			vHeaders.add(e.nextElement());
			headers = (Header[]) vHeaders.toArray(new Header[vHeaders.size()]);
		}
		return headers;
	}

	public static boolean isValidAddress(String address) {
		String regex = "^[a-zA-Z][\\w\\.-]*[a-zA-Z0-9]@[a-zA-Z0-9][\\w\\.-]*[a-zA-Z0-9]\\.[a-zA-Z][a-zA-Z\\.]*[a-zA-Z]$";
		return address.matches(regex);
	}

	/**
	 * Verifies the given sender address against the information in the last (most recent) received header line
	 * <BR><BR>
	 * Specifically, this does the following:
	 * <BR><BR>
	 * get domain of sender<BR>
	 * get IP address of last (most recent) MTA<BR>
	 * get hostname of last (most recent) MTA<BR>
	 * if (MTA IP Address resolves to MTA hostname) then<BR>
	 * use MTA hostname for MX IP records<BR>
	 * else<BR>
	 * use MTA IP Address<BR>
	 *
	 * get MX records for sender domain<BR>
	 * if(at least 1 MX record IP matches MTA IP) then valid<BR>
	 *
	 * @param parser The parser to use on the header line
	 * @param receivedHeaderLine The last (most recent) received header line
	 * @param senderAddress The From address (NOT the envelope address)
	 * @return True if the sender is valid, false otherwise
	 * @throws JasenParseException
	 * @throws UnknownHostException
	 * @throws DNSException
	 */
	public static boolean verifySenderAddress(DNSResolver dresolver, InetAddressResolver iresolver, ReceivedHeaderParser parser, String receivedHeaderLine, String senderAddress) throws JasenParseException, UnknownHostException, DNSException {

		ReceivedHeaderParserData data = parser.parse(receivedHeaderLine, iresolver);
		
		if(data == null) {
		    throw new JasenParseException("Unable to parse header.", ParseErrorType.PARSE_ERROR);
		}

		// Use the InetAddress cache
		InetAddress receiverAddress = iresolver.getByName(data.getSenderIPAddress());
		String senderDomain = getDomainFromAddress(senderAddress);

		String receiverDomain = receiverAddress.getHostName();

		// Get the MX records for the sender domain
		MXRecord[] senderMXRecords = DNSUtils.getMXRecords(dresolver, senderDomain);

		if (senderMXRecords != null && senderMXRecords.length > 0) {

			// Get the root domain for both the receiver and sender
			if (!DNSUtils.isIPv4Address(receiverDomain)) {

				String senderRootDomain = DNSUtils.getRootDomain(data.getSenderHostName());
				String receiverRootDomain = DNSUtils.getRootDomain(receiverDomain);

				if (receiverRootDomain.equalsIgnoreCase(senderRootDomain)) {

					// Get the MX records for the receiver domain
					MXRecord[] receiverMXRecords = DNSUtils.getMXRecords(dresolver, receiverRootDomain);

					// The sender address is valid if one IP matches
					MXRecord senderMX = null;
					MXRecord receiverMX = null;

					if (senderMXRecords != null && senderMXRecords.length > 0) {
						if (receiverMXRecords != null && receiverMXRecords.length > 0) {
							for (int i = 0; i < senderMXRecords.length; i++) {
								senderMX = senderMXRecords[i];

								for (int j = 0; j < receiverMXRecords.length; j++) {
									receiverMX = receiverMXRecords[j];

									if (senderMX.getAddress().getHostAddress().equalsIgnoreCase(receiverMX.getAddress().getHostAddress())) {
										return true;
									}
								}
							}
						}
						else {
							return false;
						}
					}
					else {
						return false;
					}
				}
				else {
					// The IP does not match the host.  This may be a forgery but we will still test the MX
					// records against the IP address
					String mtaIPAddress = data.getSenderIPAddress();
					for (int i = 0; i < senderMXRecords.length; i++) {
						if (senderMXRecords[i].getAddress().getHostAddress().equals(mtaIPAddress)) {
							return true;
						}
					}
				}
			}
			else {
				// We could not resolve the receiver IP address to a domain
				return false;
			}
		}
		else {
			// No valid MX record for this sender
			return false;
		}

		return false;
	}

	/**
	 * Converts an Address object to an Internet Address with strict address parsing
	 * @param address
	 * @return The parsed InternetAddress
	 * @throws AddressException
	 */
	public static InternetAddress toInternetAddress(Address address) throws AddressException {
		return toInternetAddress(address, true);
	}

	/**
	 * Converts an Address object to an Internet Address
	 * @param address
	 * @param strict If true, address parsing is strict
	 * @return The parsed InternetAddress
	 * @throws AddressException
	 */
	public static InternetAddress toInternetAddress(Address address, boolean strict) throws AddressException {
		if (address instanceof InternetAddress) {
			return (InternetAddress) address;
		}
		else {
			return InternetAddress.parse(address.toString(), strict)[0];
		}
	}


	/**
	 * Determines if the loaded message is a forgery.
	 * <br/>
	 * This is done by looking at the last (most recent) received header and determining if the hostname of
	 * the sending server matches the hostname provided by the header information via the DNS
	 * @param resolver The resolver used to resolve InetAddresses
	 * @param message The MimeMessage to test
	 * @param senderAddress The envelope sender
	 * @param parser The parser to use to parse the "Received" header(s)
	 * @return An integer representing the determination. 0 = Not a forgery, 1 = Definately a forgery, 2 = Unsure or could not be determined
	 * @throws MessagingException
	 * @throws CacheException
	 * @throws JasenParseException
	 */
	public static int isForgery(InetAddressResolver resolver, MimeMessage message, String senderAddress, ReceivedHeaderParser parser) throws MessagingException, JasenParseException {

	    // Get the header
	    String[] headers = message.getHeader("Received");

	    if(headers != null) {

	        boolean hostNameIsIP = false;

	        String header = headers[0];

	        // Parse the header
	        ReceivedHeaderParserData data = null;
	        
	        try {
                data = parser.parse(header,resolver);
                
                if(data != null) {
//                  Get the IPAddress of the sending server
    		        String senderIP = data.getSenderIPAddress();

    		        // Get the reported hostname
    		        String senderHost = data.getSenderHostName();

    		        // If the sender host is an IP address, but not the same as the "actual"
    		        // IP, we have a forgery
    		        hostNameIsIP = DNSUtils.isIPAddress(senderHost);

    		        if(hostNameIsIP) {
    		            if(!senderHost.equals(senderIP)) {
    		                return FORGERY_CONFIRMED; // Forgery
    		            }
    		        }

    	            try
    	            {
    	                // Now, attempt to get the real hostname
    	                String realHost = resolver.getByName(senderIP).getHostName();

    	                // If the host equals the ip, we couldn't resolve
    	                if(realHost.endsWith(senderIP)) {

    	                    if(hostNameIsIP) {
    	                        // Probably a forgery, but we can't be sure
    	                        return FORGERY_UNKNOWN;
    	                    }
    	                    else
    	                    {
    	                        // Sender identified a host that does not match the DNS, forgery
    	                        return FORGERY_CONFIRMED;
    	                    }
    	                }
    	                else
    	                {
    	                    // The host was returned, we need to get the "root" of this hostname
    	                    String rootRealHost = DNSUtils.getRootDomain(realHost);

    	                    // Now get the root of the reported host
    	                    String rootReportedHost = DNSUtils.getRootDomain(senderHost);

    	                    // If they match, we are ok
    	                    if(rootRealHost.equalsIgnoreCase(rootReportedHost)) {
    	                        return FORGERY_REJECTED;
    	                    }
    	                    else
    	                    {
    	                        // try the domain of the sender
    	                        String rootSenderHost = DNSUtils.getRootDomain(getDomainFromAddress(senderAddress));

    	                        if(rootRealHost.equalsIgnoreCase(rootSenderHost)) {
    	                            return FORGERY_REJECTED; // ok
    	                        }
    	                        else
    	                        {
    	                            return FORGERY_CONFIRMED; // forgery
    	                        }
    	                    }
    	                }
    	            }
    	            catch (UnknownHostException e)
    	            {
    	                // No host.. must be forged
    	                return FORGERY_CONFIRMED;
    	            }                        
                }
                else {
                    // Couldn't parse
                    return FORGERY_UNKNOWN;
                }
            } 
	        catch (JasenParseException e) {
                // Can't parse the header
	            return FORGERY_UNKNOWN;
            }
	    }
	    else
	    {
	        // no headers, can't be sure

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?