Instruction:
作为 Java 安全专家，检测此代码中的安全漏洞：
## 内容1
### 文件路径：
xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/web/XWikiServletResponse.java
### 代码内容：
/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package com.xpn.xwiki.web;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.URL;
import java.util.Collection;
import java.util.Locale;
import java.util.regex.Pattern;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xwiki.url.URLSecurityManager;

public class XWikiServletResponse implements XWikiResponse
{
    private static final Logger LOGGER = LoggerFactory.getLogger(XWikiServletResponse.class);
    private static final Pattern ABSOLUTE_URL_PATTERN = Pattern.compile("[a-z0-9]+://.*");

    private HttpServletResponse response;

    public XWikiServletResponse(HttpServletResponse response)
    {
        this.response = response;
    }

    @Override
    public int getStatus()
    {
        return this.response.getStatus();
    }

    @Override
    public HttpServletResponse getHttpServletResponse()
    {
        return this.response;
    }

    @Override
    public void sendRedirect(String redirect) throws IOException
    {
        if (StringUtils.isBlank(redirect)) {
            // Nowhere to go to
            return;
        }
        if (StringUtils.containsAny(redirect, '\r', '\n')) {
            LOGGER.warn("Possible HTTP Response Splitting attack, attempting to redirect to [{}]", redirect);
            return;
        }

        // check for trusted domains, only if the given location is an absolute URL.
        if (ABSOLUTE_URL_PATTERN.matcher(redirect).matches()) {
            if (!getURLSecurityManager().isDomainTrusted(new URL(redirect))) {
                LOGGER.warn(
                    "Possible phishing attack, attempting to redirect to [{}], this request has been blocked. "
                        + "If the request was legitimate, add the domain related to this request in the list "
                        + "of trusted domains in the configuration: it can be configured in xwiki.properties in "
                        + "url.trustedDomains.", redirect);
                return;
            }
        }
        this.response.sendRedirect(redirect);
    }

    private URLSecurityManager getURLSecurityManager()
    {
        return Utils.getComponent(URLSecurityManager.class);
    }

    @Override
    public void setContentType(String type)
    {
        this.response.setContentType(type);
    }

    @Override
    public void setBufferSize(int i)
    {
        this.response.setBufferSize(i);
    }

    @Override
    public int getBufferSize()
    {
        return this.response.getBufferSize();
    }

    @Override
    public void flushBuffer() throws IOException
    {
        this.response.flushBuffer();
    }

    @Override
    public void resetBuffer()
    {
        this.response.resetBuffer();
    }

    @Override
    public boolean isCommitted()
    {
        return this.response.isCommitted();
    }

    @Override
    public void reset()
    {
        this.response.reset();
    }

    @Override
    public void setContentLength(int length)
    {
        this.response.setContentLength(length);
    }

    @Override
    public void setContentLengthLong(long len)
    {
        this.response.setContentLengthLong(len);
    }

    @Override
    public String getCharacterEncoding()
    {
        return this.response.getCharacterEncoding();
    }

    @Override
    public ServletOutputStream getOutputStream() throws IOException
    {
        return this.response.getOutputStream();
    }

    @Override
    public PrintWriter getWriter() throws IOException
    {
        return this.response.getWriter();
    }

    @Override
    public void setCharacterEncoding(String s)
    {
        this.response.setCharacterEncoding(s);
    }

    @Override
    public void addCookie(Cookie cookie)
    {
        this.response.addCookie(cookie);
    }

    public void addCookie(String cookieName, String cookieValue, int age)
    {
        Cookie cookie = new Cookie(cookieName, cookieValue);
        cookie.setVersion(1);
        cookie.setMaxAge(age);
        this.response.addCookie(cookie);
    }

    /**
     * Remove a cookie.
     *
     * @param request The servlet request needed to find the cookie to remove
     * @param cookieName The name of the cookie that must be removed.
     */
    @Override
    public void removeCookie(String cookieName, XWikiRequest request)
    {
        Cookie cookie = request.getCookie(cookieName);
        if (cookie != null) {
            cookie.setMaxAge(0);
            cookie.setPath(cookie.getPath());
            addCookie(cookie);
        }
    }

    @Override
    public void setLocale(Locale locale)
    {
        this.response.setLocale(locale);
    }

    @Override
    public Locale getLocale()
    {
        return this.response.getLocale();
    }

    @Override
    public void setDateHeader(String name, long value)
    {
        this.response.setDateHeader(name, value);
    }

    @Override
    public void setIntHeader(String name, int value)
    {
        this.response.setIntHeader(name, value);
    }

    @Override
    public void setHeader(String name, String value)
    {
        this.response.setHeader(name, value);
    }

    @Override
    public void addHeader(String name, String value)
    {
        this.response.addHeader(name, value);
    }

    @Override
    public void addDateHeader(String name, long value)
    {
        this.response.addDateHeader(name, value);
    }

    @Override
    public void addIntHeader(String name, int value)
    {
        this.response.addIntHeader(name, value);
    }

    @Override
    public void setStatus(int i)
    {
        this.response.setStatus(i);
    }

    /**
     * @deprecated
     */
    @Override
    @Deprecated
    public void setStatus(int i, String s)
    {
        this.response.setStatus(i, s);
    }

    @Override
    public boolean containsHeader(String name)
    {
        return this.response.containsHeader(name);
    }

    @Override
    public String encodeURL(String s)
    {
        return this.response.encodeURL(s);
    }

    @Override
    public String encodeRedirectURL(String s)
    {
        return this.response.encodeRedirectURL(s);
    }

    /**
     * @deprecated
     */
    @Override
    @Deprecated
    public String encodeUrl(String s)
    {
        return this.response.encodeUrl(s);
    }

    /**
     * @deprecated
     */
    @Override
    @Deprecated
    public String encodeRedirectUrl(String s)
    {
        return this.response.encodeRedirectUrl(s);
    }

    @Override
    public void sendError(int i, String s) throws IOException
    {
        this.response.sendError(i, s);
    }

    @Override
    public void sendError(int i) throws IOException
    {
        this.response.sendError(i);
    }

    @Override
    public String getContentType()
    {
        return this.response.getContentType();
    }

    @Override
    public String getHeader(String s)
    {
        return this.response.getHeader(s);
    }

    @Override
    public Collection<String> getHeaders(String s)
    {
        return this.response.getHeaders(s);
    }

    @Override
    public Collection<String> getHeaderNames()
    {
        return this.response.getHeaderNames();
    }
}

Output:
【漏洞类型】HTTP响应拆分（HTTP Response Splitting）
【严重性】High
【漏洞摘要】HTTP响应拆分是一种安全漏洞，攻击者通过在URL中插入回车符和换行符来操纵服务器的响应，可能导致注入恶意内容。在原始代码中，`sendRedirect`方法只检查了URL中是否包含回车符和换行符，而没有检查URL是否完整或是否以正确的协议开头。这可能导致攻击者利用短路径（例如`//`）来绕过安全检查，从而进行响应拆分攻击。具体问题行在第72行。
【关键代码模式】`sendRedirect`方法中的`StringUtils.containsAny(redirect, '\r', '\n')`检查，以及`ABSOLUTE_URL_PATTERN`的正则表达式匹配。
【修复方法说明】修复方法通过添加对URL以`//`开头的检查，并对其发出警告，以防止短路径的利用。这遵循了最佳实践，即对用户输入进行严格的验证和过滤。


Instruction:
作为 Java 安全专家，检测此代码中的安全漏洞：
## 内容1
### 文件路径：
src/main/java/org/opcfoundation/ua/transport/tcp/nio/OpcTcpServer.java
### 代码内容：
package org.opcfoundation.ua.transport.tcp.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

import org.opcfoundation.ua.application.Application;
import org.opcfoundation.ua.application.Server;
import org.opcfoundation.ua.common.ServiceResultException;
import org.opcfoundation.ua.core.StatusCodes;
import org.opcfoundation.ua.encoding.EncoderContext;
import org.opcfoundation.ua.transport.CloseableObjectState;
import org.opcfoundation.ua.transport.Endpoint;
import org.opcfoundation.ua.transport.EndpointBinding;
import org.opcfoundation.ua.transport.EndpointServer;
import org.opcfoundation.ua.transport.IConnectionListener;
import org.opcfoundation.ua.transport.ServerConnection;
import org.opcfoundation.ua.transport.UriUtil;
import org.opcfoundation.ua.transport.endpoint.EndpointBindingCollection;
import org.opcfoundation.ua.transport.impl.ConnectionCollection;
import org.opcfoundation.ua.transport.tcp.impl.ReverseHello;
import org.opcfoundation.ua.utils.AbstractState;
import org.opcfoundation.ua.utils.StackUtils;
import org.opcfoundation.ua.utils.asyncsocket.AsyncServerSocket;
import org.opcfoundation.ua.utils.asyncsocket.AsyncSocketImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * EndpointOpcTcp hosts an endpoint for opc.tcp socket.
 *
 * Endpoint discovery is provided if endpoint url is unknown.
 *
 * @see Executors for creating executor instances
 */
public class OpcTcpServer extends AbstractState<CloseableObjectState, ServiceResultException> implements EndpointServer {

	/** Logger */
	static Logger logger = LoggerFactory.getLogger(OpcTcpServer.class);
	/** Application */
	Application application;
	/** Secure channel counter */
	AtomicInteger secureChannelCounter = new AtomicInteger();
	/** Endpoint bindings */
	EndpointBindingCollection endpointBindings = new EndpointBindingCollection();

	/** Service server used when client connects with "" url for endpoint discovery */
	public Server discoveryServer;
	public EndpointBinding discoveryEndpointBinding;

	private int receiveBufferSize = 0;

	private boolean initialized = false;

	/** Endpoint handles */
	Map<SocketAddress, SocketHandle> socketHandles = new HashMap<SocketAddress, SocketHandle>();

	/**
	 *
getEncoderContext.

	 *
	 * @return a {@link org.opcfoundation.ua.encoding.EncoderContext} object.
	 */
	public EncoderContext getEncoderContext() {
		return application.getEncoderContext();
	}

	/**
	 *
Getter for the field receiveBufferSize.

	 *
	 * @return the receiveBufferSize to use for the connection socket of the server.
	 */
	public int getReceiveBufferSize() {
		return receiveBufferSize;
	}

