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) 2011 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.Closeable; 042import java.io.IOException; 043import java.io.Serializable; 044import java.net.*; 045import java.security.GeneralSecurityException; 046import java.util.*; 047 048import javax.net.ssl.SSLContext; 049import javax.net.ssl.SSLSocket; 050import javax.net.ssl.SSLSocketFactory; 051 052import org.dcm4che3.conf.core.api.ConfigurableClass; 053import org.dcm4che3.conf.core.api.ConfigurableProperty; 054import org.dcm4che3.conf.core.api.ConfigurableProperty.ConfigurablePropertyType; 055import org.dcm4che3.conf.core.api.ConfigurableProperty.Tag; 056import org.dcm4che3.conf.core.api.LDAP; 057import org.dcm4che3.net.proxy.ProxyManager; 058import org.dcm4che3.net.proxy.ProxyService; 059import org.dcm4che3.util.SafeClose; 060import org.dcm4che3.util.StringUtils; 061import org.slf4j.Logger; 062import org.slf4j.LoggerFactory; 063 064 065/** 066 * A DICOM Part 15, Annex H compliant class, <code>NetworkConnection</code> 067 * encapsulates the properties associated with a connection to a TCP/IP network. 068 * <p> 069 * The <i>network connection</i> describes one TCP port on one network device. 070 * This can be used for a TCP connection over which a DICOM association can be 071 * negotiated with one or more Network AEs. It specifies 8 the hostname and TCP 072 * port number. A network connection may support multiple Network AEs. The 073 * Network AE selection takes place during association negotiation based on the 074 * called and calling AE-titles. 075 * 076 * @author Gunter Zeilinger <gunterze@gmail.com> 077 */ 078@LDAP(objectClasses = {"dicomNetworkConnection", "dcmNetworkConnection"}) 079@ConfigurableClass(referable = true) 080public class Connection implements Serializable { 081 082 private static final long serialVersionUID = -7814748788035232055L; 083 084 public String getUuid() { 085 return uuid; 086 } 087 088 public void setUuid(String uuid) { 089 this.uuid = uuid; 090 } 091 092 public enum Protocol { 093 DICOM, HL7, SYSLOG_TLS, SYSLOG_UDP; 094 095 public boolean isTCP() { 096 return this != SYSLOG_UDP; 097 } 098 099 public boolean isSyslog() { 100 return this == SYSLOG_TLS || this == SYSLOG_UDP; 101 } 102 } 103 104 public static final Logger LOG = LoggerFactory.getLogger(Connection.class); 105 106 public static final String NO_TIMEOUT_STR = "0"; 107 public static final int NO_TIMEOUT = Integer.valueOf(NO_TIMEOUT_STR); 108 109 public static final int SYNCHRONOUS_MODE = 1; 110 public static final int NOT_LISTENING = -1; 111 public static final int DEF_BACKLOG = 50; 112 public static final int DEF_SOCKETDELAY = 50; 113 114 public static final String DEF_BUFFERSIZE_STR = "0"; 115 public static final int DEF_BUFFERSIZE = Integer.valueOf(DEF_BUFFERSIZE_STR); 116 117 118 public static final int DEF_MAX_PDU_LENGTH = 16378; 119 // to fit into SunJSSE TLS Application Data Length 16408 120 121 public static final String TLS_RSA_WITH_NULL_SHA = "SSL_RSA_WITH_NULL_SHA"; 122 public static final String TLS_RSA_WITH_3DES_EDE_CBC_SHA = "SSL_RSA_WITH_3DES_EDE_CBC_SHA"; 123 public static final String TLS_RSA_WITH_AES_128_CBC_SHA = "TLS_RSA_WITH_AES_128_CBC_SHA"; 124 private static final String[] DEFAULT_TLS_PROTOCOLS = { "TLSv1.2", "TLSv1.1", "TLSv1" }; 125 126 private Device device; 127 128 @ConfigurableProperty(name = "cn", label = "Name", tags = Tag.PRIMARY) 129 private String commonName; 130 131 @ConfigurableProperty(name = "dicomHostname", label = "Hostname", tags = Tag.PRIMARY) 132 private String hostname; 133 134 @ConfigurableProperty(type = ConfigurablePropertyType.OptimisticLockingHash) 135 private String olockHash; 136 137 @ConfigurableProperty(type = ConfigurablePropertyType.UUID) 138 private String uuid = UUID.randomUUID().toString(); 139 140 @ConfigurableProperty(name = "dcmBindAddress") 141 private String bindAddress; 142 143 @ConfigurableProperty(name = "dcmClientBindAddress") 144 private String clientBindAddress; 145 146 @ConfigurableProperty(name = "dcmHTTPProxy") 147 private String httpProxy; 148 149 @ConfigurableProperty( 150 name = "dicomPort", 151 defaultValue = "-1", 152 label = "Port", 153 tags = Tag.PRIMARY) 154 private int port = NOT_LISTENING; 155 156 @ConfigurableProperty(name = "dcmTCPBacklog", defaultValue = "50") 157 private int backlog = DEF_BACKLOG; 158 159 @ConfigurableProperty(name = "dcmTCPConnectTimeout", defaultValue = NO_TIMEOUT_STR) 160 private int connectTimeout; 161 162 @ConfigurableProperty(name = "dcmAARQTimeout", defaultValue = NO_TIMEOUT_STR) 163 private int requestTimeout; 164 165 @ConfigurableProperty(name = "dcmAAACTimeout", defaultValue = NO_TIMEOUT_STR) 166 private int acceptTimeout; 167 168 @ConfigurableProperty(name = "dcmARRPTimeout", defaultValue = NO_TIMEOUT_STR) 169 private int releaseTimeout; 170 171 @ConfigurableProperty(name = "dcmResponseTimeout", defaultValue = NO_TIMEOUT_STR) 172 private int responseTimeout; 173 174 @ConfigurableProperty(name = "dcmRetrieveTimeout", defaultValue = NO_TIMEOUT_STR) 175 private int retrieveTimeout; 176 177 @ConfigurableProperty(name = "dcmIdleTimeout", defaultValue = NO_TIMEOUT_STR) 178 private int idleTimeout; 179 180 @ConfigurableProperty(name = "dcmSocketTimeout", defaultValue = NO_TIMEOUT_STR) 181 private int socketTimeout; 182 183 @ConfigurableProperty(name = "dcmTCPCloseDelay", defaultValue = "50") 184 private int socketCloseDelay = DEF_SOCKETDELAY; 185 186 @ConfigurableProperty(name = "dcmTCPSendBufferSize", defaultValue = DEF_BUFFERSIZE_STR) 187 private int sendBufferSize; 188 189 @ConfigurableProperty(name = "dcmTCPReceiveBufferSize", defaultValue = DEF_BUFFERSIZE_STR) 190 private int receiveBufferSize; 191 192 @ConfigurableProperty(name = "dcmSendPDULength", defaultValue = "16378") 193 private int sendPDULength = DEF_MAX_PDU_LENGTH; 194 195 @ConfigurableProperty(name = "dcmReceivePDULength", defaultValue = "16378") 196 private int receivePDULength = DEF_MAX_PDU_LENGTH; 197 198 @ConfigurableProperty(name = "dcmMaxOpsPerformed", defaultValue = "1") 199 private int maxOpsPerformed = SYNCHRONOUS_MODE; 200 201 @ConfigurableProperty(name = "dcmMaxOpsInvoked", defaultValue = "1") 202 private int maxOpsInvoked = SYNCHRONOUS_MODE; 203 204 @ConfigurableProperty(name = "dcmPackPDV", defaultValue = "true") 205 private boolean packPDV = true; 206 207 @ConfigurableProperty(name = "dcmTCPNoDelay", defaultValue = "true") 208 private boolean tcpNoDelay = true; 209 210 @ConfigurableProperty(name = "dcmTLSNeedClientAuth", defaultValue = "true") 211 private boolean tlsNeedClientAuth = true; 212 213 @ConfigurableProperty(name = "dicomTLSCipherSuite") 214 private String[] tlsCipherSuites = {}; 215 216 @ConfigurableProperty(name = "dcmHTTPProxyProviderName", defaultValue = ProxyService.DEFAULT_PROVIDER_NAME) 217 private String httpProxyProviderName = ProxyService.DEFAULT_PROVIDER_NAME; 218 219 @ConfigurableProperty(name = "dcmHTTPProxyProviderVersion", defaultValue = ProxyService.DEFAULT_VERSION) 220 private String httpProxyProviderVersion = ProxyService.DEFAULT_VERSION; 221 222 @ConfigurableProperty(name = "dcmTLSProtocol") 223 private String[] tlsProtocols = {}; 224 225 @ConfigurableProperty(name = "dcmBlacklistedHostname") 226 private String[] blacklist = {}; 227 228 @ConfigurableProperty(name = "dicomInstalled") 229 private Boolean connectionInstalled; 230 231 @ConfigurableProperty(name = "connectionExtensions", isExtensionsProperty = true) 232 private Map<Class<? extends ConnectionExtension>, ConnectionExtension> extensions = 233 new HashMap<Class<? extends ConnectionExtension>, ConnectionExtension>(); 234 235 @ConfigurableProperty( 236 name = "dcmProtocol", 237 defaultValue = "DICOM", 238 label = "Protocol", 239 tags = Tag.PRIMARY 240 ) 241 private Protocol protocol = Protocol.DICOM; 242 243 private static final EnumMap<Protocol, TCPProtocolHandler> tcpHandlers = 244 new EnumMap<Protocol, TCPProtocolHandler>(Protocol.class); 245 private static final EnumMap<Protocol, UDPProtocolHandler> udpHandlers = 246 new EnumMap<Protocol, UDPProtocolHandler>(Protocol.class); 247 248 private transient List<InetAddress> blacklistAddrs; 249 private transient InetAddress hostAddr; 250 private transient InetAddress bindAddr; 251 private transient InetAddress clientBindAddr; 252 private transient volatile Listener listener; 253 private transient boolean rebindNeeded; 254 255 static { 256 registerTCPProtocolHandler(Protocol.DICOM, DicomProtocolHandler.INSTANCE); 257 } 258 259 public Connection() { 260 } 261 262 public Connection(String commonName, String hostname) { 263 this(commonName, hostname, NOT_LISTENING); 264 } 265 266 public Connection(String commonName, String hostname, int port) { 267 this.commonName = commonName; 268 this.hostname = hostname; 269 this.port = port; 270 } 271 272 /** 273 * 274 * @param commonName 275 * @param hostname 276 * @param port 277 * @param timeout value in seconds to assign to all timeouts 278 */ 279 public Connection(String commonName, String hostname, int port, int timeout) { 280 this(commonName, hostname, port); 281 282 setConnectTimeout(timeout); 283 setRequestTimeout(timeout); 284 setAcceptTimeout(timeout); 285 setReleaseTimeout(timeout); 286 setResponseTimeout(timeout); 287 setRetrieveTimeout(timeout); 288 setIdleTimeout(timeout); 289 setSocketTimeout(timeout); 290 } 291 292 293 public static TCPProtocolHandler registerTCPProtocolHandler( 294 Protocol protocol, TCPProtocolHandler handler) { 295 return tcpHandlers.put(protocol, handler); 296 } 297 298 public static TCPProtocolHandler unregisterTCPProtocolHandler( 299 Protocol protocol) { 300 return tcpHandlers.remove(protocol); 301 } 302 303 public static UDPProtocolHandler registerUDPProtocolHandler( 304 Protocol protocol, UDPProtocolHandler handler) { 305 return udpHandlers.put(protocol, handler); 306 } 307 308 public static UDPProtocolHandler unregisterUDPProtocolHandler( 309 Protocol protocol) { 310 return udpHandlers.remove(protocol); 311 } 312 313 /** 314 * Get the <code>Device</code> object that this Network Connection belongs 315 * to. 316 * 317 * @return Device 318 */ 319 public Device getDevice() { 320 return device; 321 } 322 323 /** 324 * Set the <code>Device</code> object that this Network Connection belongs 325 * to. 326 * 327 * @param device The owning <code>Device</code> object. 328 */ 329 public void setDevice(Device device) { 330 if (device != null && this.device != null) 331 throw new IllegalStateException("already owned by " + device); 332 this.device = device; 333 } 334 335 /** 336 * This is the DNS name for this particular connection. This is used to 337 * obtain the current IP address for connections. Hostname must be 338 * sufficiently qualified to be unambiguous for any client DNS user. 339 * 340 * @return A String containing the host name. 341 */ 342 public final String getHostname() { 343 return hostname; 344 } 345 346 public String getHttpProxyProviderName() { 347 return httpProxyProviderName; 348 } 349 350 public String getHttpProxyProviderVersion() { 351 return httpProxyProviderVersion; 352 } 353 354 /** 355 * This is the DNS name for this particular connection. This is used to 356 * obtain the current IP address for connections. Hostname must be 357 * sufficiently qualified to be unambiguous for any client DNS user. 358 * 359 * @param hostname A String containing the host name. 360 */ 361 public final void setHostname(String hostname) { 362 if (hostname != null 363 ? hostname.equals(this.hostname) 364 : this.hostname == null) 365 return; 366 367 this.hostname = hostname; 368 needRebind(); 369 } 370 371 /** 372 * Bind address of listening socket or {@code null}. If {@code null}, bind 373 * listening socket to {@link #getHostname()}. This is the default. 374 * <p> 375 * The bind address can also include system properties with the 376 * <em>${system.property}</em> syntax, that need to be resolved. 377 * 378 * @return Bind address of the connection or {@code null} 379 */ 380 public final String getBindAddress() { 381 return bindAddress; 382 } 383 384 /** 385 * Bind address of listening socket or {@code null}. If {@code null}, bind 386 * listening socket to {@link #getHostname()}. 387 * <p> 388 * The bind address can also include system properties with the 389 * <em>${system.property}</em> syntax, that will be resolved. 390 * 391 * @param bindAddress 392 * Bind address of listening socket or {@code null} 393 */ 394 public final void setBindAddress(String bindAddress) { 395 if (bindAddress != null 396 ? bindAddress.equals(this.bindAddress) 397 : this.bindAddress == null) 398 return; 399 400 this.bindAddress = bindAddress; 401 this.bindAddr = null; 402 needRebind(); 403 } 404 405 /** 406 * Bind address of outgoing connections, {@code "0.0.0.0"} or {@code null}. 407 * If {@code "0.0.0.0"} the system pick up any local ip for outgoing 408 * connections. If {@code null}, bind outgoing connections to 409 * {@link #getHostname()}. This is the default. 410 * 411 * @return Bind address of outgoing connection, {@code 0.0.0.0} or 412 * {@code null} 413 */ 414 public String getClientBindAddress() { 415 return clientBindAddress; 416 } 417 418 419 public ProxyManager getProxyManager() { 420 return ProxyService.getInstance().getProxyManager(this.httpProxyProviderName,this.httpProxyProviderVersion); 421 } 422 423 /** 424 * Bind address of outgoing connections, {@code "0.0.0.0"} or {@code null}. 425 * If {@code "0.0.0.0"} the system pick up any local ip for outgoing 426 * connections. If {@code null}, bind outgoing connections to 427 * {@link #getHostname()}. 428 * 429 * @param bindAddress Bind address of outgoing connection or {@code null} 430 */ 431 public void setClientBindAddress(String bindAddress) { 432 if (bindAddress != null 433 ? bindAddress.equals(this.clientBindAddress) 434 : this.clientBindAddress == null) 435 return; 436 437 this.clientBindAddress = bindAddress; 438 this.clientBindAddr = null; 439 } 440 441 public Protocol getProtocol() { 442 return protocol; 443 } 444 445 public void setProtocol(Protocol protocol) { 446 if (protocol == null) 447 throw new NullPointerException(); 448 449 if (this.protocol == protocol) 450 return; 451 452 this.protocol = protocol; 453 needRebind(); 454 } 455 456 boolean isRebindNeeded() { 457 return rebindNeeded; 458 } 459 460 void needRebind() { 461 this.rebindNeeded = true; 462 } 463 464 /** 465 * An arbitrary name for the Network Connections object. Can be a meaningful 466 * name or any unique sequence of characters. 467 * 468 * @return A String containing the name. 469 */ 470 public final String getCommonName() { 471 return commonName; 472 } 473 474 /** 475 * An arbitrary name for the Network Connections object. Can be a meaningful 476 * name or any unique sequence of characters. 477 * 478 * @param name A String containing the name. 479 */ 480 public final void setCommonName(String name) { 481 this.commonName = name; 482 } 483 484 /** 485 * The TCP port that the AE is listening on or <code>-1</code> for a 486 * network connection that only initiates associations. 487 * 488 * @return An int containing the port number or <code>-1</code>. 489 */ 490 public final int getPort() { 491 return port; 492 } 493 494 /** 495 * The TCP port that the AE is listening on or <code>0</code> for a 496 * network connection that only initiates associations. 497 * <p> 498 * A valid port value is between 0 and 65535. 499 * 500 * @param port The port number or <code>-1</code>. 501 */ 502 public final void setPort(int port) { 503 if (this.port == port) 504 return; 505 506 if ((port <= 0 || port > 0xFFFF) && port != NOT_LISTENING) 507 throw new IllegalArgumentException("port out of range:" + port); 508 509 this.port = port; 510 needRebind(); 511 } 512 513 public Map<Class<? extends ConnectionExtension>, ConnectionExtension> getExtensions() { 514 return extensions; 515 } 516 517 public void setExtensions(Map<Class<? extends ConnectionExtension>, ConnectionExtension> extensions) { 518 this.extensions = extensions; 519 } 520 521 public <T> T getExtension(Class<T> clazz) { 522 return (T) extensions.get(clazz); 523 } 524 525 public void addExtension(ConnectionExtension connectionExtension) { 526 connectionExtension.setConnection(this); 527 extensions.put(connectionExtension.getClass(), connectionExtension); 528 } 529 530 public void setHttpProxyProviderName(String httpProxyProviderName) { 531 this.httpProxyProviderName = httpProxyProviderName; 532 } 533 534 public void setHttpProxyProviderVersion(String httpProxyProviderVersion) { 535 this.httpProxyProviderVersion = httpProxyProviderVersion; 536 } 537 538 private void reconfigureExtensions(Connection from) { 539 for (Iterator<Class<? extends ConnectionExtension>> it = 540 extensions.keySet().iterator(); it.hasNext(); ) { 541 if (!from.extensions.containsKey(it.next())) 542 it.remove(); 543 } 544 for (ConnectionExtension src : from.extensions.values()) { 545 Class<? extends ConnectionExtension> clazz = src.getClass(); 546 ConnectionExtension ext = extensions.get(clazz); 547 if (ext == null) 548 try { 549 addExtension(ext = clazz.newInstance()); 550 } catch (Exception e) { 551 throw new RuntimeException( 552 "Failed to instantiate " + clazz.getName(), e); 553 } 554 ext.reconfigure(src); 555 } 556 } 557 558 public final String getHttpProxy() { 559 return httpProxy; 560 } 561 562 public final void setHttpProxy(String proxy) { 563 this.httpProxy = proxy; 564 } 565 566 public final boolean useHttpProxy() { 567 return httpProxy != null; 568 } 569 570 public final boolean isServer() { 571 return port > 0; 572 } 573 574 public final int getBacklog() { 575 return backlog; 576 } 577 578 public final void setBacklog(int backlog) { 579 if (this.backlog == backlog) 580 return; 581 582 if (backlog < 1) 583 throw new IllegalArgumentException("backlog: " + backlog); 584 585 this.backlog = backlog; 586 needRebind(); 587 } 588 589 public final int getConnectTimeout() { 590 return connectTimeout; 591 } 592 593 public final void setConnectTimeout(int timeout) { 594 if (timeout < 0) 595 throw new IllegalArgumentException("timeout: " + timeout); 596 this.connectTimeout = timeout; 597 } 598 599 /** 600 * Timeout in ms for receiving A-ASSOCIATE-RQ, 5000 by default 601 * 602 * @return An int value containing the milliseconds. 603 */ 604 public final int getRequestTimeout() { 605 return requestTimeout; 606 } 607 608 /** 609 * Timeout in ms for receiving A-ASSOCIATE-RQ, 5000 by default 610 * 611 * @param timeout An int value containing the milliseconds. 612 */ 613 public final void setRequestTimeout(int timeout) { 614 if (timeout < 0) 615 throw new IllegalArgumentException("timeout: " + timeout); 616 this.requestTimeout = timeout; 617 } 618 619 public final int getAcceptTimeout() { 620 return acceptTimeout; 621 } 622 623 public final void setAcceptTimeout(int timeout) { 624 if (timeout < 0) 625 throw new IllegalArgumentException("timeout: " + timeout); 626 this.acceptTimeout = timeout; 627 } 628 629 630 /** 631 * Timeout in ms for receiving A-RELEASE-RP, 5000 by default. 632 * 633 * @return An int value containing the milliseconds. 634 */ 635 public final int getReleaseTimeout() { 636 return releaseTimeout; 637 } 638 639 /** 640 * Timeout in ms for receiving A-RELEASE-RP, 5000 by default. 641 * 642 * @param timeout An int value containing the milliseconds. 643 */ 644 public final void setReleaseTimeout(int timeout) { 645 if (timeout < 0) 646 throw new IllegalArgumentException("timeout: " + timeout); 647 this.releaseTimeout = timeout; 648 } 649 650 /** 651 * Delay in ms for Socket close after sending A-ABORT, 50ms by default. 652 * 653 * @return An int value containing the milliseconds. 654 */ 655 public final int getSocketCloseDelay() { 656 return socketCloseDelay; 657 } 658 659 /** 660 * Delay in ms for Socket close after sending A-ABORT, 50ms by default. 661 * 662 * @param delay An int value containing the milliseconds. 663 */ 664 public final void setSocketCloseDelay(int delay) { 665 if (delay < 0) 666 throw new IllegalArgumentException("delay: " + delay); 667 this.socketCloseDelay = delay; 668 } 669 670 public final void setResponseTimeout(int timeout) { 671 this.responseTimeout = timeout; 672 } 673 674 public final int getResponseTimeout() { 675 return responseTimeout; 676 } 677 678 public final int getRetrieveTimeout() { 679 return retrieveTimeout; 680 } 681 682 public final void setRetrieveTimeout(int timeout) { 683 this.retrieveTimeout = timeout; 684 } 685 686 public final int getIdleTimeout() { 687 return idleTimeout; 688 } 689 690 public final void setIdleTimeout(int idleTimeout) { 691 this.idleTimeout = idleTimeout; 692 } 693 694 public final int getSocketTimeout() { 695 return socketTimeout; 696 } 697 698 public final void setSocketTimeout(int socketTimeout) { 699 if (socketTimeout < 0) 700 throw new IllegalArgumentException("timeout: " + socketTimeout); 701 this.socketTimeout = socketTimeout; 702 } 703 704 /** 705 * The TLS CipherSuites that are supported on this particular connection. 706 * TLS CipherSuites shall be described using an RFC-2246 string 707 * representation (e.g. 'SSL_RSA_WITH_3DES_EDE_CBC_SHA') 708 * 709 * @return A String array containing the supported cipher suites 710 */ 711 public String[] getTlsCipherSuites() { 712 return tlsCipherSuites; 713 } 714 715 /** 716 * The TLS CipherSuites that are supported on this particular connection. 717 * TLS CipherSuites shall be described using an RFC-2246 string 718 * representation (e.g. 'SSL_RSA_WITH_3DES_EDE_CBC_SHA') 719 * 720 * @param tlsCipherSuites A String array containing the supported cipher suites 721 */ 722 public void setTlsCipherSuites(String... tlsCipherSuites) { 723 if (Arrays.equals(this.tlsCipherSuites, tlsCipherSuites)) 724 return; 725 726 this.tlsCipherSuites = tlsCipherSuites; 727 needRebind(); 728 } 729 730 public final boolean isTls() { 731 return tlsCipherSuites.length > 0; 732 } 733 734 public final String[] tlsProtocols() { 735 return tlsProtocols.length != 0 ? tlsProtocols : DEFAULT_TLS_PROTOCOLS; 736 } 737 738 public final String[] getTlsProtocols() { 739 return tlsProtocols; 740 } 741 742 public final void setTlsProtocols(String... tlsProtocols) { 743 if (Arrays.equals(this.tlsProtocols, tlsProtocols)) 744 return; 745 746 this.tlsProtocols = tlsProtocols; 747 needRebind(); 748 } 749 750 public final boolean isTlsNeedClientAuth() { 751 return tlsNeedClientAuth; 752 } 753 754 public final void setTlsNeedClientAuth(boolean tlsNeedClientAuth) { 755 if (this.tlsNeedClientAuth == tlsNeedClientAuth) 756 return; 757 758 this.tlsNeedClientAuth = tlsNeedClientAuth; 759 needRebind(); 760 } 761 762 /** 763 * Get the SO_RCVBUF socket value in KB. 764 * 765 * @return An int value containing the buffer size in KB. 766 */ 767 public final int getReceiveBufferSize() { 768 return receiveBufferSize; 769 } 770 771 /** 772 * Set the SO_RCVBUF socket option to specified value in KB. 773 * 774 * @param size An int value containing the buffer size in KB. 775 */ 776 public final void setReceiveBufferSize(int size) { 777 if (size < 0) 778 throw new IllegalArgumentException("size: " + size); 779 this.receiveBufferSize = size; 780 } 781 782 /** 783 * Get the SO_SNDBUF socket option value in KB, 784 * 785 * @return An int value containing the buffer size in KB. 786 */ 787 public final int getSendBufferSize() { 788 return sendBufferSize; 789 } 790 791 /** 792 * Set the SO_SNDBUF socket option to specified value in KB, 793 * 794 * @param size An int value containing the buffer size in KB. 795 */ 796 public final void setSendBufferSize(int size) { 797 if (size < 0) 798 throw new IllegalArgumentException("size: " + size); 799 this.sendBufferSize = size; 800 } 801 802 public final int getSendPDULength() { 803 return sendPDULength; 804 } 805 806 public final void setSendPDULength(int sendPDULength) { 807 this.sendPDULength = sendPDULength; 808 } 809 810 public final int getReceivePDULength() { 811 return receivePDULength; 812 } 813 814 public final void setReceivePDULength(int receivePDULength) { 815 this.receivePDULength = receivePDULength; 816 } 817 818 public final int getMaxOpsPerformed() { 819 return maxOpsPerformed; 820 } 821 822 public final void setMaxOpsPerformed(int maxOpsPerformed) { 823 this.maxOpsPerformed = maxOpsPerformed; 824 } 825 826 public final int getMaxOpsInvoked() { 827 return maxOpsInvoked; 828 } 829 830 public final void setMaxOpsInvoked(int maxOpsInvoked) { 831 this.maxOpsInvoked = maxOpsInvoked; 832 } 833 834 public final boolean isPackPDV() { 835 return packPDV; 836 } 837 838 public final void setPackPDV(boolean packPDV) { 839 this.packPDV = packPDV; 840 } 841 842 /** 843 * Determine if this network connection is using Nagle's algorithm as part 844 * of its network communication. 845 * 846 * @return boolean True if TCP no delay (disable Nagle's algorithm) is used. 847 */ 848 public final boolean isTcpNoDelay() { 849 return tcpNoDelay; 850 } 851 852 /** 853 * Set whether or not this network connection should use Nagle's algorithm 854 * as part of its network communication. 855 * 856 * @param tcpNoDelay boolean True if TCP no delay (disable Nagle's algorithm) 857 * should be used. 858 */ 859 public final void setTcpNoDelay(boolean tcpNoDelay) { 860 this.tcpNoDelay = tcpNoDelay; 861 } 862 863 /** 864 * True if the Network Connection is installed on the network. If not 865 * present, information about the installed status of the Network Connection 866 * is inherited from the device. 867 * 868 * @return boolean True if the NetworkConnection is installed on the 869 * network. 870 */ 871 public boolean isInstalled() { 872 return device != null && device.isInstalled() 873 && (connectionInstalled == null || connectionInstalled.booleanValue()); 874 } 875 876 public Boolean getConnectionInstalled() { 877 return connectionInstalled; 878 } 879 880 /** 881 * True if the Network Connection is installed on the network. If not 882 * present, information about the installed status of the Network Connection 883 * is inherited from the device. 884 * 885 * @param installed True if the NetworkConnection is installed on the network. 886 * @throws GeneralSecurityException 887 */ 888 public void setConnectionInstalled(Boolean installed) { 889 if (this.connectionInstalled == installed) 890 return; 891 892 boolean prev = isInstalled(); 893 this.connectionInstalled = installed; 894 if (isInstalled() != prev) 895 needRebind(); 896 } 897 898 synchronized void rebind() throws IOException, GeneralSecurityException { 899 unbind(); 900 bind(); 901 } 902 903 /** 904 * Get a list of IP addresses from which we should ignore connections. 905 * Useful in an environment that utilizes a load balancer. In the case of a 906 * TCP ping from a load balancing switch, we don't want to spin off a new 907 * thread and try to negotiate an association. 908 * 909 * @return Returns the list of IP addresses which should be ignored. 910 */ 911 public final String[] getBlacklist() { 912 return blacklist; 913 } 914 915 /** 916 * Set a list of IP addresses from which we should ignore connections. 917 * Useful in an environment that utilizes a load balancer. In the case of a 918 * TCP ping from a load balancing switch, we don't want to spin off a new 919 * thread and try to negotiate an association. 920 * 921 * @param blacklist the list of IP addresses which should be ignored. 922 */ 923 public final void setBlacklist(String[] blacklist) { 924 this.blacklist = blacklist; 925 this.blacklistAddrs = null; 926 } 927 928 @Override 929 public String toString() { 930 return promptTo(new StringBuilder(), "").toString(); 931 } 932 933 public StringBuilder promptTo(StringBuilder sb, String indent) { 934 String indent2 = indent + " "; 935 StringUtils.appendLine(sb, indent, "Connection[cn: ", commonName); 936 StringUtils.appendLine(sb, indent2, "host: ", hostname); 937 StringUtils.appendLine(sb, indent2, "port: ", port); 938 StringUtils.appendLine(sb, indent2, "ciphers: ", Arrays.toString(tlsCipherSuites)); 939 StringUtils.appendLine(sb, indent2, "installed: ", getConnectionInstalled()); 940 return sb.append(indent).append(']'); 941 } 942 943 void setSocketSendOptions(Socket s) throws SocketException { 944 int size = s.getSendBufferSize(); 945 if (sendBufferSize == 0) { 946 sendBufferSize = size; 947 } else if (sendBufferSize != size) { 948 s.setSendBufferSize(sendBufferSize); 949 sendBufferSize = s.getSendBufferSize(); 950 } 951 if (s.getTcpNoDelay() != tcpNoDelay) { 952 s.setTcpNoDelay(tcpNoDelay); 953 } 954 if (s.getSoTimeout() != socketTimeout) { 955 s.setSoTimeout(socketTimeout); 956 } 957 } 958 959 private void setReceiveBufferSize(Socket s) throws SocketException { 960 int size = s.getReceiveBufferSize(); 961 if (receiveBufferSize == 0) { 962 receiveBufferSize = size; 963 } else if (receiveBufferSize != size) { 964 s.setReceiveBufferSize(receiveBufferSize); 965 receiveBufferSize = s.getReceiveBufferSize(); 966 } 967 } 968 969 void setReceiveBufferSize(ServerSocket ss) throws SocketException { 970 int size = ss.getReceiveBufferSize(); 971 if (receiveBufferSize == 0) { 972 receiveBufferSize = size; 973 } else if (receiveBufferSize != size) { 974 ss.setReceiveBufferSize(receiveBufferSize); 975 receiveBufferSize = ss.getReceiveBufferSize(); 976 } 977 } 978 979 public void setReceiveBufferSize(DatagramSocket ds) throws SocketException { 980 int size = ds.getReceiveBufferSize(); 981 if (receiveBufferSize == 0) { 982 receiveBufferSize = size; 983 } else if (receiveBufferSize != size) { 984 ds.setReceiveBufferSize(receiveBufferSize); 985 receiveBufferSize = ds.getReceiveBufferSize(); 986 } 987 } 988 989 private InetAddress hostAddr() throws UnknownHostException { 990 if (hostAddr == null && hostname != null) 991 hostAddr = InetAddress.getByName(hostname); 992 993 return hostAddr; 994 } 995 996 private InetAddress bindAddr() throws UnknownHostException { 997 if (bindAddress == null) 998 return hostAddr(); 999 1000 if (bindAddr == null) { 1001 String resolvedBindAddress = StringUtils.replaceSystemProperties(bindAddress); 1002 bindAddr = InetAddress.getByName(resolvedBindAddress); 1003 } 1004 1005 return bindAddr; 1006 } 1007 1008 private InetAddress clientBindAddr() throws UnknownHostException { 1009 if (clientBindAddress == null) 1010 return hostAddr(); 1011 1012 if (clientBindAddr == null) 1013 clientBindAddr = InetAddress.getByName(clientBindAddress); 1014 1015 return clientBindAddr; 1016 } 1017 1018 private List<InetAddress> blacklistAddrs() { 1019 if (blacklistAddrs == null) { 1020 blacklistAddrs = new ArrayList<InetAddress>(blacklist.length); 1021 for (String hostname : blacklist) 1022 try { 1023 blacklistAddrs.add(InetAddress.getByName(hostname)); 1024 } catch (UnknownHostException e) { 1025 LOG.warn("Failed to lookup InetAddress of " + hostname, e); 1026 } 1027 } 1028 return blacklistAddrs; 1029 } 1030 1031 1032 public InetSocketAddress getEndPoint() throws UnknownHostException { 1033 return new InetSocketAddress(hostAddr(), port); 1034 } 1035 1036 public InetSocketAddress getBindPoint() throws UnknownHostException { 1037 return new InetSocketAddress(bindAddr(), port); 1038 } 1039 1040 public InetSocketAddress getClientBindPoint() throws UnknownHostException { 1041 return new InetSocketAddress(clientBindAddr(), 0); 1042 } 1043 1044 private void checkInstalled() { 1045 if (!isInstalled()) 1046 throw new IllegalStateException("Not installed"); 1047 } 1048 1049 private void checkCompatible(Connection remoteConn) throws IncompatibleConnectionException { 1050 if (!isCompatible(remoteConn)) 1051 throw new IncompatibleConnectionException(remoteConn.toString()); 1052 } 1053 1054 /** 1055 * Bind this network connection to a TCP port and start a server socket 1056 * accept loop. 1057 * 1058 * @throws IOException If there is a problem with the network interaction. 1059 * @throws GeneralSecurityException 1060 */ 1061 public synchronized boolean bind() throws IOException, GeneralSecurityException { 1062 if (!(isInstalled() && isServer())) { 1063 rebindNeeded = false; 1064 return false; 1065 } 1066 if (device == null) 1067 throw new IllegalStateException("Not attached to Device"); 1068 if (isListening()) 1069 throw new IllegalStateException("Already listening - " + listener); 1070 if (protocol.isTCP()) { 1071 TCPProtocolHandler handler = tcpHandlers.get(protocol); 1072 if (handler == null) 1073 throw new IllegalStateException("No TCP Protocol Handler for protocol " + protocol); 1074 listener = new TCPListener(this, handler); 1075 } else { 1076 UDPProtocolHandler handler = udpHandlers.get(protocol); 1077 if (handler == null) 1078 throw new IllegalStateException("No UDP Protocol Handler for protocol " + protocol); 1079 listener = new UDPListener(this, handler); 1080 } 1081 rebindNeeded = false; 1082 return true; 1083 } 1084 1085 public final boolean isListening() { 1086 return listener != null; 1087 } 1088 1089 public boolean isBlackListed(InetAddress ia) { 1090 return blacklistAddrs().contains(ia); 1091 } 1092 1093 public synchronized void unbind() { 1094 Closeable tmp = listener; 1095 if (tmp == null) 1096 return; 1097 listener = null; 1098 try { 1099 tmp.close(); 1100 } catch (Throwable e) { 1101 // Ignore errors when closing the server socket. 1102 } 1103 } 1104 1105 public Socket connect(Connection remoteConn) 1106 throws IOException, IncompatibleConnectionException, GeneralSecurityException { 1107 checkInstalled(); 1108 if (!protocol.isTCP()) 1109 throw new IllegalStateException("Not a TCP Connection"); 1110 checkCompatible(remoteConn); 1111 SocketAddress bindPoint = getClientBindPoint(); 1112 String remoteHostname = remoteConn.getHostname(); 1113 int remotePort = remoteConn.getPort(); 1114 LOG.info("Initiate connection from {} to {}:{}", 1115 bindPoint, remoteHostname, remotePort); 1116 Socket s = new Socket(); 1117 ConnectionMonitor monitor = device != null 1118 ? device.getConnectionMonitor() 1119 : null; 1120 try { 1121 s.bind(bindPoint); 1122 setReceiveBufferSize(s); 1123 setSocketSendOptions(s); 1124 String remoteProxy = remoteConn.getHttpProxy(); 1125 if (remoteProxy != null) { 1126 String userauth = null; 1127 String[] ss = StringUtils.split(remoteProxy, '@'); 1128 if (ss.length > 1) { 1129 userauth = ss[0]; 1130 remoteProxy = ss[1]; 1131 } 1132 ss = StringUtils.split(remoteProxy, ':'); 1133 int proxyPort = ss.length > 1 ? Integer.parseInt(ss[1]) : 8080; 1134 s.connect(new InetSocketAddress(ss[0], proxyPort), connectTimeout); 1135 try { 1136 getProxyManager().doProxyHandshake(s, remoteHostname, remotePort, userauth, 1137 connectTimeout); 1138 } catch (IOException e) { 1139 SafeClose.close(s); 1140 throw e; 1141 } 1142 } else { 1143 s.connect(remoteConn.getEndPoint(), connectTimeout); 1144 } 1145 if (isTls()) 1146 s = createTLSSocket(s, remoteConn); 1147 if (monitor != null) 1148 monitor.onConnectionEstablished(this, remoteConn, s); 1149 LOG.info("Established connection {}", s); 1150 return s; 1151 } catch (GeneralSecurityException e) { 1152 if (monitor != null) 1153 monitor.onConnectionFailed(this, remoteConn, s, e); 1154 SafeClose.close(s); 1155 throw e; 1156 } catch (IOException e) { 1157 if (monitor != null) 1158 monitor.onConnectionFailed(this, remoteConn, s, e); 1159 SafeClose.close(s); 1160 throw new IOException("Error while trying to establish connection "+getHostname()+" -> "+remoteHostname+":"+remotePort,e); 1161 } 1162 } 1163 1164 public DatagramSocket createDatagramSocket() throws IOException { 1165 checkInstalled(); 1166 if (protocol.isTCP()) 1167 throw new IllegalStateException("Not a UDP Connection"); 1168 1169 DatagramSocket ds = new DatagramSocket(getClientBindPoint()); 1170 int size = ds.getSendBufferSize(); 1171 if (sendBufferSize == 0) { 1172 sendBufferSize = size; 1173 } else if (sendBufferSize != size) { 1174 ds.setSendBufferSize(sendBufferSize); 1175 sendBufferSize = ds.getSendBufferSize(); 1176 } 1177 return ds; 1178 } 1179 1180 public Listener getListener() { 1181 return listener; 1182 } 1183 1184// private void doProxyHandshake(Socket s, String hostname, int port, 1185// String userauth, int connectTimeout) throws IOException { 1186// 1187// StringBuilder request = new StringBuilder(128); 1188// request.append("CONNECT ") 1189// .append(hostname).append(':').append(port) 1190// .append(" HTTP/1.1\r\nHost: ") 1191// .append(hostname).append(':').append(port); 1192// if (userauth != null) { 1193// byte[] b = userauth.getBytes("UTF-8"); 1194// char[] base64 = new char[(b.length + 2) / 3 * 4]; 1195// Base64.encode(b, 0, b.length, base64, 0); 1196// request.append("\r\nProxy-Authorization: basic ") 1197// .append(base64); 1198// } 1199// request.append("\r\n\r\n"); 1200// OutputStream out = s.getOutputStream(); 1201// out.write(request.toString().getBytes("US-ASCII")); 1202// out.flush(); 1203// 1204// s.setSoTimeout(connectTimeout); 1205// @SuppressWarnings("resource") 1206// String response = new HTTPResponse(s).toString(); 1207// s.setSoTimeout(socketTimeout); 1208// if (!response.startsWith("HTTP/1.1 2")) 1209// throw new IOException("Unable to tunnel through " + s 1210// + ". Proxy returns \"" + response + '\"'); 1211// } 1212// 1213// private static class HTTPResponse extends ByteArrayOutputStream { 1214// 1215// private final String rsp; 1216// 1217// public HTTPResponse(Socket s) throws IOException { 1218// super(64); 1219// InputStream in = s.getInputStream(); 1220// boolean eol = false; 1221// int b; 1222// while ((b = in.read()) != -1) { 1223// write(b); 1224// if (b == '\n') { 1225// if (eol) { 1226// rsp = new String(super.buf, 0, super.count, "US-ASCII"); 1227// return; 1228// } 1229// eol = true; 1230// } else if (b != '\r') { 1231// eol = false; 1232// } 1233// } 1234// throw new IOException("Unexpected EOF from " + s); 1235// } 1236// 1237// @Override 1238// public String toString() { 1239// return rsp; 1240// } 1241// } 1242 1243 private SSLSocket createTLSSocket(Socket s, Connection remoteConn) 1244 throws GeneralSecurityException, IOException { 1245 SSLContext sslContext = device.sslContext(); 1246 SSLSocketFactory sf = sslContext.getSocketFactory(); 1247 SSLSocket ssl = (SSLSocket) sf.createSocket(s, 1248 remoteConn.getHostname(), remoteConn.getPort(), true); 1249 ssl.setEnabledProtocols( 1250 intersect(remoteConn.tlsProtocols(), tlsProtocols())); 1251 ssl.setEnabledCipherSuites( 1252 intersect(remoteConn.tlsCipherSuites, tlsCipherSuites)); 1253 ssl.startHandshake(); 1254 return ssl; 1255 } 1256 1257 public void close(Socket s) { 1258 LOG.info("Close connection {}", s); 1259 SafeClose.close(s); 1260 } 1261 1262 public boolean isCompatible(Connection remoteConn) { 1263 if (remoteConn.protocol != protocol) 1264 return false; 1265 1266 if (!protocol.isTCP()) 1267 return true; 1268 1269 if (!isTls()) 1270 return !remoteConn.isTls(); 1271 1272 return hasCommon(remoteConn.tlsProtocols(), tlsProtocols()) 1273 && hasCommon(remoteConn.tlsCipherSuites, tlsCipherSuites); 1274 } 1275 1276 private boolean hasCommon(String[] ss1, String[] ss2) { 1277 for (String s1 : ss1) 1278 for (String s2 : ss2) 1279 if (s1.equals(s2)) 1280 return true; 1281 return false; 1282 } 1283 1284 private static String[] intersect(String[] ss1, String[] ss2) { 1285 String[] ss = new String[Math.min(ss1.length, ss2.length)]; 1286 int len = 0; 1287 for (String s1 : ss1) 1288 for (String s2 : ss2) 1289 if (s1.equals(s2)) { 1290 ss[len++] = s1; 1291 break; 1292 } 1293 ; 1294 if (len == ss.length) 1295 return ss; 1296 1297 String[] dest = new String[len]; 1298 System.arraycopy(ss, 0, dest, 0, len); 1299 return dest; 1300 } 1301 1302 boolean equalsRDN(Connection other) { 1303 return commonName != null 1304 ? commonName.equals(other.commonName) 1305 : other.commonName == null 1306 && hostname.equals(other.hostname) 1307 && port == other.port 1308 && protocol == other.protocol; 1309 } 1310 1311 public String getOlockHash() { 1312 return olockHash; 1313 } 1314 1315 public void setOlockHash(String olockHash) { 1316 this.olockHash = olockHash; 1317 } 1318 1319 void reconfigure(Connection from) { 1320 setOlockHash(from.olockHash); 1321 setUuid(from.uuid); 1322 setCommonName(from.commonName); 1323 setHostname(from.hostname); 1324 setPort(from.port); 1325 setBindAddress(from.bindAddress); 1326 setClientBindAddress(from.clientBindAddress); 1327 setProtocol(from.protocol); 1328 setHttpProxy(from.httpProxy); 1329 setBacklog(from.backlog); 1330 setConnectTimeout(from.connectTimeout); 1331 setRequestTimeout(from.requestTimeout); 1332 setAcceptTimeout(from.acceptTimeout); 1333 setReleaseTimeout(from.releaseTimeout); 1334 setResponseTimeout(from.responseTimeout); 1335 setRetrieveTimeout(from.retrieveTimeout); 1336 setIdleTimeout(from.idleTimeout); 1337 setSocketTimeout(from.socketTimeout); 1338 setSocketCloseDelay(from.socketCloseDelay); 1339 setSendBufferSize(from.sendBufferSize); 1340 setReceiveBufferSize(from.receiveBufferSize); 1341 setSendPDULength(from.sendPDULength); 1342 setReceivePDULength(from.receivePDULength); 1343 setMaxOpsPerformed(from.maxOpsPerformed); 1344 setMaxOpsPerformed(from.maxOpsInvoked); 1345 setPackPDV(from.packPDV); 1346 setTcpNoDelay(from.tcpNoDelay); 1347 setTlsNeedClientAuth(from.tlsNeedClientAuth); 1348 setTlsCipherSuites(from.tlsCipherSuites); 1349 setTlsProtocols(from.tlsProtocols); 1350 setBlacklist(from.blacklist); 1351 setConnectionInstalled(from.connectionInstalled); 1352 reconfigureExtensions(from); 1353 } 1354 1355}