001/* ***** BEGIN LICENSE BLOCK ***** 002 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 003 * 004 * The contents of this file are subject to the Mozilla Public License Version 005 * 1.1 (the "License"); you may not use this file except in compliance with 006 * the License. You may obtain a copy of the License at 007 * http://www.mozilla.org/MPL/ 008 * 009 * Software distributed under the License is distributed on an "AS IS" basis, 010 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 011 * for the specific language governing rights and limitations under the 012 * License. 013 * 014 * The Original Code is part of dcm4che, an implementation of DICOM(TM) in 015 * Java(TM), hosted at https://github.com/gunterze/dcm4che. 016 * 017 * The Initial Developer of the Original Code is 018 * Agfa Healthcare. 019 * Portions created by the Initial Developer are Copyright (C) 2013 020 * the Initial Developer. All Rights Reserved. 021 * 022 * Contributor(s): 023 * See @authors listed below 024 * 025 * Alternatively, the contents of this file may be used under the terms of 026 * either the GNU General Public License Version 2 or later (the "GPL"), or 027 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 028 * in which case the provisions of the GPL or the LGPL are applicable instead 029 * of those above. If you wish to allow use of your version of this file only 030 * under the terms of either the GPL or the LGPL, and not to allow others to 031 * use your version of this file under the terms of the MPL, indicate your 032 * decision by deleting the provisions above and replace them with the notice 033 * and other provisions required by the GPL or the LGPL. If you do not delete 034 * the provisions above, a recipient may use your version of this file under 035 * the terms of any one of the MPL, the GPL or the LGPL. 036 * 037 * ***** END LICENSE BLOCK ***** */ 038 039package org.dcm4che3.net; 040 041import java.io.IOException; 042import java.net.ServerSocket; 043import java.net.Socket; 044import java.net.SocketAddress; 045import java.security.GeneralSecurityException; 046 047import javax.net.ssl.SSLContext; 048import javax.net.ssl.SSLServerSocket; 049import javax.net.ssl.SSLServerSocketFactory; 050import javax.net.ssl.SSLSocket; 051 052/** 053 * @author Gunter Zeilinger <gunterze@gmail.com> 054 * 055 */ 056class TCPListener implements Listener { 057 058 private final Connection conn; 059 private final TCPProtocolHandler handler; 060 private final ServerSocket ss; 061 062 public TCPListener(Connection conn, TCPProtocolHandler handler) 063 throws IOException, GeneralSecurityException { 064 try { 065 066 this.conn = conn; 067 this.handler = handler; 068 ss = conn.isTls() ? createTLSServerSocket(conn) : new ServerSocket(); 069 conn.setReceiveBufferSize(ss); 070 ss.bind(conn.getBindPoint(), conn.getBacklog()); 071 conn.getDevice().execute(new Runnable(){ 072 073 @Override 074 public void run() { listen(); } 075 }); 076 077 } catch (IOException e) { 078 throw new IOException("Unable to start TCPListener on "+conn.getHostname()+":"+conn.getPort(), e); 079 } 080 } 081 082 private ServerSocket createTLSServerSocket(Connection conn) 083 throws IOException, GeneralSecurityException { 084 SSLContext sslContext = conn.getDevice().sslContext(); 085 SSLServerSocketFactory ssf = sslContext.getServerSocketFactory(); 086 SSLServerSocket ss = (SSLServerSocket) ssf.createServerSocket(); 087 ss.setEnabledProtocols(conn.tlsProtocols()); 088 ss.setEnabledCipherSuites(conn.getTlsCipherSuites()); 089 ss.setNeedClientAuth(conn.isTlsNeedClientAuth()); 090 return ss; 091 } 092 093 private void listen() { 094 SocketAddress sockAddr = ss.getLocalSocketAddress(); 095 Connection.LOG.info("Start TCP Listener on {}", sockAddr); 096 try { 097 while (!ss.isClosed()) { 098 Connection.LOG.debug("Wait for connection on {}", sockAddr); 099 Socket s = ss.accept(); 100 ConnectionMonitor monitor = conn.getDevice() != null 101 ? conn.getDevice().getConnectionMonitor() 102 : null; 103 if (conn.isBlackListed(s.getInetAddress())) { 104 if (monitor != null) 105 monitor.onConnectionRejectedBlacklisted(conn, s); 106 Connection.LOG.info("Reject blacklisted connection {}", s); 107 conn.close(s); 108 } else { 109 try { 110 conn.setSocketSendOptions(s); 111 if (s instanceof SSLSocket) { 112 ((SSLSocket) s).startHandshake(); 113 } 114 } catch (Throwable e) { 115 if (monitor != null) 116 monitor.onConnectionRejected(conn, s, e); 117 Connection.LOG.warn("Reject connection {}:",s, e); 118 conn.close(s); 119 continue; 120 } 121 122 if (monitor != null) 123 monitor.onConnectionAccepted(conn, s); 124 Connection.LOG.info("Accept connection {}", s); 125 try { 126 handler.onAccept(conn, s); 127 } catch (Throwable e) { 128 Connection.LOG.warn("Exception on accepted connection {}:",s, e); 129 conn.close(s); 130 } 131 } 132 } 133 } catch (Throwable e) { 134 if (!ss.isClosed()) // ignore exception caused by close() 135 Connection.LOG.error("Exception on listing on {}:", sockAddr, e); 136 } 137 Connection.LOG.info("Stop TCP Listener on {}", sockAddr); 138 } 139 140 141 @Override 142 public SocketAddress getEndPoint() { 143 return ss.getLocalSocketAddress(); 144 } 145 146 @Override 147 public void close() throws IOException { 148 try { 149 ss.close(); 150 } catch (Throwable e) { 151 // Ignore errors when closing the server socket. 152 } 153 } 154}