	/**
	 * Define the receiveBufferSize to use for the connection socket of the server.
	 *

	 * Default value: 0, which omits the parameter and the default value for the
	 * socket (depending on the operating system) is used.
	 *
	 * @param receiveBufferSize the new size in bytes
	 * @throws org.opcfoundation.ua.common.ServiceResultException if any.
	 * @see "http://fasterdata.es.net/host-tuning/background/"
	 */
	public void setReceiveBufferSize(int receiveBufferSize) throws ServiceResultException {
		this.receiveBufferSize = receiveBufferSize;
		if (receiveBufferSize > 0) {
			for (SocketHandle sh : socketHandleSnapshot()) {
				try {
					AsyncServerSocket ass = sh.socket;
					if ( ass!=null ) {
						ass.socket().setReceiveBufferSize(receiveBufferSize);
					}
				} catch (SocketException e) {
					throw new ServiceResultException(StatusCodes.Bad_InternalError, e);
				}
			}
		}
	}

	/** AsyncServerSocket Connect listener */
	org.opcfoundation.ua.utils.asyncsocket.AsyncServerSocket.ConnectListener connectListener = new org.opcfoundation.ua.utils.asyncsocket.AsyncServerSocket.ConnectListener() {
		public void onConnected(AsyncServerSocket sender, AsyncSocketImpl newConnection) {
			logger.info("{}: {} connected", OpcTcpServer.this, newConnection.socket().getRemoteSocketAddress());
			final OpcTcpServerConnection conn = new OpcTcpServerConnection(OpcTcpServer.this, newConnection);
			connections.addConnection(conn);

			conn.addConnectionListener(new IConnectionListener() {
				@Override
				public void onClosed(ServiceResultException closeError) {
						connections.removeConnection(conn);
				}
				@Override
				public void onOpen() {
				}});

	          // Check for connection count limits
	          // Per 1.05 Part 4 section 5.5.2, To protect against misbehaving Clients and denial of
	          // service attacks, the Server shall close the oldest unused SecureChannelthat has no
	          // Session assigned before reaching the maximum number of supported SecureChannels.

	          List<ServerConnection> conns = new ArrayList<ServerConnection>();
	          connections.getConnections(conns);

	          logger.trace("Checking maximum number of connections, limit: {}, current: {}", maxConnections, conns.size());

	          // at limit is enough per the 5.5.2
	          if (conns.size() >= maxConnections) {
	            int delta = maxConnections - conns.size() + 1;
	            logger.trace("We are at max or over limit, number of connections to purge if possible: {}", delta);
	            // TODO ordering based on timestamps of creation
	            int purged = 0;
	            for (ServerConnection tmp : conns) {
	              if (tmp instanceof OpcTcpServerConnection) {
	                // Every connection should be of this type in this OpcTcpServer)
	                OpcTcpServerConnection opcTcpTmp = (OpcTcpServerConnection) tmp;

	                // We are itself part of the connections as well. Skip ourselves here (that is
	                // handled at the end of this method). If we remove ourselves here, and would be the
	                // "final legit" connection (and then we would be at max capacity), we would be
	                // closed before we can ActivateSession.
	                if (conn == opcTcpTmp) {
	                  continue;
	                }

	                if (opcTcpTmp.isPurgeEligible()) {
	                  opcTcpTmp.close();
	                  purged++;
	                  if (purged >= delta) {
	                    break;
	                  }
	                }
	              }
	            }
	            logger.trace("We are at max or over limit, purged {} old connections", purged);
	          }
	          // It is possible that we were not able to purge anything, if we happen to be at max
	          // connections that all have activated sessions, in this case, close this connection
	          // instead.
	          List<ServerConnection> tmp = new ArrayList<ServerConnection>();
	          connections.getConnections(tmp);
	          /*
	           * But! This connection itself is within the connections, if we are exactly at max, that
	           * is still OK here. But if we are over (i.e. we already had at max and none cannot be
	           * removed), then remove this connection.
	           */
	          if (tmp.size() > maxConnections) {
	            logger.trace("We are at over limit, unable to purge enough old connections, closing this connection");
	            conn.close();
	          } else {
	        	  if (tmp.size() == maxConnections) {
	        		logger.trace("We are exactly at maximum connections (including this connection). ")	;
	        	  } else {
	        		logger.trace("We are below maximum connection limit");
	        	  }
	              conn.init();
	          }
	    }};
	ConnectionCollection connections = new ConnectionCollection(this);

	int maxConnections;
	int maxSecureChannelsPerConnection;

	/**
	 *
Constructor for OpcTcpServer.

	 *
	 * @param application a {@link org.opcfoundation.ua.application.Application} object.
	 * @throws org.opcfoundation.ua.common.ServiceResultException if any.
	 */
	public OpcTcpServer(Application application) throws ServiceResultException
	{
		super(CloseableObjectState.Closed, CloseableObjectState.Closed);
		this.application = application;
		try {
			ServerSocketChannel channel = ServerSocketChannel.open();
			channel.configureBlocking(false);

			// Create a service server for connections that query endpoints (url = "")
			discoveryServer = new Server( application );
			discoveryServer.setEndpointBindings( endpointBindings );
			discoveryEndpointBinding = new EndpointBinding(this, discoveryEndpoint, discoveryServer);
		} catch (IOException e) {
			throw new ServiceResultException(StatusCodes.Bad_InternalError, e);
		}
	}

	/** {@inheritDoc} */
	@Override
	public EndpointHandle bind(SocketAddress socketAddress, EndpointBinding endpointBinding) throws ServiceResultException {
		if ( endpointBinding == null || socketAddress == null || endpointBinding.endpointServer!=this )
			throw new IllegalArgumentException();

		init();

		String scheme = UriUtil.getTransportProtocol( endpointBinding.endpointAddress.getEndpointUrl() );
		if ( !"opc.tcp".equals(scheme) ) throw new ServiceResultException(StatusCodes.Bad_UnexpectedError, "Cannot bind "+scheme+" to opc.tcp server");
		SocketHandle socketHandle = getOrCreateSocketHandle(socketAddress);

		if ( socketHandle.socket == null ) {
			try {
				socketHandle.setChannel(ServerSocketChannel.open());
				socketHandle.getChannel().configureBlocking(false);

				socketHandle.socket = new AsyncServerSocket(
						socketHandle.getChannel(),
						StackUtils.getNonBlockingWorkExecutor()//,
						//new AsyncSelector(Selector.open())
						,StackUtils.getSelector()
						);
				socketHandle.socket.bind(socketHandle.socketAddress, 0);
				socketHandle.socket.addListener(connectListener);
				logger.info("TCP/IP Socket bound to {}", socketAddress);
			} catch (IOException e) {
				logger.error("Failed to bind address "+ socketHandle.socketAddress, e);
				socketHandle.close();
				throw new ServiceResultException(StatusCodes.Bad_InternalError, e);
			}

		}

		OpcTcpEndpointHandle endpointHandle = socketHandle.getOrCreate(endpointBinding);
		return endpointHandle;
	}

	@Override
	public void bindReverse(final SocketAddress addressToConnect,
			final String endpointUrl) {
		if(addressToConnect == null || endpointUrl == null) {
			throw new IllegalArgumentException();
		}
		init();

		ReverseSocketHandle socketHandle = new ReverseSocketHandle(addressToConnect);
		if(socketHandle.socket == null) {
			try {
				socketHandle.setChannel(SocketChannel.open());
				socketHandle.getChannel().configureBlocking(false);

				socketHandle.socket = new AsyncSocketImpl(socketHandle.getChannel(), StackUtils.getNonBlockingWorkExecutor(), StackUtils.getSelector());

				ReverseHello rh = new ReverseHello();
				rh.setEndpointUrl(endpointUrl);
				rh.setServerUri(application.getApplicationDescription().getApplicationUri());
				final OpcTcpServerConnection conn = new OpcTcpServerConnection(OpcTcpServer.this, socketHandle.socket, rh);
				connections.addConnection(conn);

				conn.addConnectionListener(new IConnectionListener() {
					@Override
					public void onClosed(ServiceResultException closeError) {
						connections.removeConnection(conn);

						//1.04 Part 6 section 7.1.3 when closed, server must restart the process
						logger.debug("ReverseHello connection closed, rescheduling connection process");
						bindReverse(addressToConnect, endpointUrl);
					}
					@Override
					public void onOpen() {
					}});
		        // start listening to messages
				conn.init();
				//async, do last, others listen on socket state.
				socketHandle.socket.connect(socketHandle.socketAddress);
			}catch(IOException e) {
				logger.error("Failed to create a ReverseSocketHandle", e);
				socketHandle.close();
			}

		}

	}

	/** {@inheritDoc} */
	@Override
	public List<SocketAddress> getBoundSocketAddresses() {
		ArrayList<SocketAddress> result = new ArrayList<SocketAddress>();
		for (SocketHandle sh : socketHandleSnapshot()) result.add( sh.socketAddress );
		return result;
	}

	/**
	 *
getBoundAddress.

	 *
	 * @return a {@link java.net.SocketAddress} object.
	 */
	public SocketAddress getBoundAddress() {
		SocketHandle[] shs = socketHandleSnapshot();
		for (SocketHandle sh: shs) {
			if ( sh.socket!=null) return sh.socketAddress;
		}
		return null;
	}

	/**
	 * Disconnect all existing connections.
	 */
	public void disconnectAll()
	{
		List<ServerConnection> list = new ArrayList<ServerConnection>();
		getConnections(list);
		for (ServerConnection connection : list) {
			OpcTcpServerConnection c = (OpcTcpServerConnection) connection;
			c.close();
		}
	}

	/**
	 * {@inheritDoc}
	 *
	 * Closes server socket. Does not disconnect existing connections.
	 */
	@Override
	public synchronized OpcTcpServer close() {
		logger.info("{} closed", getBoundAddress());
		if (!getState().isClosed()) {
			setState(CloseableObjectState.Closing);
		}

		try {
			for (SocketHandle sh : socketHandleSnapshot())
				sh.close();
		} finally {
			setState(CloseableObjectState.Closed);
		}
		return this;
	}

