001: package org.jacorb.orb.iiop;
002:
003: /*
004: * JacORB - a free Java ORB
005: *
006: * Copyright (C) 1999-2004 Gerald Brose
007: *
008: * This library is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU Library General Public
010: * License as published by the Free Software Foundation; either
011: * version 2 of the License, or (at your option) any later version.
012: *
013: * This library is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016: * Library General Public License for more details.
017: *
018: * You should have received a copy of the GNU Library General Public
019: * License along with this library; if not, write to the Free
020: * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
021: *
022: */
023:
024: import java.io.*;
025: import java.net.InetAddress;
026: import java.net.Socket;
027: import java.net.ServerSocket;
028: import java.net.UnknownHostException;
029:
030: import org.omg.ETF.*;
031: import org.omg.CSIIOP.*;
032: import org.omg.SSLIOP.*;
033:
034: import org.apache.avalon.framework.configuration.*;
035:
036: import org.jacorb.orb.BasicAdapter;
037: import org.jacorb.orb.factory.*;
038: import org.jacorb.orb.listener.AcceptorExceptionEvent;
039: import org.jacorb.orb.listener.AcceptorExceptionListener;
040: import org.jacorb.orb.listener.DefaultAcceptorExceptionListener;
041: import org.jacorb.orb.listener.NullAcceptorExceptionListener;
042: import org.jacorb.orb.listener.SSLListenerUtil;
043: import org.jacorb.orb.listener.TCPConnectionEvent;
044: import org.jacorb.orb.listener.TCPConnectionListener;
045: import org.jacorb.orb.etf.ProtocolAddressBase;
046:
047: /**
048: * @author Andre Spiegel
049: * @version $Id: IIOPListener.java,v 1.37 2006/07/19 15:25:46 alphonse.bendt Exp $
050: */
051: public class IIOPListener extends org.jacorb.orb.etf.ListenerBase {
052: /** the maximum set of security options supported by the SSL mechanism */
053: private static final int MAX_SSL_OPTIONS = Integrity.value
054: | Confidentiality.value | DetectReplay.value
055: | DetectMisordering.value | EstablishTrustInTarget.value
056: | EstablishTrustInClient.value;
057:
058: /** the minimum set of security options supported by the SSL mechanism
059: * which cannot be turned off, so they are always supported
060: */
061: private static final int MIN_SSL_OPTIONS = Integrity.value
062: | DetectReplay.value | DetectMisordering.value;
063:
064: private SocketFactoryManager socketFactoryManager = null;
065:
066: private SSLAcceptor sslAcceptor = null;
067: private LoopbackAcceptor loopbackAcceptor;
068:
069: private boolean supportSSL = false;
070: private int serverTimeout = 0;
071: private IIOPAddress address = null;
072: private IIOPAddress sslAddress = null;
073: private int target_supports = 0;
074: private int target_requires = 0;
075: private boolean generateSSLComponents = true;
076:
077: public void configure(Configuration configuration)
078: throws ConfigurationException {
079: super .configure(configuration);
080:
081: socketFactoryManager = orb.getTransportManager()
082: .getSocketFactoryManager();
083:
084: String address_str = configuration.getAttribute("OAAddress",
085: null);
086: if (address_str != null) {
087: ProtocolAddressBase addr = orb.createAddress(address_str);
088: if (addr instanceof IIOPAddress) {
089: address = (IIOPAddress) addr;
090: }
091: } else {
092: int oaPort = configuration.getAttributeAsInteger("OAPort",
093: 0);
094: String oaHost = configuration.getAttribute("OAIAddr", "");
095: address = new IIOPAddress(oaHost, oaPort);
096: }
097:
098: if (address != null) {
099: address.configure(configuration);
100: }
101:
102: address_str = configuration.getAttribute("OASSLAddress", null);
103: if (address_str != null) {
104: ProtocolAddressBase addr = orb.createAddress(address_str);
105: if (addr instanceof IIOPAddress) {
106: sslAddress = (IIOPAddress) addr;
107: }
108: } else {
109: int sslPort = configuration.getAttributeAsInteger(
110: "OASSLPort", 0);
111: String sslHost = configuration.getAttribute("OAIAddr", "");
112: sslAddress = new IIOPAddress(sslHost, sslPort);
113: }
114:
115: if (sslAddress != null) {
116: sslAddress.configure(configuration);
117: }
118:
119: serverTimeout = configuration.getAttributeAsInteger(
120: "jacorb.connection.server.timeout", 0);
121:
122: supportSSL = configuration.getAttribute(
123: "jacorb.security.support_ssl", "off").equals("on");
124:
125: target_supports = Integer.parseInt(configuration.getAttribute(
126: "jacorb.security.ssl.server.supported_options", "20"),
127: 16); // 16 is the base as we take the string value as hex!
128:
129: // make sure that the minimum options are always in the set of supported options
130: target_supports |= MIN_SSL_OPTIONS;
131:
132: target_requires = Integer
133: .parseInt(configuration.getAttribute(
134: "jacorb.security.ssl.server.required_options",
135: "0"), 16);
136:
137: generateSSLComponents = configuration
138: .getAttribute(
139: "jacorb.security.ssl_components_added_by_ior_interceptor",
140: "off").equals("off");
141:
142: if (!isSSLRequired()
143: || configuration
144: .getAttributeAsBoolean(
145: "jacorb.security.ssl.always_open_unsecured_address",
146: false)) {
147: acceptor = new Acceptor("ServerSocketListener");
148: ((Acceptor) acceptor).init();
149: }
150:
151: if (supportSSL) {
152: sslAcceptor = new SSLAcceptor();
153: sslAcceptor.init();
154: }
155:
156: loopbackAcceptor = new LoopbackAcceptor();
157:
158: profile = createAddressProfile();
159: }
160:
161: /**
162: * {@inheritDoc}
163: */
164: public void listen() {
165: super .listen();
166:
167: if (sslAcceptor != null) {
168: sslAcceptor.start();
169: }
170:
171: loopbackAcceptor.start();
172: }
173:
174: /**
175: * {@inheritDoc}
176: */
177: public void destroy() {
178: loopbackAcceptor.terminate();
179:
180: if (sslAcceptor != null) {
181: sslAcceptor.terminate();
182: }
183:
184: super .destroy();
185: }
186:
187: public void renewSSLServerSocket() {
188: if (sslAcceptor != null) {
189: sslAcceptor.renewSSLServerSocket();
190: }
191: }
192:
193: // internal methods below this line
194:
195: /**
196: * Returns true if this Listener should support SSL connections.
197: */
198: private boolean isSSLSupported() {
199: return supportSSL;
200: }
201:
202: /**
203: * Returns true if this Listener should <i>require</i> SSL, and not
204: * offer plain connections.
205: */
206: private boolean isSSLRequired() {
207: if (isSSLSupported()) {
208: // the following is used as a bit mask to check if any SSL
209: // options are required
210: return ((target_requires & MAX_SSL_OPTIONS) != 0);
211: }
212: return false;
213: }
214:
215: /**
216: * Creates a new IIOPProfile that describes this transport address.
217: */
218: private IIOPProfile createAddressProfile()
219: throws ConfigurationException {
220: if (acceptor != null) {
221: if (address.getPort() == 0) {
222: address.setPort(((Acceptor) acceptor).getLocalAddress()
223: .getPort());
224: } else {
225: if (logger.isDebugEnabled()) {
226: logger.debug("Using port " + address.getPort());
227: }
228: }
229: } else if (sslAcceptor == null) {
230: throw new org.omg.CORBA.INITIALIZE(
231: "no acceptors found, cannot create address profile");
232: }
233:
234: IIOPProfile result = new IIOPProfile(address, null);
235: if (sslAcceptor != null && generateSSLComponents) {
236: result.addComponent(TAG_SSL_SEC_TRANS.value, createSSL(),
237: SSLHelper.class);
238: }
239:
240: result.configure(configuration);
241: return result;
242: }
243:
244: private SSL createSSL() {
245: return new SSL((short) target_supports,
246: (short) target_requires, (short) sslAcceptor
247: .getLocalAddress().getPort());
248: }
249:
250: /**
251: * Creates an ETF connection around a live socket and passes it
252: * up to the ORB, using either the upcall mechanism or the
253: * polling mechanism.
254: */
255: private void deliverConnection(Socket socket, boolean isSSL) {
256: Connection result = null;
257: try {
258: result = createServerConnection(socket, isSSL);
259: } catch (IOException ex) {
260: if (logger.isErrorEnabled()) {
261: logger
262: .error(
263: "Could not create connection from socket: ",
264: ex);
265: }
266: return;
267: }
268:
269: deliverConnection(result);
270: }
271:
272: /**
273: * Template method to create a server-side ETF Connection.
274: * This can be overridden by subclasses to pass a different
275: * kind of Connection up to the ORB.
276: */
277: protected Connection createServerConnection(Socket socket,
278: boolean is_ssl) throws IOException {
279: final TCPConnectionListener tcpListener = socketFactoryManager
280: .getTCPListener();
281: ServerIIOPConnection result = new ServerIIOPConnection(socket,
282: is_ssl, tcpListener);
283:
284: if (tcpListener.isListenerEnabled()) {
285: tcpListener.connectionOpened(new TCPConnectionEvent(result,
286: socket.getInetAddress().getHostAddress(), socket
287: .getPort(), socket.getLocalPort(),
288: getLocalhost()));
289: }
290:
291: try {
292: result.configure(configuration);
293: } catch (ConfigurationException ce) {
294: throw new org.omg.CORBA.INTERNAL("ConfigurationException: "
295: + ce.toString());
296: }
297: return result;
298: }
299:
300: // Acceptor classes below this line
301:
302: private String getLocalhost() {
303: String localhost;
304:
305: try {
306: localhost = InetAddress.getLocalHost().getHostAddress();
307: } catch (UnknownHostException uhe) {
308: if (logger.isDebugEnabled()) {
309: logger
310: .debug("Unable to resolve local host - using default 127.0.0.1");
311: }
312:
313: localhost = "127.0.0.1";
314: }
315: return localhost;
316: }
317:
318: public class Acceptor extends
319: org.jacorb.orb.etf.ListenerBase.Acceptor {
320: private final Object runSync = new Object();
321: private final boolean keepAlive;
322: protected ServerSocket serverSocket;
323:
324: protected boolean terminated = false;
325:
326: /**
327: * <code>acceptorExceptionListener</code> is listener to be notified
328: * of terminal failures by the acceptor.
329: */
330: private final AcceptorExceptionListener acceptorExceptionListener;
331:
332: /**
333: * <code>firstPass</code> stores whether we have already done
334: * one pass in the run method i.e. have accepted one socket.
335: */
336: private boolean firstPass;
337:
338: protected Acceptor(String name) {
339: super ();
340: // initialization deferred to init() method due to JDK bug
341: setDaemon(true);
342: setName(name);
343:
344: keepAlive = configuration.getAttributeAsBoolean(
345: "jacorb.connection.server.keepalive", false);
346:
347: try {
348: acceptorExceptionListener = (AcceptorExceptionListener) configuration
349: .getAttributeAsObject(
350: "jacorb.acceptor_exception_listener",
351: DefaultAcceptorExceptionListener.class
352: .getName());
353: } catch (ConfigurationException e) {
354: logger.error(
355: "couldn't create a AcceptorExceptionListener",
356: e);
357: throw new IllegalArgumentException(
358: "wrong configuration: " + e);
359: }
360: }
361:
362: public void init() {
363: serverSocket = createServerSocket();
364:
365: if (logger.isDebugEnabled()) {
366: logger.debug("Created socket listener on "
367: + serverSocket.getInetAddress() + ":"
368: + serverSocket.getLocalPort());
369: }
370: }
371:
372: /**
373: * template method that is invoked during the accept loop
374: * before an incoming connection is accepted.
375: */
376: protected void beginAccept() throws InterruptedException {
377: // empty to be overridden
378: }
379:
380: /**
381: * template method that is invoked during the accept loop
382: * after an incoming connection was processed.
383: */
384: protected void endAccept() {
385: // empty to be overridden
386: }
387:
388: public final void run() {
389: while (true) {
390: try {
391: synchronized (runSync) {
392: if (terminated) {
393: if (logger.isInfoEnabled()) {
394: logger.info("Listener exited");
395: }
396:
397: return;
398: }
399: }
400:
401: beginAccept();
402:
403: try {
404: Socket socket = serverSocket.accept();
405: setup(socket);
406: deliverConnection(socket);
407: firstPass = true;
408: } finally {
409: endAccept();
410: }
411: } catch (Exception e) {
412: synchronized (runSync) {
413: handleExceptionInRunLoop(e, terminated);
414: }
415: }
416: }
417: }
418:
419: /**
420: * template method that is invoked when
421: * an exception occurs during the run loop.
422: */
423: private void handleExceptionInRunLoop(Exception exception,
424: boolean isTerminated) {
425: if (!isTerminated) {
426: logger.warn("unexpected exception in runloop",
427: exception);
428: }
429:
430: acceptorExceptionListener
431: .exceptionCaught(new AcceptorExceptionEvent(this ,
432: ((BasicAdapter) up).getORB(), exception));
433: }
434:
435: protected void doHandleExceptionInRunLoop(Exception exception,
436: boolean isTerminated) {
437: // empty to be overridden
438: }
439:
440: /**
441: * Terminates this Acceptor by closing the ServerSocket and interrupting
442: * the run loop.
443: */
444: public void terminate() {
445: synchronized (runSync) {
446: terminated = true;
447: }
448:
449: try {
450: serverSocket.close();
451: } catch (java.io.IOException e) {
452: if (logger.isWarnEnabled()) {
453: logger.warn("failed to close ServerSocket", e);
454: }
455: }
456:
457: interrupt();
458: }
459:
460: public IIOPAddress getLocalAddress() {
461: IIOPAddress addr = new IIOPAddress(serverSocket
462: .getInetAddress().toString(), serverSocket
463: .getLocalPort());
464:
465: if (configuration != null) {
466: try {
467: addr.configure(configuration);
468: } catch (ConfigurationException ce) {
469: if (logger.isWarnEnabled()) {
470: logger.warn("ConfigurationException", ce);
471: }
472: }
473: }
474: return addr;
475: }
476:
477: /**
478: * Template method that creates the server socket.
479: */
480: protected ServerSocket createServerSocket() {
481: try {
482: return socketFactoryManager.getServerSocketFactory()
483: .createServerSocket(address.getPort(), 20,
484: address.getConfiguredHost());
485: } catch (IOException ex) {
486: logger.warn("could not create server socket port: "
487: + address.getPort() + " host: "
488: + address.getConfiguredHost(), ex);
489: throw new org.omg.CORBA.INITIALIZE(
490: "Could not create server socket ("
491: + address.getPort() + "): "
492: + ex.toString());
493: }
494: }
495:
496: /**
497: * Template method that sets up the socket right after the
498: * connection has been established. Subclass implementations
499: * may implement their own logic by overriding doSetup
500: */
501: protected final void setup(Socket socket) throws IOException {
502: socket.setSoTimeout(serverTimeout);
503: socket.setKeepAlive(keepAlive);
504:
505: SSLListenerUtil.addListener(orb, socket);
506:
507: doSetup(socket);
508: }
509:
510: protected void doSetup(Socket socket) {
511: // empty to be overridden
512: }
513:
514: protected void deliverConnection(Socket socket) {
515: IIOPListener.this .deliverConnection(socket, false);
516: }
517:
518: /**
519: * <code>getAcceptorSocketLoop</code> returns whether we have done
520: * a socket accept. This is useful for the AcceptorExceptionListener
521: * so it can determine for instance if the SSLException has been
522: * thrown before any connections have been made or after x amount of
523: * connections - this allows differentiation between initial
524: * configuration failure and failure to connect to a single client.
525: *
526: * @return a <code>boolean</code> value
527: */
528: public boolean getAcceptorSocketLoop() {
529: return firstPass;
530: }
531: }
532:
533: private class SSLAcceptor extends Acceptor {
534: private final Object renewSocketSync = new Object();
535: private boolean renewingSocket;
536: private boolean blockedOnAccept;
537: private final int soTimeout;
538:
539: private SSLAcceptor() {
540: super ("SSLServerSocketListener");
541:
542: soTimeout = configuration.getAttributeAsInteger(
543: "jacorb.listener.server_socket_timeout", 0);
544: }
545:
546: protected ServerSocket createServerSocket() {
547: try {
548: int port = sslAddress.getPort();
549: InetAddress configuredHost = sslAddress
550: .getConfiguredHost();
551: ServerSocket socket = newServerSocket(port,
552: configuredHost);
553:
554: return socket;
555: } catch (IOException e) {
556: logger.warn("could not create SSL server socket", e);
557: throw new org.omg.CORBA.INITIALIZE(
558: "Could not create SSL server socket");
559: }
560: }
561:
562: private ServerSocket newServerSocket(int port,
563: InetAddress configuredHost) throws IOException {
564: ServerSocket socket = socketFactoryManager
565: .getSSLServerSocketFactory().createServerSocket(
566: port, 20, configuredHost);
567:
568: if (soTimeout > 0) {
569: serverSocket.setSoTimeout(soTimeout);
570: }
571:
572: return socket;
573: }
574:
575: protected void deliverConnection(Socket socket) {
576: IIOPListener.this .deliverConnection(socket, true);
577: }
578:
579: protected void beginAccept() throws InterruptedException {
580: synchronized (renewSocketSync) {
581: //wait 'til new socket has been created
582: while (renewingSocket) {
583: renewSocketSync.wait();
584: }
585:
586: //tell renewing thread we're busy
587: blockedOnAccept = true;
588: }
589: }
590:
591: protected void endAccept() {
592: synchronized (renewSocketSync) {
593: //tell renewing thread we're done
594: blockedOnAccept = false;
595: renewSocketSync.notifyAll();
596: }
597: }
598:
599: protected void doHandleExceptionInRunLoop(Exception exception,
600: boolean isTerminated) {
601: // we are only interested in InterruptedExceptions here
602: if (!(exception instanceof InterruptedException)) {
603: return;
604: }
605:
606: // accept took too long
607: if (soTimeout > 0) {
608: return;
609: }
610:
611: // Thread was interrupted
612: if (isTerminated) {
613: return;
614: }
615:
616: logger
617: .warn(
618: "InterruptedException should only occur if soTimeout > 0",
619: exception);
620: }
621:
622: /**
623: * Replace an existing SSLServersocket by a new one (possibly using
624: * new key material) that's opened on the same address/port
625: * combination.
626: */
627: public void renewSSLServerSocket() {
628: //remember old settings
629: int oldPort = serverSocket.getLocalPort();
630: InetAddress oldAddress = serverSocket.getInetAddress();
631:
632: try {
633: synchronized (renewSocketSync) {
634: //tell listener we want to renew. Attention: this needs to
635: //be done prior to waiting, otherwise we may starve
636: renewingSocket = true;
637:
638: //wait 'til listener isn't accepting anymore
639: while (blockedOnAccept) {
640: try {
641: renewSocketSync.wait();
642: } catch (InterruptedException e) {
643: // ignored
644: }
645: }
646: }
647:
648: try {
649: //close old socket
650: serverSocket.close();
651: } catch (Exception e) {
652: logger.warn("failed to close SSLServerSocket", e);
653: }
654:
655: try {
656: //create new ServerSocket with old host/port
657: serverSocket = newServerSocket(oldPort, oldAddress);
658: } catch (Exception e) {
659: logger.warn("Failed to create SSLServerSocket", e);
660: throw new org.omg.CORBA.INITIALIZE(
661: "Could not create SSL server socket: " + e);
662: }
663: } finally {
664: // leave critical section
665: synchronized (renewSocketSync) {
666: //tell listener we're done
667: renewingSocket = false;
668: renewSocketSync.notifyAll();
669: }
670: }
671: }
672: }
673:
674: private class LoopbackAcceptor implements IIOPLoopback {
675: public void start() {
676: IIOPLoopbackRegistry.getRegistry().register(getAddress(),
677: this );
678: }
679:
680: public void terminate() {
681: IIOPLoopbackRegistry.getRegistry().unregister(getAddress());
682: }
683:
684: public void initLoopback(final IIOPLoopbackInputStream lis,
685: final IIOPLoopbackOutputStream los) {
686: final IIOPLoopbackConnection connection = new IIOPLoopbackConnection(
687: lis, los);
688: deliverConnection(connection);
689: }
690:
691: private IIOPAddress getAddress() {
692: final IIOPProfile profile = (IIOPProfile) IIOPListener.this .profile;
693: return (IIOPAddress) profile.getAddress();
694: }
695: }
696: }
|