	/** {@inheritDoc} */
	@Override
	public void addConnectionListener(org.opcfoundation.ua.transport.ConnectionMonitor.ConnectListener l) {
		connections.addConnectionListener(l);
	}

	/** {@inheritDoc} */
	@Override
	public void getConnections(Collection<ServerConnection> result) {
		connections.getConnections(result);
	}

	/** {@inheritDoc} */
	@Override
	public void removeConnectionListener(org.opcfoundation.ua.transport.ConnectionMonitor.ConnectListener l) {
		connections.removeConnectionListener(l);
	}

	/** {@inheritDoc} */
	@Override
	public EndpointBindingCollection getEndpointBindings() {
		return endpointBindings;
	}

	/** {@inheritDoc} */
	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("OpcTcpServer");
		sb.append("(");
		for (SocketHandle sh : socketHandleSnapshot()) sb.append( sh.toString() );
		sb.append(")");
		return sb.toString();
	}

	synchronized SocketHandle getOrCreateSocketHandle(SocketAddress socketAddress)
	throws ServiceResultException
	{
		SocketHandle handle = socketHandles.get(socketAddress);
		if ( handle == null ) {
			handle = new SocketHandle(socketAddress);
			socketHandles.put(socketAddress, handle);
		}
		return handle;
	}

	/**
	 *
socketHandleSnapshot.

	 *
	 * @return an array of {@link org.opcfoundation.ua.transport.tcp.nio.OpcTcpServer.SocketHandle} objects.
	 */
	public SocketHandle[] socketHandleSnapshot() {
		return socketHandles.values().toArray( new SocketHandle[ socketHandles.size()] );
	}

	public static class ReverseSocketHandle{

		private SocketAddress socketAddress;
		private SocketChannel channel;
		private AsyncSocketImpl socket;

		public ReverseSocketHandle(SocketAddress socketAddress) {
			this.socketAddress = socketAddress;
		}

		public SocketChannel getChannel() {
			return channel;
		}

		public void setChannel(SocketChannel channel) {
			this.channel = channel;
		}

		public SocketAddress getSocketAddress() {
			return socketAddress;
		}

		public AsyncSocketImpl getSocket() {
			return socket;
		}

		public void setSocket(AsyncSocketImpl socket) {
			this.socket = socket;
		}

		void close() {
			SocketChannel c = channel;
			if(c != null) {
				try {
					c.close();
				} catch (IOException e) {
					logger.error("Failure in closing ReverseSockeHandle", e);
				}
			}
		}
	}

	public class SocketHandle {
		SocketAddress socketAddress;
		/** Server Socket */
		AsyncServerSocket socket;
		private ServerSocketChannel channel;
		int port;
		Map<Endpoint, OpcTcpEndpointHandle> endpoints = new HashMap<Endpoint, OpcTcpEndpointHandle>();
		SocketHandle(SocketAddress socketAddress)
		{
			this.socketAddress = socketAddress;
		}

		public synchronized OpcTcpEndpointHandle[] endpointHandleSnapshot() {
			return endpoints.values().toArray( new OpcTcpEndpointHandle[ endpoints.size()] );
		}

		synchronized void endpointHandleSnapshot(Collection<OpcTcpEndpointHandle> handles) {
			handles.addAll( endpoints.values() );
		}

		synchronized OpcTcpEndpointHandle getOrCreate(EndpointBinding endpointBinding) throws ServiceResultException {
			OpcTcpEndpointHandle handle = endpoints.get(endpointBinding.endpointAddress);
			if ( handle == null ) {
				handle = new OpcTcpEndpointHandle(this, endpointBinding);
				// Add endpointBinding to SocketHandle
				endpoints.put(endpointBinding.endpointAddress, handle);
				// Add endpointBinding to EndpointServer
				endpointBindings.add(endpointBinding);
				// Add endpointBinding to Server
				endpointBinding.serviceServer.getEndpointBindings().add(endpointBinding);
			} else {
				if ( !handle.endpointBinding.equals( endpointBinding ) ) {
					throw new ServiceResultException( StatusCodes.Bad_UnexpectedError, "Cannot bind an endpoint address to two different servers." );
				}
			}
			return handle;
		}
		int getPort() {
			return ((InetSocketAddress)socketAddress).getPort();
		}

		void close() {
			for ( OpcTcpEndpointHandle eph : endpoints.values() ) eph.close__();
			socketHandles.remove(socketAddress);
			if ( socket!=null ) {
				AsyncServerSocket ass = socket;
				socket = null;
				ass.close();
			}
		}
		@Override
		public String toString() {
			StringBuilder sb = new StringBuilder();
			sb.append( "opc.tcp("+socketAddress+", ");
			for (OpcTcpEndpointHandle ep : endpoints.values()) {
				sb.append( ep.toString() );
			}
			sb.append( ")");
			return sb.toString();
		}

		/**
		 * @return the channel
		 */
		public ServerSocketChannel getChannel() {
			return channel;
		}

		/**
		 * @param channel the channel to set
		 */
		public void setChannel(ServerSocketChannel channel) {
			this.channel = channel;
		}

		public SocketAddress getSocketAddress() {
			return socketAddress;
		}
	}

	public class OpcTcpEndpointHandle implements EndpointHandle {
		EndpointBinding endpointBinding;
		SocketHandle socketHandle;
		OpcTcpEndpointHandle(SocketHandle socketHandle, EndpointBinding endpointBinding) {
			this.socketHandle = socketHandle;
			this.endpointBinding = endpointBinding;
		}
		@Override
		public SocketAddress socketAddress() {
			return socketHandle.socketAddress;
		}
		@Override
		public EndpointBinding endpointBinding() {
			return endpointBinding;
		}
		@Override
		public void close() {
			close_();
			close__();
		}
		void close_() {
			// Remove endpointBinding from SocketHandle
			socketHandle.endpoints.remove(endpointBinding.endpointAddress);
			// Close socket if this was the last
			if ( socketHandle.endpoints.isEmpty() ) {
				socketHandle.close();
			}
		}
		void close__() {
			// Close endpointHandler if this was the last
			int count = countEndpoints(endpointBinding.endpointAddress);
			if ( count == 0 ) {
				// Remove endpointBinding from EndpointServer
				endpointBindings.remove(endpointBinding);
				// Remove endpointBinding from Server
				endpointBinding.serviceServer.getEndpointBindings().remove(endpointBinding);
			}
		}
		@Override
		public String toString() {
			return "("+endpointBinding.endpointAddress.toString()+")";
		}
	}

	private void init() {
		if (!initialized) {
			int maxConnections = application.getOpctcpSettings().getMaxConnections();
			if (maxConnections <= 0) {
				throw new IllegalStateException("Maximum number of connections was not configured; must be greater than 0");
			}
			this.maxConnections = maxConnections;

			int maxSecureChannelsPerConnection = application.getOpctcpSettings().getMaxSecureChannelsPerConnection();
			if (maxSecureChannelsPerConnection <= 0) {
				throw new IllegalStateException(
		            "Maximum number of secure channels per connection was not configured; must be greater than 0");
			}
			this.maxSecureChannelsPerConnection = maxSecureChannelsPerConnection;
			initialized = true;
		}
	}

	List<OpcTcpEndpointHandle> findEndpoints(String forUri) {
		List<OpcTcpEndpointHandle> result = new ArrayList<OpcTcpEndpointHandle>();
		for (SocketHandle sh : socketHandleSnapshot() ) sh.endpointHandleSnapshot(result);
		return result;
	}
	int countEndpoints(Endpoint endpointAddress) {
		int count = 0;
		for (SocketHandle sh : socketHandleSnapshot() ) {
			for (OpcTcpEndpointHandle eh : sh.endpointHandleSnapshot()) {
				if (eh.endpointBinding.endpointAddress.equals(endpointAddress)) count++;
			}
		}
		return count;
	}

}

## 内容2
### 文件路径：
src/main/java/org/opcfoundation/ua/transport/tcp/nio/OpcTcpServerConnection.java
### 代码内容：
package org.opcfoundation.ua.transport.tcp.nio;

import static org.opcfoundation.ua.core.StatusCodes.Bad_SecureChannelIdInvalid;
import static org.opcfoundation.ua.core.StatusCodes.Bad_SecurityChecksFailed;
import static org.opcfoundation.ua.core.StatusCodes.Bad_SecurityPolicyRejected;
import static org.opcfoundation.ua.core.StatusCodes.Bad_TcpInternalError;
import static org.opcfoundation.ua.core.StatusCodes.Bad_Timeout;
import static org.opcfoundation.ua.core.StatusCodes.Bad_UnexpectedError;

import java.io.IOException;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

import org.opcfoundation.ua.builtintypes.StatusCode;
import org.opcfoundation.ua.builtintypes.UnsignedInteger;
import org.opcfoundation.ua.common.ServiceResultException;
import org.opcfoundation.ua.core.ActivateSessionResponse;
import org.opcfoundation.ua.core.CloseSecureChannelRequest;
import org.opcfoundation.ua.core.EndpointConfiguration;
import org.opcfoundation.ua.core.MessageSecurityMode;
import org.opcfoundation.ua.core.OpenSecureChannelRequest;
import org.opcfoundation.ua.core.SecurityTokenRequestType;
import org.opcfoundation.ua.core.StatusCodes;
import org.opcfoundation.ua.encoding.EncoderContext;
import org.opcfoundation.ua.encoding.IEncodeable;
import org.opcfoundation.ua.encoding.binary.BinaryEncoder;
import org.opcfoundation.ua.transport.AsyncWrite;
import org.opcfoundation.ua.transport.CloseableObject;
import org.opcfoundation.ua.transport.CloseableObjectState;
import org.opcfoundation.ua.transport.Endpoint;
import org.opcfoundation.ua.transport.EndpointBinding;
import org.opcfoundation.ua.transport.IConnectionListener;
import org.opcfoundation.ua.transport.ServerSecureChannel;
import org.opcfoundation.ua.transport.endpoint.AbstractServerSecureChannel;
import org.opcfoundation.ua.transport.endpoint.EndpointBindingCollection;
import org.opcfoundation.ua.transport.security.CertificateValidator;
import org.opcfoundation.ua.transport.security.KeyPair;
import org.opcfoundation.ua.transport.security.SecurityAlgorithm;
import org.opcfoundation.ua.transport.security.SecurityConfiguration;
import org.opcfoundation.ua.transport.security.SecurityMode;
import org.opcfoundation.ua.transport.security.SecurityPolicy;
import org.opcfoundation.ua.transport.tcp.impl.Acknowledge;
import org.opcfoundation.ua.transport.tcp.impl.ChunkAsymmEncryptSigner;
import org.opcfoundation.ua.transport.tcp.impl.ChunkFactory;
import org.opcfoundation.ua.transport.tcp.impl.ChunkFactory.AcknowledgeChunkFactory;
import org.opcfoundation.ua.transport.tcp.impl.ChunkFactory.ErrorMessageChunkFactory;
import org.opcfoundation.ua.transport.tcp.impl.ChunkSymmEncryptSigner;
import org.opcfoundation.ua.transport.tcp.impl.ChunkUtils;
import org.opcfoundation.ua.transport.tcp.impl.ErrorMessage;
import org.opcfoundation.ua.transport.tcp.impl.Hello;
import org.opcfoundation.ua.transport.tcp.impl.ReverseHello;
import org.opcfoundation.ua.transport.tcp.impl.SecurityToken;
import org.opcfoundation.ua.transport.tcp.impl.TcpMessageType;
import org.opcfoundation.ua.transport.tcp.nio.Channel.ChannelListener;
import org.opcfoundation.ua.transport.tcp.nio.SecureInputMessageBuilder.MessageListener;
import org.opcfoundation.ua.utils.CertificateUtils;
import org.opcfoundation.ua.utils.CryptoUtil;
import org.opcfoundation.ua.utils.IStatefulObject;
import org.opcfoundation.ua.utils.ObjectUtils;
import org.opcfoundation.ua.utils.SizeCalculationOutputStream;
import org.opcfoundation.ua.utils.StackUtils;
import org.opcfoundation.ua.utils.StateListener;
import org.opcfoundation.ua.utils.TimerUtil;
import org.opcfoundation.ua.utils.asyncsocket.AsyncInputStream;
import org.opcfoundation.ua.utils.asyncsocket.AsyncSocket;
import org.opcfoundation.ua.utils.asyncsocket.AsyncSocketImpl;
import org.opcfoundation.ua.utils.asyncsocket.BufferMonitorState;
import org.opcfoundation.ua.utils.asyncsocket.MonitorListener;
import org.opcfoundation.ua.utils.asyncsocket.SocketState;
import org.opcfoundation.ua.utils.bytebuffer.ByteBufferArrayWriteable2;
import org.opcfoundation.ua.utils.bytebuffer.ByteBufferArrayWriteable2.ChunkListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
OpcTcpServerConnection class.

 *
 * @author jaro
 */
public class OpcTcpServerConnection extends AbstractServerConnection {

	/** Logger */
	private final static Logger logger = LoggerFactory.getLogger(OpcTcpServerConnection.class);

	// Give client 10 minutes to handshake
	private static long handshakeTimeout = 10 * 60 * 1000; // 10 minutes
	/**
	 *
Getter for the field handshakeTimeout.

	 *
	 * @return the handshakeTimeout
	 */
	public static long getHandshakeTimeout() {
		return handshakeTimeout;
	}
	/**
	 * Define the handshake timeout for new connections. If the client does not
	 * establish a connection during the timeout, the socket is released.
	 * A smaller value might be usable to avoid DNS attacks.
	 *
	 * @param handshakeTimeout
	 *            the handshakeTimeout to set. Default 600000 = 10 minutes.
	 */
	public static void setHandshakeTimeout(long handshakeTimeout) {
		OpcTcpServerConnection.handshakeTimeout = handshakeTimeout;
	}
	/** Agreed protocol version */
	int agreedProtocolVersion;
	/**  Request id - Pending Request mapping */
	Map<Integer, PendingRequest> pendingRequests  = new ConcurrentHashMap<Integer, PendingRequest>();

	/** An endpoint binding associated with this connection. Set after hello */
	EndpointBinding binding;

	/** Endpoint Server */
	OpcTcpServer endpointServer;

	/** Event based asynchronous socket */
	AsyncSocket s;

	/** Optional ReverseHello message, if operating in ReverseHello-mode */
	ReverseHello rh;

	/**
	 * Tracks if this connection has ever been successfully SessionActivated at least once. Impl note:
	 * once set to true, shall be never set to false.
	 */
	AtomicBoolean hasBeenSuccessfullySessionActivated = new AtomicBoolean(false);

	/** Timer used for handshake timing */
	Timer timer = TimerUtil.getTimer();

	TimerTask timeoutTimer;
	Runnable timeout = new Runnable() {
		@Override
		public void run() {
			setError(Bad_Timeout);
		}};

		EncoderContext encoderCtx;
		EndpointConfiguration endpointConfiguration;

		/// ??? ///
		// Handles incoming data //
		MonitorListener inputListener =
				new MonitorListener() {
			@Override
			public void onStateTransition(IStatefulObject<BufferMonitorState, ?> sender,
					BufferMonitorState oldState, BufferMonitorState newState) {

				// Trigger is unreachable
				if (newState.isUnreachable()) {
					if (secureMessageBuilder!=null) {
						secureMessageBuilder.close();
						secureMessageBuilder = null;
					}
					return;
				}

				if (newState != BufferMonitorState.Triggered)
				{
					logger.error("Unexpected trigger state {}", newState);
					return;
				}

				// -- assert -- atleast 8 bytes are available --

				AsyncInputStream is = s.getInputStream();
				ByteBuffer hdr = is.peek(8);
				hdr.order(ByteOrder.LITTLE_ENDIAN);
				hdr.getInt();
				int chunkSize = hdr.getInt();

				if (chunkSize<12) {
					setError(StatusCodes.Bad_TcpInternalError);
					if (secureMessageBuilder!=null)
						secureMessageBuilder.close();
					return;
				}

				if (chunkSize > ctx.maxRecvChunkSize)
				{
					setError(new ServiceResultException(StatusCodes.Bad_TcpMessageTooLarge, "Chunk size ("+chunkSize+") exceeded maximum ("+ctx.maxRecvChunkSize+")"));
					if (secureMessageBuilder!=null)
						secureMessageBuilder.close();
					return;
				}

				if (is.available()>=chunkSize) {
					// Chunk is readable
					ByteBuffer chunk = is.read(chunkSize);
					chunk.rewind();
					try {
						try {
							handleChunk(chunk);
						} catch (RuntimeException e) {
							logger.warn("Error in handleChunk", e);
							throw StackUtils.toServiceResultException(e);
						}
					} catch (ServiceResultException se) {
						logger.info("Error in handleChunk", se);
						setError(se);
					}
					// Wait for next chunk header
					is.createMonitor(is.getPosition()+8, this);

				} else {
					// Wake me up when the chunk is fully readable
					is.createMonitor(is.getPosition()+chunkSize, this);
				}
			}
		};
		/// ??? ///

		// Handle incoming messages, ran in BlockingWorkExecutor //
		MessageListener messageListener = new MessageListener() {
			@Override
			public void onMessageComplete(InputMessage sender) {
				IEncodeable msg = sender.getMessage();

				for (ChannelListener cl : channelListeners)
					if (cl.handleMessage(sender)) return;

				// Handler error
				if (msg==null)
					synchronized (this) {
						Exception error = sender.getError();
						if (error == null)
							return; // aborted;
						setError(StackUtils.toServiceResultException(error));
						return;
					}

				// Handle message
				try {
					if (sender.getMessageType() == TcpMessageType.MESSAGE) {
						handleSecureMessage(sender);
					} else if (sender.getMessageType() == TcpMessageType.CLOSE) {
						handleCloseSecureChannelRequest(sender);
					} else if (sender.getMessageType() == TcpMessageType.OPEN) {
						handleOpenSecureChannelRequest(sender);
					}
				} catch (ServiceResultException e) {
					setError(e);
				}
			}};

			/**
			 * Creates a new OpcTcpConnection with the given parameters.
			 *
			 * @param endpointServer EndpointServer for opc.tcp
			 * @param s socket
			 */
			OpcTcpServerConnection(OpcTcpServer endpointServer, AsyncSocketImpl s) {
				this(endpointServer, s, null);
			}

			/**
			 * Creates a new OpcTcpConnection with the given parameters.
			 *
			 * @param endpointServer EndpointServer for opc.tcp
			 * @param s socket
			 * @param rh, if null, assumes normal connection, if non-null assumes ReverseHello connection.
			 */
			OpcTcpServerConnection(OpcTcpServer endpointServer, AsyncSocketImpl s, final ReverseHello rh) {
				super();
				this.endpointServer = endpointServer;
				this.s = s;
				this.rh = rh;

				this.encoderCtx = endpointServer.getEncoderContext();

				// Monitor the state of the socket, make changes reflect to the state of the UATcpConnection
				socketListener =
						new StateListener<SocketState>() {
					@Override
					public void onStateTransition(IStatefulObject<SocketState, ?> monitor, SocketState oldState, SocketState newState) {
						if (newState==SocketState.Error)
						{
							setError( StackUtils.toServiceResultException( OpcTcpServerConnection.this.s.getStateMonitor().getError() ) );
						}
						if (newState==SocketState.Closed)
						{
							close();
						}
					}
				};

				setState(CloseableObjectState.Opening);

			}

			/**
			 * Start listening for data from the connection, note that data might be processed already before
			 * this method returns.
			 */
			public void init() {
				s.getStateMonitor().addStateListener(socketListener);

			    // must set timeout timer here, because it might be canceled before the below monitor
			    // is triggered
				if(rh == null) {
					timeoutTimer = TimerUtil.schedule(
							timer, timeout,
							StackUtils.getBlockingWorkExecutor(),
							System.currentTimeMillis() + handshakeTimeout);
				}

			    // Start listening for the Hello (the inputListener will schedule itself again)
				s.getInputStream().createMonitor(8, inputListener);

				if(rh != null) {
					s.getStateMonitor().addStateListener(new StateListener<SocketState>() {
						@Override
						public void onStateTransition(
								IStatefulObject<SocketState, ?> sender,
								SocketState oldState, SocketState newState) {
							if(oldState == SocketState.Connecting && newState == SocketState.Connected) {
								timeoutTimer = TimerUtil.schedule(
										timer, timeout,
										StackUtils.getBlockingWorkExecutor(),
										System.currentTimeMillis() + handshakeTimeout);
								sendReverseHello(rh);
							}
						}
					});
				}
			  }

			/** {@inheritDoc} */
			@Override
			public void addConnectionListener(IConnectionListener listener) {
				logger.debug("addConnectionListener: listener={}", listener);
				super.addConnectionListener(listener);

			}

			/** {@inheritDoc} */
			@Override
			public synchronized CloseableObject close() {
			    // clear timeout timer, if exist, otherwise it will keep this object alive GC-wise.
			    cancelTimeoutTimer();
			    try {
					setState(CloseableObjectState.Closing);
				} finally {
					try {
						s.close();
					} catch (IOException e) {
					}
					setState(CloseableObjectState.Closed);
				}
				return this;
			}

			/** {@inheritDoc} */
			@Override
			public SocketAddress getLocalAddress() {
				Socket socket = s.socket();
				if (socket==null) return null;
				return socket.getLocalSocketAddress();
			}

			/** {@inheritDoc} */
			@Override
			public SocketAddress getRemoteAddress() {
				Socket socket = s.socket();
				if (socket==null) return null;
				return socket.getRemoteSocketAddress();
			}

			/**
			 * Returns true if this connection has (or had) a Session that was successfully Activated at least
			 * once. Returns false in other cases.
			 */
			public boolean hasBeenSuccessfullySessionActivated() {
				return hasBeenSuccessfullySessionActivated.get();
			}

			/**
			 * Returns true if this connection is eligble to be purged if we approach max number of
			 * connections, false if this connection is valid and should not be purged. Per 1.05 Part 4
			 * section 5.5.2 "To protect against misbehaving Clientsand denial of service attacks, the
			 * Servershall close the oldest unused SecureChannelthat has no Sessionassigned before reaching
			 * the maximum number of supported SecureChannels.". However, in addition if the connection was
			 * formed via a ReverseHello, it is not considered to be eligible as server was the one initiating
			 * the connection. Thus this returns true, if this connection is neither a reverse connection and
			 * also has not yet been activated by a Session.
			 */
			public boolean isPurgeEligible() {
				return !isReverse() && !hasBeenSuccessfullySessionActivated();
			}

			/**
			 * Return true if this connection is a reverse connection i.e. where the server send a
			 * ReverseHello to a Client to initiate the connection.
			 */
			public boolean isReverse() {
				return rh != null;
			}

			/** {@inheritDoc} */
			@Override
			public void removeConnectionListener(IConnectionListener listener) {
				logger.debug("removeConnectionListener: listener={}", listener);
				super.removeConnectionListener(listener);
				cancelTimeoutTimer();
			}

			private void checkSecureChannelLimit() throws ServiceResultException {
				if (secureChannels.size() >= endpointServer.maxSecureChannelsPerConnection) {
					throw new ServiceResultException(StatusCodes.Bad_TcpNotEnoughResources);
				}
			}

			/**
			 * @param uri
			 * @return
			 */
			private String trimUrl(String uri) {
				if ( uri==null ) return null;
				// Also remove an optional '/' from the end, since it is not significant
				if (uri.endsWith("/"))
					uri = uri.substring(0, uri.length()-1);
				return uri;
			}

			/**
			 *
cancelTimeoutTimer.

			 */
			protected void cancelTimeoutTimer() {
			    // Cancel hand-shake time-out, note that this can never be started again, thus it wont hurt if
			    // this happens multiple times
			    final TimerTask tmp = timeoutTimer; // multithread-guard
			    if (tmp != null) {
			    	tmp.cancel();
			    	timeoutTimer = null;
					timeout = null;
				}
			}

			/**
			 * Flushes queued chunks (see startChunkSend())
			 *
			 * @param chunk chunk to send
			 */
			protected void endChunkSend(ByteBuffer chunk)
			{
				chunkIncubator.hatch(chunk);
				synchronized(this) {
					while (chunkIncubator.nextIsHatched()) {
						ByteBuffer c = chunkIncubator.removeNextHatchedIfAvailable();
						c.rewind();
						s.getOutputStream().offer(c);
					}
				}
			}

			/**
			 *
flush.

			 *
			 * @param position a long.
			 * @return a {@link org.opcfoundation.ua.utils.asyncsocket.BufferMonitorState} object.
			 * @throws java.lang.InterruptedException if any.
			 * @throws java.io.IOException if any.
			 */
			protected BufferMonitorState flush(long position)
					throws InterruptedException, IOException
			{
				return s.getOutputStream().createMonitor(position, null).waitForState(BufferMonitorState.FINAL_STATES);
			}

			/** {@inheritDoc} */
			@Override
			protected CertificateValidator getRemoteCertificateValidator() {
				return binding==null ? null: binding.serviceServer.getApplication().getOpctcpSettings().getCertificateValidator();
			}

			/**
			 *
handleAcknowledgeMessage.

			 *
			 * @param a a {@link org.opcfoundation.ua.transport.tcp.impl.Acknowledge} object.
			 * @throws org.opcfoundation.ua.common.ServiceResultException if any.
			 */
			protected void handleAcknowledgeMessage(Acknowledge a) throws ServiceResultException {
				throw new ServiceResultException(Bad_UnexpectedError);
			}

			/**
			 *
handleAsymmChunk.

			 *
			 * @param chunk a {@link java.nio.ByteBuffer} object.
			 * @throws org.opcfoundation.ua.common.ServiceResultException if any.
			 */
			protected void handleAsymmChunk(ByteBuffer chunk) throws ServiceResultException {
				chunk.rewind();
				if (secureMessageBuilder!=null && !secureMessageBuilder.moreChunksRequired()) secureMessageBuilder = null;

				// First chunk of the message
				if (secureMessageBuilder==null) {
					// Read remote certificate and thumbprint of the expected local certificate
					//			chunk.position(8);
					//			int secureChannelId = chunk.getInt();
					int secureChannelId = ChunkUtils.getSecureChannelId(chunk);
					OpcTcpServerSecureChannel secureChannel =(OpcTcpServerSecureChannel) secureChannels.get(secureChannelId);
					String securityPolicyUri = ChunkUtils.getString(chunk);
					SecurityPolicy securityPolicy = SecurityPolicy.getSecurityPolicy(securityPolicyUri);
					if (securityPolicy==null)
					{
						logger.warn("Security policy \"{}\" is not supported by the stack", securityPolicyUri);
						throw new ServiceResultException(Bad_SecurityPolicyRejected, "Security policy \""+securityPolicyUri+"\" is not supported by the stack");
					}

					//			if (!binding.endpointAddress.supportsSecurityPolicy(securityPolicy))
					//			{
					//				logger.warn("Security policy \""+securityPolicyUri+"\" is not supported by the endpoint");
					//				throw new ServiceResultException(Bad_SecurityPolicyRejected, "Security policy \""+securityPolicyUri+"\" is not supported by the endpoint");
					//			}

					byte[] encodedRemoteCertificate = ChunkUtils.getByteString(chunk);
					byte[] encodedLocalCertificateThumbprint = ChunkUtils.getByteString(chunk);
					logger.debug("secureChannelId={}", secureChannelId);
					logger.debug("secureChannel={}", secureChannel);
					logger.debug("securityPolicyUri={}", securityPolicyUri);
					logger.debug("securityPolicy={}", securityPolicy);
					logger.debug("encodedRemoteCertificate={}", encodedRemoteCertificate);
					logger.debug("encodedLocalCertificateThumbprint={}", encodedLocalCertificateThumbprint);

					KeyPair localCertificate = binding.serviceServer.getApplication().getApplicationInstanceCertificate(encodedLocalCertificateThumbprint);

					if (localCertificate==null && securityPolicy != SecurityPolicy.NONE) {
						logger.warn("Requested Application Instance Certificate is not found in the server");
						throw new ServiceResultException(Bad_SecurityChecksFailed, "Requested Application Instance Certificate is not found in the server");
					}

					// Decode remote certificate
					org.opcfoundation.ua.transport.security.Cert remoteCertificate;
					try {
						remoteCertificate =
								encodedRemoteCertificate==null ?
										null :
											new org.opcfoundation.ua.transport.security.Cert( CertificateUtils.decodeX509Certificate(encodedRemoteCertificate) );
					} catch (CertificateException e) {
						throw new ServiceResultException(Bad_SecurityChecksFailed); //see 1.02.2 errdata
					}

					// Validate Client's Certificate
					CertificateValidator validator = getRemoteCertificateValidator();
					if (validator != null) {
						StatusCode code = validator.validateCertificate(remoteCertificate);
						if (code != null && !code.isGood()) {
							logger.warn("Remote certificate not accepted: {}", code.toString());
							throw new ServiceResultException(code);
						}
					}

					// MessageMode is unknown at this time. It is fixed in UATcpServerSecureChannel.onOpenChannel
					MessageSecurityMode msm = securityPolicy == SecurityPolicy.NONE ? MessageSecurityMode.None : MessageSecurityMode.SignAndEncrypt;
					SecurityMode mode = new SecurityMode(securityPolicy, msm);
					securityConfiguration = new SecurityConfiguration(mode, localCertificate, remoteCertificate);

					AtomicInteger recvSequenceNumber = secureChannel==null ? null : secureChannel.recvSequenceNumber;

					secureMessageBuilder = new SecureInputMessageBuilder(securityConfiguration, messageListener, ctx, encoderCtx, recvSequenceNumber);
				}
				logger.debug("onAsymmSecureChunk: {}", chunk);
				secureMessageBuilder.addChunk(chunk);
			}

			/**
			 *
handleChunk.

			 *
			 * @param chunk a {@link java.nio.ByteBuffer} object.
			 * @throws org.opcfoundation.ua.common.ServiceResultException if any.
			 */
			protected void handleChunk(ByteBuffer chunk) throws ServiceResultException {
				int type = ChunkUtils.getMessageType(chunk);
				int messageType = type & TcpMessageType.MESSAGE_TYPE_MASK;
				// Secure message
				if (messageType == TcpMessageType.MESSAGE) {
					handleSymmChunk(chunk);
				} else if (messageType == TcpMessageType.CLOSE) {
					handleCloseChunk(chunk);
				} else if (messageType == TcpMessageType.OPEN) {
					handleAsymmChunk(chunk);
				} else if (messageType == TcpMessageType.HELLO || messageType == TcpMessageType.ACKNOWLEDGE || messageType == TcpMessageType.ERROR) {
					handleRawChunk(chunk);
				} else {
					// Unknown chunk
					close();
				}
			}

			/**
			 *
handleCloseChunk.

			 *
			 * @param chunk a {@link java.nio.ByteBuffer} object.
			 * @throws org.opcfoundation.ua.common.ServiceResultException if any.
			 */
			protected void handleCloseChunk(ByteBuffer chunk) throws ServiceResultException {
				close();
			}

			/**
			 *
handleCloseSecureChannelRequest.

			 *
			 * @param mb a {@link org.opcfoundation.ua.transport.tcp.nio.InputMessage} object.
			 * @throws org.opcfoundation.ua.common.ServiceResultException if any.
			 */
			protected void handleCloseSecureChannelRequest(InputMessage mb) throws ServiceResultException {
				logger.debug("onCloseChannel");
				IEncodeable msg = mb.getMessage();
				if (!(msg instanceof CloseSecureChannelRequest))
					throw new ServiceResultException(Bad_UnexpectedError);

				CloseSecureChannelRequest req = (CloseSecureChannelRequest) msg;
				int secureChannelId = mb.getSecureChannelId();
				OpcTcpServerSecureChannel chan = (OpcTcpServerSecureChannel) secureChannels.get(secureChannelId);
				if (chan==null) throw new ServiceResultException( Bad_SecureChannelIdInvalid );
				chan.handleCloseSecureChannelRequest(mb, req);
			}

			/**
			 *
handleErrorMessage.

			 *
			 * @param e a {@link org.opcfoundation.ua.transport.tcp.impl.ErrorMessage} object.
			 */
			protected void handleErrorMessage(ErrorMessage e) {
				logger.debug("onError: {}", e);
				setError(e.getError());
			}

			/**
			 *
handleHelloMessage.

			 *
			 * @param h a {@link org.opcfoundation.ua.transport.tcp.impl.Hello} object.
			 * @throws org.opcfoundation.ua.common.ServiceResultException if any.
			 */
			protected void handleHelloMessage(Hello h) throws ServiceResultException {
				cancelTimeoutTimer();

				EndpointBindingCollection c = endpointServer.getEndpointBindings();
				if (c==null) throw new ServiceResultException(Bad_UnexpectedError);

			    checkSecureChannelLimit();

			    String url = trimUrl(h.getEndpointUrl());

				logger.debug("onHello: url={}", url);

				if ( url==null || url.equals("") ) {
					binding = endpointServer.discoveryEndpointBinding;
				} else {
					List<EndpointBinding> bindingsForUrl = c.get( url );
					if (bindingsForUrl.isEmpty()) {
						binding = c.getDefault(url);
						if(binding == null){
							throw new ServiceResultException(StatusCodes.Bad_TcpEndpointUrlInvalid);
						}
					} else {
						binding = bindingsForUrl.get(0);
					}
				}
				// Makes sense to check isDebugEnabled because Arrays.toString method probably takes longer than this check.
				if(logger.isDebugEnabled()){
					logger.debug(" endpoints={}", Arrays.toString(c.getEndpointAddresses().toArray(new Endpoint[0])));
					logger.debug(" endpoint=" + (binding==null?"binding is null":binding.endpointAddress));
				}

				if (getState()!=CloseableObjectState.Opening) throw new ServiceResultException(Bad_UnexpectedError);

				Acknowledge a = new Acknowledge();

				// Assert sane values from client
				if (h.getSendBufferSize().longValue()<8192)
					setError(new ServiceResultException(Bad_TcpInternalError, "Peer send buffer size <  8192"));
				if (h.getReceiveBufferSize().longValue()<8192)
					setError(new ServiceResultException(Bad_TcpInternalError, "Peer recv buffer size <  8192"));
				if (getError() != null)
					logger.warn("onHello: " + getError());

				// Determine communication protocol version
				agreedProtocolVersion = Math.min(StackUtils.TCP_PROTOCOL_VERSION, h.getProtocolVersion().intValue());
				a.setProtocolVersion( UnsignedInteger.getFromBits(agreedProtocolVersion) );

				// Message size
				if (h.getMaxMessageSize()!=null && h.getMaxMessageSize().intValue()!=0) {
					if (ctx.maxSendMessageSize==0)
						ctx.maxSendMessageSize = h.getMaxMessageSize().intValue();
					else
						ctx.maxSendMessageSize = Math.min(ctx.maxSendMessageSize, h.getMaxMessageSize().intValue());
				}

				if ( binding != null ) {
					int maxMsgSize = binding.endpointAddress.getEndpointConfiguration().getMaxMessageSize();
					ctx.maxSendMessageSize = ctx.maxSendMessageSize == 0 ? maxMsgSize : Math.min(ctx.maxSendMessageSize, maxMsgSize);
					ctx.maxRecvMessageSize = ctx.maxRecvMessageSize == 0 ? maxMsgSize : Math.min(ctx.maxRecvMessageSize, maxMsgSize);
					encoderCtx.maxMessageSize = maxMsgSize;
				} else {
					encoderCtx.maxMessageSize = ctx.maxSendMessageSize;
				}

				// Chunk count
				if (h.getMaxChunkCount().intValue()!=0)
					ctx.maxSendChunkCount = Math.min(ctx.maxSendChunkCount, h.getMaxChunkCount().intValue());
				a.setMaxChunkCount( UnsignedInteger.getFromBits( ctx.maxRecvChunkCount ) );

				// Chunk sizes
				ctx.maxSendChunkSize = Math.min(h.getReceiveBufferSize().intValue(), ctx.maxSendChunkSize);
				ctx.maxRecvChunkSize = Math.min(h.getSendBufferSize().intValue(), ctx.maxRecvChunkSize);
				a.setSendBufferSize(UnsignedInteger.getFromBits(ctx.maxSendChunkSize));
				a.setReceiveBufferSize(UnsignedInteger.getFromBits(ctx.maxRecvChunkSize));

				// Send buffer (chunk) size
				ctx.maxRecvChunkSize = Math.min(ctx.maxRecvChunkSize, h.getReceiveBufferSize().intValue());
				ctx.maxSendChunkSize = Math.min(ctx.maxSendChunkSize, h.getSendBufferSize().intValue());
				setState(CloseableObjectState.Opening);

				ctx.endpointUrl = h.getEndpointUrl();

				sendAcknowledge(a);
				setState(CloseableObjectState.Open);
			}

			/**
			 *
handleOpenSecureChannelRequest.

			 *
			 * @param mb a {@link org.opcfoundation.ua.transport.tcp.nio.InputMessage} object.
			 * @throws org.opcfoundation.ua.common.ServiceResultException if any.
			 */
			protected void handleOpenSecureChannelRequest(InputMessage mb) throws ServiceResultException {
				IEncodeable msg = mb.getMessage();
				if (msg == null)
					synchronized (this) {
						Exception e = mb.getError();
						logger.warn("InputMessage has error", e);
						throw new ServiceResultException(Bad_UnexpectedError, e);
					}
				if (!(msg instanceof OpenSecureChannelRequest))
					throw new ServiceResultException(Bad_UnexpectedError);

				OpenSecureChannelRequest req		= (OpenSecureChannelRequest) msg;

				if (req.getRequestType() == SecurityTokenRequestType.Issue)
				{
					checkSecureChannelLimit();

					OpcTcpServerSecureChannel channel = new OpcTcpServerSecureChannel( this, endpointServer.secureChannelCounter.incrementAndGet() );
					logger.debug("handleOpenSecureChannelRequest: endpointServer={} SecureChannelId={}", endpointServer, channel.getSecureChannelId());
					channel.handleOpenChannel(mb, req);
				} else if (req.getRequestType() == SecurityTokenRequestType.Renew) {
					OpcTcpServerSecureChannel channel = (OpcTcpServerSecureChannel) secureChannels.get(mb.getSecureChannelId());
					if (channel==null)
						throw new ServiceResultException( Bad_SecureChannelIdInvalid );

					if (!ObjectUtils.objectEquals(req.getRequestType(), SecurityTokenRequestType.Renew)) throw new ServiceResultException(Bad_UnexpectedError);

					channel.handleRenewSecureChannelRequest(mb, req);
				}

				// Remove closed channels, should maybe be based on timestamps TODO
				Object[] channelArray = secureChannels.values().toArray();
				for (Object channel : channelArray)
					if(((AbstractServerSecureChannel)channel).getState().equals(CloseableObjectState.Closed))
						secureChannels.remove(((AbstractServerSecureChannel)channel).getSecureChannelId());

			}

			/**
			 *
handleRawChunk.

			 *
			 * @param chunk a {@link java.nio.ByteBuffer} object.
			 */
			protected void handleRawChunk(ByteBuffer chunk) {
				int type = ChunkUtils.getMessageType(chunk);
				int messageType = type & TcpMessageType.MESSAGE_TYPE_MASK;
				int chunkType = type & TcpMessageType.CHUNK_TYPE_MASK;
				if (chunkType != TcpMessageType.FINAL) {
					close();
				}
				chunk.position(8);
				try {
					if (messageType == TcpMessageType.HELLO) {
						ChunksToMessage c2m = new ChunksToMessage(ctx, encoderCtx, Hello.class, chunk);
						handleHelloMessage( (Hello) c2m.call() );
					} else if (messageType == TcpMessageType.ACKNOWLEDGE) {
						ChunksToMessage c2m = new ChunksToMessage(ctx, encoderCtx, Acknowledge.class, chunk);
						handleAcknowledgeMessage( (Acknowledge) c2m.call() );
					} else if (messageType == TcpMessageType.ERROR) {
						ChunksToMessage c2m = new ChunksToMessage(ctx, encoderCtx, ErrorMessage.class, chunk);
						handleErrorMessage( (ErrorMessage) c2m.call() );
					}
				} catch (Exception e) {
					setError( StackUtils.toServiceResultException(e) );
				}
			}

			/**
			 *
handleSecureMessage.

			 *
			 * @param mb a {@link org.opcfoundation.ua.transport.tcp.nio.InputMessage} object.
			 * @throws org.opcfoundation.ua.common.ServiceResultException if any.
			 */
			protected void handleSecureMessage(InputMessage mb) throws ServiceResultException {
				IEncodeable msg = mb.getMessage();
				int secureChannelId = mb.getSecureChannelId();
				if (logger.isDebugEnabled())
					logger.debug("handleSecureMessage: secureChannelId=" + secureChannelId + "msg=" + (msg == null ? "null" : msg.getClass().getSimpleName()));

				OpcTcpServerSecureChannel chan = (OpcTcpServerSecureChannel) secureChannels.get(secureChannelId);
				if (chan==null)
					throw new ServiceResultException( Bad_SecureChannelIdInvalid );

				if (msg instanceof OpenSecureChannelRequest) {
					OpenSecureChannelRequest req = (OpenSecureChannelRequest) msg;

					if (!ObjectUtils.objectEquals(req.getRequestType(), SecurityTokenRequestType.Renew)) throw new ServiceResultException(Bad_UnexpectedError);

					chan.handleRenewSecureChannelRequest(mb, req);
				} else if (msg instanceof CloseSecureChannelRequest) {
					chan.handleCloseSecureChannelRequest(mb, (CloseSecureChannelRequest)msg);
				} else {
					chan.handleSecureMessage(mb, msg);
				}
			}

			/**
			 *
handleSymmChunk.

			 *
			 * @param chunk a {@link java.nio.ByteBuffer} object.
			 * @throws org.opcfoundation.ua.common.ServiceResultException if any.
			 */
			protected void handleSymmChunk(ByteBuffer chunk) throws ServiceResultException {
				int secureChannelId = ChunkUtils.getSecureChannelId(chunk);
				int tokenId = ChunkUtils.getTokenId(chunk);
				chunk.rewind();

				OpcTcpServerSecureChannel channel = (OpcTcpServerSecureChannel) secureChannels.get(secureChannelId);
				if (channel==null)
					throw new ServiceResultException(StatusCodes.Bad_TcpSecureChannelUnknown);
				if (channel.getState().equals(CloseableObjectState.Closed))
					throw new ServiceResultException(StatusCodes.Bad_SecureChannelIdInvalid);
				SecurityToken token = channel.getSecurityToken(tokenId);
				if (token==null)
					throw new ServiceResultException(StatusCodes.Bad_SecureChannelTokenUnknown);
				if (!token.isValid())
					token = channel.getLatestNonExpiredToken();
				if (token==null || !token.isValid()) {
					System.err.println("Token expired");
					throw new ServiceResultException(StatusCodes.Bad_SecureChannelClosed);
				}

				SecurityToken activeToken = channel.getActiveSecurityToken();
				if (token!=activeToken) {
					logger.debug("handleSymmChunk: activeToken={}, token={}", activeToken, token);
					if (activeToken.getCreationTime() < token.getCreationTime()) {
						channel.setActiveSecurityToken(token);
					}
				}
				logger.debug("handleSymmChunk: {}", secureMessageBuilder);
				if (secureMessageBuilder!=null && !secureMessageBuilder.moreChunksRequired()) {
				      // Ensures streams eventually close by putting a "poison pill" there
				      secureMessageBuilder.softClose();
				      secureMessageBuilder = null;
				}
				if (secureMessageBuilder==null) {
					secureMessageBuilder = new SecureInputMessageBuilder(token/*channel*/, messageListener, ctx, encoderCtx, channel.recvSequenceNumber);
					logger.debug("handleSymmChunk: secureMessageBuilder={}", secureMessageBuilder);
					//				onSecureMessageBegin(secureMessageBuilder, chunk);
				}

				secureMessageBuilder.addChunk(chunk);
			}

			// Propagate connection closed/error to channels
			/** {@inheritDoc} */
			@Override
			protected synchronized void onStateTransition(CloseableObjectState oldState,
					CloseableObjectState newState) {
				super.onStateTransition(oldState, newState);
				logger.debug("onStateTransition: {}->{}", oldState, newState);

				if (newState == CloseableObjectState.Closing)
				{
					ServiceResultException err = getError();
					List<ServerSecureChannel> list = new ArrayList<ServerSecureChannel>();
					OpcTcpServerConnection.this.getSecureChannels(list);
					for (ServerSecureChannel c : list)
					{
						OpcTcpServerSecureChannel cc = (OpcTcpServerSecureChannel) c;
						if (cc != null && err!=null) cc.setError(err);
						c.close();
					}
				}
			}

			/**
			 *
sendAcknowledge.

			 *
			 * @param a a {@link org.opcfoundation.ua.transport.tcp.impl.Acknowledge} object.
			 * @throws org.opcfoundation.ua.common.ServiceResultException if any.
			 */
			protected void sendAcknowledge(Acknowledge a)
					throws ServiceResultException
			{
				ChunkFactory rawChunkFactory = new AcknowledgeChunkFactory();
				MessageToChunks mc = new MessageToChunks(a, ctx, encoderCtx, rawChunkFactory, MessageType.Encodeable);
				final ByteBuffer[] plaintexts = mc.call();
				final ByteBuffer[] chunks = rawChunkFactory.expandToCompleteChunk(plaintexts);
				assert(chunks.length==1);

				// Set header and write
				chunks[0].putInt(TcpMessageType.ACKNOWLEDGE | TcpMessageType.FINAL);
				chunks[0].rewind();
				sendChunks(chunks);
			}

			/**
			 * {@inheritDoc}
			 *
			 * Send asymmetric secure message.
			 */
			@Override
			protected int sendAsymmSecureMessage(
					final AsyncWrite msg,
					final SecurityConfiguration securityConfiguration,
					int secureChannelId,
					int requestNumber,
					AtomicInteger sendSequenceNumber)
							throws ServiceResultException
			{
				synchronized(msg) {
					if (msg.isCanceled()) return -1;
					msg.setQueued();
				}
				ChunkFactory cf = new ChunkFactory.AsymmMsgChunkFactory(ctx.maxSendChunkSize, securityConfiguration);

				MessageToChunks mc = new MessageToChunks(msg.getMessage(), ctx, encoderCtx, cf, MessageType.Message);
				final ByteBuffer[] plaintexts = mc.call();
				final ByteBuffer[] chunks = cf.expandToCompleteChunk(plaintexts);
				synchronized(msg) {
					if (msg.isCanceled()) return -1;
					msg.setWriting();
				}
				SecurityPolicy policy = securityConfiguration.getSecurityPolicy();

				startChunkSend(chunks);
				for (int i=0; i<chunks.length; i++)
				{
					ByteBuffer chunk = chunks[i];
					ByteBuffer plaintext = plaintexts[i];
					boolean finalChunk = chunk == chunks[chunks.length-1];
					chunk.rewind();
					chunk.putInt( TcpMessageType.OPEN | (finalChunk ? TcpMessageType.FINAL : TcpMessageType.CONTINUE) );
					chunk.position(8);
					chunk.putInt(secureChannelId);

					// -- Security Header --
					// Policy URI
					byte[] data = policy.getEncodedPolicyUri();
					chunk.putInt(data.length);
					chunk.put(data);

					// Sender Certificate
					data = securityConfiguration.getEncodedLocalCertificate();
					chunk.putInt( data==null ? -1 : data.length);
					if (data!=null)	chunk.put(data);

					// Recv Certificate Thumbprint
					data = securityConfiguration.getEncodedRemoteCertificateThumbprint();
					chunk.putInt( data == null ? -1 : data.length);
					if (data!=null)	chunk.put(data);

					// -- Sequence header --
					chunk.putInt(sendSequenceNumber.getAndIncrement());
					chunk.putInt(requestNumber); // Request number

					new ChunkAsymmEncryptSigner(chunk, plaintext, securityConfiguration).run();
					chunk.rewind();
					endChunkSend(chunk);
				}
				msg.setWritten();
				return chunks.length;
			}

			/**
			 * Send chunks.
			 *
			 * @param chunks a {@link java.nio.ByteBuffer} object.
			 */
			protected synchronized void sendChunks(ByteBuffer...chunks)
			{
				startChunkSend(chunks);
				for (ByteBuffer chunk : chunks)
					endChunkSend(chunk);
			}

			/**
			 *
sendError.

			 *
			 * @param e a {@link org.opcfoundation.ua.transport.tcp.impl.ErrorMessage} object.
			 * @throws org.opcfoundation.ua.common.ServiceResultException if any.
			 */
			protected void sendError(ErrorMessage e)
					throws ServiceResultException
			{
				ChunkFactory rawChunkFactory = new ErrorMessageChunkFactory();
				MessageToChunks mc = new MessageToChunks(e, ctx, encoderCtx, rawChunkFactory, MessageType.Encodeable);
				final ByteBuffer[] plaintexts = mc.call();
				final ByteBuffer[] chunks = rawChunkFactory.expandToCompleteChunk(plaintexts);
				assert(chunks.length==1);

				// Set header and write
				chunks[0].putInt(TcpMessageType.ERROR | TcpMessageType.FINAL);
				chunks[0].rewind();
				sendChunks(chunks);
			}

			/**
			 *
sendHello.

			 *
			 * @param h a {@link org.opcfoundation.ua.transport.tcp.impl.Hello} object.
			 */
			protected void sendHello(Hello h)
			{
				ctx.endpointUrl = h.getEndpointUrl();
				ChunkFactory rawChunkFactory = new ChunkFactory(ctx.maxSendChunkSize, 8, 0, 0, 0, 1, MessageSecurityMode.None, 0);
				//ChunkFactory rawChunkFactory = new HelloChunkFactory();
				MessageToChunks mc = new MessageToChunks(h, ctx, encoderCtx, rawChunkFactory, MessageType.Encodeable);
				final ByteBuffer[] plaintexts = mc.call();
				final ByteBuffer[] chunks = rawChunkFactory.expandToCompleteChunk(plaintexts);
				assert(chunks.length==1);

				// Set header and write
				chunks[0].putInt(TcpMessageType.HELLO | TcpMessageType.FINAL);
				chunks[0].rewind();
				sendChunks(chunks);
			}

			protected void sendReverseHello(ReverseHello rh) {
				ChunkFactory rawChunkFactory = new ChunkFactory(ctx.maxSendChunkSize, 8, 0, 0, 0, 1, MessageSecurityMode.None, 0);
				MessageToChunks mc = new MessageToChunks(rh, ctx, encoderCtx, rawChunkFactory, MessageType.Encodeable);
				final ByteBuffer[] plaintexts = mc.call();
				final ByteBuffer[] chunks = rawChunkFactory.expandToCompleteChunk(plaintexts);

				// Set header and write
				chunks[0].putInt(TcpMessageType.REVERSE_HELLO | TcpMessageType.FINAL);
				chunks[0].rewind();
				sendChunks(chunks);

				logger.debug("Sent ReverseHello: {}", rh);
			}

			/**
			 * {@inheritDoc}
			 *
			 * Send symmetric secure message
			 */
			@Override
			protected void sendSecureMessage(
					final AsyncWrite msg,
					final SecurityToken token,
					final int requestId,
					final int messageType,
					final AtomicInteger sendSequenceNumber
					)
			{
				assert(token!=null);
				ByteBuffer chunks[], plaintexts[];
				boolean concurrent;
				try {
					synchronized(msg) {
						if (msg.isCanceled()) return;
						msg.setQueued();
					}
					//Probably more efficient to check isTraceEnabled before executing ObjectUtils.printFieldsDeep
					if (logger.isTraceEnabled())
						logger.trace("sendSecureMessage: " + ObjectUtils.printFieldsDeep(msg.getMessage()));
					SizeCalculationOutputStream calcBuf = new SizeCalculationOutputStream();
					BinaryEncoder calc = new BinaryEncoder(calcBuf);
					calc.setEncoderContext(encoderCtx);
					calc.putMessage(msg.getMessage());
					int len = calcBuf.getLength();

					if (len>ctx.maxSendMessageSize && ctx.maxSendMessageSize!=0)
						throw new ServiceResultException(StatusCodes.Bad_TcpMessageTooLarge);

					SecurityPolicy policy = token.getSecurityPolicy();
					MessageSecurityMode mode = token.getMessageSecurityMode();
					SecurityAlgorithm symmEncryptAlgo = policy.getSymmetricEncryptionAlgorithm();
					SecurityAlgorithm symmSignAlgo = policy.getSymmetricSignatureAlgorithm();
					int cipherBlockSize = CryptoUtil.getCipherBlockSize(symmEncryptAlgo, null);
					int signatureSize = CryptoUtil.getSignatureSize(symmSignAlgo, null);
					int keySize = mode == MessageSecurityMode.SignAndEncrypt ? token.getRemoteEncryptingKey().length : 0;
					int paddingSize = mode == MessageSecurityMode.SignAndEncrypt ? keySize > 2048 ? 2 : 1 : 0;

					int maxPlaintextSize = ctx.maxSendChunkSize - 24 - paddingSize - signatureSize;
					maxPlaintextSize -= (maxPlaintextSize + paddingSize + signatureSize + 8) % cipherBlockSize;
					final int CORES = StackUtils.cores();

					int optimalPayloadSize = (len+CORES-1) / CORES;
					if (optimalPayloadSize > maxPlaintextSize)
						optimalPayloadSize = maxPlaintextSize;
					if (optimalPayloadSize < 4096)
						optimalPayloadSize = 4096;
					int optimalChunkSize = optimalPayloadSize + 24 + paddingSize + signatureSize;

					ChunkFactory cf = new ChunkFactory(
							optimalChunkSize,
							8,
							8,
							8,
							signatureSize,
							cipherBlockSize,
							mode,
							keySize);

					// Calculate chunk count
					final int count = (len + cf.maxPlaintextSize-1) / cf.maxPlaintextSize;
					if (count>ctx.maxSendChunkCount && ctx.maxSendChunkCount!=0)
						throw new ServiceResultException(StatusCodes.Bad_TcpMessageTooLarge);
					concurrent = (count > 1) && (CORES>0) && (mode != MessageSecurityMode.None);

					// Allocate chunks
					int bytesLeft = len;
					plaintexts = new ByteBuffer[count];
					chunks = new ByteBuffer[count];
					for (int i=0; i<count; i++) {
						plaintexts[i] = cf.allocate(bytesLeft);
						chunks[i] = cf.expandToCompleteChunk(plaintexts[i]);
						bytesLeft -= plaintexts[i].remaining();
					}
					assert(bytesLeft==0);

					// Start write
					synchronized(msg) {
						if (msg.isCanceled()) return;
						msg.setWriting();
					}
				} catch (ServiceResultException se) {
					msg.setError(se);
					return;
				}
				final ByteBuffer _chunks[] = chunks;
				final ByteBuffer _plaintexts[] = plaintexts;
				final int count = chunks.length;
				final boolean parallel = concurrent;

				int sequenceNumber = 0;
				synchronized(this) {
					sequenceNumber = sendSequenceNumber.getAndAdd(chunks.length);
					startChunkSend(chunks);
				}

				// Add chunk headers
				for (ByteBuffer chunk : chunks) {
					boolean finalChunk = chunk == chunks[chunks.length-1];
					chunk.rewind();
					chunk.putInt( messageType | (finalChunk ? TcpMessageType.FINAL : TcpMessageType.CONTINUE) );
					chunk.position(8);
					chunk.putInt(token.getSecureChannelId());

					// -- Security Header --
					chunk.putInt(token.getTokenId());

					// -- Sequence Header --
					chunk.putInt(sequenceNumber++);
					chunk.putInt(requestId);
				}

				// a Chunk-has-been-encoded handler
				final AtomicInteger chunksComplete = new AtomicInteger();
				ChunkListener completitionListener = new ChunkListener() {
					@Override
					public void onChunkComplete(ByteBuffer[] bufs, final int index) {
						Runnable action = new Runnable() {
							@Override
							public void run() {
								// Chunk contains message data, it needs to be encrypted and signed
								//							try {
								// Encrypt & sign
								new ChunkSymmEncryptSigner(_chunks[index], _plaintexts[index], token).run();
								_chunks[index].rewind();

								// Write chunk
								endChunkSend(_chunks[index]);

								// All chunks are completed
								if (chunksComplete.incrementAndGet()==count)
									msg.setWritten();

								//							} catch (ServiceResultException se) {
								//								msg.setError(se);
								//							}
							}};
							if (parallel && count>1) {
								StackUtils.getNonBlockingWorkExecutor().execute(action);
							} else {
								action.run();
							}
					}
				};

				// Create encoder
				ByteBufferArrayWriteable2 out = new ByteBufferArrayWriteable2(plaintexts, completitionListener);
				out.order(ByteOrder.LITTLE_ENDIAN);

				final BinaryEncoder enc = new BinaryEncoder(out);
				enc.setEncoderContext(encoderCtx);

				Runnable encoder = new Runnable() {
					@Override
					public void run() {
						try {
							enc.putMessage(msg.getMessage());

							// If the message was ActivateSessionResponse, which was Good, then mark that this
							// connection has a successful activated session (and thus is not removed when we are at
							// max connections. If we are at max connections, per 1.05 Part 4 5.5.2: "To protect
							// against misbehaving Clients and denial of service attacks, the Server shall close the
							// oldest unused SecureChannelthat has no Session assigned before reaching the maximum
							// number of supported SecureChannels. ")
							if (msg.getMessage() instanceof ActivateSessionResponse) {
								ActivateSessionResponse res = (ActivateSessionResponse) msg.getMessage();
								if ((res.getResponseHeader() != null) && (res.getResponseHeader().getServiceResult() != null) &&
										(res.getResponseHeader().getServiceResult().isGood())) {
									hasBeenSuccessfullySessionActivated.set(true);
								}
							}
						} catch (ServiceResultException e) {
							msg.setError( StackUtils.toServiceResultException(e) );
						}
					}};
					StackUtils.getBlockingWorkExecutor().execute(encoder);
			}

			/** {@inheritDoc} */
			@Override
			protected synchronized void setError(ServiceResultException e) {
				if(!hasError()){
					try {
						sendError(new ErrorMessage(e.getStatusCode(), e.getMessage()));
					} catch (ServiceResultException e1) {
						logger.warn("Could not send error message",e1);
					}
					super.setError(e);
				}
			}

			/**
			 * Put chunks into send queue. Chunks will be given a sequence number
			 * but will be flushed in endChunkSend().
			 *
			 * @param chunks a {@link java.nio.ByteBuffer} object.
			 */
			protected void startChunkSend(ByteBuffer...chunks)
			{
				synchronized(chunkIncubator) {
					for (ByteBuffer chunk : chunks)
						chunkIncubator.incubate(chunk);
				}
			}

}

Output:
【漏洞类型】业务逻辑安全漏洞

【严重性】High

【漏洞摘要】在原始代码中，`OpcTcpServer`类的`bindReverse`方法中，对`ReverseSocketHandle`的连接操作没有在连接失败时进行适当的清理和重试控制。具体来说，`ReverseSocketHandle`在连接失败时只会关闭当前连接，但不会重新尝试连接，这可能导致服务不稳定或无法处理后续请求。此外，`OpcTcpServerConnection`类的`init`方法中，对于正常连接和 ReverseHello 连接的初始化顺序处理不当，可能引发潜在的竞态条件或未定义行为。

【关键代码模式】
1. `bindReverse`方法中的`socketHandle.socket.connect(socketHandle.socketAddress);`可能失败。
2. `OpcTcpServerConnection`类的`init`方法中，`s.getInputStream().createMonitor(8, inputListener);`的调用顺序问题。
3. 未验证的用户输入或配置可能导致资源耗尽攻击。

【修复方法说明】
- 修复了`bindReverse`方法，确保在连接失败时进行适当的清理和重试控制。
- 调整了`OpcTcpServerConnection`类的`init`方法中，对于正常连接和 ReverseHello 连接的初始化顺序，确保在连接成功后才开始监听数据。
- 采用最佳实践，确保在连接失败时进行清理，并在适当条件下进行重试，以提高服务的稳定性和可靠性。