From aabab2794464366eb302dadaff655c2eba3980b0 Mon Sep 17 00:00:00 2001 From: chegar Date: Thu, 16 Apr 2009 17:42:00 +0100 Subject: [PATCH] 4927640: Implementation of the sctp protocol Summary: An implementation-specific API for the Stream Control Transmission Protocol Reviewed-by: alanb, michaelm, jccollet --- make/com/sun/Makefile | 2 +- make/com/sun/nio/Makefile | 38 + make/com/sun/nio/sctp/Exportedfiles.gmk | 39 + make/com/sun/nio/sctp/FILES_c.gmk | 31 + make/com/sun/nio/sctp/FILES_java.gmk | 67 + make/com/sun/nio/sctp/Makefile | 80 ++ make/com/sun/nio/sctp/mapfile-vers | 49 + make/docs/NON_CORE_PKGS.gmk | 6 +- make/java/nio/mapfile-linux | 1 + make/java/nio/mapfile-solaris | 1 + .../nio/sctp/AbstractNotificationHandler.java | 140 +++ .../classes/com/sun/nio/sctp/Association.java | 104 ++ .../sctp/AssociationChangeNotification.java | 92 ++ .../com/sun/nio/sctp/HandlerResult.java | 47 + .../sun/nio/sctp/IllegalReceiveException.java | 49 + .../sun/nio/sctp/IllegalUnbindException.java | 49 + .../sun/nio/sctp/InvalidStreamException.java | 48 + .../classes/com/sun/nio/sctp/MessageInfo.java | 303 +++++ .../com/sun/nio/sctp/Notification.java | 45 + .../com/sun/nio/sctp/NotificationHandler.java | 65 + .../sctp/PeerAddressChangeNotification.java | 107 ++ .../classes/com/sun/nio/sctp/SctpChannel.java | 859 +++++++++++++ .../com/sun/nio/sctp/SctpMultiChannel.java | 731 +++++++++++ .../com/sun/nio/sctp/SctpServerChannel.java | 421 +++++++ .../com/sun/nio/sctp/SctpSocketOption.java | 38 + .../nio/sctp/SctpStandardSocketOption.java | 419 +++++++ .../sun/nio/sctp/SendFailedNotification.java | 89 ++ .../sun/nio/sctp/ShutdownNotification.java | 47 + .../com/sun/nio/sctp/package-info.java | 75 ++ .../sun/nio/ch/SctpMessageInfoImpl.java | 170 +++ .../sun/nio/ch/SctpStdSocketOption.java | 76 ++ .../classes/sun/nio/ch/SctpAssocChange.java | 120 ++ .../sun/nio/ch/SctpAssociationImpl.java | 52 + .../classes/sun/nio/ch/SctpChannelImpl.java | 1073 +++++++++++++++++ .../sun/nio/ch/SctpMultiChannelImpl.java | 959 +++++++++++++++ src/solaris/classes/sun/nio/ch/SctpNet.java | 268 ++++ .../classes/sun/nio/ch/SctpNotification.java | 37 + .../sun/nio/ch/SctpPeerAddrChange.java | 119 ++ .../sun/nio/ch/SctpResultContainer.java | 126 ++ .../classes/sun/nio/ch/SctpSendFailed.java | 110 ++ .../sun/nio/ch/SctpServerChannelImpl.java | 429 +++++++ .../classes/sun/nio/ch/SctpShutdown.java | 69 ++ .../sun/nio/ch/SctpSocketDispatcher.java | 68 ++ src/solaris/native/sun/nio/ch/Sctp.h | 329 +++++ .../native/sun/nio/ch/SctpChannelImpl.c | 594 +++++++++ src/solaris/native/sun/nio/ch/SctpNet.c | 609 ++++++++++ .../native/sun/nio/ch/SctpServerChannelImpl.c | 53 + .../classes/sun/nio/ch/SctpChannelImpl.java | 150 +++ .../sun/nio/ch/SctpMultiChannelImpl.java | 137 +++ .../sun/nio/ch/SctpServerChannelImpl.java | 102 ++ test/com/sun/nio/sctp/MessageInfoTests.java | 148 +++ test/com/sun/nio/sctp/SctpChannel/Bind.java | 350 ++++++ .../com/sun/nio/sctp/SctpChannel/Connect.java | 283 +++++ .../com/sun/nio/sctp/SctpChannel/Receive.java | 361 ++++++ test/com/sun/nio/sctp/SctpChannel/Send.java | 419 +++++++ .../sun/nio/sctp/SctpChannel/Shutdown.java | 274 +++++ .../sctp/SctpChannel/SocketOptionTests.java | 157 +++ test/com/sun/nio/sctp/SctpChannel/Util.java | 138 +++ .../sun/nio/sctp/SctpMultiChannel/Send.java | 320 +++++ .../sun/nio/sctp/SctpMultiChannel/Util.java | 138 +++ .../nio/sctp/SctpServerChannel/Accept.java | 274 +++++ .../SctpServerChannel/NonBlockingAccept.java | 230 ++++ .../sun/nio/sctp/SctpServerChannel/Util.java | 138 +++ 63 files changed, 12920 insertions(+), 2 deletions(-) create mode 100644 make/com/sun/nio/Makefile create mode 100644 make/com/sun/nio/sctp/Exportedfiles.gmk create mode 100644 make/com/sun/nio/sctp/FILES_c.gmk create mode 100644 make/com/sun/nio/sctp/FILES_java.gmk create mode 100644 make/com/sun/nio/sctp/Makefile create mode 100644 make/com/sun/nio/sctp/mapfile-vers create mode 100644 src/share/classes/com/sun/nio/sctp/AbstractNotificationHandler.java create mode 100644 src/share/classes/com/sun/nio/sctp/Association.java create mode 100644 src/share/classes/com/sun/nio/sctp/AssociationChangeNotification.java create mode 100644 src/share/classes/com/sun/nio/sctp/HandlerResult.java create mode 100644 src/share/classes/com/sun/nio/sctp/IllegalReceiveException.java create mode 100644 src/share/classes/com/sun/nio/sctp/IllegalUnbindException.java create mode 100644 src/share/classes/com/sun/nio/sctp/InvalidStreamException.java create mode 100644 src/share/classes/com/sun/nio/sctp/MessageInfo.java create mode 100644 src/share/classes/com/sun/nio/sctp/Notification.java create mode 100644 src/share/classes/com/sun/nio/sctp/NotificationHandler.java create mode 100644 src/share/classes/com/sun/nio/sctp/PeerAddressChangeNotification.java create mode 100644 src/share/classes/com/sun/nio/sctp/SctpChannel.java create mode 100644 src/share/classes/com/sun/nio/sctp/SctpMultiChannel.java create mode 100644 src/share/classes/com/sun/nio/sctp/SctpServerChannel.java create mode 100644 src/share/classes/com/sun/nio/sctp/SctpSocketOption.java create mode 100644 src/share/classes/com/sun/nio/sctp/SctpStandardSocketOption.java create mode 100644 src/share/classes/com/sun/nio/sctp/SendFailedNotification.java create mode 100644 src/share/classes/com/sun/nio/sctp/ShutdownNotification.java create mode 100644 src/share/classes/com/sun/nio/sctp/package-info.java create mode 100644 src/share/classes/sun/nio/ch/SctpMessageInfoImpl.java create mode 100644 src/share/classes/sun/nio/ch/SctpStdSocketOption.java create mode 100644 src/solaris/classes/sun/nio/ch/SctpAssocChange.java create mode 100644 src/solaris/classes/sun/nio/ch/SctpAssociationImpl.java create mode 100644 src/solaris/classes/sun/nio/ch/SctpChannelImpl.java create mode 100644 src/solaris/classes/sun/nio/ch/SctpMultiChannelImpl.java create mode 100644 src/solaris/classes/sun/nio/ch/SctpNet.java create mode 100644 src/solaris/classes/sun/nio/ch/SctpNotification.java create mode 100644 src/solaris/classes/sun/nio/ch/SctpPeerAddrChange.java create mode 100644 src/solaris/classes/sun/nio/ch/SctpResultContainer.java create mode 100644 src/solaris/classes/sun/nio/ch/SctpSendFailed.java create mode 100644 src/solaris/classes/sun/nio/ch/SctpServerChannelImpl.java create mode 100644 src/solaris/classes/sun/nio/ch/SctpShutdown.java create mode 100644 src/solaris/classes/sun/nio/ch/SctpSocketDispatcher.java create mode 100644 src/solaris/native/sun/nio/ch/Sctp.h create mode 100644 src/solaris/native/sun/nio/ch/SctpChannelImpl.c create mode 100644 src/solaris/native/sun/nio/ch/SctpNet.c create mode 100644 src/solaris/native/sun/nio/ch/SctpServerChannelImpl.c create mode 100644 src/windows/classes/sun/nio/ch/SctpChannelImpl.java create mode 100644 src/windows/classes/sun/nio/ch/SctpMultiChannelImpl.java create mode 100644 src/windows/classes/sun/nio/ch/SctpServerChannelImpl.java create mode 100644 test/com/sun/nio/sctp/MessageInfoTests.java create mode 100644 test/com/sun/nio/sctp/SctpChannel/Bind.java create mode 100644 test/com/sun/nio/sctp/SctpChannel/Connect.java create mode 100644 test/com/sun/nio/sctp/SctpChannel/Receive.java create mode 100644 test/com/sun/nio/sctp/SctpChannel/Send.java create mode 100644 test/com/sun/nio/sctp/SctpChannel/Shutdown.java create mode 100644 test/com/sun/nio/sctp/SctpChannel/SocketOptionTests.java create mode 100644 test/com/sun/nio/sctp/SctpChannel/Util.java create mode 100644 test/com/sun/nio/sctp/SctpMultiChannel/Send.java create mode 100644 test/com/sun/nio/sctp/SctpMultiChannel/Util.java create mode 100644 test/com/sun/nio/sctp/SctpServerChannel/Accept.java create mode 100644 test/com/sun/nio/sctp/SctpServerChannel/NonBlockingAccept.java create mode 100644 test/com/sun/nio/sctp/SctpServerChannel/Util.java diff --git a/make/com/sun/Makefile b/make/com/sun/Makefile index 188e73670..a6549e2a4 100644 --- a/make/com/sun/Makefile +++ b/make/com/sun/Makefile @@ -41,7 +41,7 @@ endif # Omit mirror since it's built with the apt tool. SUBDIRS = $(SCRIPT_SUBDIR) image security crypto/provider jndi jmx \ java inputmethods org xml rowset net/httpserver net/ssl demo \ - tools jarsigner tracing servicetag + tools jarsigner tracing servicetag nio all build clean clobber:: $(SUBDIRS-loop) diff --git a/make/com/sun/nio/Makefile b/make/com/sun/nio/Makefile new file mode 100644 index 000000000..74151db7d --- /dev/null +++ b/make/com/sun/nio/Makefile @@ -0,0 +1,38 @@ +# +# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Sun designates this +# particular file as subject to the "Classpath" exception as provided +# by Sun in the LICENSE file that accompanied this code. +# +# This code 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 General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# + +# +# Makefile for com.sun.nio packages. +# + +BUILDDIR = ../../.. +include $(BUILDDIR)/common/Defs.gmk + +SUBDIRS = sctp +all build clean clobber:: + $(SUBDIRS-loop) + +clean clobber:: + $(RM) -r $(CLASSDESTDIR)/com/sun/nio diff --git a/make/com/sun/nio/sctp/Exportedfiles.gmk b/make/com/sun/nio/sctp/Exportedfiles.gmk new file mode 100644 index 000000000..df60b97c3 --- /dev/null +++ b/make/com/sun/nio/sctp/Exportedfiles.gmk @@ -0,0 +1,39 @@ +# +# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Sun designates this +# particular file as subject to the "Classpath" exception as provided +# by Sun in the LICENSE file that accompanied this code. +# +# This code 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 General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# + +# +# These are the names of Java classes for which we will make .h files. +# + +ifneq ($(PLATFORM), windows) +FILES_export = \ + sun/nio/ch/SctpAssocChange.java \ + sun/nio/ch/SctpChannelImpl.java \ + sun/nio/ch/SctpNet.java \ + sun/nio/ch/SctpPeerAddrChange.java \ + sun/nio/ch/SctpResultContainer.java \ + sun/nio/ch/SctpServerChannelImpl.java \ + sun/nio/ch/SctpStdSocketOption.java +endif diff --git a/make/com/sun/nio/sctp/FILES_c.gmk b/make/com/sun/nio/sctp/FILES_c.gmk new file mode 100644 index 000000000..55c687aea --- /dev/null +++ b/make/com/sun/nio/sctp/FILES_c.gmk @@ -0,0 +1,31 @@ +# +# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Sun designates this +# particular file as subject to the "Classpath" exception as provided +# by Sun in the LICENSE file that accompanied this code. +# +# This code 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 General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# + +ifneq ($(PLATFORM),windows) +FILES_c = \ + SctpNet.c \ + SctpChannelImpl.c \ + SctpServerChannelImpl.c +endif diff --git a/make/com/sun/nio/sctp/FILES_java.gmk b/make/com/sun/nio/sctp/FILES_java.gmk new file mode 100644 index 000000000..8be820fae --- /dev/null +++ b/make/com/sun/nio/sctp/FILES_java.gmk @@ -0,0 +1,67 @@ +# +# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Sun designates this +# particular file as subject to the "Classpath" exception as provided +# by Sun in the LICENSE file that accompanied this code. +# +# This code 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 General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +FILES_java = \ + com/sun/nio/sctp/AbstractNotificationHandler.java \ + com/sun/nio/sctp/Association.java \ + com/sun/nio/sctp/AssociationChangeNotification.java \ + com/sun/nio/sctp/HandlerResult.java \ + com/sun/nio/sctp/IllegalReceiveException.java \ + com/sun/nio/sctp/IllegalUnbindException.java \ + com/sun/nio/sctp/InvalidStreamException.java \ + com/sun/nio/sctp/MessageInfo.java \ + com/sun/nio/sctp/Notification.java \ + com/sun/nio/sctp/NotificationHandler.java \ + com/sun/nio/sctp/PeerAddressChangeNotification.java \ + com/sun/nio/sctp/SctpChannel.java \ + com/sun/nio/sctp/SctpMultiChannel.java \ + com/sun/nio/sctp/SctpServerChannel.java \ + com/sun/nio/sctp/SctpSocketOption.java \ + com/sun/nio/sctp/SctpStandardSocketOption.java \ + com/sun/nio/sctp/SendFailedNotification.java \ + com/sun/nio/sctp/ShutdownNotification.java \ + \ + sun/nio/ch/SctpMessageInfoImpl.java \ + sun/nio/ch/SctpStdSocketOption.java + +ifneq ($(PLATFORM), windows) +FILES_java += \ + sun/nio/ch/SctpAssocChange.java \ + sun/nio/ch/SctpAssociationImpl.java \ + sun/nio/ch/SctpChannelImpl.java \ + sun/nio/ch/SctpMultiChannelImpl.java \ + sun/nio/ch/SctpNet.java \ + sun/nio/ch/SctpNotification.java \ + sun/nio/ch/SctpPeerAddrChange.java \ + sun/nio/ch/SctpResultContainer.java \ + sun/nio/ch/SctpSendFailed.java \ + sun/nio/ch/SctpServerChannelImpl.java \ + sun/nio/ch/SctpShutdown.java \ + sun/nio/ch/SctpSocketDispatcher.java +else +FILES_java += \ + sun/nio/ch/SctpChannelImpl.java \ + sun/nio/ch/SctpMultiChannelImpl.java \ + sun/nio/ch/SctpServerChannelImpl.java +endif diff --git a/make/com/sun/nio/sctp/Makefile b/make/com/sun/nio/sctp/Makefile new file mode 100644 index 000000000..d5acea340 --- /dev/null +++ b/make/com/sun/nio/sctp/Makefile @@ -0,0 +1,80 @@ +# +# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Sun designates this +# particular file as subject to the "Classpath" exception as provided +# by Sun in the LICENSE file that accompanied this code. +# +# This code 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 General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# + +# +# Makefile for com.sun.nio.sctp +# + +BUILDDIR = ../../../.. +PACKAGE = com.sun.nio.sctp +LIBRARY = sctp +PRODUCT = sun +#OTHER_JAVACFLAGS += -Xmaxwarns 1000 -Xlint +include $(BUILDDIR)/common/Defs.gmk + +# +# Files to compile +# +include FILES_c.gmk +include FILES_java.gmk +include Exportedfiles.gmk + +ifneq ($(PLATFORM), windows) +include $(BUILDDIR)/common/Mapfile-vers.gmk +include $(BUILDDIR)/common/Library.gmk + +# +# Find platform-specific C source files +# +vpath %.c $(PLATFORM_SRC)/native/sun/nio/ch + +# +# Include nio.h, net_util.h, sun_nio_ch_IOStatus.h, etc +# +OTHER_INCLUDES += \ + -I$(SHARE_SRC)/native/sun/nio/ch \ + -I$(SHARE_SRC)/native/java/net \ + -I$(PLATFORM_SRC)/native/java/net \ + -I$(CLASSHDRDIR)/../../../../java/java.nio/nio/CClassHeaders + +ifeq ($(PLATFORM), linux) +COMPILER_WARNINGS_FATAL=true +#OTHER_LDLIBS += -L$(LIBDIR)/$(LIBARCH) -ljava -lnet -lpthread -ldl +OTHER_LDLIBS += -L$(LIBDIR)/$(LIBARCH) -lnio -lnet -lpthread -ldl +endif +ifeq ($(PLATFORM), solaris) +#LIBSCTP = -lsctp +OTHER_LDLIBS += $(LIBSOCKET) -L$(LIBDIR)/$(LIBARCH) -lnet -lnio +endif # PLATFORM + +else # windows +include $(BUILDDIR)/common/Classes.gmk +endif # ifneq windows + + +clean clobber:: + $(RM) -r $(CLASSDESTDIR)/com/sun/nio/sctp + $(RM) -r $(CLASSDESTDIR)/sun/nio/ch + diff --git a/make/com/sun/nio/sctp/mapfile-vers b/make/com/sun/nio/sctp/mapfile-vers new file mode 100644 index 000000000..8ffd4c666 --- /dev/null +++ b/make/com/sun/nio/sctp/mapfile-vers @@ -0,0 +1,49 @@ +# +# Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Sun designates this +# particular file as subject to the "Classpath" exception as provided +# by Sun in the LICENSE file that accompanied this code. +# +# This code 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 General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# + +SUNWprivate_1.1 { + global: + Java_sun_nio_ch_SctpNet_socket0; + Java_sun_nio_ch_SctpNet_bindx; + Java_sun_nio_ch_SctpNet_getLocalAddresses0; + Java_sun_nio_ch_SctpNet_getRemoteAddresses0; + Java_sun_nio_ch_SctpNet_getPrimAddrOption0; + Java_sun_nio_ch_SctpNet_setPrimAddrOption0; + Java_sun_nio_ch_SctpNet_setPeerPrimAddrOption0; + Java_sun_nio_ch_SctpNet_getInitMsgOption0; + Java_sun_nio_ch_SctpNet_setInitMsgOption0; + Java_sun_nio_ch_SctpNet_getIntOption0; + Java_sun_nio_ch_SctpNet_setIntOption0; + Java_sun_nio_ch_SctpNet_shutdown0; + Java_sun_nio_ch_SctpChannelImpl_initIDs; + Java_sun_nio_ch_SctpChannelImpl_checkConnect; + Java_sun_nio_ch_SctpChannelImpl_receive0; + Java_sun_nio_ch_SctpChannelImpl_send0; + Java_sun_nio_ch_SctpServerChannelImpl_initIDs; + Java_sun_nio_ch_SctpServerChannelImpl_accept0; + JNI_OnLoad; + local: + *; +}; diff --git a/make/docs/NON_CORE_PKGS.gmk b/make/docs/NON_CORE_PKGS.gmk index 6028a85da..b6793c2fd 100644 --- a/make/docs/NON_CORE_PKGS.gmk +++ b/make/docs/NON_CORE_PKGS.gmk @@ -86,6 +86,8 @@ TREEAPI_PKGS = com.sun.source.tree \ SMARTCARDIO_PKGS = javax.smartcardio +SCTPAPI_PKGS = com.sun.nio.sctp + TRACING_PKGS = com.sun.tracing \ com.sun.tracing.dtrace @@ -98,4 +100,6 @@ NON_CORE_PKGS = $(DOMAPI_PKGS) \ $(OLD_JSSE_PKGS) \ $(HTTPSERVER_PKGS) \ $(SMARTCARDIO_PKGS) \ - $(TRACING_PKGS) + $(TRACING_PKGS) \ + $(SCTPAPI_PKGS) + diff --git a/make/java/nio/mapfile-linux b/make/java/nio/mapfile-linux index 0bd240811..13353edcd 100644 --- a/make/java/nio/mapfile-linux +++ b/make/java/nio/mapfile-linux @@ -189,6 +189,7 @@ SUNWprivate_1.1 { Java_sun_nio_fs_UnixNativeDispatcher_getgrnam0; Java_sun_nio_fs_UnixNativeDispatcher_getextmntent; Java_sun_nio_fs_UnixCopyFile_transfer; + handleSocketError; local: *; diff --git a/make/java/nio/mapfile-solaris b/make/java/nio/mapfile-solaris index 2192a5a77..129eaf477 100644 --- a/make/java/nio/mapfile-solaris +++ b/make/java/nio/mapfile-solaris @@ -175,6 +175,7 @@ SUNWprivate_1.1 { Java_sun_nio_fs_SolarisWatchService_portDissociate; Java_sun_nio_fs_SolarisWatchService_portSend; Java_sun_nio_fs_SolarisWatchService_portGetn; + handleSocketError; local: *; diff --git a/src/share/classes/com/sun/nio/sctp/AbstractNotificationHandler.java b/src/share/classes/com/sun/nio/sctp/AbstractNotificationHandler.java new file mode 100644 index 000000000..2d9afb711 --- /dev/null +++ b/src/share/classes/com/sun/nio/sctp/AbstractNotificationHandler.java @@ -0,0 +1,140 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package com.sun.nio.sctp; + +/** + * A skeletal handler that consumes notifications and continues. + * + *

This class trivially implements the {@code handleNotification} methods to + * return {@link HandlerResult#CONTINUE CONTINUE} so that all notifications are + * consumed and the channel continues to try and receive a message. + * + *

It also provides overloaded versions of the {@code handleNotification} + * methods, one for each of the required supported notification types, {@link + * AssociationChangeNotification}, {@link PeerAddressChangeNotification}, + * {@link SendFailedNotification}, and {@link ShutdownNotification}. The + * appropriate method will be invoked when the notification is received. + * + * @since 1.7 + */ +public class AbstractNotificationHandler + implements NotificationHandler +{ + /** + * Initializes a new instance of this class. + */ + protected AbstractNotificationHandler() {} + + /** + * Invoked when an implementation specific notification is received from the + * SCTP stack. + * + * @param notification + * The notification + * + * @param attachment + * The object attached to the {@code receive} operation when it was + * initiated. + * + * @return The handler result + */ + @Override + public HandlerResult handleNotification(Notification notification, + T attachment) { + return HandlerResult.CONTINUE; + } + + /** + * Invoked when an {@link AssociationChangeNotification} is received from + * the SCTP stack. + * + * @param notification + * The notification + * + * @param attachment + * The object attached to the {@code receive} operation when it was + * initiated. + * + * @return The handler result + */ + public HandlerResult handleNotification(AssociationChangeNotification notification, + T attachment) { + return HandlerResult.CONTINUE; + } + + /** + * Invoked when an {@link PeerAddressChangeNotification} is received from + * the SCTP stack. + * + * @param notification + * The notification + * + * @param attachment + * The object attached to the {@code receive} operation when it was + * initiated. + * + * @return The handler result + */ + public HandlerResult handleNotification(PeerAddressChangeNotification notification, + T attachment) { + return HandlerResult.CONTINUE; + } + + /** + * Invoked when an {@link SendFailedNotification} is received from + * the SCTP stack. + * + * @param notification + * The notification + * + * @param attachment + * The object attached to the {@code receive} operation when it was + * initiated. + * + * @return The handler result + */ + public HandlerResult handleNotification(SendFailedNotification notification, + T attachment) { + return HandlerResult.CONTINUE; + } + + /** + * Invoked when an {@link ShutdownNotification} is received from + * the SCTP stack. + * + * @param notification + * The notification + * + * @param attachment + * The object attached to the {@code receive} operation when it was + * initiated. + * + * @return The handler result + */ + public HandlerResult handleNotification(ShutdownNotification notification, + T attachment) { + return HandlerResult.CONTINUE; + } +} diff --git a/src/share/classes/com/sun/nio/sctp/Association.java b/src/share/classes/com/sun/nio/sctp/Association.java new file mode 100644 index 000000000..a00bd126b --- /dev/null +++ b/src/share/classes/com/sun/nio/sctp/Association.java @@ -0,0 +1,104 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package com.sun.nio.sctp; + +/** + * A class that represents an SCTP association. + * + *

An association exists between two SCTP endpoints. Each endpoint is + * represented by a list of transport addresses through which that endpoint can + * be reached and from which it will originate SCTP messages. The association + * spans over all of the possible source/destination combinations which may be + * generated from each endpoint's lists of addresses. + * + *

Associations are identified by their Association ID. + * Association ID's are guaranteed to be unique for the lifetime of the + * association. An association ID may be reused after the association has been + * shutdown. An association ID is not unique across multiple SCTP channels. + * An Association's local and remote addresses may change if the SCTP + * implementation supports Dynamic Address Reconfiguration as defined by + * RFC5061, see the + * {@code bindAddress} and {@code unbindAddress} methods of {@link SctpChannel}, + * {@link SctpServerChannel}, and {@link SctpMultiChannel}. + * + *

An {@code Association} is returned from an {@link + * SctpChannel#association SctpChannel} or an {@link + * SctpMultiChannel#associations SctpMultiChannel}, as well + * as being given as a parameter to {@link NotificationHandler + * NotificationHandler} methods. + * + * @since 1.7 + */ +public class Association { + private final int associationID; + private final int maxInStreams; + private final int maxOutStreams; + + /** + * Initializes a new instance of this class. + */ + protected Association(int associationID, + int maxInStreams, + int maxOutStreams) { + this.associationID = associationID; + this.maxInStreams = maxInStreams; + this.maxOutStreams = maxOutStreams; + } + + /** + * Returns the associationID. + * + * @return The association ID + */ + public final int associationID() { + return associationID; + }; + + /** + * Returns the maximum number of inbound streams that this association + * supports. + * + *

Data received on this association will be on stream number + * {@code s}, where {@code 0 <= s < maxInboundStreams()}. + * + * @return The maximum number of inbound streams + */ + public final int maxInboundStreams() { + return maxInStreams; + }; + + /** + * Returns the maximum number of outbound streams that this association + * supports. + * + *

Data sent on this association must be on stream number + * {@code s}, where {@code 0 <= s < maxOutboundStreams()}. + * + * @return The maximum number of outbound streams + */ + public final int maxOutboundStreams() { + return maxOutStreams; + }; +} diff --git a/src/share/classes/com/sun/nio/sctp/AssociationChangeNotification.java b/src/share/classes/com/sun/nio/sctp/AssociationChangeNotification.java new file mode 100644 index 000000000..0262ffb8e --- /dev/null +++ b/src/share/classes/com/sun/nio/sctp/AssociationChangeNotification.java @@ -0,0 +1,92 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package com.sun.nio.sctp; + +/** + * Notification emitted when an association has either opened or closed. + * + * @since 1.7 + */ +public abstract class AssociationChangeNotification + implements Notification +{ + /** + * Defines the type of change event that happened to the association. + * + * @since 1.7 + */ + public enum AssocChangeEvent + { + /** + * A new association is now ready and data may be exchanged with this peer. + */ + COMM_UP, + + /** + * The association has failed. A series of SCTP send failed notifications + * will follow this notification, one for each outstanding message. + */ + COMM_LOST, + + /** + * SCTP has detected that the peer has restarted. + */ + RESTART, + + /** + * The association has gracefully closed. + */ + SHUTDOWN, + + /** + * The association failed to setup. If a message was sent on a {@link + * SctpMultiChannel} in non-blocking mode, an + * SCTP send failed notification will follow this notification for the + * outstanding message. + */ + CANT_START + } + + /** + * Initializes a new instance of this class. + */ + protected AssociationChangeNotification() {} + + /** + * Returns the association that this notification is applicable to. + * + * @return The association whose state has changed, or {@code null} if + * there is no association, that is {@linkplain + * AssocChangeEvent#CANT_START CANT_START} + */ + public abstract Association association(); + + /** + * Returns the type of change event. + * + * @return The event + */ + public abstract AssocChangeEvent event(); +} diff --git a/src/share/classes/com/sun/nio/sctp/HandlerResult.java b/src/share/classes/com/sun/nio/sctp/HandlerResult.java new file mode 100644 index 000000000..5b5f8ec0f --- /dev/null +++ b/src/share/classes/com/sun/nio/sctp/HandlerResult.java @@ -0,0 +1,47 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package com.sun.nio.sctp; + +/** + * Defines notification handler results. + * + *

The {@code HandlerResult} is used to determine the behavior of the + * channel after it handles a notification from the SCTP stack. Essentially its + * value determines if the channel should try to receive another notificaiton or + * a message before returning. + * + * @since 1.7 + */ +public enum HandlerResult { + /** + * Try to receieve another message or notification. + */ + CONTINUE, + + /** + * Return without trying to receive any more data. + */ + RETURN; +} diff --git a/src/share/classes/com/sun/nio/sctp/IllegalReceiveException.java b/src/share/classes/com/sun/nio/sctp/IllegalReceiveException.java new file mode 100644 index 000000000..09d087e56 --- /dev/null +++ b/src/share/classes/com/sun/nio/sctp/IllegalReceiveException.java @@ -0,0 +1,49 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package com.sun.nio.sctp; + +/** + * Unchecked exception thrown when an attempt is made to invoke the + * {@code receive} method of {@link SctpChannel} or {@link SctpMultiChannel} + * from a notification handler. + * + * @since 1.7 + */ +public class IllegalReceiveException extends IllegalStateException { + private static final long serialVersionUID = 2296619040988576224L; + + /** + * Constructs an instance of this class. + */ + public IllegalReceiveException() { } + + /** + * Constructs an instance of this class with the specified message. + */ + public IllegalReceiveException(String msg) { + super(msg); + } +} + diff --git a/src/share/classes/com/sun/nio/sctp/IllegalUnbindException.java b/src/share/classes/com/sun/nio/sctp/IllegalUnbindException.java new file mode 100644 index 000000000..c24025fc8 --- /dev/null +++ b/src/share/classes/com/sun/nio/sctp/IllegalUnbindException.java @@ -0,0 +1,49 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package com.sun.nio.sctp; + +/** + * Unchecked exception thrown when an attempt is made to remove an + * address that is not bound to the channel, or remove an address from a + * channel that has only one address bound to it. + * + * @since 1.7 + */ +public class IllegalUnbindException extends IllegalStateException { + private static final long serialVersionUID = -310540883995532224L; + + /** + * Constructs an instance of this class. + */ + public IllegalUnbindException() { } + + /** + * Constructs an instance of this class with the specified detailed message. + */ + public IllegalUnbindException(String msg) { + super(msg); + } +} + diff --git a/src/share/classes/com/sun/nio/sctp/InvalidStreamException.java b/src/share/classes/com/sun/nio/sctp/InvalidStreamException.java new file mode 100644 index 000000000..de2100fee --- /dev/null +++ b/src/share/classes/com/sun/nio/sctp/InvalidStreamException.java @@ -0,0 +1,48 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package com.sun.nio.sctp; + +/** + * Unchecked exception thrown when an attempt is made to send a + * message to an invalid stream. + * + * @since 1.7 + */ +public class InvalidStreamException extends IllegalArgumentException { + private static final long serialVersionUID = -9172703378046665558L; + + /** + * Constructs an instance of this class. + */ + public InvalidStreamException() { } + + /** + * Constructs an instance of this class with the specified detailed message. + */ + public InvalidStreamException(String msg) { + super(msg); + } +} + diff --git a/src/share/classes/com/sun/nio/sctp/MessageInfo.java b/src/share/classes/com/sun/nio/sctp/MessageInfo.java new file mode 100644 index 000000000..69c47cf3b --- /dev/null +++ b/src/share/classes/com/sun/nio/sctp/MessageInfo.java @@ -0,0 +1,303 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package com.sun.nio.sctp; + +import java.net.SocketAddress; + +/** + * The {@code MessageInfo} class provides additional ancillary information about + * messages. + * + *

Received SCTP messages, returned by + * {@link SctpChannel#receive SctpChannel.receive} and {@link + * SctpMultiChannel#receive SctpMultiChannel.receive}, + * return a {@code MessageInfo} instance that can be queried to determine + * ancillary information about the received message. Messages being sent should + * use one of the {@link #createOutgoing(java.net.SocketAddress,int) + * createOutgoing} methods to provide ancillary data for the message being + * sent, and may use the appropriate setter methods to override the default + * values provided for {@link #isUnordered() unordered}, {@link #timeToLive() + * timeToLive}, {@link #isComplete() complete} and {@link #payloadProtocolID() + * payloadProtocolID}, before sending the message. + * + *

For out going messages the {@code timeToLive} parameter is a time period + * that the sending side SCTP stack may expire the message if it has not been + * sent. This time period is an indication to the stack that the message is no + * longer required to be sent after the time period expires. It is not a hard + * timeout and may be influenced by whether the association supports the partial + * reliability extension, RFC 3758 + * + * + *

{@code MessageInfo} instances are not safe for use by multiple concurrent + * threads. If a MessageInfo is to be used by more than one thread then access + * to the MessageInfo should be controlled by appropriate synchronization. + * + * @since 1.7 + */ +public abstract class MessageInfo { + /** + * Initializes a new instance of this class. + */ + protected MessageInfo() {} + + /** + * Creates a {@code MessageInfo} instance suitable for use when + * sending a message. + * + *

The returned instance will have its {@link #isUnordered() unordered} + * value set to {@code false}, its {@link #timeToLive() timeToLive} value + * set to {@code 0}, its {@link #isComplete() complete} value set + * to {@code true}, and its {@link #payloadProtocolID() payloadProtocolID} + * value set to {@code 0}. These values, if required, can be set through + * the appropriate setter method before sending the message. + * + * @param address + * For a connected {@code SctpChannel} the address is the + * preferred peer address of the association to send the message + * to, or {@code null} to use the peer primary address. For an + * {@code SctpMultiChannel} the address is used to determine + * the association, or if no association exists with a peer of that + * address then one is setup. + * + * @param streamNumber + * The stream number that the message will be sent on + * + * @return The outgoing message info + * + * @throws IllegalArgumentException + * If the streamNumber is negative or greater than {@code 65536} + */ + public static MessageInfo createOutgoing(SocketAddress address, + int streamNumber) { + if (streamNumber < 0 || streamNumber > 65536) + throw new IllegalArgumentException("Invalid stream number"); + + return new sun.nio.ch.SctpMessageInfoImpl(null, address, streamNumber); + } + /** + * Creates a {@code MessageInfo} instance suitable for use when + * sending a message to a given association. Typically used for + * {@code SctpMultiChannel} when an association has already been setup. + * + *

The returned instance will have its {@link #isUnordered() unordered} + * value set to {@code false}, its {@link #timeToLive() timeToLive} value + * set to {@code 0}, its {@link #isComplete() complete} value set + * to {@code true}, and its {@link #payloadProtocolID() payloadProtocolID} + * value set to {@code 0}. These values, if required, can be set through + * the appropriate setter method before sending the message. + * + * @param association + * The association to send the message on + * + * @param address + * The preferred peer address of the association to send the message + * to, or {@code null} to use the peer primary address + * + * @param streamNumber + * The stream number that the message will be sent on. + * + * @return The outgoing message info + * + * @throws IllegalArgumentException + * If {@code association} is {@code null}, or the streamNumber is + * negative or greater than {@code 65536} + */ + public static MessageInfo createOutgoing(Association association, + SocketAddress address, + int streamNumber) { + if (association == null) + throw new IllegalArgumentException("association cannot be null"); + + if (streamNumber < 0 || streamNumber > 65536) + throw new IllegalArgumentException("Invalid stream number"); + + return new sun.nio.ch.SctpMessageInfoImpl(association, address, + streamNumber); + } + + /** + * Returns the source socket address if the message has been received, + * otherwise the preferred destination of the message to be sent. + * + * @return The socket address, or {@code null} if this instance is to be + * used for sending a message and has been construced without + * specifying a preferred destination address + * + */ + public abstract SocketAddress address(); + + /** + * Returns the association that the message was received on, if the message + * has been received, otherwise the association that the message is to be + * sent on. + * + * @return The association, or {@code null} if this instance is to be + * used for sending a message and has been construced using the + * the {@link #createOutgoing(SocketAddress,int) + * createOutgoing(SocketAddress,int)} static factory method + */ + public abstract Association association(); + + /** + * Returns the number of bytes read for the received message. + * + *

This method is only appicable for received messages, it has no + * meaning for messages being sent. + * + * @return The number of bytes read, {@code -1} if the channel is an {@link + * SctpChannel} that has reached end-of-stream, otherwise + * {@code 0} + */ + public abstract int bytes(); + + /** + * Tells whether or not the message is complete. + * + *

For received messages {@code true} indicates that the message was + * completely received. For messages being sent {@code true} indicates that + * the message is complete, {@code false} indicates that the message is not + * complete. How the send channel interprets this value depends on the value + * of its {@link SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE + * SCTP_EXPLICIT_COMPLETE} socket option. + * + * @return {@code true} if, and only if, the message is complete + */ + public abstract boolean isComplete(); + + /** + * Sets whether or not the message is complete. + * + *

For messages being sent {@code true} indicates that + * the message is complete, {@code false} indicates that the message is not + * complete. How the send channel interprets this value depends on the value + * of its {@link SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE + * SCTP_EXPLICIT_COMPLETE} socket option. + * + * @param complete + * {@code true} if, and only if, the message is complete + * + * @return This MessageInfo + * + * @see MessageInfo#isComplete() + */ + public abstract MessageInfo complete(boolean complete); + + /** + * Tells whether or not the message is unordered. For received messages + * {@code true} indicates that the message was sent non-ordered. For + * messages being sent {@code true} requests the un-ordered delivery of the + * message, {@code false} indicates that the message is ordered. + * + * @return {@code true} if the message is unordered, otherwise + * {@code false} + */ + public abstract boolean isUnordered(); + + /** + * Sets whether or not the message is unordered. + * + * @param unordered + * {@code true} requests the un-ordered delivery of the message, + * {@code false} indicates that the message is ordered. + * + * @return This MessageInfo + * + * @see MessageInfo#isUnordered() + */ + public abstract MessageInfo unordered(boolean unordered); + + /** + * Returns the payload protocol Identifier. + * + *

A value indicating the type of payload protocol data being + * transmitted/received. This value is passed as opaque data by SCTP. + * {@code 0} indicates an unspecified payload protocol identifier. + * + * @return The Payload Protocol Identifier + */ + public abstract int payloadProtocolID(); + + /** + * Sets the payload protocol Identifier. + * + *

A value indicating the type of payload protocol data being + * transmitted. This value is passed as opaque data by SCTP. + * + * @param ppid + * The Payload Protocol Identifier, or {@code 0} indicate an + * unspecified payload protocol identifier. + * + * @return This MessageInfo + * + * @see MessageInfo#payloadProtocolID() + */ + public abstract MessageInfo payloadProtocolID(int ppid); + + /** + * Returns the stream number that the message was received on, if the + * message has been received, otherwise the stream number that the message + * is to be sent on. + * + * @return The stream number + */ + public abstract int streamNumber(); + + /** + * Sets the stream number that the message is to be sent on. + * + * @param streamNumber + * The stream number + * + * @throws IllegalArgumentException + * If the streamNumber is negative or greater than {@code 65536} + * + * @return This MessageInfo + */ + public abstract MessageInfo streamNumber(int streamNumber); + + /** + * The time period that the sending side may expire the message if it has + * not been sent, or {@code 0} to indicate that no timeout should occur. This + * value is only applicable for messages being sent, it has no meaning for + * received messages. + * + * @return The time period in milliseconds, or {@code 0} + */ + public abstract long timeToLive(); + + /** + * Sets the time period that the sending side may expire the message if it + * has not been sent. + * + * @param millis + * The time period in milliseconds, or {@code 0} to indicate that no + * timeout should occur + * + * @return This MessageInfo + * + * @see MessageInfo#timeToLive() + */ + public abstract MessageInfo timeToLive(long millis); +} diff --git a/src/share/classes/com/sun/nio/sctp/Notification.java b/src/share/classes/com/sun/nio/sctp/Notification.java new file mode 100644 index 000000000..073f11f61 --- /dev/null +++ b/src/share/classes/com/sun/nio/sctp/Notification.java @@ -0,0 +1,45 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package com.sun.nio.sctp; + +/** + * A notification from the SCTP stack. + * + *

Objects of this type are passed to the {@link NotificationHandler} when + * a notification is received. + * + *

An SCTP channel supports the following notifications: {@link + * AssociationChangeNotification}, {@link PeerAddressChangeNotification}, + * {@link SendFailedNotification}, {@link ShutdownNotification}, and may support + * additional implementation specific notifications. + * + * @since 1.7 + */ +public interface Notification { + /** + * Returns the association that this notification is applicable to. + */ + public Association association(); +} diff --git a/src/share/classes/com/sun/nio/sctp/NotificationHandler.java b/src/share/classes/com/sun/nio/sctp/NotificationHandler.java new file mode 100644 index 000000000..65eb8b777 --- /dev/null +++ b/src/share/classes/com/sun/nio/sctp/NotificationHandler.java @@ -0,0 +1,65 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package com.sun.nio.sctp; + +/** + * A handler for consuming notifications from the SCTP stack. + * + *

The SCTP channels defined in this package allow a notification handler to + * be specified to consume notifications from the SCTP stack. When a + * notification is received the {@linkplain #handleNotification + * handleNotification} method of the handler is invoked to handle that + * notification. + * + *

Additionally, an attachment object can be attached to the {@code receive} + * operation to provide context when consuming the notification. The + * attachment is important for cases where a state-less {@code + * NotificationHandler} is used to consume the result of many {@code receive} + * operations. + * + *

Handler implementations are encouraged to extend the {@link + * AbstractNotificationHandler} class which implements this interface and + * provide notification specific methods. However, an API should generally use + * this handler interface as the type for parameters, return type, etc. rather + * than the abstract class. + * + * @param T The type of the object attached to the receive operation + * + * @since 1.7 + */ +public interface NotificationHandler { + /** + * Invoked when a notification is received from the SCTP stack. + * + * @param notification + * The notification + * + * @param attachment + * The object attached to the receive operation when it was initiated. + * + * @return The handler result + */ + HandlerResult handleNotification(Notification notification, T attachment); +} diff --git a/src/share/classes/com/sun/nio/sctp/PeerAddressChangeNotification.java b/src/share/classes/com/sun/nio/sctp/PeerAddressChangeNotification.java new file mode 100644 index 000000000..ded8cbd23 --- /dev/null +++ b/src/share/classes/com/sun/nio/sctp/PeerAddressChangeNotification.java @@ -0,0 +1,107 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package com.sun.nio.sctp; + +import java.net.SocketAddress; + +/** + * Notification emitted when a destination address on a multi-homed peer + * encounters a change. + * + * @since 1.7 + */ +public abstract class PeerAddressChangeNotification + implements Notification +{ + /** + * Defines the type of address change event that occurred to the destination + * address on a multi-homed peer when it encounters a change of interface + * details. + * + *

Some of these events types are only generated when the association + * supports dynamic address reconfiguration, e.g. {@code SCTP_ADDR_ADDED}, + * {@code SCTP_ADDR_REMOVED}, etc. + * + * @since 1.7 + */ + public enum AddressChangeEvent { + /** + * This address is now reachable. + */ + ADDR_AVAILABLE, + + /** + * The address specified can no longer be reached. Any data sent to this + * address is rerouted to an alternate until this address becomes reachable. + */ + ADDR_UNREACHABLE, + + /** + * The address is no longer part of the association. + */ + ADDR_REMOVED, + + /** + * The address is now part of the association. + */ + ADDR_ADDED, + + /** + * This address has now been made to be the primary destination address. + */ + ADDR_MADE_PRIMARY, + + /** + * This address has now been confirmed as a valid address. + */ + ADDR_CONFIRMED; + } + + /** + * Initializes a new instance of this class. + */ + protected PeerAddressChangeNotification() {} + + /** + * Returns the peer address. + * + * @return The peer address + */ + public abstract SocketAddress address(); + + /** + * Returns the association that this notification is applicable to. + * + * @return The association whose peer address changed + */ + public abstract Association association(); + + /** + * Returns the type of change event. + * + * @return The event + */ + public abstract AddressChangeEvent event(); +} diff --git a/src/share/classes/com/sun/nio/sctp/SctpChannel.java b/src/share/classes/com/sun/nio/sctp/SctpChannel.java new file mode 100644 index 000000000..2f0626389 --- /dev/null +++ b/src/share/classes/com/sun/nio/sctp/SctpChannel.java @@ -0,0 +1,859 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package com.sun.nio.sctp; + +import java.net.SocketAddress; +import java.net.InetAddress; +import java.io.IOException; +import java.util.Set; +import java.nio.ByteBuffer; +import java.nio.channels.spi.AbstractSelectableChannel; +import java.nio.channels.spi.SelectorProvider; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.SelectionKey; + +/** + * A selectable channel for message-oriented connected SCTP sockets. + * + *

An SCTP channel can only control one SCTP association. + * An {@code SCTPChannel} is created by invoking one of the + * {@link #open open} methods of this class. A newly-created channel is open but + * not yet connected, that is, there is no association setup with a remote peer. + * An attempt to invoke an I/O operation upon an unconnected + * channel will cause a {@link java.nio.channels.NotYetConnectedException} to be + * thrown. An association can be setup by connecting the channel using one of + * its {@link #connect connect} methods. Once connected, the channel remains + * connected until it is closed. Whether or not a channel is connected may be + * determined by invoking {@link #getRemoteAddresses getRemoteAddresses}. + * + *

SCTP channels support non-blocking connection: A + * channel may be created and the process of establishing the link to + * the remote socket may be initiated via the {@link #connect connect} method + * for later completion by the {@link #finishConnect finishConnect} method. + * Whether or not a connection operation is in progress may be determined by + * invoking the {@link #isConnectionPending isConnectionPending} method. + * + *

Socket options are configured using the + * {@link #setOption(SctpSocketOption,Object) setOption} method. An SCTP + * channel support the following options: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Option NameDescription
{@link SctpStandardSocketOption#SCTP_DISABLE_FRAGMENTS + * SCTP_DISABLE_FRAGMENTS} Enables or disables message fragmentation
{@link SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE + * SCTP_EXPLICIT_COMPLETE} Enables or disables explicit message completion
{@link SctpStandardSocketOption#SCTP_FRAGMENT_INTERLEAVE + * SCTP_FRAGMENT_INTERLEAVE} Controls how the presentation of messages occur for the message + * receiver
{@link SctpStandardSocketOption#SCTP_INIT_MAXSTREAMS + * SCTP_INIT_MAXSTREAMS} The maximum number of streams requested by the local endpoint during + * association initialization
{@link SctpStandardSocketOption#SCTP_NODELAY SCTP_NODELAY} Enables or disable a Nagle-like algorithm
{@link SctpStandardSocketOption#SCTP_PRIMARY_ADDR + * SCTP_PRIMARY_ADDR} Requests that the local SCTP stack use the given peer address as the + * association primary
{@link SctpStandardSocketOption#SCTP_SET_PEER_PRIMARY_ADDR + * SCTP_SET_PEER_PRIMARY_ADDR} Requests that the peer mark the enclosed address as the association + * primary
{@link SctpStandardSocketOption#SO_SNDBUF + * SO_SNDBUF} The size of the socket send buffer
{@link SctpStandardSocketOption#SO_RCVBUF + * SO_RCVBUF} The size of the socket receive buffer
{@link SctpStandardSocketOption#SO_LINGER + * SO_LINGER} Linger on close if data is present (when configured in blocking mode + * only)
+ *
+ * Additional (implementation specific) options may also be supported. The list + * of options supported is obtained by invoking the {@link #supportedOptions() + * supportedOptions} method. + * + *

SCTP channels are safe for use by multiple concurrent threads. + * They support concurrent reading and writing, though at most one thread may be + * reading and at most one thread may be writing at any given time. The + * {@link #connect connect} and {@link #finishConnect + * finishConnect} methods are mutually synchronized against each other, and + * an attempt to initiate a send or receive operation while an invocation of one + * of these methods is in progress will block until that invocation is complete. + * + * @since 1.7 + */ +public abstract class SctpChannel + extends AbstractSelectableChannel +{ + /** + * Initializes a new instance of this class. + * + * @param provider + * The selector provider for this channel + */ + protected SctpChannel(SelectorProvider provider) { + super(provider); + } + + /** + * Opens an SCTP channel. + * + *

The new channel is unbound and unconnected. + * + * @return A new SCTP channel + * + * @throws UnsupportedOperationException + * If the SCTP protocol is not supported + * + * @throws IOException + * If an I/O error occurs + */ + public static SctpChannel open() throws + IOException { + return new sun.nio.ch.SctpChannelImpl((SelectorProvider)null); + } + + /** + * Opens an SCTP channel and connects it to a remote address. + * + *

This is a convenience method and is equivalent to evaluating the + * following expression: + *

+     * open().connect(remote, maxOutStreams, maxInStreams);
+     * 
+ * + * @param remote + * The remote address to which the new channel is to be connected + * + * @param maxOutStreams + * The number of streams that the application wishes to be able + * to send to. Must be non negative and no larger than {@code 65536}. + * {@code 0} to use the endpoints default value. + * + * @param maxInStreams + * The maximum number of inbound streams the application is prepared + * to support. Must be non negative and no larger than {@code 65536}. + * {@code 0} to use the endpoints default value. + * + * @return A new SCTP channel connected to the given address + * + * @throws java.nio.channels.AsynchronousCloseException + * If another thread closes this channel + * while the connect operation is in progress + * + * @throws java.nio.channels.ClosedByInterruptException + * If another thread interrupts the current thread + * while the connect operation is in progress, thereby + * closing the channel and setting the current thread's + * interrupt status + * + * @throws java.nio.channels.UnresolvedAddressException + * If the given remote address is not fully resolved + * + * @throws java.nio.channels.UnsupportedAddressTypeException + * If the type of the given remote address is not supported + * + * @throws SecurityException + * If a security manager has been installed + * and it does not permit access to the given remote peer + * + * @throws UnsupportedOperationException + * If the SCTP protocol is not supported + * + * @throws IOException + * If some other I/O error occurs + */ + public static SctpChannel open(SocketAddress remote, int maxOutStreams, + int maxInStreams) throws IOException { + SctpChannel ssc = SctpChannel.open(); + ssc.connect(remote, maxOutStreams, maxInStreams); + return ssc; + } + + /** + * Returns the association on this channel's socket. + * + * @return the association, or {@code null} if the channel's socket is not + * connected. + * + * @throws ClosedChannelException + * If the channel is closed + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract Association association() throws IOException; + + /** + * Binds the channel's socket to a local address. + * + *

This method is used to establish a relationship between the socket + * and the local addresses. Once a relationship is established then + * the socket remains bound until the channel is closed. This relationship + * may not necesssarily be with the address {@code local} as it may be removed + * by {@link #unbindAddress unbindAddress}, but there will always be at least + * one local address bound to the channel's socket once an invocation of + * this method successfully completes. + * + *

Once the channel's socket has been successfully bound to a specific + * address, that is not automatically assigned, more addresses + * may be bound to it using {@link #bindAddress bindAddress}, or removed + * using {@link #unbindAddress unbindAddress}. + * + * @param local + * The local address to bind the socket, or {@code null} to + * bind the socket to an automatically assigned socket address + * + * @return This channel + * + * @throws java.nio.channels.AlreadyConnectedException + * If this channel is already connected + * + * @throws java.nio.channels.ClosedChannelException + * If this channel is closed + * + * @throws java.nio.channels.ConnectionPendingException + * If a non-blocking connection operation is already in progress on this channel + * + * @throws java.nio.channels.AlreadyBoundException + * If this channel is already bound + * + * @throws java.nio.channels.UnsupportedAddressTypeException + * If the type of the given address is not supported + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract SctpChannel bind(SocketAddress local) + throws IOException; + + /** + * Adds the given address to the bound addresses for the channel's + * socket. + * + *

The given address must not be the {@link + * java.net.InetAddress#isAnyLocalAddress wildcard} address. + * The channel must be first bound using {@link #bind bind} before + * invoking this method, otherwise {@link + * java.nio.channels.NotYetBoundException} is thrown. The {@link #bind bind} + * method takes a {@code SocketAddress} as its argument which typically + * contains a port number as well as an address. Addresses subquently bound + * using this method are simply addresses as the SCTP port number remains + * the same for the lifetime of the channel. + * + *

Adding addresses to a connected association is optional functionality. + * If the endpoint supports dynamic address reconfiguration then it may + * send the appropriate message to the peer to change the peers address + * lists. + * + * @param address + * The address to add to the bound addresses for the socket + * + * @return This channel + * + * @throws java.nio.channels.ClosedChannelException + * If this channel is closed + * + * @throws java.nio.channels.ConnectionPendingException + * If a non-blocking connection operation is already in progress on + * this channel + * + * @throws java.nio.channels.NotYetBoundException + * If this channel is not yet bound + * + * @throws java.nio.channels.AlreadyBoundException + * If this channel is already bound to the given address + * + * @throws IllegalArgumentException + * If address is {@code null} or the {@link + * java.net.InetAddress#isAnyLocalAddress wildcard} address + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract SctpChannel bindAddress(InetAddress address) + throws IOException; + + /** + * Removes the given address from the bound addresses for the channel's + * socket. + * + *

The given address must not be the {@link + * java.net.InetAddress#isAnyLocalAddress wildcard} address. + * The channel must be first bound using {@link #bind bind} before + * invoking this method, otherwise {@link java.nio.channels.NotYetBoundException} + * is thrown. If this method is invoked on a channel that does not have + * {@code address} as one of its bound addresses or that has only one + * local address bound to it, then this method throws + * {@link IllegalUnbindException}. + * The initial address that the channel's socket is bound to using {@link + * #bind bind} may be removed from the bound addresses for the channel's socket. + * + *

Removing addresses from a connected association is optional + * functionality. If the endpoint supports dynamic address reconfiguration + * then it may send the appropriate message to the peer to change the peers + * address lists. + * + * @param address + * The address to remove from the bound addresses for the socket + * + * @return This channel + * + * @throws java.nio.channels.ClosedChannelException + * If this channel is closed + * + * @throws java.nio.channels.ConnectionPendingException + * If a non-blocking connection operation is already in progress on + * this channel + * + * @throws java.nio.channels.NotYetBoundException + * If this channel is not yet bound + * + * @throws IllegalArgumentException + * If address is {@code null} or the {@link + * java.net.InetAddress#isAnyLocalAddress wildcard} address + * + * @throws IllegalUnbindException + * If {@code address} is not bound to the channel's socket. or + * the channel has only one address bound to it + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract SctpChannel unbindAddress(InetAddress address) + throws IOException; + + /** + * Connects this channel's socket. + * + *

If this channel is in non-blocking mode then an invocation of this + * method initiates a non-blocking connection operation. If the connection + * is established immediately, as can happen with a local connection, then + * this method returns {@code true}. Otherwise this method returns + * {@code false} and the connection operation must later be completed by + * invoking the {@link #finishConnect finishConnect} method. + * + *

If this channel is in blocking mode then an invocation of this + * method will block until the connection is established or an I/O error + * occurs. + * + *

If a security manager has been installed then this method verifies + * that its {@link java.lang.SecurityManager#checkConnect checkConnect} + * method permits connecting to the address and port number of the given + * remote peer. + * + *

This method may be invoked at any time. If a {@link #send send} or + * {@link #receive receive} operation upon this channel is invoked while an + * invocation of this method is in progress then that operation will first + * block until this invocation is complete. If a connection attempt is + * initiated but fails, that is, if an invocation of this method throws a + * checked exception, then the channel will be closed. + * + * @param remote + * The remote peer to which this channel is to be connected + * + * @return {@code true} if a connection was established, {@code false} if + * this channel is in non-blocking mode and the connection + * operation is in progress + * + * @throws java.nio.channels.AlreadyConnectedException + * If this channel is already connected + * + * @throws java.nio.channels.ConnectionPendingException + * If a non-blocking connection operation is already in progress on + * this channel + * + * @throws java.nio.channels.ClosedChannelException + * If this channel is closed + * + * @throws java.nio.channels.AsynchronousCloseException + * If another thread closes this channel + * while the connect operation is in progress + * + * @throws java.nio.channels.ClosedByInterruptException + * If another thread interrupts the current thread + * while the connect operation is in progress, thereby + * closing the channel and setting the current thread's + * interrupt status + * + * @throws java.nio.channels.UnresolvedAddressException + * If the given remote address is not fully resolved + * + * @throws java.nio.channels.UnsupportedAddressTypeException + * If the type of the given remote address is not supported + * + * @throws SecurityException + * If a security manager has been installed + * and it does not permit access to the given remote peer + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract boolean connect(SocketAddress remote) throws IOException; + + /** + * Connects this channel's socket. + * + *

This is a convience method and is equivalent to evaluating the + * following expression: + *

+     * setOption(SctpStandardSocketOption.SCTP_INIT_MAXSTREAMS, SctpStandardSocketOption.InitMaxStreams.create(maxInStreams, maxOutStreams))
+     *  .connect(remote);
+     * 
+ * + *

The {@code maxOutStreams} and {@code maxInStreams} parameters + * represent the maximum number of streams that the application wishes to be + * able to send to and receive from. They are negotiated with the remote + * peer and may be limited by the operating system. + * + * @param remote + * The remote peer to which this channel is to be connected + * + * @param maxOutStreams + * Must be non negative and no larger than {@code 65536}. + * {@code 0} to use the endpoints default value. + * + * @param maxInStreams + * Must be non negative and no larger than {@code 65536}. + * {@code 0} to use the endpoints default value. + * + * @return {@code true} if a connection was established, {@code false} if + * this channel is in non-blocking mode and the connection operation + * is in progress + * + * @throws java.nio.channels.AlreadyConnectedException + * If this channel is already connected + * + * @throws java.nio.channels.ConnectionPendingException + * If a non-blocking connection operation is already in progress on + * this channel + * + * @throws java.nio.channels.ClosedChannelException + * If this channel is closed + * + * @throws java.nio.channels.AsynchronousCloseException + * If another thread closes this channel + * while the connect operation is in progress + * + * @throws java.nio.channels.ClosedByInterruptException + * If another thread interrupts the current thread + * while the connect operation is in progress, thereby + * closing the channel and setting the current thread's + * interrupt status + * + * @throws java.nio.channels.UnresolvedAddressException + * If the given remote address is not fully resolved + * + * @throws java.nio.channels.UnsupportedAddressTypeException + * If the type of the given remote address is not supported + * + * @throws SecurityException + * If a security manager has been installed + * and it does not permit access to the given remote peer + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract boolean connect(SocketAddress remote, + int maxOutStreams, + int maxInStreams) + throws IOException; + + /** + * Tells whether or not a connection operation is in progress on this channel. + * + * @return {@code true} if, and only if, a connection operation has been initiated + * on this channel but not yet completed by invoking the + * {@link #finishConnect} method + */ + public abstract boolean isConnectionPending(); + + /** + * Finishes the process of connecting an SCTP channel. + * + *

A non-blocking connection operation is initiated by placing a socket + * channel in non-blocking mode and then invoking one of its {@link #connect + * connect} methods. Once the connection is established, or the attempt has + * failed, the channel will become connectable and this method may + * be invoked to complete the connection sequence. If the connection + * operation failed then invoking this method will cause an appropriate + * {@link java.io.IOException} to be thrown. + * + *

If this channel is already connected then this method will not block + * and will immediately return true. If this channel is in + * non-blocking mode then this method will return false if the + * connection process is not yet complete. If this channel is in blocking + * mode then this method will block until the connection either completes + * or fails, and will always either return true or throw a checked + * exception describing the failure. + * + *

This method may be invoked at any time. If a {@link #send send} or {@link #receive receive} + * operation upon this channel is invoked while an invocation of this + * method is in progress then that operation will first block until this + * invocation is complete. If a connection attempt fails, that is, if an + * invocation of this method throws a checked exception, then the channel + * will be closed. + * + * @return {@code true} if, and only if, this channel's socket is now + * connected + * + * @throws java.nio.channels.NoConnectionPendingException + * If this channel is not connected and a connection operation + * has not been initiated + * + * @throws java.nio.channels.ClosedChannelException + * If this channel is closed + * + * @throws java.nio.channels.AsynchronousCloseException + * If another thread closes this channel + * while the connect operation is in progress + * + * @throws java.nio.channels.ClosedByInterruptException + * If another thread interrupts the current thread + * while the connect operation is in progress, thereby + * closing the channel and setting the current thread's + * interrupt status + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract boolean finishConnect() throws IOException; + + /** + * Returns all of the socket addresses to which this channel's socket is + * bound. + * + * @return All the socket addresses that this channel's socket is + * bound to, or an empty {@code Set} if the channel's socket is not + * bound + * + * @throws ClosedChannelException + * If the channel is closed + * + * @throws IOException + * If an I/O error occurs + */ + public abstract Set getAllLocalAddresses() + throws IOException; + + /** + * Returns all of the remote addresses to which this channel's socket + * is connected. + * + *

If the channel is connected to a remote peer that is bound to + * multiple addresses then it is these addresses that the channel's socket + * is connected. + * + * @return All of the remote addresses to which this channel's socket + * is connected, or an empty {@code Set} if the channel's socket is + * not connected + * + * @throws ClosedChannelException + * If the channel is closed + * + * @throws IOException + * If an I/O error occurs + */ + public abstract Set getRemoteAddresses() + throws IOException; + + /** + * Shutdown a connection without closing the channel. + * + *

Sends a shutdown command to the remote peer, effectively preventing + * any new data from being written to the socket by either peer. Further + * sends will throw {@link java.nio.channels.ClosedChannelException}. The + * channel remains open to allow the for any data (and notifications) to be + * received that may have been sent by the peer before it received the + * shutdown command. If the channel is already shutdown then invoking this + * method has no effect. + * + * @return This channel + * + * @throws java.nio.channels.NotYetConnectedException + * If this channel is not yet connected + * + * @throws java.nio.channels.ClosedChannelException + * If this channel is closed + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract SctpChannel shutdown() throws IOException; + + /** + * Returns the value of a socket option. + * + * @param name + * The socket option + * + * @return The value of the socket option. A value of {@code null} may be + * a valid value for some socket options. + * + * @throws UnsupportedOperationException + * If the socket option is not supported by this channel + * + * @throws ClosedChannelException + * If this channel is closed + * + * @throws IOException + * If an I/O error occurs + * + * @see SctpStandardSocketOption + */ + public abstract T getOption(SctpSocketOption name) + throws IOException; + + /** + * Sets the value of a socket option. + * + * @param name + * The socket option + * + * @param value + * The value of the socket option. A value of {@code null} may be + * a valid value for some socket options. + * + * @return This channel + * + * @throws UnsupportedOperationException + * If the socket option is not supported by this channel + * + * @throws IllegalArgumentException + * If the value is not a valid value for this socket option + * + * @throws ClosedChannelException + * If this channel is closed + * + * @throws IOException + * If an I/O error occurs + * + * @see SctpStandardSocketOption + */ + public abstract SctpChannel setOption(SctpSocketOption name, T value) + throws IOException; + + /** + * Returns a set of the socket options supported by this channel. + * + *

This method will continue to return the set of options even after the + * channel has been closed. + * + * @return A set of the socket options supported by this channel + */ + public abstract Set> supportedOptions(); + + /** + * Returns an operation set identifying this channel's supported operations. + * + *

SCTP channels support connecting, reading, and writing, so this + * method returns ({@link SelectionKey#OP_CONNECT} + * | {@link SelectionKey#OP_READ} | {@link + * SelectionKey#OP_WRITE}).

+ * + * @return The valid-operation set + */ + @Override + public final int validOps() { + return (SelectionKey.OP_READ | + SelectionKey.OP_WRITE | + SelectionKey.OP_CONNECT); + } + + /** + * Receives a message into the given buffer and/or handles a notification. + * + *

If a message or notification is immediately available, or if this + * channel is in blocking mode and one eventually becomes available, then + * the message or notification is returned or handled, respectively. If this + * channel is in non-blocking mode and a message or notification is not + * immediately available then this method immediately returns {@code null}. + * + *

If this method receives a message it is copied into the given byte + * buffer. The message is transferred into the given byte buffer starting at + * its current position and the buffers position is incremented by the + * number of bytes read. If there are fewer bytes remaining in the buffer + * than are required to hold the message, or the underlying input buffer + * does not contain the complete message, then an invocation of {@link + * MessageInfo#isComplete isComplete} on the returned {@code + * MessageInfo} will return {@code false}, and more invocations of this + * method will be necessary to completely consume the messgae. Only + * one message at a time will be partially delivered in any stream. The + * socket option {@link SctpStandardSocketOption#SCTP_FRAGMENT_INTERLEAVE + * SCTP_FRAGMENT_INTERLEAVE} controls various aspects of what interlacing of + * messages occurs. + * + *

If this method receives a notification then the appropriate method of + * the given handler, if there is one, is invoked. If the handler returns + * {@link HandlerResult#CONTINUE CONTINUE} then this method will try to + * receive another message/notification, otherwise, if {@link + * HandlerResult#RETURN RETURN} is returned this method will return {@code + * null}. If an uncaught exception is thrown by the handler it will be + * propagated up the stack through this method. + * + *

This method may be invoked at any time. If another thread has + * already initiated a receive operation upon this channel, then an + * invocation of this method will block until the first operation is + * complete. The given handler is invoked without holding any locks used + * to enforce the above synchronization policy, that way handlers + * will not stall other threads from receiving. A handler should not invoke + * the {@code receive} method of this channel, if it does an + * {@link IllegalReceiveException} will be thrown. + * + * @param dst + * The buffer into which message bytes are to be transferred + * + * @param attachment + * The object to attach to the receive operation; can be + * {@code null} + * + * @param handler + * A handler to handle notifications from the SCTP stack, or {@code + * null} to ignore any notifications. + * + * @return The {@code MessageInfo}, {@code null} if this channel is in + * non-blocking mode and no messages are immediately available or + * the notification handler returns {@link HandlerResult#RETURN + * RETURN} after handling a notification + * + * @throws java.nio.channels.ClosedChannelException + * If this channel is closed + * + * @throws java.nio.channels.AsynchronousCloseException + * If another thread closes this channel + * while the read operation is in progress + * + * @throws java.nio.channels.ClosedByInterruptException + * If another thread interrupts the current thread + * while the read operation is in progress, thereby + * closing the channel and setting the current thread's + * interrupt status + * + * @throws java.nio.channels.NotYetConnectedException + * If this channel is not yet connected + * + * @throws IllegalReceiveException + * If the given handler invokes the {@code receive} method of this + * channel + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract MessageInfo receive(ByteBuffer dst, + T attachment, + NotificationHandler handler) + throws IOException; + + /** + * Sends a message via this channel. + * + *

If this channel is in non-blocking mode and there is sufficient room + * in the underlying output buffer, or if this channel is in blocking mode + * and sufficient room becomes available, then the remaining bytes in the + * given byte buffer are transmitted as a single message. Sending a message + * is atomic unless explicit message completion {@link + * SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE SCTP_EXPLICIT_COMPLETE} + * socket option is enabled on this channel's socket. + * + *

The message is transferred from the byte buffer as if by a regular + * {@link java.nio.channels.WritableByteChannel#write(java.nio.ByteBuffer) + * write} operation. + * + *

The bytes will be written to the stream number that is specified by + * {@link MessageInfo#streamNumber streamNumber} in the given {@code + * messageInfo}. + * + *

This method may be invoked at any time. If another thread has already + * initiated a send operation upon this channel, then an invocation of + * this method will block until the first operation is complete. + * + * @param src + * The buffer containing the message to be sent + * + * @param messageInfo + * Ancillary data about the message to be sent + * + * @return The number of bytes sent, which will be either the number of + * bytes that were remaining in the messages buffer when this method + * was invoked or, if this channel is non-blocking, may be zero if + * there was insufficient room for the message in the underlying + * output buffer + * + * @throws InvalidStreamExcepton + * If {@code streamNumner} is negative or greater than or equal to + * the maximum number of outgoing streams + * + * @throws java.nio.channels.ClosedChannelException + * If this channel is closed + * + * @throws java.nio.channels.AsynchronousCloseException + * If another thread closes this channel + * while the read operation is in progress + * + * @throws java.nio.channels.ClosedByInterruptException + * If another thread interrupts the current thread + * while the read operation is in progress, thereby + * closing the channel and setting the current thread's + * interrupt status + * + * @throws java.nio.channels.NotYetConnectedException + * If this channel is not yet connected + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract int send(ByteBuffer src, MessageInfo messageInfo) + throws IOException; +} diff --git a/src/share/classes/com/sun/nio/sctp/SctpMultiChannel.java b/src/share/classes/com/sun/nio/sctp/SctpMultiChannel.java new file mode 100644 index 000000000..d9325c103 --- /dev/null +++ b/src/share/classes/com/sun/nio/sctp/SctpMultiChannel.java @@ -0,0 +1,731 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package com.sun.nio.sctp; + +import java.net.SocketAddress; +import java.net.InetAddress; +import java.io.IOException; +import java.util.Set; +import java.nio.ByteBuffer; +import java.nio.channels.spi.AbstractSelectableChannel; +import java.nio.channels.spi.SelectorProvider; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.NotYetBoundException; +import java.nio.channels.SelectionKey; + +/** + * A selectable channel for message-oriented SCTP sockets. + * + *

An SCTP multi channel supports many associations on a single socket. + * An {@code SctpMultiChannel} is created by invoking the + * {@link #open open} method of this class. A newly-created channel is open but + * not yet bound. An attempt to invoke the {@link #receive receive} method of an + * unbound channel will cause the {@link NotYetBoundException} + * to be thrown. An attempt to invoke the {@link #send send} method of an + * unbound channel will cause it to first invoke the {@link #bind bind} method. + * The address(es) that the channel's socket is bound to can be retrieved by + * calling {@link #getAllLocalAddresses getAllLocalAddresses}. + * + *

Messages may be sent and received without explicitly setting up an + * association with the remote peer. The channel will implicitly setup + * a new association whenever it sends or receives a message from a remote + * peer if there is not already an association with that peer. Upon successful + * association setup, an {@link AssociationChangeNotification + * association changed} notification will be put to the SCTP stack with its + * {@code event} parameter set to {@link + * AssociationChangeNotification.AssocChangeEvent#COMM_UP + * COMM_UP}. This notification can be received by invoking {@link #receive + * receive}. + * + *

Socket options are configured using the + * {@link #setOption(SctpSocketOption,Object,Association) setOption} method. An + * {@code SctpMultiChannel} supports the following options: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Option NameDescription
{@link SctpStandardSocketOption#SCTP_DISABLE_FRAGMENTS + * SCTP_DISABLE_FRAGMENTS} Enables or disables message fragmentation
{@link SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE + * SCTP_EXPLICIT_COMPLETE} Enables or disables explicit message completion
{@link SctpStandardSocketOption#SCTP_FRAGMENT_INTERLEAVE + * SCTP_FRAGMENT_INTERLEAVE} Controls how the presentation of messages occur for the message + * receiver
{@link SctpStandardSocketOption#SCTP_INIT_MAXSTREAMS + * SCTP_INIT_MAXSTREAMS} The maximum number of streams requested by the local endpoint during + * association initialization
{@link SctpStandardSocketOption#SCTP_NODELAY SCTP_NODELAY} Enables or disable a Nagle-like algorithm
{@link SctpStandardSocketOption#SCTP_PRIMARY_ADDR + * SCTP_PRIMARY_ADDR} Requests that the local SCTP stack use the given peer address as the + * association primary
{@link SctpStandardSocketOption#SCTP_SET_PEER_PRIMARY_ADDR + * SCTP_SET_PEER_PRIMARY_ADDR} Requests that the peer mark the enclosed address as the association + * primary
{@link SctpStandardSocketOption#SO_SNDBUF + * SO_SNDBUF} The size of the socket send buffer
{@link SctpStandardSocketOption#SO_RCVBUF + * SO_RCVBUF} The size of the socket receive buffer
{@link SctpStandardSocketOption#SO_LINGER + * SO_LINGER} Linger on close if data is present (when configured in blocking mode + * only)
+ *
+ * Additional (implementation specific) options may also be supported. The list + * of options supported is obtained by invoking the {@link #supportedOptions() + * supportedOptions} method. + * + *

SCTP multi channels are safe for use by multiple concurrent threads. + * They support concurrent sending and receiving, though at most one thread may be + * sending and at most one thread may be receiving at any given time. + * + * @since 1.7 + */ +public abstract class SctpMultiChannel + extends AbstractSelectableChannel +{ + /** + * Initializes a new instance of this class. + * + * @param provider + * The selector provider for this channel + */ + protected SctpMultiChannel(SelectorProvider provider) { + super(provider); + } + + /** + * Opens an SCTP multi channel. + * + *

The new channel is unbound. + * + * @return A new SCTP multi channel + * + * @throws UnsupportedOperationException + * If the SCTP protocol is not supported + * + * @throws IOException + * If an I/O error occurs + */ + public static SctpMultiChannel open() throws + IOException { + return new sun.nio.ch.SctpMultiChannelImpl((SelectorProvider)null); + } + + /** + * Returns the open associations on this channel's socket. + * + *

Only associations whose {@link AssociationChangeNotification.AssocChangeEvent#COMM_UP + * COMM_UP} association change event has been received are included + * in the returned set of associations. Associations for which a + * {@link AssociationChangeNotification.AssocChangeEvent#COMM_LOST COMM_LOST} or {@link + * AssociationChangeNotification.AssocChangeEvent#SHUTDOWN SHUTDOWN} association change + * event have been receive are removed from the set of associations. + * + *

The returned set of associations is a snapshot of the open + * associations at the time that this method is invoked. + * + * @return A {@code Set} containing the open associations, or an empty + * {@code Set} if there are none. + * + * @throws ClosedChannelException + * If this channel is closed + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract Set associations() + throws IOException; + + /** + * Binds the channel's socket to a local address and configures the socket + * to listen for connections. + * + *

This method is used to establish a relationship between the socket + * and the local address. Once a relationship is established then + * the socket remains bound until the channel is closed. This relationship + * may not necesssarily be with the address {@code local} as it may be removed + * by {@link #unbindAddress unbindAddress}, but there will always be at least one local + * address bound to the channel's socket once an invocation of this method + * successfully completes. + * + *

Once the channel's socket has been successfully bound to a specific + * address, that is not automatically assigned, more addresses + * may be bound to it using {@link #bindAddress bindAddress}, or removed + * using {@link #unbindAddress unbindAddress}. + * + *

The backlog parameter is the maximum number of pending connections on + * the socket. Its exact semantics are implementation specific. An implementation + * may impose an implementation specific maximum length or may choose to ignore + * the parameter. If the backlog parameter has the value {@code 0}, or a negative + * value, then an implementation specific default is used. + * + * @param local + * The local address to bind the socket, or {@code null} to + * bind the socket to an automatically assigned socket address + * + * @param backlog + * The maximum number number of pending connections + * + * @return This channel + * + * @throws ClosedChannelException + * If this channel is closed + * + * @throws java.nio.channels.AlreadyBoundException + * If this channel is already bound + * + * @throws java.nio.channels.UnsupportedAddressTypeException + * If the type of the given address is not supported + * + * @throws SecurityException + * If a security manager has been installed and its {@link + * java.lang.SecurityManager#checkListen(int) checkListen} method + * denies the operation + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract SctpMultiChannel bind(SocketAddress local, + int backlog) + throws IOException; + + /** + * Binds the channel's socket to a local address and configures the socket + * to listen for connections. + * + *

This method works as if invoking it were equivalent to evaluating the + * expression: + *

+     * bind(local, 0);
+     * 
+ * + * @param local + * The local address to bind the socket, or {@code null} to + * bind the socket to an automatically assigned socket address + * + * @return This channel + * + * @throws ClosedChannelException + * If this channel is closed + * + * @throws java.nio.channels.AlreadyBoundException + * If this channel is already bound + * + * @throws java.nio.channels.UnsupportedAddressTypeException + * If the type of the given address is not supported + * + * @throws SecurityException + * If a security manager has been installed and its {@link + * java.lang.SecurityManager#checkListen(int) checkListen} method + * denies the operation + * + * @throws IOException + * If some other I/O error occurs + */ + public final SctpMultiChannel bind(SocketAddress local) + throws IOException { + return bind(local, 0); + } + + /** + * Adds the given address to the bound addresses for the channel's + * socket. + * + *

The given address must not be the {@link + * java.net.InetAddress#isAnyLocalAddress wildcard} address. + * The channel must be first bound using {@link #bind bind} before + * invoking this method, otherwise {@link NotYetBoundException} is thrown. + * The {@link #bind bind} method takes a {@code SocketAddress} as its + * argument which typically contains a port number as well as an address. + * Addresses subquently bound using this method are simply addresses as the + * SCTP port number remains the same for the lifetime of the channel. + * + *

New associations setup after this method successfully completes + * will be associated with the given address. Adding addresses to existing + * associations is optional functionality. If the endpoint supports + * dynamic address reconfiguration then it may send the appropriate message + * to the peer to change the peers address lists. + * + * @param address + * The address to add to the bound addresses for the socket + * + * @return This channel + * + * @throws ClosedChannelException + * If this channel is closed + * + * @throws NotYetBoundException + * If this channel is not yet bound + * + * @throws java.nio.channels.AlreadyBoundException + * If this channel is already bound to the given address + * + * @throws IllegalArgumentException + * If address is {@code null} or the {@link + * java.net.InetAddress#isAnyLocalAddress wildcard} address + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract SctpMultiChannel bindAddress(InetAddress address) + throws IOException; + + /** + * Removes the given address from the bound addresses for the channel's + * socket. + * + *

The given address must not be the {@link + * java.net.InetAddress#isAnyLocalAddress wildcard} address. + * The channel must be first bound using {@link #bind bind} before + * invoking this method, otherwise {@link NotYetBoundException} is thrown. + * + *

If this method is invoked on a channel that does + * not have {@code address} as one of its bound addresses, or that has only + * one local address bound to it, then this method throws + * {@link IllegalUnbindException}. + * + *

The initial address that the channel's socket is bound to using + * {@link #bind bind} may be removed from the bound addresses for the + * channel's socket. + * + *

New associations setup after this method successfully completes + * will not be associated with the given address. Removing addresses from + * existing associations is optional functionality. If the endpoint supports + * dynamic address reconfiguration then it may send the appropriate message + * to the peer to change the peers address lists. + * + * @param address + * The address to remove from the bound addresses for the socket + * + * @return This channel + * + * @throws ClosedChannelException + * If this channel is closed + * + * @throws NotYetBoundException + * If this channel is not yet bound + * + * @throws IllegalUnbindException + * {@code address} is not bound to the channel's socket, or the + * channel has only one address bound to it + * + * @throws IllegalArgumentException + * If address is {@code null} or the {@link + * java.net.InetAddress#isAnyLocalAddress wildcard} address + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract SctpMultiChannel unbindAddress(InetAddress address) + throws IOException; + + /** + * Returns all of the socket addresses to which this channel's socket is + * bound. + * + * @return All the socket addresses that this channel's socket is + * bound to, or an empty {@code Set} if the channel's socket is not + * bound + * + * @throws ClosedChannelException + * If the channel is closed + * + * @throws IOException + * If an I/O error occurs + */ + public abstract Set getAllLocalAddresses() + throws IOException; + + /** + * Returns all of the remote addresses to which the given association on + * this channel's socket is connected. + * + * @return All of the remote addresses for the given association, or + * an empty {@code Set} if the association has been shutdown + * + * @throws ClosedChannelException + * If the channel is closed + * + * @throws IOException + * If an I/O error occurs + */ + public abstract Set getRemoteAddresses(Association association) + throws IOException; + + /** + * Shutdown an association without closing the channel. + * + * @param association + * The association to shutdown + * + * @return This channel + * + * @throws ClosedChannelException + * If this channel is closed + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract SctpMultiChannel shutdown(Association association) + throws IOException; + + /** + * Returns the value of a socket option. + * + *

Note that some options are retrieved on the channel's socket, + * therefore the {@code association} parameter is not applicable and will be + * ignored if given. However, if the option is association specific then the + * association must be given. + * + * @param name + * The socket option + * + * @param association + * The association whose option should be retrieved, or {@code null} + * if this option should be retrieved at the channel's socket level. + * + * @return The value of the socket option. A value of {@code null} may be + * a valid value for some socket options. + * + * @throws UnsupportedOperationException + * If the socket option is not supported by this channel + * + * @throws ClosedChannelException + * If this channel is closed + * + * @throws IOException + * If an I/O error occurs + * + * @see SctpStandardSocketOption + */ + public abstract T getOption(SctpSocketOption name, + Association association) + throws IOException; + + /** + * Sets the value of a socket option. + * + *

Note that some options are retrieved on the channel's socket, + * therefore the {@code association} parameter is not applicable and will be + * ignored if given. However, if the option is association specific then the + * association must be given. + * + * @param name + * The socket option + * + * @param association + * The association whose option should be set, or {@code null} + * if this option should be set at the channel's socket level. + * + * @param value + * The value of the socket option. A value of {@code null} may be + * a valid value for some socket options. + * + * @return This channel + * + * @throws UnsupportedOperationException + * If the socket option is not supported by this channel + * + * @throws IllegalArgumentException + * If the value is not a valid value for this socket option + * + * @throws ClosedChannelException + * If this channel is closed + * + * @throws IOException + * If an I/O error occurs + * + * @see SctpStandardSocketOption + */ + public abstract SctpMultiChannel setOption(SctpSocketOption name, + T value, + Association association) + throws IOException; + + /** + * Returns a set of the socket options supported by this channel. + * + *

This method will continue to return the set of options even after the + * channel has been closed. + * + * @return A set of the socket options supported by this channel + */ + public abstract Set> supportedOptions(); + + /** + * Returns an operation set identifying this channel's supported operations. + * + *

SCTP multi channels support reading, and writing, so this + * method returns + * {@code (}{@link SelectionKey#OP_READ} {@code |} {@link + * SelectionKey#OP_WRITE}{@code )}.

+ * + * @return The valid-operation set + */ + @Override + public final int validOps() { + return (SelectionKey.OP_READ | + SelectionKey.OP_WRITE ); + } + + /** + * Receives a message and/or handles a notification via this channel. + * + *

If a message or notification is immediately available, or if this + * channel is in blocking mode and one eventually becomes available, then + * the message or notification is returned or handled, respectively. If this + * channel is in non-blocking mode and a message or notification is not + * immediately available then this method immediately returns {@code null}. + * + *

If this method receives a message it is copied into the given byte + * buffer and an {@link MessageInfo} is returned. + * The message is transferred into the given byte buffer starting at its + * current position and the buffers position is incremented by the number of + * bytes read. If there are fewer bytes remaining in the buffer than are + * required to hold the message, or the underlying input buffer does not + * contain the complete message, then an invocation of {@link + * MessageInfo#isComplete isComplete} on the returned {@code + * MessageInfo} will return {@code false}, and more invocations of this + * method will be necessary to completely consume the messgae. Only + * one message at a time will be partially delivered in any stream. The + * socket option {@link SctpStandardSocketOption#SCTP_FRAGMENT_INTERLEAVE + * SCTP_FRAGMENT_INTERLEAVE} controls various aspects of what interlacing of + * messages occurs. + * + *

If this method receives a notification then the appropriate method of + * the given handler, if there is one, is invoked. If the handler returns {@link + * HandlerResult#CONTINUE CONTINUE} then this method will try to receive another + * message/notification, otherwise, if {@link HandlerResult#RETURN RETURN} is returned + * this method will return {@code null}. If an uncaught exception is thrown by the + * handler it will be propagated up the stack through this method. + * + *

If a security manager has been installed then for each new association + * setup this method verifies that the associations source address and port + * number are permitted by the security manager's {@link + * java.lang.SecurityManager#checkAccept(String,int) checkAccept} method. + * + *

This method may be invoked at any time. If another thread has + * already initiated a receive operation upon this channel, then an + * invocation of this method will block until the first operation is + * complete. The given handler is invoked without holding any locks used + * to enforce the above synchronization policy, that way handlers + * will not stall other threads from receiving. A handler should not invoke + * the {@code receive} method of this channel, if it does an + * {@link IllegalReceiveException} will be thrown. + * + * @param buffer + * The buffer into which bytes are to be transferred + * + * @param attachment + * The object to attach to the receive operation; can be + * {@code null} + * + * @param handler + * A handler to handle notifications from the SCTP stack, or + * {@code null} to ignore any notifications. + * + * @return The {@code MessageInfo}, {@code null} if this channel is in + * non-blocking mode and no messages are immediately available or + * the notification handler returns {@code RETURN} after handling + * a notification + * + * @throws java.nio.channels.ClosedChannelException + * If this channel is closed + * + * @throws java.nio.channels.AsynchronousCloseException + * If another thread closes this channel + * while the read operation is in progress + * + * @throws java.nio.channels.ClosedByInterruptException + * If another thread interrupts the current thread + * while the read operation is in progress, thereby + * closing the channel and setting the current thread's + * interrupt status + * + * @throws NotYetBoundException + * If this channel is not yet bound + * + * @throws IllegalReceiveException + * If the given handler invokes the {@code receive} method of this + * channel + * + * @throws SecurityException + * If a security manager has been installed and it does not permit + * new associations to be accepted from the message's sender + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract MessageInfo receive(ByteBuffer buffer, + T attachment, + NotificationHandler handler) + throws IOException; + + /** + * Sends a message via this channel. + * + *

If this channel is unbound then this method will invoke {@link + * #bind(SocketAddress, int) bind(null, 0)} before sending any data. + * + *

If there is no association existing between this channel's socket + * and the intended receiver, identified by the address in the given messageInfo, then one + * will be automatically setup to the intended receiver. This is considered + * to be Implicit Association Setup. Upon successful association setup, an + * {@link AssociationChangeNotification association changed} + * notification will be put to the SCTP stack with its {@code event} parameter set + * to {@link AssociationChangeNotification.AssocChangeEvent#COMM_UP COMM_UP} + * . This notification can be received by invoking {@link #receive + * receive}. + * + *

If this channel is in blocking mode, there is sufficient room in the + * underlying output buffer, then the remaining bytes in the given byte + * buffer are transmitted as a single message. Sending a message + * is atomic unless explicit message completion {@link + * SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE SCTP_EXPLICIT_COMPLETE} + * socket option is enabled on this channel's socket. + * + *

If this channel is in non-blocking mode, there is sufficient room + * in the underlying output buffer, and an implicit association setup is + * required, then the remaining bytes in the given byte buffer are + * transmitted as a single message, subject to {@link + * SctpStandardSocketOption#SCTP_EXPLICIT_COMPLETE SCTP_EXPLICIT_COMPLETE}. + * If for any reason the message cannot + * be delivered an {@link AssociationChangeNotification association + * changed} notification is put on the SCTP stack with its {@code event} parameter set + * to {@link AssociationChangeNotification.AssocChangeEvent#CANT_START CANT_START}. + * + *

The message is transferred from the byte buffer as if by a regular + * {@link java.nio.channels.WritableByteChannel#write(java.nio.ByteBuffer) + * write} operation. + * + *

If a security manager has been installed then for each new association + * setup this method verifies that the given remote peers address and port + * number are permitted by the security manager's {@link + * java.lang.SecurityManager#checkConnect(String,int) checkConnect} method. + * + *

This method may be invoked at any time. If another thread has already + * initiated a send operation upon this channel, then an invocation of + * this method will block until the first operation is complete. + * + * @param buffer + * The buffer containing the message to be sent + * + * @param messageInfo + * Ancillary data about the message to be sent + * + * @return The number of bytes sent, which will be either the number of + * bytes that were remaining in the messages buffer when this method + * was invoked or, if this channel is non-blocking, may be zero if + * there was insufficient room for the message in the underlying + * output buffer + * + * @throws InvalidStreamExcepton + * If {@code streamNumber} is negative, or if an association already + * exists and {@code streamNumber} is greater than the maximum number + * of outgoing streams + * + * @throws java.nio.channels.ClosedChannelException + * If this channel is closed + * + * @throws java.nio.channels.AsynchronousCloseException + * If another thread closes this channel + * while the read operation is in progress + * + * @throws java.nio.channels.ClosedByInterruptException + * If another thread interrupts the current thread + * while the read operation is in progress, thereby + * closing the channel and setting the current thread's + * interrupt status + * + * @throws SecurityException + * If a security manager has been installed and it does not permit + * new associations to be setup with the the messages's address + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract int send(ByteBuffer buffer, MessageInfo messageInfo) + throws IOException; + + /** + * Branches off an association. + * + *

An application can invoke this method to branch off an association + * into a separate channel. The new bound and connected {@link SctpChannel} + * will be created for the association. The branched off association will no + * longer be part of this channel. + * + *

This is particularly useful when, for instance, the application + * wishes to have a number of sporadic message senders/receivers remain + * under the original SCTP multi channel but branch off those + * associations carrying high volume data traffic into their own + * separate SCTP channels. + * + * @param association + * The association to branch off + * + * @return The {@code SctpChannel} + * + * @throws java.nio.channels.ClosedChannelException + * If this channel is closed + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract SctpChannel branch(Association association) + throws IOException; +} diff --git a/src/share/classes/com/sun/nio/sctp/SctpServerChannel.java b/src/share/classes/com/sun/nio/sctp/SctpServerChannel.java new file mode 100644 index 000000000..eb79c40f5 --- /dev/null +++ b/src/share/classes/com/sun/nio/sctp/SctpServerChannel.java @@ -0,0 +1,421 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package com.sun.nio.sctp; + +import java.net.SocketAddress; +import java.net.InetAddress; +import java.io.IOException; +import java.util.Set; +import java.nio.channels.SelectionKey; +import java.nio.channels.spi.SelectorProvider; +import java.nio.channels.spi.AbstractSelectableChannel; + +/** + * A selectable channel for message-oriented listening SCTP sockets. + * + *

An {@code SCTPServerChannel} is created by invoking the + * {@link #open open} method of this class. A newly-created SCTP server + * channel is open but not yet bound. An attempt to invoke the + * {@link #accept accept} method of an unbound channel will cause the + * {@link java.nio.channels.NotYetBoundException} to be thrown. An SCTP server + * channel can be bound by invoking one of the + * {@link #bind(java.net.SocketAddress,int) bind} methods defined by this class. + * + *

Socket options are configured using the + * {@link #setOption(SctpSocketOption,Object) setOption} method. SCTP server socket + * channels support the following options: + *

+ * + * + * + * + * + * + * + * + * + *
Option NameDescription
{@link SctpStandardSocketOption#SCTP_INIT_MAXSTREAMS + * SCTP_INIT_MAXSTREAMS} The maximum number of streams requested by the local endpoint during + * association initialization
+ *
+ * Additional (implementation specific) options may also be supported. The list + * of options supported is obtained by invoking the {@link #supportedOptions() + * supportedOptions} method. + * + *

SCTP server channels are safe for use by multiple concurrent threads. + * + * @since 1.7 + */ +public abstract class SctpServerChannel + extends AbstractSelectableChannel +{ + /** + * Initializes a new instance of this class. + * + * @param provider + * The selector provider for this channel + */ + protected SctpServerChannel(SelectorProvider provider) { + super(provider); + } + + /** + * Opens an SCTP server channel. + * + *

The new channel's socket is initially unbound; it must be bound + * to a specific address via one of its socket's {@link #bind bind} + * methods before associations can be accepted. + * + * @return A new SCTP server channel + * + * @throws UnsupportedOperationException + * If the SCTP protocol is not supported + * + * @throws IOException + * If an I/O error occurs + */ + public static SctpServerChannel open() throws + IOException { + return new sun.nio.ch.SctpServerChannelImpl((SelectorProvider)null); + } + + /** + * Accepts an association on this channel's socket. + * + *

If this channel is in non-blocking mode then this method will + * immediately return {@code null} if there are no pending associations. + * Otherwise it will block indefinitely until a new association is + * available or an I/O error occurs. + * + *

The {@code SCTPChannel} returned by this method, if any, will be in + * blocking mode regardless of the blocking mode of this channel. + * + *

If a security manager has been installed then for each new + * association this method verifies that the address and port number of the + * assocaitions's remote peer are permitted by the security manager's {@link + * java.lang.SecurityManager#checkAccept(String,int) checkAccept} method. + * + * @return The SCTP channel for the new association, or {@code null} + * if this channel is in non-blocking mode and no association is + * available to be accepted + * + * @throws java.nio.channels.ClosedChannelException + * If this channel is closed + * + * @throws java.nio.channels.AsynchronousCloseException + * If another thread closes this channel + * while the accept operation is in progress + * + * @throws java.nio.channels.ClosedByInterruptException + * If another thread interrupts the current thread + * while the accept operation is in progress, thereby + * closing the channel and setting the current thread's + * interrupt status + * + * @throws java.nio.channels.NotYetBoundException + * If this channel's socket has not yet been bound + * + * @throws SecurityException + * If a security manager has been installed and it does not permit + * access to the remote peer of the new association + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract SctpChannel accept() throws IOException; + + /** + * Binds the channel's socket to a local address and configures the socket + * to listen for associations. + * + *

This method works as if invoking it were equivalent to evaluating the + * expression: + *

+     * bind(local, 0);
+     * 
+ * + * @param local + * The local address to bind the socket, or {@code null} to + * bind the socket to an automatically assigned socket address + * + * @return This channel + * + * @throws java.nio.channels.ClosedChannelException + * If this channel is closed + * + * @throws java.nio.channels.AlreadyBoundException + * If this channel is already bound + * + * @throws java.nio.channels.UnsupportedAddressTypeException + * If the type of the given address is not supported + * + * @throws SecurityException + * If a security manager has been installed and its {@link + * java.lang.SecurityManager#checkListen(int) checkListen} method + * denies the operation + * + * @throws IOException + * If some other I/O error occurs + */ + public final SctpServerChannel bind(SocketAddress local) + throws IOException { + return bind(local, 0); + } + + /** + * Binds the channel's socket to a local address and configures the socket + * to listen for associations. + * + *

This method is used to establish a relationship between the socket + * and the local address. Once a relationship is established then + * the socket remains bound until the channel is closed. This relationship + * may not necesssarily be with the address {@code local} as it may be + * removed by {@link #unbindAddress unbindAddress}, but there will always be + * at least one local address bound to the channel's socket once an + * invocation of this method successfully completes. + * + *

Once the channel's socket has been successfully bound to a specific + * address, that is not automatically assigned, more addresses + * may be bound to it using {@link #bindAddress bindAddress}, or removed + * using {@link #unbindAddress unbindAddress}. + * + *

The backlog parameter is the maximum number of pending associations + * on the socket. Its exact semantics are implementation specific. An + * implementation may impose an implementation specific maximum length or + * may choose to ignore the parameter. If the backlog parameter has the + * value {@code 0}, or a negative value, then an implementation specific + * default is used. + * + * @param local + * The local address to bind the socket, or {@code null} to + * bind the socket to an automatically assigned socket address + * + * @param backlog + * The maximum number number of pending associations + * + * @return This channel + * + * @throws java.nio.channels.ClosedChannelException + * If this channel is closed + * + * @throws java.nio.channels.AlreadyBoundException + * If this channel is already bound + * + * @throws java.nio.channels.UnsupportedAddressTypeException + * If the type of the given address is not supported + * + * @throws SecurityException + * If a security manager has been installed and its {@link + * java.lang.SecurityManager#checkListen(int) checkListen} method + * denies the operation + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract SctpServerChannel bind(SocketAddress local, + int backlog) + throws IOException; + + /** + * Adds the given address to the bound addresses for the channel's + * socket. + * + *

The given address must not be the {@link + * java.net.InetAddress#isAnyLocalAddress wildcard} address. + * The channel must be first bound using {@link #bind bind} before + * invoking this method, otherwise {@link + * java.nio.channels.NotYetBoundException} is thrown. The {@link #bind bind} + * method takes a {@code SocketAddress} as its argument which typically + * contains a port number as well as an address. Addresses subquently bound + * using this method are simply addresses as the SCTP port number remains + * the same for the lifetime of the channel. + * + *

New associations accepted after this method successfully completes + * will be associated with the given address. + * + * @param address + * The address to add to the bound addresses for the socket + * + * @return This channel + * + * @throws java.nio.channels.ClosedChannelException + * If this channel is closed + * + * @throws java.nio.channels.NotYetBoundException + * If this channel is not yet bound + * + * @throws java.nio.channels.AlreadyBoundException + * If this channel is already bound to the given address + * + * @throws IllegalArgumentException + * If address is {@code null} or the {@link + * java.net.InetAddress#isAnyLocalAddress wildcard} address + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract SctpServerChannel bindAddress(InetAddress address) + throws IOException; + + /** + * Removes the given address from the bound addresses for the channel's + * socket. + * + *

The given address must not be the {@link + * java.net.InetAddress#isAnyLocalAddress wildcard} address. + * The channel must be first bound using {@link #bind bind} before + * invoking this method, otherwise + * {@link java.nio.channels.NotYetBoundException} is thrown. + * If this method is invoked on a channel that does not have + * {@code address} as one of its bound addresses, or that has only one + * local address bound to it, then this method throws {@link + * IllegalUnbindException}. + * The initial address that the channel's socket is bound to using + * {@link #bind bind} may be removed from the bound addresses for the + * channel's socket. + * + *

New associations accepted after this method successfully completes + * will not be associated with the given address. + * + * @param address + * The address to remove from the bound addresses for the socket + * + * @return This channel + * + * @throws java.nio.channels.ClosedChannelException + * If this channel is closed + * + * @throws java.nio.channels.NotYetBoundException + * If this channel is not yet bound + * + * @throws IllegalArgumentException + * If address is {@code null} or the {@link + * java.net.InetAddress#isAnyLocalAddress wildcard} address + * + * @throws IllegalUnbindException + * If the implementation does not support removing addresses from a + * listening socket, {@code address} is not bound to the channel's + * socket, or the channel has only one address bound to it + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract SctpServerChannel unbindAddress(InetAddress address) + throws IOException; + + /** + * Returns all of the socket addresses to which this channel's socket is + * bound. + * + * @return All the socket addresses that this channel's socket is + * bound to, or an empty {@code Set} if the channel's socket is not + * bound + * + * @throws java.nio.channels.ClosedChannelException + * If the channel is closed + * + * @throws IOException + * If an I/O error occurs + */ + public abstract Set getAllLocalAddresses() + throws IOException; + + /** + * Returns the value of a socket option. + * + * @param name + * The socket option + * + * @return The value of the socket option. A value of {@code null} may be + * a valid value for some socket options. + * + * @throws UnsupportedOperationException + * If the socket option is not supported by this channel + * + * @throws java.nio.channels.ClosedChannelException + * If this channel is closed + * + * @throws IOException + * If an I/O error occurs + * + * @see SctpStandardSocketOption + */ + public abstract T getOption(SctpSocketOption name) throws IOException; + + /** + * Sets the value of a socket option. + * + * @param name + * The socket option + * + * @param value + * The value of the socket option. A value of {@code null} may be + * a valid value for some socket options. + * + * @return This channel + * + * @throws UnsupportedOperationException + * If the socket option is not supported by this channel + * + * @throws IllegalArgumentException + * If the value is not a valid value for this socket option + * + * @throws java.nio.channels.ClosedChannelException + * If this channel is closed + * + * @throws IOException + * If an I/O error occurs + * + * @see SctpStandardSocketOption + */ + public abstract SctpServerChannel setOption(SctpSocketOption name, + T value) + throws IOException; + + /** + * Returns a set of the socket options supported by this channel. + * + *

This method will continue to return the set of options even after the + * channel has been closed. + * + * @return A set of the socket options supported by this channel + */ + public abstract Set> supportedOptions(); + + /** + * Returns an operation set identifying this channel's supported + * operations. + * + *

SCTP server channels only support the accepting of new + * associations, so this method returns + * {@link java.nio.channels.SelectionKey#OP_ACCEPT}. + * + * @return The valid-operation set + */ + @Override + public final int validOps() { + return SelectionKey.OP_ACCEPT; + } +} diff --git a/src/share/classes/com/sun/nio/sctp/SctpSocketOption.java b/src/share/classes/com/sun/nio/sctp/SctpSocketOption.java new file mode 100644 index 000000000..091f91aec --- /dev/null +++ b/src/share/classes/com/sun/nio/sctp/SctpSocketOption.java @@ -0,0 +1,38 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package com.sun.nio.sctp; + +import java.net.SocketOption; + +/** + * A socket option associated with an SCTP channel. + * + * @param The type of the socket option value. + * + * @since 1.7 + * + * @see SctpStandardSocketOption + */ +public interface SctpSocketOption extends SocketOption { } diff --git a/src/share/classes/com/sun/nio/sctp/SctpStandardSocketOption.java b/src/share/classes/com/sun/nio/sctp/SctpStandardSocketOption.java new file mode 100644 index 000000000..ce581d3b6 --- /dev/null +++ b/src/share/classes/com/sun/nio/sctp/SctpStandardSocketOption.java @@ -0,0 +1,419 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package com.sun.nio.sctp; + +import java.net.SocketAddress; +import sun.nio.ch.SctpStdSocketOption; + +/** + * SCTP channels supports the socket options defined by this class + * (as well as those listed in the particular channel class) and may support + * additional Implementation specific socket options. + * + * @since 1.7 + */ +public class SctpStandardSocketOption { + private SctpStandardSocketOption() {} + /** + * Enables or disables message fragmentation. + * + *

The value of this socket option is a {@code Boolean} that represents + * whether the option is enabled or disabled. If enabled no SCTP message + * fragmentation will be performed. Instead if a message being sent + * exceeds the current PMTU size, the message will NOT be sent and + * an error will be indicated to the user. + * + *

It is implementation specific whether or not this option is + * supported. + */ + public static final SctpSocketOption SCTP_DISABLE_FRAGMENTS = new + SctpStdSocketOption("SCTP_DISABLE_FRAGMENTS", Boolean.class, + sun.nio.ch.SctpStdSocketOption.SCTP_DISABLE_FRAGMENTS); + + /** + * Enables or disables explicit message completion. + * + *

The value of this socket option is a {@code Boolean} that represents + * whether the option is enabled or disabled. When this option is enabled, + * the {@code send} method may be invoked multiple times to a send message. + * The {@code isComplete} parameter of the {@link MessageInfo} must only + * be set to {@code true} for the final send to indicate that the message is + * complete. If this option is disabled then each individual {@code send} + * invocation is considered complete. + * + *

The default value of the option is {@code false} indicating that the + * option is disabled. It is implementation specific whether or not this + * option is supported. + */ + public static final SctpSocketOption SCTP_EXPLICIT_COMPLETE = new + SctpStdSocketOption("SCTP_EXPLICIT_COMPLETE", Boolean.class, + sun.nio.ch.SctpStdSocketOption.SCTP_EXPLICIT_COMPLETE); + + /** + * Fragmented interleave controls how the presentation of messages occur + * for the message receiver. There are three levels of fragment interleave + * defined. Two of the levels effect {@link SctpChannel}, while + * {@link SctpMultiChannel} is effected by all three levels. + * + *

This option takes an {@code Integer} value. It can be set to a value + * of {@code 0}, {@code 1} or {@code 2}. + * + *

Setting the three levels provides the following receiver + * interactions: + * + *

{@code level 0} - Prevents the interleaving of any messages. This + * means that when a partial delivery begins, no other messages will be + * received except the message being partially delivered. If another message + * arrives on a different stream (or association) that could be delivered, + * it will be blocked waiting for the user to read all of the partially + * delivered message. + * + *

{@code level 1} - Allows interleaving of messages that are from + * different associations. For {@code SctpChannel}, level 0 and + * level 1 have the same meaning since an {@code SctpChannel} always + * receives messages from the same association. Note that setting an {@code + * SctpMultiChannel} to this level may cause multiple partial + * delivers from different associations but for any given association, only + * one message will be delivered until all parts of a message have been + * delivered. This means that one large message, being read with an + * association identification of "X", will block other messages from + * association "X" from being delivered. + * + *

{@code level 2} - Allows complete interleaving of messages. This + * level requires that the sender carefully observe not only the peer + * {@code Association} but also must pay careful attention to the stream + * number. With this option enabled a partially delivered message may begin + * being delivered for association "X" stream "Y" and the next subsequent + * receive may return a message from association "X" stream "Z". Note that + * no other messages would be delivered for association "X" stream "Y" + * until all of stream "Y"'s partially delivered message was read. + * Note that this option effects both channel types. Also note that + * for an {@code SctpMultiChannel} not only may another streams + * message from the same association be delivered from the next receive, + * some other associations message may be delivered upon the next receive. + * + *

It is implementation specific whether or not this option is + * supported. + */ + public static final SctpSocketOption SCTP_FRAGMENT_INTERLEAVE = + new SctpStdSocketOption("SCTP_FRAGMENT_INTERLEAVE", + Integer.class, + sun.nio.ch.SctpStdSocketOption.SCTP_FRAGMENT_INTERLEAVE); + + /** + * The maximum number of streams requested by the local endpoint during + * association initialization. + * + *

The value of this socket option is an {@link + * SctpStandardSocketOption.InitMaxStreams InitMaxStreams}, that represents + * the maximum number of inbound and outbound streams that an association + * on the channel is prepared to support. + * + *

For an {@link SctpChannel} this option may only be used to + * change the number of inbound/outbound streams prior to connecting. + * + *

For an {@link SctpMultiChannel} this option determines + * the maximum number of inbound/outbound streams new associations setup + * on the channel will be prepared to support. + * + *

For an {@link SctpServerChannel} this option determines the + * maximum number of inbound/outbound streams accepted sockets will + * negotiate with their connecting peer. + * + *

In all cases the value set by this option is used in the negotiation + * of new associations setup on the channel's socket and the actual + * maximum number of inbound/outbound streams that have been negotiated + * with the peer can be retrieved from the appropriate {@link + * Association}. The {@code Association} can be retrieved from the + * {@link AssociationChangeNotification.AssocChangeEvent#COMM_UP COMM_UP} + * {@link AssociationChangeNotification} belonging to that association. + * + *

This value is bounded by the actual implementation. In other + * words the user may be able to support more streams than the Operating + * System. In such a case, the Operating System limit may override the + * value requested by the user. The default value of 0 indicates to use + * the endpoints default value. + */ + public static final SctpSocketOption + SCTP_INIT_MAXSTREAMS = + new SctpStdSocketOption( + "SCTP_INIT_MAXSTREAMS", SctpStandardSocketOption.InitMaxStreams.class); + + /** + * Enables or disables a Nagle-like algorithm. + * + *

The value of this socket option is a {@code Boolean} that represents + * whether the option is enabled or disabled. SCTP uses an algorithm like + * The Nagle Algorithm to coalesce short segments and + * improve network efficiency. + */ + public static final SctpSocketOption SCTP_NODELAY = + new SctpStdSocketOption("SCTP_NODELAY", Boolean.class, + sun.nio.ch.SctpStdSocketOption.SCTP_NODELAY); + + /** + * Requests that the local SCTP stack use the given peer address as + * the association primary. + * + *

The value of this socket option is a {@code SocketAddress} + * that represents the peer address that the local SCTP stack should use as + * the association primary. The address must be one of the association + * peer's addresses. + * + *

An {@code SctpMultiChannel} can control more than one + * association, the association parameter must be given when setting or + * retrieving this option. + * + *

Since {@code SctpChannel} only controls one association, + * the association parameter is not required and this option can be + * set or queried directly. + */ + public static final SctpSocketOption SCTP_PRIMARY_ADDR = + new SctpStdSocketOption + ("SCTP_PRIMARY_ADDR", SocketAddress.class); + + /** + * Requests that the peer mark the enclosed address as the association + * primary. + * + *

The value of this socket option is a {@code SocketAddress} + * that represents the local address that the peer should use as its + * primary address. The given address must be one of the association's + * locally bound addresses. + * + *

An {@code SctpMultiChannel} can control more than one + * association, the association parameter must be given when setting or + * retrieving this option. + * + *

Since {@code SctpChannel} only controls one association, + * the association parameter is not required and this option can be + * queried directly. + * + *

Note, this is a set only option and cannot be retrieved by {@code + * getOption}. It is implementation specific whether or not this + * option is supported. + */ + public static final SctpSocketOption SCTP_SET_PEER_PRIMARY_ADDR = + new SctpStdSocketOption + ("SCTP_SET_PEER_PRIMARY_ADDR", SocketAddress.class); + + /** + * The size of the socket send buffer. + * + *

The value of this socket option is an {@code Integer} that is the + * size of the socket send buffer in bytes. The socket send buffer is an + * output buffer used by the networking implementation. It may need to be + * increased for high-volume connections. The value of the socket option is + * a hint to the implementation to size the buffer and the actual + * size may differ. The socket option can be queried to retrieve the actual + * size. + * + *

For {@code SctpChannel}, this controls the amount of data + * the SCTP stack may have waiting in internal buffers to be sent. This + * option therefore bounds the maximum size of data that can be sent in a + * single send call. + * + *

For {@code SctpMultiChannel}, the effect is the same as for {@code + * SctpChannel}, except that it applies to all associations. The option + * applies to each association's window size separately. + * + *

An implementation allows this socket option to be set before the + * socket is bound or connected. Whether an implementation allows the + * socket send buffer to be changed after the socket is bound is system + * dependent. + */ + public static final SctpSocketOption SO_SNDBUF = + new SctpStdSocketOption("SO_SNDBUF", Integer.class, + sun.nio.ch.SctpStdSocketOption.SO_SNDBUF); + + /** + * The size of the socket receive buffer. + * + *

The value of this socket option is an {@code Integer} that is the + * size of the socket receive buffer in bytes. The socket receive buffer is + * an input buffer used by the networking implementation. It may need to be + * increased for high-volume connections or decreased to limit the possible + * backlog of incoming data. The value of the socket option is a + * hint to the implementation to size the buffer and the actual + * size may differ. + * + *

For {@code SctpChannel}, this controls the receiver window size. + * + *

For {@code SctpMultiChannel}, the meaning is implementation + * dependent. It might control the receive buffer for each association bound + * to the socket descriptor or it might control the receive buffer for the + * whole socket. + * + *

An implementation allows this socket option to be set before the + * socket is bound or connected. Whether an implementation allows the + * socket receive buffer to be changed after the socket is bound is system + * dependent. + */ + public static final SctpSocketOption SO_RCVBUF = + new SctpStdSocketOption("SO_RCVBUF", Integer.class, + sun.nio.ch.SctpStdSocketOption.SO_RCVBUF); + + /** + * Linger on close if data is present. + * + *

The value of this socket option is an {@code Integer} that controls + * the action taken when unsent data is queued on the socket and a method + * to close the socket is invoked. If the value of the socket option is zero + * or greater, then it represents a timeout value, in seconds, known as the + * linger interval. The linger interval is the timeout for the + * {@code close} method to block while the operating system attempts to + * transmit the unsent data or it decides that it is unable to transmit the + * data. If the value of the socket option is less than zero then the option + * is disabled. In that case the {@code close} method does not wait until + * unsent data is transmitted; if possible the operating system will transmit + * any unsent data before the connection is closed. + * + *

This socket option is intended for use with sockets that are configured + * in {@link java.nio.channels.SelectableChannel#isBlocking() blocking} mode + * only. The behavior of the {@code close} method when this option is + * enabled on a non-blocking socket is not defined. + * + *

The initial value of this socket option is a negative value, meaning + * that the option is disabled. The option may be enabled, or the linger + * interval changed, at any time. The maximum value of the linger interval + * is system dependent. Setting the linger interval to a value that is + * greater than its maximum value causes the linger interval to be set to + * its maximum value. + */ + public static final SctpSocketOption SO_LINGER = + new SctpStdSocketOption("SO_LINGER", Integer.class, + sun.nio.ch.SctpStdSocketOption.SO_LINGER); + + /** + * This class is used to set the maximum number of inbound/outbound streams + * used by the local endpoint during association initialization. An + * instance of this class is used to set the {@link + * SctpStandardSocketOption#SCTP_INIT_MAXSTREAMS SCTP_INIT_MAXSTREAMS} + * socket option. + * + * @since 1.7 + */ + public static class InitMaxStreams { + private int maxInStreams; + private int maxOutStreams; + + private InitMaxStreams(int maxInStreams, int maxOutStreams) { + this.maxInStreams = maxInStreams; + this.maxOutStreams = maxOutStreams; + } + + /** + * Creates an InitMaxStreams instance. + * + * @param maxInStreams + * The maximum number of inbound streams, where + * {@code 0 <= maxInStreams <= 65536} + * + * @param maxOutStreams + * The maximum number of outbound streams, where + * {@code 0 <= maxOutStreams <= 65536} + * + * @return An {@code InitMaxStreams} instance + * + * @throws IllegalArgumentException + * If an argument is outside of specified bounds + */ + public static InitMaxStreams create + (int maxInStreams, int maxOutStreams) { + if (maxOutStreams < 0 || maxOutStreams > 65535) + throw new IllegalArgumentException( + "Invalid maxOutStreams value"); + if (maxInStreams < 0 || maxInStreams > 65535) + throw new IllegalArgumentException( + "Invalid maxInStreams value"); + + return new InitMaxStreams(maxInStreams, maxOutStreams); + } + + /** + * Returns the maximum number of inbound streams. + * + * @return Maximum inbound streams + */ + public int maxInStreams() { + return maxInStreams; + } + + /** + * Returns the maximum number of outbound streams. + * + * @return Maximum outbound streams + */ + public int maxOutStreams() { + return maxOutStreams; + } + + /** + * Returns a string representation of this init max streams, including + * the maximum in and out bound streams. + * + * @return A string representation of this init max streams + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(super.toString()).append(" ["); + sb.append("maxInStreams:").append(maxInStreams); + sb.append("maxOutStreams:").append(maxOutStreams).append("]"); + return sb.toString(); + } + + /** + * Returns true if the specified object is another {@code InitMaxStreams} + * instance with the same number of in and out bound streams. + * + * @param obj + * The object to be compared with this init max streams + * + * @return true if the specified object is another + * {@code InitMaxStreams} instance with the same number of in + * and out bound streams + */ + @Override + public boolean equals(Object obj) { + if (obj != null && obj instanceof InitMaxStreams) { + InitMaxStreams that = (InitMaxStreams) obj; + if (this.maxInStreams == that.maxInStreams && + this.maxOutStreams == that.maxOutStreams) + return true; + } + return false; + } + + /** + * Returns a hash code value for this init max streams. + */ + @Override + public int hashCode() { + int hash = 7 ^ maxInStreams ^ maxOutStreams; + return hash; + } + } +} diff --git a/src/share/classes/com/sun/nio/sctp/SendFailedNotification.java b/src/share/classes/com/sun/nio/sctp/SendFailedNotification.java new file mode 100644 index 000000000..9df25f02e --- /dev/null +++ b/src/share/classes/com/sun/nio/sctp/SendFailedNotification.java @@ -0,0 +1,89 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package com.sun.nio.sctp; + +import java.nio.ByteBuffer; +import java.net.SocketAddress; + +/** + * Notification emitted when a send failed notification has been received. + * + *

A send failed notification indicates that a message cannot be delivered. + * Typically this is because the association has been shutdown with unsent data + * in the socket output buffer, or in the case of a {@link SctpMultiChannel} + * the association failed to setup. + * + * @since 1.7 + */ +public abstract class SendFailedNotification implements Notification { + /** + * Initializes a new instance of this class. + */ + protected SendFailedNotification() {} + + /** + * Returns the association that this notification is applicable to. + * + * @return The association that failed to send, or {@code null} if + * there is no association, that is, the notification follows a + * {@linkplain + * com.sun.nio.sctp.AssociationChangeNotification.AssocChangeEvent#CANT_START} + */ + @Override + public abstract Association association(); + + /** + * Returns the address. + * + * @return The peer primary address of the association or the address that + * the message was sent to + */ + public abstract SocketAddress address(); + + /** + * Returns the data that was to be sent. + * + * @return The user data. The buffers position will be {@code 0} and its + * limit will be set to the end of the data. + */ + public abstract ByteBuffer buffer(); + + /** + * Returns the error code. + * + *

The errorCode gives the reason why the send failed, and if set, will + * be a SCTP protocol error code as defined in RFC2960 section 3.3.10 + * + * @return The error code + */ + public abstract int errorCode(); + + /** + * Returns the stream number that the messge was to be sent on. + * + * @return The stream number + */ + public abstract int streamNumber(); +} diff --git a/src/share/classes/com/sun/nio/sctp/ShutdownNotification.java b/src/share/classes/com/sun/nio/sctp/ShutdownNotification.java new file mode 100644 index 000000000..027b09737 --- /dev/null +++ b/src/share/classes/com/sun/nio/sctp/ShutdownNotification.java @@ -0,0 +1,47 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package com.sun.nio.sctp; + +/** + * Notification emitted when a peers shutdowns an the association. + * + *

When a peer sends a SHUTDOWN, the SCTP stack delivers this + * notification to inform the application that it should cease sending data. + * + * @since 1.7 + */ +public abstract class ShutdownNotification implements Notification { + /** + * Initializes a new instance of this class. + */ + protected ShutdownNotification() {} + + /** + * Returns the association that this notification is applicable to. + * + * @return The association that received the shutdown + */ + public abstract Association association(); +} diff --git a/src/share/classes/com/sun/nio/sctp/package-info.java b/src/share/classes/com/sun/nio/sctp/package-info.java new file mode 100644 index 000000000..808c6a4eb --- /dev/null +++ b/src/share/classes/com/sun/nio/sctp/package-info.java @@ -0,0 +1,75 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/** + * A Java API for Stream Control Transport Protocol. + * + *

The Stream Control Transport Protocol (SCTP) is a reliable, + * message-oriented, transport protocol existing at an equivalent level with UDP + * (User Datagram Protocol) and TCP (Transmission Control Protocol). SCTP is + * session oriented and an association between the endpoints must be established + * before any data can be transmitted. + * + *

SCTP has direct support for multi-homing, meaning than an endpoint may be + * represented by more than one address and each address may be used for sending + * and receiving data, thus providing network redundancy. The connection between + * two endpoints is referred to as an association between those endpoints. + * Endpoints can exchange a list of addresses during association setup. One + * address is designated as the primary address, this is the default address that + * the peer will use for sending data. A single port number is used across the + * entire address list at an endpoint for a specific session. + * + *

SCTP is message based. I/O operations operate upon messages and message + * boundaries are preserved. Each association may support multiple independant + * logical streams. Each stream represents a sequence of messages within a single + * association and streams are independant of one another, meaning that stream + * identifiers and sequence numbers are included in the data packet to allow + * sequencing of messages on a per-stream basis. + * + *

This package provides two programming model styles. The one-to-one style + * supported by {@link com.sun.nio.sctp.SctpChannel} and {@link + * com.sun.nio.sctp.SctpServerChannel}, and the one-to-many + * style supported by {@link com.sun.nio.sctp.SctpMultiChannel}. + * The semantics of the one-to-one style interface are very similar to TCP. + * An {@code SctpChannel} can only control one SCTP association. The + * semantics of the one-to-many style interface are very similar to UDP. An + * {@code SctpMutliChannel} can control multiple SCTP associations. + * + *

Applications can send and receive per-message ancillary information through + * {@link com.sun.nio.sctp.MessageInfo}. For example, the stream number that + * the message it is to be sent or received from. The SCTP stack is event driven + * and applications can receive notifications of certain SCTP events by invoking + * the {@code receive} method of the SCTP channel with an appropriate {@link + * com.sun.nio.sctp.NotificationHandler notification handler}. + * + *

The SCTP protocol is defined by + * RFC4960, and the optional + * extension for Dynamic Address Reconfiguration is defined by + * RFC5061. + * + * @since 1.7 + */ + +package com.sun.nio.sctp; diff --git a/src/share/classes/sun/nio/ch/SctpMessageInfoImpl.java b/src/share/classes/sun/nio/ch/SctpMessageInfoImpl.java new file mode 100644 index 000000000..cc5a06467 --- /dev/null +++ b/src/share/classes/sun/nio/ch/SctpMessageInfoImpl.java @@ -0,0 +1,170 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package sun.nio.ch; + +import java.net.SocketAddress; +import com.sun.nio.sctp.MessageInfo; +import com.sun.nio.sctp.Association; + +/** + * An implementation of a MessageInfo. + */ +public class SctpMessageInfoImpl extends MessageInfo { + private final SocketAddress address; + private final int bytes; /* 0 */ + + private Association association; + private int assocId; + private int streamNumber; + private boolean complete = true; + private boolean unordered; /* false */ + private long timeToLive; /* 0L */ + private int ppid; /* 0 */ + + public SctpMessageInfoImpl(Association association, + SocketAddress address, + int streamNumber) { + this.association = association; + this.address = address; + this.streamNumber = streamNumber; + bytes = 0; + } + + /* Invoked from native */ + private SctpMessageInfoImpl(int assocId, + SocketAddress address, + int bytes, + int streamNumber, + boolean complete, + boolean unordered, + int ppid) { + this.assocId = assocId; + this.address = address; + this.bytes = bytes; + this.streamNumber = streamNumber; + this.complete = complete; + this.unordered = unordered; + this.ppid = ppid; + } + + @Override + public Association association() { + return association; + } + + /** + * SctpMessageInfoImpl instances created from native will need to have their + * association set from the channel. + */ + void setAssociation(Association association) { + this.association = association; + } + + int associationID() { + return assocId; + } + + @Override + public SocketAddress address() { + return address; + } + + @Override + public int bytes() { + return bytes; + } + + @Override + public int streamNumber() { + return streamNumber; + } + + @Override + public MessageInfo streamNumber(int streamNumber) { + if (streamNumber < 0 || streamNumber > 65536) + throw new IllegalArgumentException("Invalid stream number"); + + this.streamNumber = streamNumber; + return this; + } + + @Override + public int payloadProtocolID() { + return ppid; + } + + @Override + public MessageInfo payloadProtocolID(int ppid) { + this.ppid = ppid; + return this; + } + + @Override + public boolean isComplete() { + return complete; + } + + @Override + public MessageInfo complete(boolean complete) { + this.complete = complete; + return this; + } + + @Override + public boolean isUnordered() { + return unordered; + } + + @Override + public MessageInfo unordered(boolean unordered) { + this.unordered = unordered; + return this; + } + + @Override + public long timeToLive() { + return timeToLive; + } + + @Override + public MessageInfo timeToLive(long millis) { + timeToLive = millis; + return this; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(super.toString()); + sb.append( "[Address: ").append(address) + .append(", Association: ").append(association) + .append(", Assoc ID: ").append(assocId) + .append(", Bytes: ").append(bytes) + .append(", Stream Number: ").append(streamNumber) + .append(", Complete: ").append(complete) + .append(", isUnordered: ").append(unordered) + .append("]"); + return sb.toString(); + } +} diff --git a/src/share/classes/sun/nio/ch/SctpStdSocketOption.java b/src/share/classes/sun/nio/ch/SctpStdSocketOption.java new file mode 100644 index 000000000..34f917a53 --- /dev/null +++ b/src/share/classes/sun/nio/ch/SctpStdSocketOption.java @@ -0,0 +1,76 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package sun.nio.ch; + +import com.sun.nio.sctp.SctpSocketOption; + +public class SctpStdSocketOption + implements SctpSocketOption +{ + /* for native mapping of int options */ + public static final int SCTP_DISABLE_FRAGMENTS = 1; + public static final int SCTP_EXPLICIT_COMPLETE = 2; + public static final int SCTP_FRAGMENT_INTERLEAVE = 3; + public static final int SCTP_NODELAY = 4; + public static final int SO_SNDBUF = 5; + public static final int SO_RCVBUF = 6; + public static final int SO_LINGER = 7; + + private final String name; + private final Class type; + + /* for native mapping of int options */ + private int constValue; + + public SctpStdSocketOption(String name, Class type) { + this.name = name; + this.type = type; + } + + public SctpStdSocketOption(String name, Class type, int constValue) { + this.name = name; + this.type = type; + this.constValue = constValue; + } + + @Override + public String name() { + return name; + } + + @Override + public Class type() { + return type; + } + + @Override + public String toString() { + return name; + } + + int constValue() { + return constValue; + } +} diff --git a/src/solaris/classes/sun/nio/ch/SctpAssocChange.java b/src/solaris/classes/sun/nio/ch/SctpAssocChange.java new file mode 100644 index 000000000..53638b138 --- /dev/null +++ b/src/solaris/classes/sun/nio/ch/SctpAssocChange.java @@ -0,0 +1,120 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package sun.nio.ch; + +import com.sun.nio.sctp.Association; +import com.sun.nio.sctp.AssociationChangeNotification; + +/** + * An implementation of AssociationChangeNotification + */ +public class SctpAssocChange extends AssociationChangeNotification + implements SctpNotification +{ + /* static final ints so that they can be referenced from native */ + private final static int SCTP_COMM_UP = 1; + private final static int SCTP_COMM_LOST = 2; + private final static int SCTP_RESTART = 3; + private final static int SCTP_SHUTDOWN = 4; + private final static int SCTP_CANT_START = 5; + + private Association association; + + /* assocId is used to lookup the association before the notification is + * returned to user code */ + private int assocId; + private AssocChangeEvent event; + private int maxOutStreams; + private int maxInStreams; + + /* Invoked from native */ + private SctpAssocChange(int assocId, + int intEvent, + int maxOutStreams, + int maxInStreams) { + switch (intEvent) { + case SCTP_COMM_UP : + this.event = AssocChangeEvent.COMM_UP; + break; + case SCTP_COMM_LOST : + this.event = AssocChangeEvent.COMM_LOST; + break; + case SCTP_RESTART : + this.event = AssocChangeEvent.RESTART; + break; + case SCTP_SHUTDOWN : + this.event = AssocChangeEvent.SHUTDOWN; + break; + case SCTP_CANT_START : + this.event = AssocChangeEvent.CANT_START; + break; + default : + throw new AssertionError( + "Unknown Association Change Event type: " + intEvent); + } + + this.assocId = assocId; + this.maxOutStreams = maxOutStreams; + this.maxInStreams = maxInStreams; + } + + @Override + public int assocId() { + return assocId; + } + + @Override + public void setAssociation(Association association) { + this.association = association; + } + + @Override + public Association association() { + assert association != null; + return association; + } + + @Override + public AssocChangeEvent event() { + return event; + } + + int maxOutStreams() { + return maxOutStreams; + } + + int maxInStreams() { + return maxInStreams; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(super.toString()).append(" ["); + sb.append("Association:").append(association); + sb.append(", Event: ").append(event).append("]"); + return sb.toString(); + } +} diff --git a/src/solaris/classes/sun/nio/ch/SctpAssociationImpl.java b/src/solaris/classes/sun/nio/ch/SctpAssociationImpl.java new file mode 100644 index 000000000..0d580c947 --- /dev/null +++ b/src/solaris/classes/sun/nio/ch/SctpAssociationImpl.java @@ -0,0 +1,52 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package sun.nio.ch; + +import com.sun.nio.sctp.Association; + +/** + * An implementation of Association + */ +public class SctpAssociationImpl extends Association { + public SctpAssociationImpl(int associationID, + int maxInStreams, + int maxOutStreams) { + super(associationID, maxInStreams, maxOutStreams); + } + + @Override + public String toString() { + StringBuffer sb = new StringBuffer(super.toString()); + return sb.append("[associationID:") + .append(associationID()) + .append(", maxIn:") + .append(maxInboundStreams()) + .append(", maxOut:") + .append(maxOutboundStreams()) + .append("]") + .toString(); + } +} + diff --git a/src/solaris/classes/sun/nio/ch/SctpChannelImpl.java b/src/solaris/classes/sun/nio/ch/SctpChannelImpl.java new file mode 100644 index 000000000..0be9bea85 --- /dev/null +++ b/src/solaris/classes/sun/nio/ch/SctpChannelImpl.java @@ -0,0 +1,1073 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package sun.nio.ch; + +import java.net.InetAddress; +import java.net.SocketAddress; +import java.net.InetSocketAddress; +import java.io.FileDescriptor; +import java.io.IOException; +import java.util.Collections; +import java.util.Set; +import java.util.HashSet; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.ConnectionPendingException; +import java.nio.channels.NoConnectionPendingException; +import java.nio.channels.AlreadyBoundException; +import java.nio.channels.AlreadyConnectedException; +import java.nio.channels.NotYetBoundException; +import java.nio.channels.NotYetConnectedException; +import java.nio.channels.spi.SelectorProvider; +import com.sun.nio.sctp.AbstractNotificationHandler; +import com.sun.nio.sctp.Association; +import com.sun.nio.sctp.AssociationChangeNotification; +import com.sun.nio.sctp.HandlerResult; +import com.sun.nio.sctp.IllegalReceiveException; +import com.sun.nio.sctp.InvalidStreamException; +import com.sun.nio.sctp.IllegalUnbindException; +import com.sun.nio.sctp.MessageInfo; +import com.sun.nio.sctp.NotificationHandler; +import com.sun.nio.sctp.SctpChannel; +import com.sun.nio.sctp.SctpSocketOption; +import sun.nio.ch.NativeDispatcher; +import sun.nio.ch.PollArrayWrapper; +import sun.nio.ch.SelChImpl; +import static com.sun.nio.sctp.SctpStandardSocketOption.*; +import static sun.nio.ch.SctpResultContainer.SEND_FAILED; +import static sun.nio.ch.SctpResultContainer.ASSOCIATION_CHANGED; +import static sun.nio.ch.SctpResultContainer.PEER_ADDRESS_CHANGED; +import static sun.nio.ch.SctpResultContainer.SHUTDOWN; + +/** + * An implementation of an SctpChannel + */ +public class SctpChannelImpl extends SctpChannel + implements SelChImpl +{ + /* Used to make native close and preClose calls */ + private static NativeDispatcher nd; + + private final FileDescriptor fd; + + private final int fdVal; + + /* IDs of native threads doing send and receivess, for signalling */ + private volatile long receiverThread = 0; + private volatile long senderThread = 0; + + /* Lock held by current receiving or connecting thread */ + private final Object receiveLock = new Object(); + + /* Lock held by current sending or connecting thread */ + private final Object sendLock = new Object(); + + private final ThreadLocal receiveInvoked = + new ThreadLocal() { + @Override protected Boolean initialValue() { + return Boolean.FALSE; + } + }; + + /* Lock held by any thread that modifies the state fields declared below + DO NOT invoke a blocking I/O operation while holding this lock! */ + private final Object stateLock = new Object(); + + private enum ChannelState { + UNINITIALIZED, + UNCONNECTED, + PENDING, + CONNECTED, + KILLPENDING, + KILLED, + } + /* -- The following fields are protected by stateLock -- */ + private ChannelState state = ChannelState.UNINITIALIZED; + + /* Binding; Once bound the port will remain constant. */ + int port = -1; + private HashSet localAddresses = new HashSet(); + /* Has the channel been bound to the wildcard address */ + private boolean wildcard; /* false */ + //private InetSocketAddress remoteAddress = null; + + /* Input/Output open */ + private boolean readyToConnect; + + /* Shutdown */ + private boolean isShutdown; + + private Association association; + + /* -- End of fields protected by stateLock -- */ + + private SctpResultContainer commUpResultContainer; /* null */ + + /** + * Constructor for normal connecting sockets + */ + public SctpChannelImpl(SelectorProvider provider) throws IOException { + //TODO: update provider remove public modifier + super(provider); + this.fd = SctpNet.socket(true); + this.fdVal = IOUtil.fdVal(fd); + this.state = ChannelState.UNCONNECTED; + } + + /** + * Constructor for sockets obtained from server sockets + */ + public SctpChannelImpl(SelectorProvider provider, FileDescriptor fd) + throws IOException { + super(provider); + this.fd = fd; + this.fdVal = IOUtil.fdVal(fd); + this.state = ChannelState.CONNECTED; + port = (Net.localAddress(fd)).getPort(); + + /* Receive COMM_UP */ + ByteBuffer buf = Util.getTemporaryDirectBuffer(50); + try { + receive(buf, null, null, true); + } finally { + Util.releaseTemporaryDirectBuffer(buf); + } + } + + /** + * Binds the channel's socket to a local address. + */ + @Override + public SctpChannel bind(SocketAddress local) throws IOException { + synchronized (receiveLock) { + synchronized (sendLock) { + synchronized (stateLock) { + ensureOpenAndUnconnected(); + if (isBound()) + throw new AlreadyBoundException(); + InetSocketAddress isa = (local == null) ? + new InetSocketAddress(0) : Net.checkAddress(local); + Net.bind(fd, isa.getAddress(), isa.getPort()); + InetSocketAddress boundIsa = Net.localAddress(fd); + port = boundIsa.getPort(); + localAddresses.add(isa); + if (isa.getAddress().isAnyLocalAddress()) + wildcard = true; + } + } + } + return this; + } + + @Override + public SctpChannel bindAddress(InetAddress address) + throws IOException { + bindUnbindAddress(address, true); + localAddresses.add(new InetSocketAddress(address, port)); + return this; + } + + @Override + public SctpChannel unbindAddress(InetAddress address) + throws IOException { + bindUnbindAddress(address, false); + localAddresses.remove(new InetSocketAddress(address, port)); + return this; + } + + private SctpChannel bindUnbindAddress(InetAddress address, boolean add) + throws IOException { + if (address == null) + throw new IllegalArgumentException(); + + synchronized (receiveLock) { + synchronized (sendLock) { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (!isBound()) + throw new NotYetBoundException(); + if (wildcard) + throw new IllegalStateException( + "Cannot add or remove addresses from a channel that is bound to the wildcard address"); + if (address.isAnyLocalAddress()) + throw new IllegalArgumentException( + "Cannot add or remove the wildcard address"); + if (add) { + for (InetSocketAddress addr : localAddresses) { + if (addr.getAddress().equals(address)) { + throw new AlreadyBoundException(); + } + } + } else { /*removing */ + /* Verify that there is more than one address + * and that address is already bound */ + if (localAddresses.size() <= 1) + throw new IllegalUnbindException("Cannot remove address from a channel with only one address bound"); + boolean foundAddress = false; + for (InetSocketAddress addr : localAddresses) { + if (addr.getAddress().equals(address)) { + foundAddress = true; + break; + } + } + if (!foundAddress ) + throw new IllegalUnbindException("Cannot remove address from a channel that is not bound to that address"); + } + + SctpNet.bindx(fdVal, new InetAddress[]{address}, port, add); + + /* Update our internal Set to reflect the addition/removal */ + if (add) + localAddresses.add(new InetSocketAddress(address, port)); + else { + for (InetSocketAddress addr : localAddresses) { + if (addr.getAddress().equals(address)) { + localAddresses.remove(addr); + break; + } + } + } + } + } + } + return this; + } + + private boolean isBound() { + synchronized (stateLock) { + return port == -1 ? false : true; + } + } + + private boolean isConnected() { + synchronized (stateLock) { + return (state == ChannelState.CONNECTED); + } + } + + private void ensureOpenAndUnconnected() throws IOException { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (isConnected()) + throw new AlreadyConnectedException(); + if (state == ChannelState.PENDING) + throw new ConnectionPendingException(); + } + } + + private boolean ensureReceiveOpen() throws ClosedChannelException { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (!isConnected()) + throw new NotYetConnectedException(); + else + return true; + } + } + + private void ensureSendOpen() throws ClosedChannelException { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (isShutdown) + throw new ClosedChannelException(); + if (!isConnected()) + throw new NotYetConnectedException(); + } + } + + private void receiverCleanup() throws IOException { + synchronized (stateLock) { + receiverThread = 0; + if (state == ChannelState.KILLPENDING) + kill(); + } + } + + private void senderCleanup() throws IOException { + synchronized (stateLock) { + senderThread = 0; + if (state == ChannelState.KILLPENDING) + kill(); + } + } + + @Override + public Association association() throws ClosedChannelException { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (!isConnected()) + return null; + + return association; + } + } + + @Override + public boolean connect(SocketAddress endpoint) throws IOException { + synchronized (receiveLock) { + synchronized (sendLock) { + ensureOpenAndUnconnected(); + InetSocketAddress isa = Net.checkAddress(endpoint); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkConnect(isa.getAddress().getHostAddress(), + isa.getPort()); + synchronized (blockingLock()) { + int n = 0; + try { + try { + begin(); + synchronized (stateLock) { + if (!isOpen()) { + return false; + } + receiverThread = NativeThread.current(); + } + for (;;) { + InetAddress ia = isa.getAddress(); + if (ia.isAnyLocalAddress()) + ia = InetAddress.getLocalHost(); + n = Net.connect(fd, ia, isa.getPort()); + if ( (n == IOStatus.INTERRUPTED) + && isOpen()) + continue; + break; + } + } finally { + receiverCleanup(); + end((n > 0) || (n == IOStatus.UNAVAILABLE)); + assert IOStatus.check(n); + } + } catch (IOException x) { + /* If an exception was thrown, close the channel after + * invoking end() so as to avoid bogus + * AsynchronousCloseExceptions */ + close(); + throw x; + } + + if (n > 0) { + synchronized (stateLock) { + /* Connection succeeded */ + state = ChannelState.CONNECTED; + if (!isBound()) { + InetSocketAddress boundIsa = + Net.localAddress(fd); + port = boundIsa.getPort(); + } + + /* Receive COMM_UP */ + ByteBuffer buf = Util.getTemporaryDirectBuffer(50); + try { + receive(buf, null, null, true); + } finally { + Util.releaseTemporaryDirectBuffer(buf); + } + return true; + } + } else { + synchronized (stateLock) { + /* If nonblocking and no exception then connection + * pending; disallow another invocation */ + if (!isBlocking()) + state = ChannelState.PENDING; + else + assert false; + } + } + } + return false; + } + } + } + + @Override + public boolean connect(SocketAddress endpoint, + int maxOutStreams, + int maxInStreams) + throws IOException { + return setOption(SCTP_INIT_MAXSTREAMS, InitMaxStreams. + create(maxInStreams, maxOutStreams)).connect(endpoint); + + } + + @Override + public boolean isConnectionPending() { + synchronized (stateLock) { + return (state == ChannelState.PENDING); + } + } + + @Override + public boolean finishConnect() throws IOException { + synchronized (receiveLock) { + synchronized (sendLock) { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (isConnected()) + return true; + if (state != ChannelState.PENDING) + throw new NoConnectionPendingException(); + } + int n = 0; + try { + try { + begin(); + synchronized (blockingLock()) { + synchronized (stateLock) { + if (!isOpen()) { + return false; + } + receiverThread = NativeThread.current(); + } + if (!isBlocking()) { + for (;;) { + n = checkConnect(fd, false, readyToConnect); + if ( (n == IOStatus.INTERRUPTED) + && isOpen()) + continue; + break; + } + } else { + for (;;) { + n = checkConnect(fd, true, readyToConnect); + if (n == 0) { + // Loop in case of + // spurious notifications + continue; + } + if ( (n == IOStatus.INTERRUPTED) + && isOpen()) + continue; + break; + } + } + } + } finally { + synchronized (stateLock) { + receiverThread = 0; + if (state == ChannelState.KILLPENDING) { + kill(); + /* poll()/getsockopt() does not report + * error (throws exception, with n = 0) + * on Linux platform after dup2 and + * signal-wakeup. Force n to 0 so the + * end() can throw appropriate exception */ + n = 0; + } + } + end((n > 0) || (n == IOStatus.UNAVAILABLE)); + assert IOStatus.check(n); + } + } catch (IOException x) { + /* If an exception was thrown, close the channel after + * invoking end() so as to avoid bogus + * AsynchronousCloseExceptions */ + close(); + throw x; + } + + if (n > 0) { + synchronized (stateLock) { + state = ChannelState.CONNECTED; + if (!isBound()) { + InetSocketAddress boundIsa = + Net.localAddress(fd); + port = boundIsa.getPort(); + } + + /* Receive COMM_UP */ + ByteBuffer buf = Util.getTemporaryDirectBuffer(50); + try { + receive(buf, null, null, true); + } finally { + Util.releaseTemporaryDirectBuffer(buf); + } + return true; + } + } + } + } + return false; + } + + @Override + protected void implConfigureBlocking(boolean block) throws IOException { + IOUtil.configureBlocking(fd, block); + } + + @Override + public void implCloseSelectableChannel() throws IOException { + synchronized (stateLock) { + nd.preClose(fd); + + if (receiverThread != 0) + NativeThread.signal(receiverThread); + + if (senderThread != 0) + NativeThread.signal(senderThread); + + if (!isRegistered()) + kill(); + } + } + + @Override + public FileDescriptor getFD() { + return fd; + } + + @Override + public int getFDVal() { + return fdVal; + } + + /** + * Translates native poll revent ops into a ready operation ops + */ + private boolean translateReadyOps(int ops, int initialOps, SelectionKeyImpl sk) { + int intOps = sk.nioInterestOps(); + int oldOps = sk.nioReadyOps(); + int newOps = initialOps; + + if ((ops & PollArrayWrapper.POLLNVAL) != 0) { + /* This should only happen if this channel is pre-closed while a + * selection operation is in progress + * ## Throw an error if this channel has not been pre-closed */ + return false; + } + + if ((ops & (PollArrayWrapper.POLLERR + | PollArrayWrapper.POLLHUP)) != 0) { + newOps = intOps; + sk.nioReadyOps(newOps); + /* No need to poll again in checkConnect, + * the error will be detected there */ + readyToConnect = true; + return (newOps & ~oldOps) != 0; + } + + if (((ops & PollArrayWrapper.POLLIN) != 0) && + ((intOps & SelectionKey.OP_READ) != 0) && + isConnected()) + newOps |= SelectionKey.OP_READ; + + if (((ops & PollArrayWrapper.POLLCONN) != 0) && + ((intOps & SelectionKey.OP_CONNECT) != 0) && + ((state == ChannelState.UNCONNECTED) || (state == ChannelState.PENDING))) { + newOps |= SelectionKey.OP_CONNECT; + readyToConnect = true; + } + + if (((ops & PollArrayWrapper.POLLOUT) != 0) && + ((intOps & SelectionKey.OP_WRITE) != 0) && + isConnected()) + newOps |= SelectionKey.OP_WRITE; + + sk.nioReadyOps(newOps); + return (newOps & ~oldOps) != 0; + } + + @Override + public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) { + return translateReadyOps(ops, sk.nioReadyOps(), sk); + } + + @Override + @SuppressWarnings("all") + public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) { + return translateReadyOps(ops, 0, sk); + } + + @Override + public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) { + int newOps = 0; + if ((ops & SelectionKey.OP_READ) != 0) + newOps |= PollArrayWrapper.POLLIN; + if ((ops & SelectionKey.OP_WRITE) != 0) + newOps |= PollArrayWrapper.POLLOUT; + if ((ops & SelectionKey.OP_CONNECT) != 0) + newOps |= PollArrayWrapper.POLLCONN; + sk.selector.putEventOps(sk, newOps); + } + + @Override + public void kill() throws IOException { + synchronized (stateLock) { + if (state == ChannelState.KILLED) + return; + if (state == ChannelState.UNINITIALIZED) { + state = ChannelState.KILLED; + return; + } + assert !isOpen() && !isRegistered(); + + /* Postpone the kill if there is a waiting reader + * or writer thread. */ + if (receiverThread == 0 && senderThread == 0) { + nd.close(fd); + state = ChannelState.KILLED; + } else { + state = ChannelState.KILLPENDING; + } + } + } + + @Override + public SctpChannel setOption(SctpSocketOption name, T value) + throws IOException { + if (name == null) + throw new NullPointerException(); + if (!supportedOptions().contains(name)) + throw new UnsupportedOperationException("'" + name + "' not supported"); + + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + + SctpNet.setSocketOption(fdVal, name, value, 0 /*oneToOne*/); + } + return this; + } + + @Override + @SuppressWarnings("unchecked") + public T getOption(SctpSocketOption name) throws IOException { + if (name == null) + throw new NullPointerException(); + if (!supportedOptions().contains(name)) + throw new UnsupportedOperationException("'" + name + "' not supported"); + + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + + return (T)SctpNet.getSocketOption(fdVal, name, 0 /*oneToOne*/); + } + } + + private static class DefaultOptionsHolder { + static final Set> defaultOptions = defaultOptions(); + + private static Set> defaultOptions() { + HashSet> set = new HashSet>(10); + set.add(SCTP_DISABLE_FRAGMENTS); + set.add(SCTP_EXPLICIT_COMPLETE); + set.add(SCTP_FRAGMENT_INTERLEAVE); + set.add(SCTP_INIT_MAXSTREAMS); + set.add(SCTP_NODELAY); + set.add(SCTP_PRIMARY_ADDR); + set.add(SCTP_SET_PEER_PRIMARY_ADDR); + set.add(SO_SNDBUF); + set.add(SO_RCVBUF); + set.add(SO_LINGER); + return Collections.unmodifiableSet(set); + } + } + + @Override + public final Set> supportedOptions() { + return DefaultOptionsHolder.defaultOptions; + } + + @Override + public MessageInfo receive(ByteBuffer buffer, + T attachment, + NotificationHandler handler) + throws IOException { + return receive(buffer, attachment, handler, false); + } + + private MessageInfo receive(ByteBuffer buffer, + T attachment, + NotificationHandler handler, + boolean fromConnect) + throws IOException { + if (buffer == null) + throw new IllegalArgumentException("buffer cannot be null"); + + if (buffer.isReadOnly()) + throw new IllegalArgumentException("Read-only buffer"); + + if (receiveInvoked.get()) + throw new IllegalReceiveException( + "cannot invoke receive from handler"); + receiveInvoked.set(Boolean.TRUE); + + try { + SctpResultContainer resultContainer = new SctpResultContainer(); + do { + resultContainer.clear(); + synchronized (receiveLock) { + if (!ensureReceiveOpen()) + return null; + + if (commUpResultContainer != null) { + resultContainer = commUpResultContainer; + commUpResultContainer = null; + continue; + } + + int n = 0; + try { + begin(); + + synchronized (stateLock) { + if(!isOpen()) + return null; + receiverThread = NativeThread.current(); + } + + do { + n = receive(fdVal, buffer, resultContainer); + } while ((n == IOStatus.INTERRUPTED) && isOpen()); + } finally { + receiverCleanup(); + end((n > 0) || (n == IOStatus.UNAVAILABLE)); + assert IOStatus.check(n); + } + + if (!resultContainer.isNotification()) { + /* message or nothing */ + if (resultContainer.hasSomething()) { + /* Set the association before returning */ + SctpMessageInfoImpl info = + resultContainer.getMessageInfo(); + synchronized (stateLock) { + assert association != null; + info.setAssociation(association); + } + return info; + } else + /* Non-blocking may return null if nothing available*/ + return null; + } else { /* notification */ + synchronized (stateLock) { + handleNotificationInternal( + resultContainer); + } + } + + if (fromConnect) { + /* If we reach here, then it was connect that invoked + * receive an received the COMM_UP. Save it and allow + * the user handler to process it upon next receive. */ + commUpResultContainer = resultContainer; + return null; + } + } /* receiveLock */ + } while (handler == null ? true : + (invokeNotificationHandler(resultContainer, handler, attachment) + == HandlerResult.CONTINUE)); + + return null; + } finally { + receiveInvoked.set(Boolean.FALSE); + } + } + + private int receive(int fd, + ByteBuffer dst, + SctpResultContainer resultContainer) + throws IOException { + int pos = dst.position(); + int lim = dst.limit(); + assert (pos <= lim); + int rem = (pos <= lim ? lim - pos : 0); + if (dst instanceof DirectBuffer && rem > 0) + return receiveIntoNativeBuffer(fd, resultContainer, dst, rem, pos); + + /* Substitute a native buffer */ + int newSize = Math.max(rem, 1); + ByteBuffer bb = Util.getTemporaryDirectBuffer(newSize); + try { + int n = receiveIntoNativeBuffer(fd, resultContainer, bb, newSize, 0); + bb.flip(); + if (n > 0 && rem > 0) + dst.put(bb); + return n; + } finally { + Util.releaseTemporaryDirectBuffer(bb); + } + } + + private int receiveIntoNativeBuffer(int fd, + SctpResultContainer resultContainer, + ByteBuffer bb, + int rem, + int pos) + throws IOException + { + int n = receive0(fd, resultContainer, ((DirectBuffer)bb).address() + pos, rem); + + if (n > 0) + bb.position(pos + n); + return n; + } + + private InternalNotificationHandler internalNotificationHandler = + new InternalNotificationHandler(); + + private void handleNotificationInternal(SctpResultContainer resultContainer) + { + invokeNotificationHandler(resultContainer, + internalNotificationHandler, null); + } + + private class InternalNotificationHandler + extends AbstractNotificationHandler + { + @Override + public HandlerResult handleNotification( + AssociationChangeNotification not, T unused) { + if (not.event().equals( + AssociationChangeNotification.AssocChangeEvent.COMM_UP)) { + assert association == null; + SctpAssocChange sac = (SctpAssocChange) not; + association = new SctpAssociationImpl + (sac.assocId(), sac.maxInStreams(), sac.maxOutStreams()); + } + return HandlerResult.CONTINUE; + } + } + + private HandlerResult invokeNotificationHandler + (SctpResultContainer resultContainer, + NotificationHandler handler, + T attachment) { + SctpNotification notification = resultContainer.notification(); + synchronized (stateLock) { + notification.setAssociation(association); + } + + if (!(handler instanceof AbstractNotificationHandler)) { + return handler.handleNotification(notification, attachment); + } + + /* AbstractNotificationHandler */ + AbstractNotificationHandler absHandler = + (AbstractNotificationHandler)handler; + switch(resultContainer.type()) { + case ASSOCIATION_CHANGED : + return absHandler.handleNotification( + resultContainer.getAssociationChanged(), attachment); + case PEER_ADDRESS_CHANGED : + return absHandler.handleNotification( + resultContainer.getPeerAddressChanged(), attachment); + case SEND_FAILED : + return absHandler.handleNotification( + resultContainer.getSendFailed(), attachment); + case SHUTDOWN : + return absHandler.handleNotification( + resultContainer.getShutdown(), attachment); + default : + /* implementation specific handlers */ + return absHandler.handleNotification( + resultContainer.notification(), attachment); + } + } + + private void checkAssociation(Association sendAssociation) { + synchronized (stateLock) { + if (sendAssociation != null && !sendAssociation.equals(association)) { + throw new IllegalArgumentException( + "Cannot send to another association"); + } + } + } + + private void checkStreamNumber(int streamNumber) { + synchronized (stateLock) { + if (association != null) { + if (streamNumber < 0 || + streamNumber >= association.maxOutboundStreams()) + throw new InvalidStreamException(); + } + } + } + + /* TODO: Add support for ttl and isComplete to both 121 12M + * SCTP_EOR not yet supported on reference platforms + * TTL support limited... + */ + @Override + public int send(ByteBuffer buffer, MessageInfo messageInfo) + throws IOException { + if (buffer == null) + throw new IllegalArgumentException("buffer cannot be null"); + + if (messageInfo == null) + throw new IllegalArgumentException("messageInfo cannot be null"); + + checkAssociation(messageInfo.association()); + checkStreamNumber(messageInfo.streamNumber()); + + synchronized (sendLock) { + ensureSendOpen(); + + int n = 0; + try { + begin(); + + synchronized (stateLock) { + if(!isOpen()) + return 0; + senderThread = NativeThread.current(); + } + + do { + n = send(fdVal, buffer, messageInfo); + } while ((n == IOStatus.INTERRUPTED) && isOpen()); + + return IOStatus.normalize(n); + } finally { + senderCleanup(); + end((n > 0) || (n == IOStatus.UNAVAILABLE)); + assert IOStatus.check(n); + } + } + } + + private int send(int fd, ByteBuffer src, MessageInfo messageInfo) + throws IOException { + int streamNumber = messageInfo.streamNumber(); + SocketAddress target = messageInfo.address(); + boolean unordered = messageInfo.isUnordered(); + int ppid = messageInfo.payloadProtocolID(); + int pos = src.position(); + int lim = src.limit(); + + assert (pos <= lim && streamNumber > 0); + int rem = (pos <= lim ? lim - pos : 0); + + if (src instanceof DirectBuffer) + return sendFromNativeBuffer(fd, src, rem, pos, target, streamNumber, + unordered, ppid); + + /* Substitute a native buffer */ + ByteBuffer bb = Util.getTemporaryDirectBuffer(rem); + try { + bb.put(src); + bb.flip(); + /* Do not update src until we see how many bytes were written */ + src.position(pos); + + int n = sendFromNativeBuffer(fd, bb, rem, pos, target, streamNumber, + unordered, ppid); + if (n > 0) { + /* now update src */ + src.position(pos + n); + } + return n; + } finally { + Util.releaseTemporaryDirectBuffer(bb); + } + } + + private int sendFromNativeBuffer(int fd, + ByteBuffer bb, + int rem, + int pos, + SocketAddress target, + int streamNumber, + boolean unordered, + int ppid) + throws IOException { + int written = send0(fd, ((DirectBuffer)bb).address() + pos, + rem, target, -1 /*121*/, streamNumber, unordered, ppid); + if (written > 0) + bb.position(pos + written); + return written; + } + + @Override + public SctpChannel shutdown() throws IOException { + synchronized(stateLock) { + if (isShutdown) + return this; + + ensureSendOpen(); + SctpNet.shutdown(fdVal, -1); + if (senderThread != 0) + NativeThread.signal(senderThread); + isShutdown = true; + } + return this; + } + + @Override + public Set getAllLocalAddresses() + throws IOException { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (!isBound()) + return Collections.EMPTY_SET; + + return SctpNet.getLocalAddresses(fdVal); + } + } + + @Override + public Set getRemoteAddresses() + throws IOException { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (!isConnected()) + return Collections.EMPTY_SET; + + return SctpNet.getRemoteAddresses(fdVal, 0/*unused*/); + } + } + + /* Native */ + private static native void initIDs(); + + static native int receive0(int fd, SctpResultContainer resultContainer, + long address, int length) throws IOException; + + static native int send0(int fd, long address, int length, + SocketAddress target, int assocId, int streamNumber, + boolean unordered, int ppid) throws IOException; + + private static native int checkConnect(FileDescriptor fd, boolean block, + boolean ready) throws IOException; + + static { + Util.load(); /* loads nio & net native libraries */ + java.security.AccessController.doPrivileged( + new sun.security.action.LoadLibraryAction("sctp")); + initIDs(); + nd = new SctpSocketDispatcher(); + } +} diff --git a/src/solaris/classes/sun/nio/ch/SctpMultiChannelImpl.java b/src/solaris/classes/sun/nio/ch/SctpMultiChannelImpl.java new file mode 100644 index 000000000..c34e062a6 --- /dev/null +++ b/src/solaris/classes/sun/nio/ch/SctpMultiChannelImpl.java @@ -0,0 +1,959 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package sun.nio.ch; + +import java.net.InetAddress; +import java.net.SocketAddress; +import java.net.InetSocketAddress; +import java.io.FileDescriptor; +import java.io.IOException; +import java.util.Collections; +import java.util.Set; +import java.util.HashSet; +import java.util.HashMap; +import java.nio.ByteBuffer; +import java.nio.channels.SelectionKey; +import java.nio.channels.AlreadyBoundException; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.NotYetBoundException; +import java.nio.channels.spi.SelectorProvider; +import com.sun.nio.sctp.AbstractNotificationHandler; +import com.sun.nio.sctp.Association; +import com.sun.nio.sctp.AssociationChangeNotification; +import com.sun.nio.sctp.HandlerResult; +import com.sun.nio.sctp.IllegalReceiveException; +import com.sun.nio.sctp.InvalidStreamException; +import com.sun.nio.sctp.IllegalUnbindException; +import com.sun.nio.sctp.NotificationHandler; +import com.sun.nio.sctp.MessageInfo; +import com.sun.nio.sctp.SctpChannel; +import com.sun.nio.sctp.SctpMultiChannel; +import com.sun.nio.sctp.SctpSocketOption; +import static com.sun.nio.sctp.SctpStandardSocketOption.*; +import static sun.nio.ch.SctpResultContainer.*; + +/** + * An implementation of SctpMultiChannel + */ +public class SctpMultiChannelImpl extends SctpMultiChannel + implements SelChImpl +{ + /* Used to make native close and preClose calls */ + private static NativeDispatcher nd; + + private final FileDescriptor fd; + + private final int fdVal; + + /* IDs of native threads doing send and receives, for signalling */ + private volatile long receiverThread = 0; + private volatile long senderThread = 0; + + /* Lock held by current receiving thread */ + private final Object receiveLock = new Object(); + + /* Lock held by current sending thread */ + private final Object sendLock = new Object(); + + /* Lock held by any thread that modifies the state fields declared below + * DO NOT invoke a blocking I/O operation while holding this lock! */ + private final Object stateLock = new Object(); + + private enum ChannelState { + UNINITIALIZED, + KILLPENDING, + KILLED, + } + + /* -- The following fields are protected by stateLock -- */ + private ChannelState state = ChannelState.UNINITIALIZED; + + /* Binding: Once bound the port will remain constant. */ + int port = -1; + private HashSet localAddresses = new HashSet(); + /* Has the channel been bound to the wildcard address */ + private boolean wildcard; /* false */ + + /* Keeps a map of addresses to association, and visa versa */ + private HashMap addressMap = + new HashMap(); + private HashMap> associationMap = + new HashMap>(); + + /* -- End of fields protected by stateLock -- */ + + /* If an association has been shutdown mark it for removal after + * the user handler has been invoked */ + private final ThreadLocal associationToRemove = + new ThreadLocal() { + @Override protected Association initialValue() { + return null; + } + }; + + /* A notification handler cannot invoke receive */ + private final ThreadLocal receiveInvoked = + new ThreadLocal() { + @Override protected Boolean initialValue() { + return Boolean.FALSE; + } + }; + + public SctpMultiChannelImpl(SelectorProvider provider) + throws IOException { + //TODO: update provider, remove public modifier + super(provider); + this.fd = SctpNet.socket(false /*one-to-many*/); + this.fdVal = IOUtil.fdVal(fd); + } + + @Override + public SctpMultiChannel bind(SocketAddress local, int backlog) + throws IOException { + synchronized (receiveLock) { + synchronized (sendLock) { + synchronized (stateLock) { + ensureOpen(); + if (isBound()) + throw new AlreadyBoundException(); + InetSocketAddress isa = (local == null) ? + new InetSocketAddress(0) : Net.checkAddress(local); + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkListen(isa.getPort()); + Net.bind(fd, isa.getAddress(), isa.getPort()); + + InetSocketAddress boundIsa = Net.localAddress(fd); + port = boundIsa.getPort(); + localAddresses.add(isa); + if (isa.getAddress().isAnyLocalAddress()) + wildcard = true; + + Net.listen(fd, backlog < 1 ? 50 : backlog); + } + } + } + return this; + } + + @Override + public SctpMultiChannel bindAddress(InetAddress address) + throws IOException { + return bindUnbindAddress(address, true); + } + + @Override + public SctpMultiChannel unbindAddress(InetAddress address) + throws IOException { + return bindUnbindAddress(address, false); + } + + private SctpMultiChannel bindUnbindAddress(InetAddress address, + boolean add) + throws IOException { + if (address == null) + throw new IllegalArgumentException(); + + synchronized (receiveLock) { + synchronized (sendLock) { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (!isBound()) + throw new NotYetBoundException(); + if (wildcard) + throw new IllegalStateException( + "Cannot add or remove addresses from a channel that is bound to the wildcard address"); + if (address.isAnyLocalAddress()) + throw new IllegalArgumentException( + "Cannot add or remove the wildcard address"); + if (add) { + for (InetSocketAddress addr : localAddresses) { + if (addr.getAddress().equals(address)) { + throw new AlreadyBoundException(); + } + } + } else { /*removing */ + /* Verify that there is more than one address + * and that address is already bound */ + if (localAddresses.size() <= 1) + throw new IllegalUnbindException("Cannot remove address from a channel with only one address bound"); + boolean foundAddress = false; + for (InetSocketAddress addr : localAddresses) { + if (addr.getAddress().equals(address)) { + foundAddress = true; + break; + } + } + if (!foundAddress ) + throw new IllegalUnbindException("Cannot remove address from a channel that is not bound to that address"); + } + + SctpNet.bindx(fdVal, new InetAddress[]{address}, port, add); + + /* Update our internal Set to reflect the addition/removal */ + if (add) + localAddresses.add(new InetSocketAddress(address, port)); + else { + for (InetSocketAddress addr : localAddresses) { + if (addr.getAddress().equals(address)) { + localAddresses.remove(addr); + break; + } + } + } + } + } + } + return this; + } + + @Override + public Set associations() + throws ClosedChannelException, NotYetBoundException { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (!isBound()) + throw new NotYetBoundException(); + + return Collections.unmodifiableSet(associationMap.keySet()); + } + } + + private boolean isBound() { + synchronized (stateLock) { + return port == -1 ? false : true; + } + } + + private void ensureOpen() throws IOException { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + } + } + + private void receiverCleanup() throws IOException { + synchronized (stateLock) { + receiverThread = 0; + if (state == ChannelState.KILLPENDING) + kill(); + } + } + + private void senderCleanup() throws IOException { + synchronized (stateLock) { + senderThread = 0; + if (state == ChannelState.KILLPENDING) + kill(); + } + } + + @Override + protected void implConfigureBlocking(boolean block) throws IOException { + IOUtil.configureBlocking(fd, block); + } + + @Override + public void implCloseSelectableChannel() throws IOException { + synchronized (stateLock) { + nd.preClose(fd); + + if (receiverThread != 0) + NativeThread.signal(receiverThread); + + if (senderThread != 0) + NativeThread.signal(senderThread); + + if (!isRegistered()) + kill(); + } + } + + @Override + public FileDescriptor getFD() { + return fd; + } + + @Override + public int getFDVal() { + return fdVal; + } + + /** + * Translates native poll revent ops into a ready operation ops + */ + private boolean translateReadyOps(int ops, int initialOps, + SelectionKeyImpl sk) { + int intOps = sk.nioInterestOps(); + int oldOps = sk.nioReadyOps(); + int newOps = initialOps; + + if ((ops & PollArrayWrapper.POLLNVAL) != 0) { + /* This should only happen if this channel is pre-closed while a + * selection operation is in progress + * ## Throw an error if this channel has not been pre-closed */ + return false; + } + + if ((ops & (PollArrayWrapper.POLLERR + | PollArrayWrapper.POLLHUP)) != 0) { + newOps = intOps; + sk.nioReadyOps(newOps); + return (newOps & ~oldOps) != 0; + } + + if (((ops & PollArrayWrapper.POLLIN) != 0) && + ((intOps & SelectionKey.OP_READ) != 0)) + newOps |= SelectionKey.OP_READ; + + if (((ops & PollArrayWrapper.POLLOUT) != 0) && + ((intOps & SelectionKey.OP_WRITE) != 0)) + newOps |= SelectionKey.OP_WRITE; + + sk.nioReadyOps(newOps); + return (newOps & ~oldOps) != 0; + } + + @Override + public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) { + return translateReadyOps(ops, sk.nioReadyOps(), sk); + } + + @Override + public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) { + return translateReadyOps(ops, 0, sk); + } + + @Override + public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) { + int newOps = 0; + if ((ops & SelectionKey.OP_READ) != 0) + newOps |= PollArrayWrapper.POLLIN; + if ((ops & SelectionKey.OP_WRITE) != 0) + newOps |= PollArrayWrapper.POLLOUT; + sk.selector.putEventOps(sk, newOps); + } + + @Override + public void kill() throws IOException { + synchronized (stateLock) { + if (state == ChannelState.KILLED) + return; + if (state == ChannelState.UNINITIALIZED) { + state = ChannelState.KILLED; + return; + } + assert !isOpen() && !isRegistered(); + + /* Postpone the kill if there is a thread sending or receiving. */ + if (receiverThread == 0 && senderThread == 0) { + nd.close(fd); + state = ChannelState.KILLED; + } else { + state = ChannelState.KILLPENDING; + } + } + } + + @Override + public SctpMultiChannel setOption(SctpSocketOption name, + T value, + Association association) + throws IOException { + if (name == null) + throw new NullPointerException(); + if (!(supportedOptions().contains(name))) + throw new UnsupportedOperationException("'" + name + "' not supported"); + + synchronized (stateLock) { + if (association != null && (name.equals(SCTP_PRIMARY_ADDR) || + name.equals(SCTP_SET_PEER_PRIMARY_ADDR))) { + checkAssociation(association); + } + if (!isOpen()) + throw new ClosedChannelException(); + + SctpNet.setSocketOption(fdVal, name, value, + association.associationID()); + } + return this; + } + + @Override + @SuppressWarnings("unchecked") + public T getOption(SctpSocketOption name, Association association) + throws IOException { + if (name == null) + throw new NullPointerException(); + if (!supportedOptions().contains(name)) + throw new UnsupportedOperationException("'" + name + "' not supported"); + + synchronized (stateLock) { + checkAssociation(association); + if (!isOpen()) + throw new ClosedChannelException(); + + return (T)SctpNet.getSocketOption(fdVal, name, + association.associationID()); + } + } + + private static class DefaultOptionsHolder { + static final Set> defaultOptions = defaultOptions(); + + private static Set> defaultOptions() { + HashSet> set = new HashSet>(10); + set.add(SCTP_DISABLE_FRAGMENTS); + set.add(SCTP_EXPLICIT_COMPLETE); + set.add(SCTP_FRAGMENT_INTERLEAVE); + set.add(SCTP_INIT_MAXSTREAMS); + set.add(SCTP_NODELAY); + set.add(SCTP_PRIMARY_ADDR); + set.add(SCTP_SET_PEER_PRIMARY_ADDR); + set.add(SO_SNDBUF); + set.add(SO_RCVBUF); + set.add(SO_LINGER); + return Collections.unmodifiableSet(set); + } + } + + @Override + public final Set> supportedOptions() { + return DefaultOptionsHolder.defaultOptions; + } + + @Override + public MessageInfo receive(ByteBuffer buffer, + T attachment, + NotificationHandler handler) + throws IOException { + if (buffer == null) + throw new IllegalArgumentException("buffer cannot be null"); + + if (buffer.isReadOnly()) + throw new IllegalArgumentException("Read-only buffer"); + + if (receiveInvoked.get()) + throw new IllegalReceiveException( + "cannot invoke receive from handler"); + receiveInvoked.set(Boolean.TRUE); + + try { + SctpResultContainer resultContainer = new SctpResultContainer(); + do { + resultContainer.clear(); + synchronized (receiveLock) { + ensureOpen(); + if (!isBound()) + throw new NotYetBoundException(); + + int n = 0; + try { + begin(); + + synchronized (stateLock) { + if(!isOpen()) + return null; + receiverThread = NativeThread.current(); + } + + do { + n = receive(fdVal, buffer, resultContainer); + } while ((n == IOStatus.INTERRUPTED) && isOpen()); + + } finally { + receiverCleanup(); + end((n > 0) || (n == IOStatus.UNAVAILABLE)); + assert IOStatus.check(n); + } + + if (!resultContainer.isNotification()) { + /* message or nothing */ + if (resultContainer.hasSomething()) { + /* Set the association before returning */ + SctpMessageInfoImpl info = + resultContainer.getMessageInfo(); + info.setAssociation(lookupAssociation(info. + associationID())); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + InetSocketAddress isa = (InetSocketAddress)info.address(); + if (!addressMap.containsKey(isa)) { + /* must be a new association */ + try { + sm.checkAccept(isa.getAddress().getHostAddress(), + isa.getPort()); + } catch (SecurityException se) { + buffer.clear(); + throw se; + } + } + } + + assert info.association() != null; + return info; + } else { + /* Non-blocking may return null if nothing available*/ + return null; + } + } else { /* notification */ + synchronized (stateLock) { + handleNotificationInternal( + resultContainer); + } + } + } /* receiveLock */ + } while (handler == null ? true : + (invokeNotificationHandler(resultContainer, handler, attachment) + == HandlerResult.CONTINUE)); + } finally { + receiveInvoked.set(Boolean.FALSE); + } + + return null; + } + + private int receive(int fd, + ByteBuffer dst, + SctpResultContainer resultContainer) + throws IOException { + int pos = dst.position(); + int lim = dst.limit(); + assert (pos <= lim); + int rem = (pos <= lim ? lim - pos : 0); + if (dst instanceof DirectBuffer && rem > 0) + return receiveIntoNativeBuffer(fd, resultContainer, dst, rem, pos); + + /* Substitute a native buffer. */ + int newSize = Math.max(rem, 1); + ByteBuffer bb = Util.getTemporaryDirectBuffer(newSize); + try { + int n = receiveIntoNativeBuffer(fd, resultContainer, bb, newSize, 0); + bb.flip(); + if (n > 0 && rem > 0) + dst.put(bb); + return n; + } finally { + Util.releaseTemporaryDirectBuffer(bb); + } + } + + private int receiveIntoNativeBuffer(int fd, + SctpResultContainer resultContainer, + ByteBuffer bb, + int rem, + int pos) + throws IOException { + int n = receive0(fd, resultContainer, ((DirectBuffer)bb).address() + pos, rem); + if (n > 0) + bb.position(pos + n); + return n; + } + + private InternalNotificationHandler internalNotificationHandler = + new InternalNotificationHandler(); + + private void handleNotificationInternal(SctpResultContainer resultContainer) + { + invokeNotificationHandler(resultContainer, + internalNotificationHandler, null); + } + + private class InternalNotificationHandler + extends AbstractNotificationHandler + { + @Override + public HandlerResult handleNotification( + AssociationChangeNotification not, T unused) { + SctpAssocChange sac = (SctpAssocChange) not; + + /* Update map to reflect change in association */ + switch (not.event()) { + case COMM_UP : + Association newAssociation = new SctpAssociationImpl + (sac.assocId(), sac.maxInStreams(), sac.maxOutStreams()); + addAssociation(newAssociation); + break; + case SHUTDOWN : + case COMM_LOST : + //case RESTART: ??? + /* mark association for removal after user handler invoked*/ + associationToRemove.set(lookupAssociation(sac.assocId())); + } + return HandlerResult.CONTINUE; + } + } + + private HandlerResult invokeNotificationHandler( + SctpResultContainer resultContainer, + NotificationHandler handler, + T attachment) { + HandlerResult result; + SctpNotification notification = resultContainer.notification(); + notification.setAssociation(lookupAssociation(notification.assocId())); + + if (!(handler instanceof AbstractNotificationHandler)) { + result = handler.handleNotification(notification, attachment); + } else { /* AbstractNotificationHandler */ + AbstractNotificationHandler absHandler = + (AbstractNotificationHandler)handler; + switch(resultContainer.type()) { + case ASSOCIATION_CHANGED : + result = absHandler.handleNotification( + resultContainer.getAssociationChanged(), attachment); + case PEER_ADDRESS_CHANGED : + result = absHandler.handleNotification( + resultContainer.getPeerAddressChanged(), attachment); + case SEND_FAILED : + result = absHandler.handleNotification( + resultContainer.getSendFailed(), attachment); + case SHUTDOWN : + result = absHandler.handleNotification( + resultContainer.getShutdown(), attachment); + default : + /* implementation specific handlers */ + result = absHandler.handleNotification( + resultContainer.notification(), attachment); + } + } + + if (!(handler instanceof InternalNotificationHandler)) { + /* Only remove associations after user handler + * has finished with them */ + Association assoc = associationToRemove.get(); + if (assoc != null) { + removeAssociation(assoc); + associationToRemove.set(null); + } + + } + + return result; + } + + private Association lookupAssociation(int assocId) { + /* Lookup the association in our internal map */ + synchronized (stateLock) { + Set assocs = associationMap.keySet(); + for (Association a : assocs) { + if (a.associationID() == assocId) { + return a; + } + } + } + return null; + } + + private void addAssociation(Association association) { + synchronized (stateLock) { + int assocId = association.associationID(); + Set addresses = null; + + try { + addresses = SctpNet.getRemoteAddresses(fdVal, assocId); + } catch (IOException unused) { + /* OK, determining connected addresses may not be possible + * shutdown, connection lost, etc */ + } + + associationMap.put(association, addresses); + if (addresses != null) { + for (SocketAddress addr : addresses) + addressMap.put(addr, association); + } + } + } + + private void removeAssociation(Association association) { + synchronized (stateLock) { + int assocId = association.associationID(); + Set addresses = null; + + try { + addresses = SctpNet.getRemoteAddresses(fdVal, assocId); + } catch (IOException unused) { + /* OK, determining connected addresses may not be possible + * shutdown, connection lost, etc */ + } + + Set assocs = associationMap.keySet(); + for (Association a : assocs) { + if (a.associationID() == assocId) { + associationMap.remove(a); + break; + } + } + if (addresses != null) { + for (SocketAddress addr : addresses) + addressMap.remove(addr); + } else { + /* We cannot determine the connected addresses */ + Set> addrAssocs = + addressMap.entrySet(); + for (java.util.Map.Entry entry : addrAssocs) { + if (entry.getValue().equals(association)) { + addressMap.remove(entry.getKey()); + } + } + } + } + } + + /** + * @throws IllegalArgumentException + * If the given association is not controlled by this channel + * + * @return {@code true} if, and only if, the given association is one + * of the current associations controlled by this channel + */ + private boolean checkAssociation(Association messageAssoc) { + synchronized (stateLock) { + for (Association association : associationMap.keySet()) { + if (messageAssoc.equals(association)) { + return true; + } + } + } + throw new IllegalArgumentException( + "Given Association is not controlled by this channel"); + } + + private void checkStreamNumber(Association assoc, int streamNumber) { + synchronized (stateLock) { + if (streamNumber < 0 || streamNumber >= assoc.maxOutboundStreams()) + throw new InvalidStreamException(); + } + } + + /* TODO: Add support for ttl and isComplete to both 121 12M + * SCTP_EOR not yet supported on reference platforms + * TTL support limited... + */ + @Override + public int send(ByteBuffer buffer, MessageInfo messageInfo) + throws IOException { + if (buffer == null) + throw new IllegalArgumentException("buffer cannot be null"); + + if (messageInfo == null) + throw new IllegalArgumentException("messageInfo cannot be null"); + + synchronized (sendLock) { + ensureOpen(); + + if (!isBound()) + bind(null, 0); + + int n = 0; + try { + int assocId = -1; + SocketAddress address = null; + begin(); + + synchronized (stateLock) { + if(!isOpen()) + return 0; + senderThread = NativeThread.current(); + + /* Determine what address or association to send to */ + Association assoc = messageInfo.association(); + InetSocketAddress addr = (InetSocketAddress)messageInfo.address(); + if (assoc != null) { + checkAssociation(assoc); + checkStreamNumber(assoc, messageInfo.streamNumber()); + assocId = assoc.associationID(); + /* have we also got a preferred address */ + if (addr != null) { + if (!assoc.equals(addressMap.get(addr))) + throw new IllegalArgumentException("given preferred address is not part of this association"); + address = addr; + } + } else if (addr != null) { + address = addr; + Association association = addressMap.get(addr); + if (association != null) { + checkStreamNumber(association, messageInfo.streamNumber()); + assocId = association.associationID(); + + } else { /* must be new association */ + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkConnect(addr.getAddress().getHostAddress(), + addr.getPort()); + } + } else { + throw new AssertionError( + "Both association and address cannot be null"); + } + } + + do { + n = send(fdVal, buffer, assocId, address, messageInfo); + } while ((n == IOStatus.INTERRUPTED) && isOpen()); + + return IOStatus.normalize(n); + } finally { + senderCleanup(); + end((n > 0) || (n == IOStatus.UNAVAILABLE)); + assert IOStatus.check(n); + } + } + } + + private int send(int fd, + ByteBuffer src, + int assocId, + SocketAddress target, + MessageInfo messageInfo) + throws IOException { + int streamNumber = messageInfo.streamNumber(); + boolean unordered = messageInfo.isUnordered(); + int ppid = messageInfo.payloadProtocolID(); + int pos = src.position(); + int lim = src.limit(); + assert (pos <= lim && streamNumber > 0); + int rem = (pos <= lim ? lim - pos : 0); + + if (src instanceof DirectBuffer) + return sendFromNativeBuffer(fd, src, rem, pos, target, assocId, + streamNumber, unordered, ppid); + + /* Substitute a native buffer */ + ByteBuffer bb = Util.getTemporaryDirectBuffer(rem); + try { + bb.put(src); + bb.flip(); + /* Do not update src until we see how many bytes were written */ + src.position(pos); + + int n = sendFromNativeBuffer(fd, bb, rem, pos, target, assocId, + streamNumber, unordered, ppid); + if (n > 0) { + /* now update src */ + src.position(pos + n); + } + return n; + } finally { + Util.releaseTemporaryDirectBuffer(bb); + } + } + + private int sendFromNativeBuffer(int fd, + ByteBuffer bb, + int rem, + int pos, + SocketAddress target, + int assocId, + int streamNumber, + boolean unordered, + int ppid) + throws IOException { + int written = send0(fd, ((DirectBuffer)bb).address() + pos, + rem, target, assocId, streamNumber, unordered, ppid); + if (written > 0) + bb.position(pos + written); + return written; + } + + @Override + public SctpMultiChannel shutdown(Association association) + throws IOException { + synchronized (stateLock) { + checkAssociation(association); + if (!isOpen()) + throw new ClosedChannelException(); + + SctpNet.shutdown(fdVal, association.associationID()); + } + return this; + } + + @Override + public Set getAllLocalAddresses() + throws IOException { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (!isBound()) + return Collections.EMPTY_SET; + + return SctpNet.getLocalAddresses(fdVal); + } + } + + @Override + public Set getRemoteAddresses(Association association) + throws IOException { + synchronized (stateLock) { + checkAssociation(association); + if (!isOpen()) + throw new ClosedChannelException(); + + return SctpNet.getRemoteAddresses(fdVal, association.associationID()); + } + } + + @Override + public SctpChannel branch(Association association) + throws IOException { + synchronized (stateLock) { + return null; //TODO: implement + } + } + + /* Use common native implementation shared between + * one-to-one and one-to-many */ + private static int receive0(int fd, + SctpResultContainer resultContainer, + long address, + int length) + throws IOException{ + return SctpChannelImpl.receive0(fd, resultContainer, address, + length); + } + + private static int send0(int fd, + long address, + int length, + SocketAddress target, + int assocId, + int streamNumber, + boolean unordered, + int ppid) + throws IOException { + return SctpChannelImpl.send0(fd, address, length, target, assocId, + streamNumber, unordered, ppid); + } + + static { + Util.load(); /* loads nio & net native libraries */ + java.security.AccessController.doPrivileged( + new sun.security.action.LoadLibraryAction("sctp")); + nd = new SctpSocketDispatcher(); + } +} diff --git a/src/solaris/classes/sun/nio/ch/SctpNet.java b/src/solaris/classes/sun/nio/ch/SctpNet.java new file mode 100644 index 000000000..1409324d2 --- /dev/null +++ b/src/solaris/classes/sun/nio/ch/SctpNet.java @@ -0,0 +1,268 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package sun.nio.ch; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.util.Set; +import java.util.HashSet; +import java.security.AccessController; +import sun.security.action.GetPropertyAction; +import com.sun.nio.sctp.SctpSocketOption; +import static com.sun.nio.sctp.SctpStandardSocketOption.*; + +public class SctpNet { + static final String osName = AccessController.doPrivileged( + new GetPropertyAction("os.name")); + + /* -- Miscellaneous SCTP utilities -- */ + + static boolean bindxIPv4MappedAddresses() { + if ("SunOS".equals(osName)) { + /* Solaris supports IPv4Mapped Addresses with bindx */ + return true; + } /* else { //other OS/implementations */ + + /* lksctp/linux requires Ipv4 addresses */ + return false; + } + + /** + * @param oneToone + * if {@code true} returns a one-to-one sctp socket, otherwise + * returns a one-to-many sctp socket + */ + static FileDescriptor socket(boolean oneToOne) throws IOException { + int nativefd = socket0(oneToOne); + return IOUtil.newFD(nativefd); + } + + static void bindx(int fd, InetAddress[] addrs, int port, boolean add) + throws IOException { + bindx(fd, addrs, port, addrs.length, add, + bindxIPv4MappedAddresses()); + } + + static Set getLocalAddresses(int fd) + throws IOException { + HashSet set = null; + SocketAddress[] saa = getLocalAddresses0(fd); + + if (saa != null) { + set = new HashSet(saa.length); + for (SocketAddress sa : saa) + set.add(sa); + } + + return set; + } + + static Set getRemoteAddresses(int fd, int assocId) + throws IOException { + HashSet set = null; + SocketAddress[] saa = getRemoteAddresses0(fd, assocId); + + if (saa != null) { + set = new HashSet(saa.length); + for (SocketAddress sa : saa) + set.add(sa); + } + + return set; + } + + static void setSocketOption(int fd, + SctpSocketOption name, + Object value, + int assocId) + throws IOException { + if (value == null) + throw new IllegalArgumentException("Invalid option value"); + + Class type = name.type(); + if (!type.isInstance(value)) + throw new IllegalArgumentException("Invalid option value"); + + if (name.equals(SCTP_INIT_MAXSTREAMS)) { + InitMaxStreams maxStreamValue = (InitMaxStreams)value; + SctpNet.setInitMsgOption0(fd, + maxStreamValue.maxInStreams(), maxStreamValue.maxOutStreams()); + } else if (name.equals(SCTP_PRIMARY_ADDR) || + name.equals(SCTP_SET_PEER_PRIMARY_ADDR)) { + + SocketAddress addr = (SocketAddress) value; + if (addr == null) + throw new IllegalArgumentException("Invalid option value"); + + Net.checkAddress(addr); + InetSocketAddress netAddr = (InetSocketAddress)addr; + + if (name.equals(SCTP_PRIMARY_ADDR)) { + setPrimAddrOption0(fd, assocId, + netAddr.getAddress(), netAddr.getPort()); + } else { + setPeerPrimAddrOption0(fd, assocId, + netAddr.getAddress(), netAddr.getPort()); + } + } else if (name.equals(SCTP_DISABLE_FRAGMENTS) || + name.equals(SCTP_EXPLICIT_COMPLETE) || + name.equals(SCTP_FRAGMENT_INTERLEAVE) || + name.equals(SCTP_NODELAY) || + name.equals(SO_SNDBUF) || + name.equals(SO_RCVBUF) || + name.equals(SO_LINGER)) { + setIntOption(fd, name, value); + } else { + throw new AssertionError("Unknown socket option"); + } + } + + static Object getSocketOption(int fd, SctpSocketOption name, int assocId) + throws IOException { + if (name.equals(SCTP_SET_PEER_PRIMARY_ADDR)) { + throw new IllegalArgumentException( + "SCTP_SET_PEER_PRIMARY_ADDR cannot be retrieved"); + } else if (name.equals(SCTP_INIT_MAXSTREAMS)) { + /* container for holding maxIn/Out streams */ + int[] values = new int[2]; + SctpNet.getInitMsgOption0(fd, values); + return InitMaxStreams.create(values[0], values[1]); + } else if (name.equals(SCTP_PRIMARY_ADDR)) { + return getPrimAddrOption0(fd, assocId); + } else if (name.equals(SCTP_DISABLE_FRAGMENTS) || + name.equals(SCTP_EXPLICIT_COMPLETE) || + name.equals(SCTP_FRAGMENT_INTERLEAVE) || + name.equals(SCTP_NODELAY) || + name.equals(SO_SNDBUF) || + name.equals(SO_RCVBUF) || + name.equals(SO_LINGER)) { + return getIntOption(fd, name); + } else { + throw new AssertionError("Unknown socket option"); + } + } + + static void setIntOption(int fd, SctpSocketOption name, Object value) + throws IOException { + if (value == null) + throw new IllegalArgumentException("Invalid option value"); + + Class type = name.type(); + if (type != Integer.class && type != Boolean.class) + throw new AssertionError("Should not reach here"); + + if (name == SO_RCVBUF || + name == SO_SNDBUF) + { + int i = ((Integer)value).intValue(); + if (i < 0) + throw new IllegalArgumentException( + "Invalid send/receive buffer size"); + } else if (name == SO_LINGER) { + int i = ((Integer)value).intValue(); + if (i < 0) + value = Integer.valueOf(-1); + if (i > 65535) + value = Integer.valueOf(65535); + } else if (name.equals(SCTP_FRAGMENT_INTERLEAVE)) { + int i = ((Integer)value).intValue(); + if (i < 0 || i > 2) + throw new IllegalArgumentException( + "Invalid value for SCTP_FRAGMENT_INTERLEAVE"); + } + + int arg; + if (type == Integer.class) { + arg = ((Integer)value).intValue(); + } else { + boolean b = ((Boolean)value).booleanValue(); + arg = (b) ? 1 : 0; + } + + setIntOption0(fd, ((SctpStdSocketOption)name).constValue(), arg); + } + + static Object getIntOption(int fd, SctpSocketOption name) + throws IOException { + Class type = name.type(); + + if (type != Integer.class && type != Boolean.class) + throw new AssertionError("Should not reach here"); + + if (!(name instanceof SctpStdSocketOption)) + throw new AssertionError("Should not reach here"); + + int value = getIntOption0(fd, + ((SctpStdSocketOption)name).constValue()); + + if (type == Integer.class) { + return Integer.valueOf(value); + } else { + return (value == 0) ? Boolean.FALSE : Boolean.TRUE; + } + } + + static void shutdown(int fd, int assocId) + throws IOException { + shutdown0(fd, assocId); + } + + /* Native Methods */ + static native int socket0(boolean oneToOne) throws IOException; + + static native void bindx(int fd, InetAddress[] addrs, int port, int length, + boolean add, boolean preferIPv6) throws IOException; + + static native int getIntOption0(int fd, int opt) throws IOException; + + static native void setIntOption0(int fd, int opt, int arg) + throws IOException; + + static native SocketAddress[] getLocalAddresses0(int fd) throws IOException; + + static native SocketAddress[] getRemoteAddresses0(int fd, int assocId) + throws IOException; + + static native void setPrimAddrOption0(int fd, int assocId, InetAddress ia, + int port) throws IOException; + + static native void setPeerPrimAddrOption0(int fd, int assocId, + InetAddress ia, int port) throws IOException; + + static native SocketAddress getPrimAddrOption0(int fd, int assocId) + throws IOException; + + /* retVals [0] maxInStreams, [1] maxOutStreams */ + static native void getInitMsgOption0(int fd, int[] retVals) throws IOException; + + static native void setInitMsgOption0(int fd, int arg1, int arg2) + throws IOException; + + static native void shutdown0(int fd, int assocId); +} + diff --git a/src/solaris/classes/sun/nio/ch/SctpNotification.java b/src/solaris/classes/sun/nio/ch/SctpNotification.java new file mode 100644 index 000000000..95033cb54 --- /dev/null +++ b/src/solaris/classes/sun/nio/ch/SctpNotification.java @@ -0,0 +1,37 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package sun.nio.ch; + +import com.sun.nio.sctp.Association; +import com.sun.nio.sctp.Notification; + +/** + * All Notification implemenations MUST implement this interface to provide + * access to the native association identidier. + */ +interface SctpNotification extends Notification { + int assocId(); + void setAssociation(Association association); +} diff --git a/src/solaris/classes/sun/nio/ch/SctpPeerAddrChange.java b/src/solaris/classes/sun/nio/ch/SctpPeerAddrChange.java new file mode 100644 index 000000000..94fdb7764 --- /dev/null +++ b/src/solaris/classes/sun/nio/ch/SctpPeerAddrChange.java @@ -0,0 +1,119 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package sun.nio.ch; + +import java.net.SocketAddress; +import com.sun.nio.sctp.Association; +import com.sun.nio.sctp.PeerAddressChangeNotification; + +/** + * An implementation of PeerAddressChangeNotification + */ +public class SctpPeerAddrChange extends PeerAddressChangeNotification + implements SctpNotification +{ + /* static final ints so that they can be referenced from native */ + private final static int SCTP_ADDR_AVAILABLE = 1; + private final static int SCTP_ADDR_UNREACHABLE = 2; + private final static int SCTP_ADDR_REMOVED = 3; + private final static int SCTP_ADDR_ADDED = 4; + private final static int SCTP_ADDR_MADE_PRIM = 5; + private final static int SCTP_ADDR_CONFIRMED =6; + + private Association association; + + /* assocId is used to lookup the association before the notification is + * returned to user code */ + private int assocId; + private SocketAddress address; + private AddressChangeEvent event; + + /* Invoked from native */ + private SctpPeerAddrChange(int assocId, SocketAddress address, int intEvent) { + switch (intEvent) { + case SCTP_ADDR_AVAILABLE : + this.event = AddressChangeEvent.ADDR_AVAILABLE; + break; + case SCTP_ADDR_UNREACHABLE : + this.event = AddressChangeEvent.ADDR_UNREACHABLE; + break; + case SCTP_ADDR_REMOVED : + this.event = AddressChangeEvent.ADDR_REMOVED; + break; + case SCTP_ADDR_ADDED : + this.event = AddressChangeEvent.ADDR_ADDED; + break; + case SCTP_ADDR_MADE_PRIM : + this.event = AddressChangeEvent.ADDR_MADE_PRIMARY; + break; + case SCTP_ADDR_CONFIRMED : + this.event = AddressChangeEvent.ADDR_CONFIRMED; + break; + default: + throw new AssertionError("Unknown event type"); + } + this.assocId = assocId; + this.address = address; + } + + @Override + public int assocId() { + return assocId; + } + + @Override + public void setAssociation(Association association) { + this.association = association; + } + + @Override + public SocketAddress address() { + assert address != null; + return address; + } + + @Override + public Association association() { + assert association != null; + return association; + } + + @Override + public AddressChangeEvent event() { + assert event != null; + return event; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(super.toString()).append(" ["); + sb.append("Address: ").append(address); + sb.append(", Association:").append(association); + sb.append(", Event: ").append(event).append("]"); + return sb.toString(); + } +} + diff --git a/src/solaris/classes/sun/nio/ch/SctpResultContainer.java b/src/solaris/classes/sun/nio/ch/SctpResultContainer.java new file mode 100644 index 000000000..430de7a75 --- /dev/null +++ b/src/solaris/classes/sun/nio/ch/SctpResultContainer.java @@ -0,0 +1,126 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package sun.nio.ch; + +/** + * Wraps the actual message or notification so that it can be + * set and returned from the native receive implementation. + */ +public class SctpResultContainer { + /* static final ints so that they can be referenced from native */ + static final int NOTHING = 0; + static final int MESSAGE = 1; + static final int SEND_FAILED = 2; + static final int ASSOCIATION_CHANGED = 3; + static final int PEER_ADDRESS_CHANGED = 4; + static final int SHUTDOWN = 5; + + private Object value; + private int type; + + int type() { + return type; + } + + boolean hasSomething() { + return type() != NOTHING; + } + + boolean isNotification() { + return type() != MESSAGE && type() != NOTHING ? true : false; + } + + void clear() { + type = NOTHING; + value = null; + } + + SctpNotification notification() { + assert type() != MESSAGE && type() != NOTHING; + + return (SctpNotification) value; + } + + SctpMessageInfoImpl getMessageInfo() { + assert type() == MESSAGE; + + if (value instanceof SctpMessageInfoImpl) + return (SctpMessageInfoImpl) value; + + return null; + } + + SctpSendFailed getSendFailed() { + assert type() == SEND_FAILED; + + if (value instanceof SctpSendFailed) + return (SctpSendFailed) value; + + return null; + } + + SctpAssocChange getAssociationChanged() { + assert type() == ASSOCIATION_CHANGED; + + if (value instanceof SctpAssocChange) + return (SctpAssocChange) value; + + return null; + } + + SctpPeerAddrChange getPeerAddressChanged() { + assert type() == PEER_ADDRESS_CHANGED; + + if (value instanceof SctpPeerAddrChange) + return (SctpPeerAddrChange) value; + + return null; + } + + SctpShutdown getShutdown() { + assert type() == SHUTDOWN; + + if (value instanceof SctpShutdown) + return (SctpShutdown) value; + + return null; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("Type: "); + switch (type) { + case NOTHING: sb.append("NOTHING"); break; + case MESSAGE: sb.append("MESSAGE"); break; + case SEND_FAILED: sb.append("SEND FAILED"); break; + case ASSOCIATION_CHANGED: sb.append("ASSOCIATION CHANGE"); break; + case PEER_ADDRESS_CHANGED: sb.append("PEER ADDRESS CHANGE"); break; + case SHUTDOWN: sb.append("SHUTDOWN"); break; + default : sb.append("Unknown result type"); + } + return sb.append(", Value: ").append(value.toString()).toString(); + } +} diff --git a/src/solaris/classes/sun/nio/ch/SctpSendFailed.java b/src/solaris/classes/sun/nio/ch/SctpSendFailed.java new file mode 100644 index 000000000..04a038da1 --- /dev/null +++ b/src/solaris/classes/sun/nio/ch/SctpSendFailed.java @@ -0,0 +1,110 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package sun.nio.ch; + +import java.nio.ByteBuffer; +import java.net.SocketAddress; +import com.sun.nio.sctp.Association; +import com.sun.nio.sctp.SendFailedNotification; + +/** + * An implementation of SendFailedNotification + */ +public class SctpSendFailed extends SendFailedNotification + implements SctpNotification +{ + private Association association; + /* assocId is used to lookup the association before the notification is + * returned to user code */ + private int assocId; + private SocketAddress address; + private ByteBuffer buffer; + private int errorCode; + private int streamNumber; + + /* Invoked from native */ + private SctpSendFailed(int assocId, + SocketAddress address, + ByteBuffer buffer, + int errorCode, + int streamNumber) { + this.assocId = assocId; + this.errorCode = errorCode; + this.streamNumber = streamNumber; + this.address = address; + this.buffer = buffer; + } + + @Override + public int assocId() { + return assocId; + } + + @Override + public void setAssociation(Association association) { + this.association = association; + } + + @Override + public Association association() { + /* may be null */ + return association; + } + + @Override + public SocketAddress address() { + assert address != null; + return address; + } + + @Override + public ByteBuffer buffer() { + assert buffer != null; + return buffer; + } + + @Override + public int errorCode() { + return errorCode; + } + + @Override + public int streamNumber() { + return streamNumber; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(super.toString()).append(" ["); + sb.append("Association:").append(association); + sb.append(", Address: ").append(address); + sb.append(", buffer: ").append(buffer); + sb.append(", errorCode: ").append(errorCode); + sb.append(", streamNumber: ").append(streamNumber); + sb.append("]"); + return sb.toString(); + } +} diff --git a/src/solaris/classes/sun/nio/ch/SctpServerChannelImpl.java b/src/solaris/classes/sun/nio/ch/SctpServerChannelImpl.java new file mode 100644 index 000000000..40126260a --- /dev/null +++ b/src/solaris/classes/sun/nio/ch/SctpServerChannelImpl.java @@ -0,0 +1,429 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package sun.nio.ch; + +import java.net.SocketAddress; +import java.net.InetSocketAddress; +import java.net.InetAddress; +import java.io.FileDescriptor; +import java.io.IOException; +import java.util.Collections; +import java.util.Set; +import java.util.HashSet; +import java.nio.channels.SelectionKey; +import java.nio.channels.AlreadyBoundException; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.NotYetBoundException; +import java.nio.channels.spi.SelectorProvider; +import com.sun.nio.sctp.IllegalUnbindException; +import com.sun.nio.sctp.SctpChannel; +import com.sun.nio.sctp.SctpServerChannel; +import com.sun.nio.sctp.SctpSocketOption; +import com.sun.nio.sctp.SctpStandardSocketOption; + +/** + * An implementation of SctpServerChannel + */ +public class SctpServerChannelImpl extends SctpServerChannel + implements SelChImpl +{ + /* Used to make native close and preClose calls */ + private static NativeDispatcher nd; + + private final FileDescriptor fd; + + private final int fdVal; + + /* IDs of native thread doing accept, for signalling */ + private volatile long thread = 0; + + /* Lock held by thread currently blocked in this channel */ + private final Object lock = new Object(); + + /* Lock held by any thread that modifies the state fields declared below + * DO NOT invoke a blocking I/O operation while holding this lock! */ + private final Object stateLock = new Object(); + + private enum ChannelState { + UNINITIALIZED, + INUSE, + KILLPENDING, + KILLED, + } + /* -- The following fields are protected by stateLock -- */ + private ChannelState state = ChannelState.UNINITIALIZED; + + /* Binding: Once bound the port will remain constant. */ + int port = -1; + private HashSet localAddresses = new HashSet(); + /* Has the channel been bound to the wildcard address */ + private boolean wildcard; /* false */ + + /* -- End of fields protected by stateLock -- */ + + /** + * Initializes a new instance of this class. + */ + public SctpServerChannelImpl(SelectorProvider provider) + throws IOException { + //TODO: update provider remove public modifier + super(provider); + this.fd = SctpNet.socket(true); + this.fdVal = IOUtil.fdVal(fd); + this.state = ChannelState.INUSE; + } + + @Override + public SctpServerChannel bind(SocketAddress local, int backlog) + throws IOException { + synchronized (lock) { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (isBound()) + throw new AlreadyBoundException(); + + InetSocketAddress isa = (local == null) ? + new InetSocketAddress(0) : Net.checkAddress(local); + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkListen(isa.getPort()); + Net.bind(fd, isa.getAddress(), isa.getPort()); + + InetSocketAddress boundIsa = Net.localAddress(fd); + port = boundIsa.getPort(); + localAddresses.add(isa); + if (isa.getAddress().isAnyLocalAddress()) + wildcard = true; + + Net.listen(fd, backlog < 1 ? 50 : backlog); + } + } + return this; + } + + @Override + public SctpServerChannel bindAddress(InetAddress address) + throws IOException { + return bindUnbindAddress(address, true); + } + + @Override + public SctpServerChannel unbindAddress(InetAddress address) + throws IOException { + return bindUnbindAddress(address, false); + } + + private SctpServerChannel bindUnbindAddress(InetAddress address, boolean add) + throws IOException { + if (address == null) + throw new IllegalArgumentException(); + + synchronized (lock) { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (!isBound()) + throw new NotYetBoundException(); + if (wildcard) + throw new IllegalStateException( + "Cannot add or remove addresses from a channel that is bound to the wildcard address"); + if (address.isAnyLocalAddress()) + throw new IllegalArgumentException( + "Cannot add or remove the wildcard address"); + if (add) { + for (InetSocketAddress addr : localAddresses) { + if (addr.getAddress().equals(address)) { + throw new AlreadyBoundException(); + } + } + } else { /*removing */ + /* Verify that there is more than one address + * and that address is already bound */ + if (localAddresses.size() <= 1) + throw new IllegalUnbindException("Cannot remove address from a channel with only one address bound"); + boolean foundAddress = false; + for (InetSocketAddress addr : localAddresses) { + if (addr.getAddress().equals(address)) { + foundAddress = true; + break; + } + } + if (!foundAddress ) + throw new IllegalUnbindException("Cannot remove address from a channel that is not bound to that address"); + } + + SctpNet.bindx(fdVal, new InetAddress[]{address}, port, add); + + /* Update our internal Set to reflect the addition/removal */ + if (add) + localAddresses.add(new InetSocketAddress(address, port)); + else { + for (InetSocketAddress addr : localAddresses) { + if (addr.getAddress().equals(address)) { + localAddresses.remove(addr); + break; + } + } + } + } + } + return this; + } + + private boolean isBound() { + synchronized (stateLock) { + return port == -1 ? false : true; + } + } + + private void acceptCleanup() throws IOException { + synchronized (stateLock) { + thread = 0; + if (state == ChannelState.KILLPENDING) + kill(); + } + } + + @Override + public SctpChannel accept() throws IOException { + synchronized (lock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (!isBound()) + throw new NotYetBoundException(); + SctpChannel sc = null; + + int n = 0; + FileDescriptor newfd = new FileDescriptor(); + InetSocketAddress[] isaa = new InetSocketAddress[1]; + + try { + begin(); + if (!isOpen()) + return null; + thread = NativeThread.current(); + for (;;) { + n = accept0(fd, newfd, isaa); + if ((n == IOStatus.INTERRUPTED) && isOpen()) + continue; + break; + } + } finally { + acceptCleanup(); + end(n > 0); + assert IOStatus.check(n); + } + + if (n < 1) + return null; + + IOUtil.configureBlocking(newfd, true); + InetSocketAddress isa = isaa[0]; + sc = new SctpChannelImpl(provider(), newfd); + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkAccept(isa.getAddress().getHostAddress(), + isa.getPort()); + + return sc; + } + } + + @Override + protected void implConfigureBlocking(boolean block) throws IOException { + IOUtil.configureBlocking(fd, block); + } + + @Override + public void implCloseSelectableChannel() throws IOException { + synchronized (stateLock) { + nd.preClose(fd); + if (thread != 0) + NativeThread.signal(thread); + if (!isRegistered()) + kill(); + } + } + + @Override + public void kill() throws IOException { + synchronized (stateLock) { + if (state == ChannelState.KILLED) + return; + if (state == ChannelState.UNINITIALIZED) { + state = ChannelState.KILLED; + return; + } + assert !isOpen() && !isRegistered(); + + // Postpone the kill if there is a thread in accept + if (thread == 0) { + nd.close(fd); + state = ChannelState.KILLED; + } else { + state = ChannelState.KILLPENDING; + } + } + } + + @Override + public FileDescriptor getFD() { + return fd; + } + + @Override + public int getFDVal() { + return fdVal; + } + + /** + * Translates native poll revent ops into a ready operation ops + */ + private boolean translateReadyOps(int ops, int initialOps, + SelectionKeyImpl sk) { + int intOps = sk.nioInterestOps(); + int oldOps = sk.nioReadyOps(); + int newOps = initialOps; + + if ((ops & PollArrayWrapper.POLLNVAL) != 0) { + /* This should only happen if this channel is pre-closed while a + * selection operation is in progress + * ## Throw an error if this channel has not been pre-closed */ + return false; + } + + if ((ops & (PollArrayWrapper.POLLERR + | PollArrayWrapper.POLLHUP)) != 0) { + newOps = intOps; + sk.nioReadyOps(newOps); + return (newOps & ~oldOps) != 0; + } + + if (((ops & PollArrayWrapper.POLLIN) != 0) && + ((intOps & SelectionKey.OP_ACCEPT) != 0)) + newOps |= SelectionKey.OP_ACCEPT; + + sk.nioReadyOps(newOps); + return (newOps & ~oldOps) != 0; + } + + @Override + public boolean translateAndUpdateReadyOps(int ops, SelectionKeyImpl sk) { + return translateReadyOps(ops, sk.nioReadyOps(), sk); + } + + @Override + public boolean translateAndSetReadyOps(int ops, SelectionKeyImpl sk) { + return translateReadyOps(ops, 0, sk); + } + + @Override + public void translateAndSetInterestOps(int ops, SelectionKeyImpl sk) { + int newOps = 0; + + /* Translate ops */ + if ((ops & SelectionKey.OP_ACCEPT) != 0) + newOps |= PollArrayWrapper.POLLIN; + /* Place ops into pollfd array */ + sk.selector.putEventOps(sk, newOps); + + } + + @Override + public SctpServerChannel setOption(SctpSocketOption name, T value) + throws IOException { + if (name == null) + throw new NullPointerException(); + if (!supportedOptions().contains(name)) + throw new UnsupportedOperationException("'" + name + "' not supported"); + + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + + SctpNet.setSocketOption(fdVal, name, value, 0 /*oneToOne*/); + return this; + } + } + + @Override + public T getOption(SctpSocketOption name) throws IOException { + if (name == null) + throw new NullPointerException(); + if (!supportedOptions().contains(name)) + throw new UnsupportedOperationException("'" + name + "' not supported"); + + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + + return (T) SctpNet.getSocketOption(fdVal, name, 0 /*oneToOne*/); + } + } + + private static class DefaultOptionsHolder { + static final Set> defaultOptions = defaultOptions(); + + private static Set> defaultOptions() { + HashSet> set = new HashSet>(1); + set.add(SctpStandardSocketOption.SCTP_INIT_MAXSTREAMS); + return Collections.unmodifiableSet(set); + } + } + + @Override + public final Set> supportedOptions() { + return DefaultOptionsHolder.defaultOptions; + } + + @Override + public Set getAllLocalAddresses() + throws IOException { + synchronized (stateLock) { + if (!isOpen()) + throw new ClosedChannelException(); + if (!isBound()) + return null; + + return SctpNet.getLocalAddresses(fdVal); + } + } + + /* Native */ + private static native void initIDs(); + + private static native int accept0(FileDescriptor ssfd, + FileDescriptor newfd, InetSocketAddress[] isaa) throws IOException; + + static { + Util.load(); // loads nio & net native libraries + java.security.AccessController.doPrivileged( + new sun.security.action.LoadLibraryAction("sctp")); + nd = new SctpSocketDispatcher(); + initIDs(); + } +} diff --git a/src/solaris/classes/sun/nio/ch/SctpShutdown.java b/src/solaris/classes/sun/nio/ch/SctpShutdown.java new file mode 100644 index 000000000..0932fc550 --- /dev/null +++ b/src/solaris/classes/sun/nio/ch/SctpShutdown.java @@ -0,0 +1,69 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package sun.nio.ch; + +import com.sun.nio.sctp.Association; +import com.sun.nio.sctp.ShutdownNotification; + +/** + * An implementation of ShutdownNotification + */ +public class SctpShutdown extends ShutdownNotification + implements SctpNotification +{ + private Association association; + /* assocId is used to lookup the association before the notification is + * returned to user code */ + private int assocId; + + /* Invoked from native */ + private SctpShutdown(int assocId) { + this.assocId = assocId; + } + + @Override + public int assocId() { + return assocId; + } + + @Override + public void setAssociation(Association association) { + this.association = association; + } + + @Override + public Association association() { + assert association != null; + return association; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(super.toString()).append(" ["); + sb.append("Association:").append(association).append("]"); + return sb.toString(); + } +} diff --git a/src/solaris/classes/sun/nio/ch/SctpSocketDispatcher.java b/src/solaris/classes/sun/nio/ch/SctpSocketDispatcher.java new file mode 100644 index 000000000..937a73924 --- /dev/null +++ b/src/solaris/classes/sun/nio/ch/SctpSocketDispatcher.java @@ -0,0 +1,68 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package sun.nio.ch; + +import java.io.IOException; +import java.io.FileDescriptor; + +/** + * Only used for {@code close} and {@code preclose}. All other methods + * throw {@code IOException}. + */ +class SctpSocketDispatcher extends NativeDispatcher { + @Override + @SuppressWarnings("unused") + int read(FileDescriptor fd, long address, int len) throws IOException { + throw new IOException("Operation Unsupported"); + } + + @Override + @SuppressWarnings("unused") + long readv(FileDescriptor fd, long address, int len) throws IOException { + throw new IOException("Operation Unsupported"); + } + + @Override + @SuppressWarnings("unused") + int write(FileDescriptor fd, long address, int len) throws IOException { + throw new IOException("Operation Unsupported"); + } + + @Override + @SuppressWarnings("unused") + long writev(FileDescriptor fd, long address, int len) throws IOException { + throw new IOException("Operation Unsupported"); + } + + @Override + void close(FileDescriptor fd) throws IOException { + FileDispatcherImpl.close0(fd); + } + + @Override + void preClose(FileDescriptor fd) throws IOException { + FileDispatcherImpl.preClose0(fd); + } +} diff --git a/src/solaris/native/sun/nio/ch/Sctp.h b/src/solaris/native/sun/nio/ch/Sctp.h new file mode 100644 index 000000000..4c1fd3bcd --- /dev/null +++ b/src/solaris/native/sun/nio/ch/Sctp.h @@ -0,0 +1,329 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +#ifndef SUN_NIO_CH_SCTP_H +#define SUN_NIO_CH_SCTP_H + +#ifdef __solaris__ + +#define _XPG4_2 +#define __EXTENSIONS__ +#include +#include +#include "jni.h" + +/* Current Solaris headers don't comply with draft rfc */ +#ifndef SCTP_EOF +#define SCTP_EOF MSG_EOF +#endif + +#ifndef SCTP_UNORDERED +#define SCTP_UNORDERED MSG_UNORDERED +#endif + +/* The current version of the socket API extension shipped with Solaris does + * not define the following options that the Java API (optionally) supports */ +#ifndef SCTP_EXPLICIT_EOR +#define SCTP_EXPLICIT_EOR -1 +#endif +#ifndef SCTP_FRAGMENT_INTERLEAVE +#define SCTP_FRAGMENT_INTERLEAVE -1 +#endif +#ifndef SCTP_SET_PEER_PRIMARY_ADDR +#define SCTP_SET_PEER_PRIMARY_ADDR -1 +#endif + +/* Function types to support dynamic linking of socket API extension functions + * for SCTP. This is so that there is no linkage depandancy during build or + * runtime for libsctp.*/ +typedef int sctp_getladdrs_func(int sock, sctp_assoc_t id, void **addrs); +typedef int sctp_freeladdrs_func(void* addrs); +typedef int sctp_getpaddrs_func(int sock, sctp_assoc_t id, void **addrs); +typedef int sctp_freepaddrs_func(void *addrs); +typedef int sctp_bindx_func(int sock, void *addrs, int addrcnt, int flags); + + +#else /* __linux__ */ +#include +#include +#include +#include +#include "jni.h" + +//Causes compiler error if not found, should make warning and uncomment +/*#include */ + +#ifndef IPPROTO_SCTP +#define IPPROTO_SCTP 132 +#endif + +/* The current version of lksctp does + * not define the following option that the Java API (optionally) supports */ +#ifndef SCTP_EXPLICIT_EOR +#define SCTP_EXPLICIT_EOR -1 +#endif + +/* Definitions taken from lksctp-tools-1.0.8/src/include/netinet/sctp.h */ +#ifndef SCTP_INITMSG + +enum sctp_optname { + SCTP_RTOINFO, +#define SCTP_RTOINFO SCTP_RTOINFO + SCTP_ASSOCINFO, +#define SCTP_ASSOCINFO SCTP_ASSOCINFO + SCTP_INITMSG, +#define SCTP_INITMSG SCTP_INITMSG + SCTP_NODELAY, /* Get/set nodelay option. */ +#define SCTP_NODELAY SCTP_NODELAY + SCTP_AUTOCLOSE, +#define SCTP_AUTOCLOSE SCTP_AUTOCLOSE + SCTP_SET_PEER_PRIMARY_ADDR, +#define SCTP_SET_PEER_PRIMARY_ADDR SCTP_SET_PEER_PRIMARY_ADDR + SCTP_PRIMARY_ADDR, +#define SCTP_PRIMARY_ADDR SCTP_PRIMARY_ADDR + SCTP_ADAPTATION_LAYER, +#define SCTP_ADAPTATION_LAYER SCTP_ADAPTATION_LAYER + SCTP_DISABLE_FRAGMENTS, +#define SCTP_DISABLE_FRAGMENTS SCTP_DISABLE_FRAGMENTS + SCTP_PEER_ADDR_PARAMS, +#define SCTP_PEER_ADDR_PARAMS SCTP_PEER_ADDR_PARAMS + SCTP_DEFAULT_SEND_PARAM, +#define SCTP_DEFAULT_SEND_PARAM SCTP_DEFAULT_SEND_PARAM + SCTP_EVENTS, +#define SCTP_EVENTS SCTP_EVENTS + SCTP_I_WANT_MAPPED_V4_ADDR, /* Turn on/off mapped v4 addresses */ +#define SCTP_I_WANT_MAPPED_V4_ADDR SCTP_I_WANT_MAPPED_V4_ADDR + SCTP_MAXSEG, /* Get/set maximum fragment. */ +#define SCTP_MAXSEG SCTP_MAXSEG + SCTP_STATUS, +#define SCTP_STATUS SCTP_STATUS + SCTP_GET_PEER_ADDR_INFO, +#define SCTP_GET_PEER_ADDR_INFO SCTP_GET_PEER_ADDR_INFO + SCTP_DELAYED_ACK_TIME, +#define SCTP_DELAYED_ACK_TIME SCTP_DELAYED_ACK_TIME + SCTP_CONTEXT, /* Receive Context */ +#define SCTP_CONTEXT SCTP_CONTEXT + SCTP_FRAGMENT_INTERLEAVE, +#define SCTP_FRAGMENT_INTERLEAVE SCTP_FRAGMENT_INTERLEAVE + SCTP_PARTIAL_DELIVERY_POINT, /* Set/Get partial delivery point */ +#define SCTP_PARTIAL_DELIVERY_POINT SCTP_PARTIAL_DELIVERY_POINT + SCTP_MAX_BURST, /* Set/Get max burst */ +#define SCTP_MAX_BURST SCTP_MAX_BURST +}; + +enum sctp_sac_state { + SCTP_COMM_UP, + SCTP_COMM_LOST, + SCTP_RESTART, + SCTP_SHUTDOWN_COMP, + SCTP_CANT_STR_ASSOC, +}; + +enum sctp_spc_state { + SCTP_ADDR_AVAILABLE, + SCTP_ADDR_UNREACHABLE, + SCTP_ADDR_REMOVED, + SCTP_ADDR_ADDED, + SCTP_ADDR_MADE_PRIM, + SCTP_ADDR_CONFIRMED, +}; + +enum sctp_sinfo_flags { + SCTP_UNORDERED = 1, /* Send/receive message unordered. */ + SCTP_ADDR_OVER = 2, /* Override the primary destination. */ + SCTP_ABORT=4, /* Send an ABORT message to the peer. */ + SCTP_EOF=MSG_FIN, /* Initiate graceful shutdown process. */ +}; + +enum sctp_sn_type { + SCTP_SN_TYPE_BASE = (1<<15), + SCTP_ASSOC_CHANGE, + SCTP_PEER_ADDR_CHANGE, + SCTP_SEND_FAILED, + SCTP_REMOTE_ERROR, + SCTP_SHUTDOWN_EVENT, + SCTP_PARTIAL_DELIVERY_EVENT, + SCTP_ADAPTATION_INDICATION, +}; + +typedef enum sctp_cmsg_type { + SCTP_INIT, /* 5.2.1 SCTP Initiation Structure */ +#define SCTP_INIT SCTP_INIT + SCTP_SNDRCV, /* 5.2.2 SCTP Header Information Structure */ +#define SCTP_SNDRCV SCTP_SNDRCV +} sctp_cmsg_t; + +enum sctp_msg_flags { + MSG_NOTIFICATION = 0x8000, +#define MSG_NOTIFICATION MSG_NOTIFICATION +}; + +#define SCTP_BINDX_ADD_ADDR 0x01 +#define SCTP_BINDX_REM_ADDR 0x02 + +typedef __s32 sctp_assoc_t; + +struct sctp_initmsg { + __u16 sinit_num_ostreams; + __u16 sinit_max_instreams; + __u16 sinit_max_attempts; + __u16 sinit_max_init_timeo; +}; + +struct sctp_sndrcvinfo { + __u16 sinfo_stream; + __u16 sinfo_ssn; + __u16 sinfo_flags; + __u32 sinfo_ppid; + __u32 sinfo_context; + __u32 sinfo_timetolive; + __u32 sinfo_tsn; + __u32 sinfo_cumtsn; + sctp_assoc_t sinfo_assoc_id; +}; + +struct sctp_event_subscribe { + __u8 sctp_data_io_event; + __u8 sctp_association_event; + __u8 sctp_address_event; + __u8 sctp_send_failure_event; + __u8 sctp_peer_error_event; + __u8 sctp_shutdown_event; + __u8 sctp_partial_delivery_event; + __u8 sctp_adaptation_layer_event; +}; + +struct sctp_send_failed { + __u16 ssf_type; + __u16 ssf_flags; + __u32 ssf_length; + __u32 ssf_error; + struct sctp_sndrcvinfo ssf_info; + sctp_assoc_t ssf_assoc_id; + __u8 ssf_data[0]; +}; + +struct sctp_assoc_change { + __u16 sac_type; + __u16 sac_flags; + __u32 sac_length; + __u16 sac_state; + __u16 sac_error; + __u16 sac_outbound_streams; + __u16 sac_inbound_streams; + sctp_assoc_t sac_assoc_id; + __u8 sac_info[0]; +}; + +struct sctp_shutdown_event { + __u16 sse_type; + __u16 sse_flags; + __u32 sse_length; + sctp_assoc_t sse_assoc_id; +}; + +struct sctp_paddr_change { + __u16 spc_type; + __u16 spc_flags; + __u32 spc_length; + struct sockaddr_storage spc_aaddr; + int spc_state; + int spc_error; + sctp_assoc_t spc_assoc_id; +} __attribute__((packed, aligned(4))); + +struct sctp_remote_error { + __u16 sre_type; + __u16 sre_flags; + __u32 sre_length; + __u16 sre_error; + sctp_assoc_t sre_assoc_id; + __u8 sre_data[0]; +}; + +struct sctp_adaptation_event { + __u16 sai_type; + __u16 sai_flags; + __u32 sai_length; + __u32 sai_adaptation_ind; + sctp_assoc_t sai_assoc_id; +}; + +struct sctp_setprim { + sctp_assoc_t ssp_assoc_id; + struct sockaddr_storage ssp_addr; +} __attribute__((packed, aligned(4))); + +struct sctp_setpeerprim { + sctp_assoc_t sspp_assoc_id; + struct sockaddr_storage sspp_addr; +} __attribute__((packed, aligned(4))); + + +struct sctp_pdapi_event { + __u16 pdapi_type; + __u16 pdapi_flags; + __u32 pdapi_length; + __u32 pdapi_indication; + sctp_assoc_t pdapi_assoc_id; +}; + +union sctp_notification { + struct { + __u16 sn_type; /* Notification type. */ + __u16 sn_flags; + __u32 sn_length; + } sn_header; + struct sctp_assoc_change sn_assoc_change; + struct sctp_paddr_change sn_paddr_change; + struct sctp_remote_error sn_remote_error; + struct sctp_send_failed sn_send_failed; + struct sctp_shutdown_event sn_shutdown_event; + struct sctp_adaptation_event sn_adaptation_event; + struct sctp_pdapi_event sn_pdapi_event; +}; + +#endif /* SCTP_INITMSG */ + +/* Function types to support dynamic linking of socket API extension functions + * for SCTP. This is so that there is no linkage depandancy during build or + * runtime for libsctp.*/ +typedef int sctp_getladdrs_func(int sd, sctp_assoc_t id, struct sockaddr **addrs); +typedef int sctp_freeladdrs_func(struct sockaddr *addrs); +typedef int sctp_getpaddrs_func(int sd, sctp_assoc_t id, struct sockaddr **addrs); +typedef int sctp_freepaddrs_func(struct sockaddr *addrs); +typedef int sctp_bindx_func(int sd, struct sockaddr *addrs, int addrcnt, int flags); + +#endif /* __linux__ */ + +sctp_getladdrs_func* nio_sctp_getladdrs; +sctp_freeladdrs_func* nio_sctp_freeladdrs; +sctp_getpaddrs_func* nio_sctp_getpaddrs; +sctp_freepaddrs_func* nio_sctp_freepaddrs; +sctp_bindx_func* nio_sctp_bindx; + +jboolean loadSocketExtensionFuncs(JNIEnv* env); + +#endif /* !SUN_NIO_CH_SCTP_H */ diff --git a/src/solaris/native/sun/nio/ch/SctpChannelImpl.c b/src/solaris/native/sun/nio/ch/SctpChannelImpl.c new file mode 100644 index 000000000..744c5563f --- /dev/null +++ b/src/solaris/native/sun/nio/ch/SctpChannelImpl.c @@ -0,0 +1,594 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +#include +#include +#include "Sctp.h" + +#include "jni.h" +#include "nio_util.h" +#include "nio.h" +#include "net_util.h" +#include "net_util_md.h" +#include "sun_nio_ch_SctpNet.h" +#include "sun_nio_ch_SctpChannelImpl.h" +#include "sun_nio_ch_SctpAssocChange.h" +#include "sun_nio_ch_SctpResultContainer.h" +#include "sun_nio_ch_SctpPeerAddrChange.h" + +/* sizeof(union sctp_notification */ +#define NOTIFICATION_BUFFER_SIZE 280 + +#define MESSAGE_IMPL_CLASS "sun/nio/ch/SctpMessageInfoImpl" +#define RESULT_CONTAINER_CLASS "sun/nio/ch/SctpResultContainer" +#define SEND_FAILED_CLASS "sun/nio/ch/SctpSendFailed" +#define ASSOC_CHANGE_CLASS "sun/nio/ch/SctpAssocChange" +#define PEER_CHANGE_CLASS "sun/nio/ch/SctpPeerAddrChange" +#define SHUTDOWN_CLASS "sun/nio/ch/SctpShutdown" + +struct controlData { + int assocId; + unsigned short streamNumber; + jboolean unordered; + unsigned int ppid; +}; + +static jclass smi_class; /* sun.nio.ch.SctpMessageInfoImpl */ +static jmethodID smi_ctrID; /* sun.nio.ch.SctpMessageInfoImpl. */ +static jfieldID src_valueID; /* sun.nio.ch.SctpResultContainer.value */ +static jfieldID src_typeID; /* sun.nio.ch.SctpResultContainer.type */ +static jclass ssf_class; /* sun.nio.ch.SctpSendFailed */ +static jmethodID ssf_ctrID; /* sun.nio.ch.SctpSendFailed. */ +static jclass sac_class; /* sun.nio.ch.SctpAssociationChanged */ +static jmethodID sac_ctrID; /* sun.nio.ch.SctpAssociationChanged. */ +static jclass spc_class; /* sun.nio.ch.SctpPeerAddressChanged */ +static jmethodID spc_ctrID; /* sun.nio.ch.SctpPeerAddressChanged. */ +static jclass ss_class; /* sun.nio.ch.SctpShutdown */ +static jmethodID ss_ctrID; /* sun.nio.ch.SctpShutdown. */ +static jfieldID isa_addrID; /* java.net.InetSocketAddress.addr */ +static jfieldID isa_portID; /* java.net.InetSocketAddress.port */ + +/* defined in SctpNet.c */ +jobject SockAddrToInetSocketAddress(JNIEnv* env, struct sockaddr* addr); + +/* use SocketChannelImpl's checkConnect implementation */ +extern jint Java_sun_nio_ch_SocketChannelImpl_checkConnect(JNIEnv* env, + jobject this, jobject fdo, jboolean block, jboolean ready); + +/* + * Class: sun_nio_ch_SctpChannelImpl + * Method: initIDs + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_sun_nio_ch_SctpChannelImpl_initIDs + (JNIEnv *env, jclass klass) { + jclass cls; + + /* SctpMessageInfoImpl */ + cls = (*env)->FindClass(env, MESSAGE_IMPL_CLASS); + CHECK_NULL(cls); + smi_class = (*env)->NewGlobalRef(env, cls); + CHECK_NULL(smi_class); + smi_ctrID = (*env)->GetMethodID(env, cls, "", + "(ILjava/net/SocketAddress;IIZZI)V"); + CHECK_NULL(smi_ctrID); + + /* SctpResultContainer */ + cls = (*env)->FindClass(env, RESULT_CONTAINER_CLASS); + CHECK_NULL(cls); + src_valueID = (*env)->GetFieldID(env, cls, "value", "Ljava/lang/Object;"); + CHECK_NULL(src_valueID); + src_typeID = (*env)->GetFieldID(env, cls, "type", "I"); + CHECK_NULL(src_typeID); + + /* SctpSendFailed */ + cls = (*env)->FindClass(env, SEND_FAILED_CLASS); + CHECK_NULL(cls); + ssf_class = (*env)->NewGlobalRef(env, cls); + CHECK_NULL(ssf_class); + ssf_ctrID = (*env)->GetMethodID(env, cls, "", + "(ILjava/net/SocketAddress;Ljava/nio/ByteBuffer;II)V"); + CHECK_NULL(ssf_ctrID); + + /* SctpAssocChange */ + cls = (*env)->FindClass(env, ASSOC_CHANGE_CLASS); + CHECK_NULL(cls); + sac_class = (*env)->NewGlobalRef(env, cls); + CHECK_NULL(sac_class); + sac_ctrID = (*env)->GetMethodID(env, cls, "", "(IIII)V"); + CHECK_NULL(sac_ctrID); + + /* SctpPeerAddrChange */ + cls = (*env)->FindClass(env, PEER_CHANGE_CLASS); + CHECK_NULL(cls); + spc_class = (*env)->NewGlobalRef(env, cls); + CHECK_NULL(spc_class); + spc_ctrID = (*env)->GetMethodID(env, cls, "", + "(ILjava/net/SocketAddress;I)V"); + CHECK_NULL(spc_ctrID); + + /* sun.nio.ch.SctpShutdown */ + cls = (*env)->FindClass(env, SHUTDOWN_CLASS); + CHECK_NULL(cls); + ss_class = (*env)->NewGlobalRef(env, cls); + CHECK_NULL(ss_class); + ss_ctrID = (*env)->GetMethodID(env, cls, "", "(I)V"); + CHECK_NULL(ss_ctrID); + + /* InetSocketAddress */ + cls = (*env)->FindClass(env, "java/net/InetSocketAddress"); + CHECK_NULL(cls); + isa_addrID = (*env)->GetFieldID(env, cls, "addr", "Ljava/net/InetAddress;"); + CHECK_NULL(isa_addrID); + isa_portID = (*env)->GetFieldID(env, cls, "port", "I"); +} + +void getControlData + (struct msghdr* msg, struct controlData* cdata) { + struct cmsghdr* cmsg; + + for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) { + if (cmsg->cmsg_level == IPPROTO_SCTP && cmsg->cmsg_type == SCTP_SNDRCV) { + struct sctp_sndrcvinfo *sri; + + sri = (struct sctp_sndrcvinfo *) CMSG_DATA(cmsg); + cdata->assocId = sri->sinfo_assoc_id; + cdata->streamNumber = sri->sinfo_stream; + cdata->unordered = (sri->sinfo_flags & SCTP_UNORDERED) ? JNI_TRUE : + JNI_FALSE; + cdata->ppid = ntohl(sri->sinfo_ppid); + + return; + } + } + return; +} + +void setControlData + (struct msghdr* msg, struct controlData* cdata) { + struct cmsghdr* cmsg; + struct sctp_sndrcvinfo *sri; + + cmsg = CMSG_FIRSTHDR(msg); + cmsg->cmsg_level = IPPROTO_SCTP; + cmsg->cmsg_type = SCTP_SNDRCV; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo)); + + /* Initialize the payload */ + sri = (struct sctp_sndrcvinfo*) CMSG_DATA(cmsg); + memset(sri, 0, sizeof (*sri)); + + if (cdata->streamNumber > 0) { + sri->sinfo_stream = cdata->streamNumber; + } + if (cdata->assocId > 0) { + sri->sinfo_assoc_id = cdata->assocId; + } + if (cdata->unordered == JNI_TRUE) { + sri->sinfo_flags = sri->sinfo_flags | SCTP_UNORDERED; + } + + if (cdata->ppid > 0) { + sri->sinfo_ppid = htonl(cdata->ppid); + } + + /* Sum of the length of all control messages in the buffer. */ + msg->msg_controllen = cmsg->cmsg_len; +} + +// TODO: test: can create send failed without any data? if so need to +// update API so that buffer can be null if no data. +void handleSendFailed + (JNIEnv* env, int fd, jobject resultContainerObj, struct sctp_send_failed *ssf, + int read, jboolean isEOR, struct sockaddr* sap) { + jobject bufferObj = NULL, resultObj, isaObj; + char *addressP; + struct sctp_sndrcvinfo *sri; + int remaining, dataLength; + + /* the actual undelivered message data is directly after the ssf */ + int dataOffset = sizeof(struct sctp_send_failed); + + sri = (struct sctp_sndrcvinfo*) &ssf->ssf_info; + + /* the number of bytes remaining to be read in the sctp_send_failed notif*/ + remaining = ssf->ssf_length - read; + + /* the size of the actual undelivered message */ + dataLength = ssf->ssf_length - dataOffset; + + /* retrieved address from sockaddr */ + isaObj = SockAddrToInetSocketAddress(env, sap); + + /* data retrieved from sff_data */ + if (dataLength > 0) { + struct iovec iov[1]; + struct msghdr msg[1]; + int rv, alreadyRead; + char *dataP = (char*) ssf; + dataP += dataOffset; + + if ((addressP = malloc(dataLength)) == NULL) { + JNU_ThrowOutOfMemoryError(env, "handleSendFailed"); + return; + } + + memset(msg, 0, sizeof (*msg)); + msg->msg_iov = iov; + msg->msg_iovlen = 1; + + bufferObj = (*env)->NewDirectByteBuffer(env, addressP, dataLength); + CHECK_NULL(bufferObj); + + alreadyRead = read - dataOffset; + if (alreadyRead > 0) { + memcpy(addressP, /*ssf->ssf_data*/ dataP, alreadyRead); + iov->iov_base = addressP + alreadyRead; + iov->iov_len = dataLength - alreadyRead; + } else { + iov->iov_base = addressP; + iov->iov_len = dataLength; + } + + if (remaining > 0) { + if ((rv = recvmsg(fd, msg, 0)) < 0) { + fprintf(stdout, "\nNative: handleSFN: recvmsg failed: errno = %d ", errno); + handleSocketError(env, errno); + return; + } + + if (rv != (dataLength - alreadyRead) || !(msg->msg_flags & MSG_EOR)) { + //TODO: assert false: "should not reach here"; + return; + } + // TODO: Set and document (in API) buffers position. + } + } + + /* create SctpSendFailed */ + resultObj = (*env)->NewObject(env, ssf_class, ssf_ctrID, ssf->ssf_assoc_id, + sri->sinfo_stream, ssf->ssf_error, isaObj, bufferObj); + CHECK_NULL(resultObj); + (*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj); + (*env)->SetIntField(env, resultContainerObj, src_typeID, + sun_nio_ch_SctpResultContainer_SEND_FAILED); +} + +void handleAssocChange + (JNIEnv* env, jobject resultContainerObj, struct sctp_assoc_change *sac) { + jobject resultObj; + int state = 0; + + switch (sac->sac_state) { + case SCTP_COMM_UP : + state = sun_nio_ch_SctpAssocChange_SCTP_COMM_UP; + break; + case SCTP_COMM_LOST : + state = sun_nio_ch_SctpAssocChange_SCTP_COMM_LOST; + break; + case SCTP_RESTART : + state = sun_nio_ch_SctpAssocChange_SCTP_RESTART; + break; + case SCTP_SHUTDOWN_COMP : + state = sun_nio_ch_SctpAssocChange_SCTP_SHUTDOWN; + break; + case SCTP_CANT_STR_ASSOC : + state = sun_nio_ch_SctpAssocChange_SCTP_CANT_START; + } + + /* create SctpAssociationChanged */ + resultObj = (*env)->NewObject(env, sac_class, sac_ctrID, sac->sac_assoc_id, + state, sac->sac_outbound_streams, sac->sac_inbound_streams); + CHECK_NULL(resultObj); + (*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj); + (*env)->SetIntField(env, resultContainerObj, src_typeID, + sun_nio_ch_SctpResultContainer_ASSOCIATION_CHANGED); +} + +void handleShutdown + (JNIEnv* env, jobject resultContainerObj, struct sctp_shutdown_event* sse) { + /* create SctpShutdown */ + jobject resultObj = (*env)->NewObject(env, ss_class, ss_ctrID, sse->sse_assoc_id); + CHECK_NULL(resultObj); + (*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj); + (*env)->SetIntField(env, resultContainerObj, src_typeID, + sun_nio_ch_SctpResultContainer_SHUTDOWN); +} + +void handlePeerAddrChange + (JNIEnv* env, jobject resultContainerObj, struct sctp_paddr_change* spc) { + int event = 0; + jobject addressObj, resultObj; + unsigned int state = spc->spc_state; + + switch (state) { + case SCTP_ADDR_AVAILABLE : + event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_AVAILABLE; + break; + case SCTP_ADDR_UNREACHABLE : + event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_UNREACHABLE; + break; + case SCTP_ADDR_REMOVED : + event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_REMOVED; + break; + case SCTP_ADDR_ADDED : + event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_ADDED; + break; + case SCTP_ADDR_MADE_PRIM : + event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_MADE_PRIM; +#ifdef __linux__ /* Solaris currently doesn't support SCTP_ADDR_CONFIRMED */ + break; + case SCTP_ADDR_CONFIRMED : + event = sun_nio_ch_SctpPeerAddrChange_SCTP_ADDR_CONFIRMED; +#endif /* __linux__ */ + } + + addressObj = SockAddrToInetSocketAddress(env, (struct sockaddr*)&spc->spc_aaddr); + + /* create SctpPeerAddressChanged */ + resultObj = (*env)->NewObject(env, spc_class, spc_ctrID, spc->spc_assoc_id, + addressObj, event); + CHECK_NULL(resultObj); + (*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj); + (*env)->SetIntField(env, resultContainerObj, src_typeID, + sun_nio_ch_SctpResultContainer_PEER_ADDRESS_CHANGED); +} + +void handleUninteresting + (union sctp_notification *snp) { + //fprintf(stdout,"\nNative: handleUninterestingNotification: Receive notification type [%u]", snp->sn_header.sn_type); +} + +/** + * Handle notifications from the SCTP stack. + * Returns JNI_TRUE if the notification is one that is of interest to the + * Java API, otherwise JNI_FALSE. + */ +jboolean handleNotification + (JNIEnv* env, int fd, jobject resultContainerObj, union sctp_notification* snp, + int read, jboolean isEOR, struct sockaddr* sap) { + switch (snp->sn_header.sn_type) { + case SCTP_SEND_FAILED: + handleSendFailed(env, fd, resultContainerObj, &snp->sn_send_failed, + read, isEOR, sap); + return JNI_TRUE; + case SCTP_ASSOC_CHANGE: + handleAssocChange(env, resultContainerObj, &snp->sn_assoc_change); + return JNI_TRUE; + case SCTP_SHUTDOWN_EVENT: + handleShutdown(env, resultContainerObj, &snp->sn_shutdown_event); + return JNI_TRUE; + case SCTP_PEER_ADDR_CHANGE: + handlePeerAddrChange(env, resultContainerObj, &snp->sn_paddr_change); + return JNI_TRUE; + default : + /* the Java API is not interested in this event, maybe we are? */ + handleUninteresting(snp); + } + return JNI_FALSE; +} + +void handleMessage + (JNIEnv* env, jobject resultContainerObj, struct msghdr* msg,int read, + jboolean isEOR, struct sockaddr* sap) { + jobject isa, resultObj; + struct controlData cdata[1]; + + if (read == 0) { + /* we reached EOF */ + read = -1; + } + + isa = SockAddrToInetSocketAddress(env, sap); + getControlData(msg, cdata); + + /* create SctpMessageInfoImpl */ + resultObj = (*env)->NewObject(env, smi_class, smi_ctrID, cdata->assocId, + isa, read, cdata->streamNumber, + isEOR ? JNI_TRUE : JNI_FALSE, + cdata->unordered, cdata->ppid); + CHECK_NULL(resultObj); + (*env)->SetObjectField(env, resultContainerObj, src_valueID, resultObj); + (*env)->SetIntField(env, resultContainerObj, src_typeID, + sun_nio_ch_SctpResultContainer_MESSAGE); +} + +/* + * Class: sun_nio_ch_SctpChannelImpl + * Method: receive0 + * Signature: (ILsun/nio/ch/SctpResultContainer;JI)I + */ +JNIEXPORT jint JNICALL Java_sun_nio_ch_SctpChannelImpl_receive0 + (JNIEnv *env, jclass klass, jint fd, jobject resultContainerObj, + jlong address, jint length) { + SOCKADDR sa; + int sa_len = sizeof(sa); + ssize_t rv = 0; + jlong *addr = jlong_to_ptr(address); + struct iovec iov[1]; + struct msghdr msg[1]; + char cbuf[CMSG_SPACE(sizeof (struct sctp_sndrcvinfo))]; + + /* Set up the msghdr structure for receiving */ + memset(msg, 0, sizeof (*msg)); + msg->msg_name = &sa; + msg->msg_namelen = sa_len; + iov->iov_base = addr; + iov->iov_len = length; + msg->msg_iov = iov; + msg->msg_iovlen = 1; + msg->msg_control = cbuf; + msg->msg_controllen = sizeof(cbuf); + msg->msg_flags = 0; + + do { + if ((rv = recvmsg(fd, msg, 0)) < 0) { + if (errno == EWOULDBLOCK) { + return IOS_UNAVAILABLE; + } else if (errno == EINTR) { + return IOS_INTERRUPTED; + +#ifdef __linux__ + } else if (errno == ENOTCONN) { + /* ENOTCONN when EOF reached */ + rv = 0; + /* there will be no control data */ + msg->msg_controllen = 0; +#endif /* __linux__ */ + + } else { + handleSocketError(env, errno); + return 0; + } + } + + if (msg->msg_flags & MSG_NOTIFICATION) { + char *bufp = (char*)addr; + union sctp_notification *snp; + + if (!(msg->msg_flags & MSG_EOR) && length < NOTIFICATION_BUFFER_SIZE) { + char buf[NOTIFICATION_BUFFER_SIZE]; + int rvSAVE = rv; + memcpy(buf, addr, rv); + iov->iov_base = buf + rv; + iov->iov_len = NOTIFICATION_BUFFER_SIZE - rv; + if ((rv = recvmsg(fd, msg, 0)) < 0) { + handleSocketError(env, errno); + return 0; + } + bufp = buf; + rv += rvSAVE; + } + snp = (union sctp_notification *) bufp; + if (handleNotification(env, fd, resultContainerObj, snp, rv, + (msg->msg_flags & MSG_EOR), + (struct sockaddr*)&sa ) == JNI_TRUE) { + /* We have received a notification that is of interest to + to the Java API. The appropriate notification will be + set in the result container. */ + return 0; + } + + // set iov back to addr, and reset msg_controllen + iov->iov_base = addr; + iov->iov_len = length; + msg->msg_control = cbuf; + msg->msg_controllen = sizeof(cbuf); + } + } while (msg->msg_flags & MSG_NOTIFICATION); + + handleMessage(env, resultContainerObj, msg, rv, + (msg->msg_flags & MSG_EOR), (struct sockaddr*)&sa); + return rv; +} + +/* + * Class: sun_nio_ch_SctpChannelImpl + * Method: send0 + * Signature: (IJILjava/net/SocketAddress;IIZI)I + */ +JNIEXPORT jint JNICALL Java_sun_nio_ch_SctpChannelImpl_send0 + (JNIEnv *env, jclass klass, jint fd, jlong address, jint length, + jobject saTarget, jint assocId, jint streamNumber, jboolean unordered, + jint ppid) { + SOCKADDR sa; + int sa_len = sizeof(sa); + ssize_t rv = 0; + jlong *addr = jlong_to_ptr(address); + struct iovec iov[1]; + struct msghdr msg[1]; + int cbuf_size = CMSG_SPACE(sizeof (struct sctp_sndrcvinfo)); + char cbuf[CMSG_SPACE(sizeof (struct sctp_sndrcvinfo))]; + struct controlData cdata[1]; + + /* SctpChannel: + * saTarget may contain the preferred address or NULL to use primary, + * assocId will always be -1 + * SctpMultiChannell: + * Setup new association, saTarget will contain address, assocId = -1 + * Association already existing, assocId != -1, saTarget = preferred addr + */ + if (saTarget != NULL /*&& assocId <= 0*/) { + + jobject targetAddress = (*env)->GetObjectField(env, saTarget, isa_addrID); + jint targetPort = (*env)->GetIntField(env, saTarget, isa_portID); + + if (NET_InetAddressToSockaddr(env, targetAddress, targetPort, + (struct sockaddr *)&sa, + &sa_len, JNI_TRUE) != 0) { + return IOS_THROWN; + } + } else { + memset(&sa, '\x0', sa_len); + sa_len = 0; + } + + /* Set up the msghdr structure for sending */ + memset(msg, 0, sizeof (*msg)); + memset(cbuf, 0, cbuf_size); + msg->msg_name = &sa; + msg->msg_namelen = sa_len; + iov->iov_base = addr; + iov->iov_len = length; + msg->msg_iov = iov; + msg->msg_iovlen = 1; + msg->msg_control = cbuf; + msg->msg_controllen = cbuf_size; + msg->msg_flags = 0; + + cdata->streamNumber = streamNumber; + cdata->assocId = assocId; + cdata->unordered = unordered; + cdata->ppid = ppid; + setControlData(msg, cdata); + + if ((rv = sendmsg(fd, msg, 0)) < 0) { + if (errno == EWOULDBLOCK) { + return IOS_UNAVAILABLE; + } else if (errno == EINTR) { + return IOS_INTERRUPTED; + } else if (errno == EPIPE) { + JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", + "Socket is shutdown for writing"); + } else { + handleSocketError(env, errno); + return 0; + } + } + + return rv; +} + +/* + * Class: sun_nio_ch_SctpChannelImpl + * Method: checkConnect + * Signature: (Ljava/io/FileDescriptor;ZZ)I + */ +JNIEXPORT jint JNICALL Java_sun_nio_ch_SctpChannelImpl_checkConnect + (JNIEnv* env, jobject this, jobject fdo, jboolean block, jboolean ready) { + return Java_sun_nio_ch_SocketChannelImpl_checkConnect(env, this, + fdo, block, ready); +} + diff --git a/src/solaris/native/sun/nio/ch/SctpNet.c b/src/solaris/native/sun/nio/ch/SctpNet.c new file mode 100644 index 000000000..d1b3b2f03 --- /dev/null +++ b/src/solaris/native/sun/nio/ch/SctpNet.c @@ -0,0 +1,609 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +#include +#include +#include + +#include "Sctp.h" +#include "jni.h" +#include "jni_util.h" +#include "nio_util.h" +#include "nio.h" +#include "net_util.h" +#include "net_util_md.h" +#include "sun_nio_ch_SctpNet.h" +#include "sun_nio_ch_SctpStdSocketOption.h" + +static jclass isaCls = 0; +static jmethodID isaCtrID = 0; + +static const char* nativeSctpLib = "libsctp.so.1"; +static jboolean funcsLoaded = JNI_FALSE; + +JNIEXPORT jint JNICALL JNI_OnLoad + (JavaVM *vm, void *reserved) { + return JNI_VERSION_1_2; +} + +/** + * Loads the native sctp library that contains the socket extension + * functions, as well as locating the individual functions. + * There will be a pending exception if this method returns false. + */ +jboolean loadSocketExtensionFuncs + (JNIEnv* env) { + if (dlopen(nativeSctpLib, RTLD_GLOBAL | RTLD_LAZY) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + if ((nio_sctp_getladdrs = (sctp_getladdrs_func*) + dlsym(RTLD_DEFAULT, "sctp_getladdrs")) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + if ((nio_sctp_freeladdrs = (sctp_freeladdrs_func*) + dlsym(RTLD_DEFAULT, "sctp_freeladdrs")) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + if ((nio_sctp_getpaddrs = (sctp_getpaddrs_func*) + dlsym(RTLD_DEFAULT, "sctp_getpaddrs")) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + if ((nio_sctp_freepaddrs = (sctp_freepaddrs_func*) + dlsym(RTLD_DEFAULT, "sctp_freepaddrs")) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + if ((nio_sctp_bindx = (sctp_bindx_func*) + dlsym(RTLD_DEFAULT, "sctp_bindx")) == NULL) { + JNU_ThrowByName(env, "java/lang/UnsupportedOperationException", + dlerror()); + return JNI_FALSE; + } + + funcsLoaded = JNI_TRUE; + return JNI_TRUE; +} + +/* + * Class: sun_nio_ch_SctpNet + * Method: socket0 + * Signature: (Z)I + */ +JNIEXPORT jint JNICALL Java_sun_nio_ch_SctpNet_socket0 + (JNIEnv *env, jclass klass, jboolean oneToOne) { + int fd; + struct sctp_event_subscribe event; + + /* Try to load the socket API extension functions */ + if (!funcsLoaded && !loadSocketExtensionFuncs(env)) { + return 0; + } + + fd = socket(ipv6_available() ? AF_INET6 : AF_INET, + (oneToOne ? SOCK_STREAM : SOCK_SEQPACKET), IPPROTO_SCTP); + + if (fd < 0) { + return handleSocketError(env, errno); + } + + /* Enable events */ + memset(&event, 0, sizeof(event)); + event.sctp_data_io_event = 1; + event.sctp_association_event = 1; + event.sctp_address_event = 1; + event.sctp_send_failure_event = 1; + //event.sctp_peer_error_event = 1; + event.sctp_shutdown_event = 1; + //event.sctp_partial_delivery_event = 1; + //event.sctp_adaptation_layer_event = 1; + if (setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, &event, sizeof(event)) != 0) { + handleSocketError(env, errno); + } + return fd; +} + +/* + * Class: sun_nio_ch_SctpNet + * Method: bindx + * Signature: (I[Ljava/net/InetAddress;IIZ)V + */ +JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_bindx + (JNIEnv *env, jclass klass, jint fd, jobjectArray addrs, jint port, + jint addrsLength, jboolean add, jboolean preferIPv6) { + SOCKADDR *sap, *tmpSap; + int i, sa_len = sizeof(SOCKADDR); + jobject ia; + + if (addrsLength < 1) + return; + + if ((sap = calloc(addrsLength, sa_len)) == NULL) { + JNU_ThrowOutOfMemoryError(env, "heap allocation failure"); + return; + } + + tmpSap = sap; + for (i=0; iGetObjectArrayElement(env, addrs, i); + if (NET_InetAddressToSockaddr(env, ia, port, (struct sockaddr*)tmpSap, + &sa_len, preferIPv6) != 0) { + free(sap); + return; + } + tmpSap++; + } + + if (nio_sctp_bindx(fd, (void*)sap, addrsLength, add ? SCTP_BINDX_ADD_ADDR : + SCTP_BINDX_REM_ADDR) != 0) { + handleSocketError(env, errno); + } + + free(sap); +} + +void initializeISA + (JNIEnv* env) { + if (isaCls == 0) { + jclass c = (*env)->FindClass(env, "java/net/InetSocketAddress"); + CHECK_NULL(c); + isaCls = (*env)->NewGlobalRef(env, c); + CHECK_NULL(isaCls); + (*env)->DeleteLocalRef(env, c); + isaCtrID = (*env)->GetMethodID(env, isaCls, "", + "(Ljava/net/InetAddress;I)V"); + } +} + +jobject SockAddrToInetSocketAddress + (JNIEnv *env, struct sockaddr* sap) { + int port = 0; + + jobject ia = NET_SockaddrToInetAddress(env, sap, &port); + if (ia == NULL) + return NULL; + + if (isaCls == 0) { + initializeISA(env); + CHECK_NULL_RETURN(isaCls, NULL); + } + + return (*env)->NewObject(env, isaCls, isaCtrID, ia, port); +} + +/* + * Class: sun_nio_ch_SctpNet + * Method: getLocalAddresses0 + * Signature: (I)[Ljava/net/SocketAddress; + */ +JNIEXPORT jobjectArray JNICALL Java_sun_nio_ch_SctpNet_getLocalAddresses0 + (JNIEnv *env, jclass klass, jint fd) { + void *addr_buf, *laddr; + struct sockaddr* sap; + int i, addrCount; + jobjectArray isaa; + +#ifdef __solaris__ + if ((addrCount = nio_sctp_getladdrs(fd, 0, (void **)&addr_buf)) == -1) { +#else /* __linux__ */ + if ((addrCount = nio_sctp_getladdrs(fd, 0, (struct sockaddr **)&addr_buf)) == -1) { +#endif + handleSocketError(env, errno); + return NULL; + } + + if (addrCount < 1) + return NULL; + + if (isaCls == 0) { + initializeISA(env); + CHECK_NULL_RETURN(isaCls, NULL); + } + + isaa = (*env)->NewObjectArray(env, addrCount, isaCls, NULL); + if (isaa == NULL) { + nio_sctp_freeladdrs(addr_buf); + return NULL; + } + + laddr = addr_buf; + for (i=0; iNewObject(env, isaCls, isaCtrID, ia, port); + if (isa != NULL) + (*env)->SetObjectArrayElement(env, isaa, i, isa); + + if (sap->sa_family == AF_INET) + addr_buf = ((struct sockaddr_in*)addr_buf) + 1; + else + addr_buf = ((struct sockaddr_in6*)addr_buf) + 1; + } + + nio_sctp_freeladdrs(laddr); + return isaa; +} + +jobjectArray getRemoteAddresses + (JNIEnv *env, jint fd, sctp_assoc_t id) { + void *addr_buf, *paddr; + struct sockaddr* sap; + int i, addrCount; + jobjectArray isaa; + +#if __solaris__ + if ((addrCount = nio_sctp_getpaddrs(fd, id, (void **)&addr_buf)) == -1) { +#else /* __linux__ */ + if ((addrCount = nio_sctp_getpaddrs(fd, id, (struct sockaddr**)&addr_buf)) == -1) { +#endif + handleSocketError(env, errno); + return NULL; + } + + if (addrCount < 1) + return NULL; + + if (isaCls == 0) { + initializeISA(env); + CHECK_NULL_RETURN(isaCls, NULL); + } + + isaa = (*env)->NewObjectArray(env, addrCount, isaCls, NULL); + if (isaa == NULL) { + nio_sctp_freepaddrs(addr_buf); + return NULL; + } + + paddr = addr_buf; + for (i=0; iNewObject(env, isaCls, isaCtrID, ia, port); + if (isa != NULL) + (*env)->SetObjectArrayElement(env, isaa, i, isa); + + if (sap->sa_family == AF_INET) + addr_buf = ((struct sockaddr_in*)addr_buf) + 1; + else + addr_buf = ((struct sockaddr_in6*)addr_buf) + 1; + } + + nio_sctp_freepaddrs(paddr); + + return isaa; +} + + /* + * Class: sun_nio_ch_SctpNet + * Method: getRemoteAddresses0 + * Signature: (II)[Ljava/net/SocketAddress; + */ +JNIEXPORT jobjectArray JNICALL Java_sun_nio_ch_SctpNet_getRemoteAddresses0 + (JNIEnv *env, jclass klass, jint fd, jint assocId) { + return getRemoteAddresses(env, fd, assocId); +} + +/* Map the Java level option to the native level */ +int mapSocketOption + (jint cmd, int *level, int *optname) { + static struct { + jint cmd; + int level; + int optname; + } const opts[] = { + { sun_nio_ch_SctpStdSocketOption_SCTP_DISABLE_FRAGMENTS, IPPROTO_SCTP, SCTP_DISABLE_FRAGMENTS }, + { sun_nio_ch_SctpStdSocketOption_SCTP_EXPLICIT_COMPLETE, IPPROTO_SCTP, SCTP_EXPLICIT_EOR }, + { sun_nio_ch_SctpStdSocketOption_SCTP_FRAGMENT_INTERLEAVE, IPPROTO_SCTP, SCTP_FRAGMENT_INTERLEAVE }, + { sun_nio_ch_SctpStdSocketOption_SCTP_NODELAY, IPPROTO_SCTP, SCTP_NODELAY }, + { sun_nio_ch_SctpStdSocketOption_SO_SNDBUF, SOL_SOCKET, SO_SNDBUF }, + { sun_nio_ch_SctpStdSocketOption_SO_RCVBUF, SOL_SOCKET, SO_RCVBUF }, + { sun_nio_ch_SctpStdSocketOption_SO_LINGER, SOL_SOCKET, SO_LINGER } }; + + int i; + for (i=0; i<(int)(sizeof(opts) / sizeof(opts[0])); i++) { + if (cmd == opts[i].cmd) { + *level = opts[i].level; + *optname = opts[i].optname; + return 0; + } + } + + /* not found */ + return -1; +} + +/* + * Class: sun_nio_ch_SctpNet + * Method: setIntOption0 + * Signature: (III)V + */ +JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_setIntOption0 + (JNIEnv *env, jclass klass, jint fd, jint opt, int arg) { + int klevel, kopt; + int result; + struct linger linger; + void *parg; + int arglen; + + if (mapSocketOption(opt, &klevel, &kopt) < 0) { + JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", + "Unsupported socket option"); + return; + } + + if (opt == sun_nio_ch_SctpStdSocketOption_SO_LINGER) { + parg = (void *)&linger; + arglen = sizeof(linger); + if (arg >= 0) { + linger.l_onoff = 1; + linger.l_linger = arg; + } else { + linger.l_onoff = 0; + linger.l_linger = 0; + } + } else { + parg = (void *)&arg; + arglen = sizeof(arg); + } + + if (setsockopt(fd, klevel, kopt, parg, arglen) < 0) { + JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", + "sun_nio_ch_SctpNet.setIntOption0"); + } +} + +/* + * Class: sun_nio_ch_SctpNet + * Method: getIntOption0 + * Signature: (II)I + */ +JNIEXPORT int JNICALL Java_sun_nio_ch_SctpNet_getIntOption0 + (JNIEnv *env, jclass klass, jint fd, jint opt) { + int klevel, kopt; + int result; + struct linger linger; + void *arg; + unsigned int arglen; + + if (mapSocketOption(opt, &klevel, &kopt) < 0) { + JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", + "Unsupported socket option"); + return -1; + } + + if (opt == sun_nio_ch_SctpStdSocketOption_SO_LINGER) { + arg = (void *)&linger; + arglen = sizeof(linger); + } else { + arg = (void *)&result; + arglen = sizeof(result); + } + + if (getsockopt(fd, klevel, kopt, arg, &arglen) < 0) { + JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", + "sun.nio.ch.Net.getIntOption"); + return -1; + } + + if (opt == sun_nio_ch_SctpStdSocketOption_SO_LINGER) + return linger.l_onoff ? linger.l_linger : -1; + else + return result; +} + +/* + * Class: sun_nio_ch_SctpNet + * Method: getPrimAddrOption0 + * Signature: (II)Ljava/net/SocketAddress; + */ +JNIEXPORT jobject JNICALL Java_sun_nio_ch_SctpNet_getPrimAddrOption0 + (JNIEnv *env, jclass klass, jint fd, jint assocId) { + struct sctp_setprim prim; + struct sockaddr_storage ss; + int ss_len = sizeof(ss); + unsigned int prim_len = sizeof(prim); + + prim.ssp_assoc_id = assocId; + prim.ssp_addr = ss; + + if (getsockopt(fd, IPPROTO_SCTP, SCTP_PRIMARY_ADDR, &prim, &prim_len) < 0) { + JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", + "sun.nio.ch.SctpNet.getPrimAddrOption0"); + return NULL; + } + + return SockAddrToInetSocketAddress(env, (struct sockaddr*)&ss); +} + +/* + * Class: sun_nio_ch_SctpNet + * Method: setPrimAddrOption0 + * Signature: (IILjava/net/InetAddress;I)V + */ +JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_setPrimAddrOption0 + (JNIEnv *env, jclass klass, jint fd, jint assocId, jobject iaObj, jint port) { + struct sctp_setprim prim; + struct sockaddr_storage ss; + int ss_len = sizeof(ss); + + if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&ss, + &ss_len, JNI_TRUE) != 0) { + return; + } + + prim.ssp_assoc_id = assocId; + prim.ssp_addr = ss; + + if (setsockopt(fd, IPPROTO_SCTP, SCTP_PRIMARY_ADDR, &prim, sizeof(prim)) < 0) { + JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", + "sun.nio.ch.SctpNet.setPrimAddrOption0"); + } +} + +/* + * Class: sun_nio_ch_SctpNet + * Method: setPeerPrimAddrOption0 + * Signature: (IILjava/net/InetAddress;I)V + */ +JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_setPeerPrimAddrOption0 + (JNIEnv *env, jclass klass, jint fd, jint assocId, jobject iaObj, jint port) { + struct sctp_setpeerprim prim; + struct sockaddr_storage ss; + int ss_len = sizeof(ss); + + if (NET_InetAddressToSockaddr(env, iaObj, port, (struct sockaddr *)&ss, + &ss_len, JNI_TRUE) != 0) { + return; + } + + prim.sspp_assoc_id = assocId; + prim.sspp_addr = ss; + + if (setsockopt(fd, IPPROTO_SCTP, SCTP_SET_PEER_PRIMARY_ADDR, &prim, + sizeof(prim)) < 0) { + JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", + "sun.nio.ch.SctpNet.setPeerPrimAddrOption0"); + } +} + +/* + * Class: sun_nio_ch_SctpNet + * Method: getInitMsgOption0 + * Signature: (I[I)V + */ +JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_getInitMsgOption0 + (JNIEnv *env, jclass klass, jint fd, jintArray retVal) { + struct sctp_initmsg sctp_initmsg; + unsigned int sim_len = sizeof(sctp_initmsg); + int vals[2]; + + if (getsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &sctp_initmsg, + &sim_len) < 0) { + JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", + "sun.nio.ch.SctpNet.getInitMsgOption0"); + return; + } + + vals[0] = sctp_initmsg.sinit_max_instreams; + vals[1] = sctp_initmsg.sinit_num_ostreams; + (*env)->SetIntArrayRegion(env, retVal, 0, 2, vals); +} + +/* + * Class: sun_nio_ch_SctpNet + * Method: setInitMsgOption0 + * Signature: (III)V + */ +JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_setInitMsgOption0 + (JNIEnv *env, jclass klass, jint fd, jint inArg, jint outArg) { + struct sctp_initmsg sctp_initmsg; + + sctp_initmsg.sinit_max_instreams = (unsigned int)inArg; + sctp_initmsg.sinit_num_ostreams = (unsigned int)outArg; + sctp_initmsg.sinit_max_attempts = 0; // default + sctp_initmsg.sinit_max_init_timeo = 0; // default + + if (setsockopt(fd, IPPROTO_SCTP, SCTP_INITMSG, &sctp_initmsg, + sizeof(sctp_initmsg)) < 0) { + JNU_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", + "sun.nio.ch.SctpNet.setInitMsgOption0"); + } +} + +/* + * Class: sun_nio_ch_SctpNet + * Method: shutdown0 + * Signature: (II)V + */ +JNIEXPORT void JNICALL Java_sun_nio_ch_SctpNet_shutdown0 + (JNIEnv *env, jclass klass, jint fd, jint assocId) { + int rv; + struct msghdr msg[1]; + struct iovec iov[1]; + int cbuf_size = CMSG_SPACE(sizeof (struct sctp_sndrcvinfo)); + char cbuf[CMSG_SPACE(sizeof (struct sctp_sndrcvinfo))]; + struct cmsghdr* cmsg; + struct sctp_sndrcvinfo *sri; + + /* SctpSocketChannel */ + if (assocId < 0) { + shutdown(fd, SHUT_WR); + return; + } + + memset(msg, 0, sizeof (*msg)); + memset(cbuf, 0, cbuf_size); + msg->msg_name = NULL; + msg->msg_namelen = 0; + iov->iov_base = NULL; + iov->iov_len = 0; + msg->msg_iov = iov; + msg->msg_iovlen = 1; + msg->msg_control = cbuf; + msg->msg_controllen = cbuf_size; + msg->msg_flags = 0; + + cmsg = CMSG_FIRSTHDR(msg); + cmsg->cmsg_level = IPPROTO_SCTP; + cmsg->cmsg_type = SCTP_SNDRCV; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo)); + + /* Initialize the payload: */ + sri = (struct sctp_sndrcvinfo*) CMSG_DATA(cmsg); + memset(sri, 0, sizeof (*sri)); + + if (assocId > 0) { + sri->sinfo_assoc_id = assocId; + } + + sri->sinfo_flags = sri->sinfo_flags | SCTP_EOF; + + /* Sum of the length of all control messages in the buffer. */ + msg->msg_controllen = cmsg->cmsg_len; + + if ((rv = sendmsg(fd, msg, 0)) < 0) { + handleSocketError(env, errno); + } +} + diff --git a/src/solaris/native/sun/nio/ch/SctpServerChannelImpl.c b/src/solaris/native/sun/nio/ch/SctpServerChannelImpl.c new file mode 100644 index 000000000..ba3742f75 --- /dev/null +++ b/src/solaris/native/sun/nio/ch/SctpServerChannelImpl.c @@ -0,0 +1,53 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +#include "sun_nio_ch_SctpServerChannelImpl.h" + +extern void Java_sun_nio_ch_ServerSocketChannelImpl_initIDs(JNIEnv* env, + jclass c); + +extern jint Java_sun_nio_ch_ServerSocketChannelImpl_accept0(JNIEnv* env, + jobject this, jobject ssfdo, jobject newfdo, jobjectArray isaa); + +/* + * Class: sun_nio_ch_SctpServerChannelImpl + * Method: initIDs + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_sun_nio_ch_SctpServerChannelImpl_initIDs + (JNIEnv* env, jclass c) { + Java_sun_nio_ch_ServerSocketChannelImpl_initIDs(env, c); +} + +/* + * Class: sun_nio_ch_SctpServerChannelImpl + * Method: accept0 + * Signature: (Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;[Ljava/net/InetSocketAddress;)I + */ +JNIEXPORT jint JNICALL Java_sun_nio_ch_SctpServerChannelImpl_accept0 + (JNIEnv* env, jobject this, jobject ssfdo, jobject newfdo, jobjectArray isaa) { + return Java_sun_nio_ch_ServerSocketChannelImpl_accept0(env, this, + ssfdo, newfdo, isaa); +} diff --git a/src/windows/classes/sun/nio/ch/SctpChannelImpl.java b/src/windows/classes/sun/nio/ch/SctpChannelImpl.java new file mode 100644 index 000000000..4a06a07dd --- /dev/null +++ b/src/windows/classes/sun/nio/ch/SctpChannelImpl.java @@ -0,0 +1,150 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package sun.nio.ch; + +import java.net.SocketAddress; +import java.net.InetAddress; +import java.io.IOException; +import java.util.Set; +import java.nio.ByteBuffer; +import java.nio.channels.spi.SelectorProvider; +import com.sun.nio.sctp.Association; +import com.sun.nio.sctp.MessageInfo; +import com.sun.nio.sctp.NotificationHandler; +import com.sun.nio.sctp.SctpChannel; +import com.sun.nio.sctp.SctpSocketOption; + +/** + * Unimplemented. + */ +public class SctpChannelImpl extends SctpChannel +{ + private static final String message = "SCTP not supported on this platform"; + + public SctpChannelImpl(SelectorProvider provider) { + super(provider); + throw new UnsupportedOperationException(message); + } + + @Override + public Association association() { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpChannel bind(SocketAddress local) + throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpChannel bindAddress(InetAddress address) + throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpChannel unbindAddress(InetAddress address) + throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public boolean connect(SocketAddress remote) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public boolean connect(SocketAddress remote, int maxOutStreams, + int maxInStreams) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public boolean isConnectionPending() { + throw new UnsupportedOperationException(message); + } + + @Override + public boolean finishConnect() throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public Set getAllLocalAddresses() + throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public Set getRemoteAddresses() + throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpChannel shutdown() throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public T getOption(SctpSocketOption name) + throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpChannel setOption(SctpSocketOption name, T value) + throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public Set> supportedOptions() { + throw new UnsupportedOperationException(message); + } + + @Override + public MessageInfo receive(ByteBuffer dst, T attachment, + NotificationHandler handler) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public int send(ByteBuffer src, MessageInfo messageInfo) + throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + protected void implConfigureBlocking(boolean block) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public void implCloseSelectableChannel() throws IOException { + throw new UnsupportedOperationException(message); + } +} diff --git a/src/windows/classes/sun/nio/ch/SctpMultiChannelImpl.java b/src/windows/classes/sun/nio/ch/SctpMultiChannelImpl.java new file mode 100644 index 000000000..372a0616d --- /dev/null +++ b/src/windows/classes/sun/nio/ch/SctpMultiChannelImpl.java @@ -0,0 +1,137 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package sun.nio.ch; + +import java.net.SocketAddress; +import java.net.InetAddress; +import java.io.IOException; +import java.util.Set; +import java.nio.ByteBuffer; +import java.nio.channels.spi.SelectorProvider; +import com.sun.nio.sctp.Association; +import com.sun.nio.sctp.SctpChannel; +import com.sun.nio.sctp.MessageInfo; +import com.sun.nio.sctp.NotificationHandler; +import com.sun.nio.sctp.SctpMultiChannel; +import com.sun.nio.sctp.SctpSocketOption; + +/** + * Unimplemented. + */ +public class SctpMultiChannelImpl extends SctpMultiChannel +{ + private static final String message = "SCTP not supported on this platform"; + + public SctpMultiChannelImpl(SelectorProvider provider) { + super(provider); + throw new UnsupportedOperationException(message); + } + + @Override + public Set associations() { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpMultiChannel bind(SocketAddress local, + int backlog) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpMultiChannel bindAddress(InetAddress address) + throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpMultiChannel unbindAddress(InetAddress address) + throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public Set getAllLocalAddresses() + throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public Set getRemoteAddresses + (Association association) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpMultiChannel shutdown(Association association) + throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public T getOption(SctpSocketOption name, + Association association) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpMultiChannel setOption(SctpSocketOption name, + T value, Association association) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public Set> supportedOptions() { + throw new UnsupportedOperationException(message); + } + + @Override + public MessageInfo receive(ByteBuffer buffer, T attachment, + NotificationHandler handler) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public int send(ByteBuffer buffer, MessageInfo messageInfo) + throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpChannel branch(Association association) + throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + protected void implConfigureBlocking(boolean block) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public void implCloseSelectableChannel() throws IOException { + throw new UnsupportedOperationException(message); + } +} diff --git a/src/windows/classes/sun/nio/ch/SctpServerChannelImpl.java b/src/windows/classes/sun/nio/ch/SctpServerChannelImpl.java new file mode 100644 index 000000000..36fbbcfd9 --- /dev/null +++ b/src/windows/classes/sun/nio/ch/SctpServerChannelImpl.java @@ -0,0 +1,102 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ +package sun.nio.ch; + +import java.net.SocketAddress; +import java.net.InetAddress; +import java.io.IOException; +import java.util.Set; +import java.nio.channels.spi.SelectorProvider; +import com.sun.nio.sctp.SctpChannel; +import com.sun.nio.sctp.SctpServerChannel; +import com.sun.nio.sctp.SctpSocketOption; + +/** + * Unimplemented. + */ +public class SctpServerChannelImpl extends SctpServerChannel +{ + private static final String message = "SCTP not supported on this platform"; + + public SctpServerChannelImpl(SelectorProvider provider) { + super(provider); + throw new UnsupportedOperationException(message); + } + + @Override + public SctpChannel accept() throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpServerChannel bind(SocketAddress local, + int backlog) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpServerChannel bindAddress(InetAddress address) + throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpServerChannel unbindAddress(InetAddress address) + throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public Set getAllLocalAddresses() + throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public T getOption(SctpSocketOption name) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public SctpServerChannel setOption(SctpSocketOption name, + T value) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public Set> supportedOptions() { + throw new UnsupportedOperationException(message); + } + + @Override + protected void implConfigureBlocking(boolean block) throws IOException { + throw new UnsupportedOperationException(message); + } + + @Override + public void implCloseSelectableChannel() throws IOException { + throw new UnsupportedOperationException(message); + } +} diff --git a/test/com/sun/nio/sctp/MessageInfoTests.java b/test/com/sun/nio/sctp/MessageInfoTests.java new file mode 100644 index 000000000..1cc1e9fdc --- /dev/null +++ b/test/com/sun/nio/sctp/MessageInfoTests.java @@ -0,0 +1,148 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 4927640 + * @summary Tests the SCTP protocol implementation + * @author chegar + */ + +import java.net.SocketAddress; +import com.sun.nio.sctp.Association; +import com.sun.nio.sctp.MessageInfo; + +public class MessageInfoTests { + static final int DEFAULT_STREAM_NUMBER = 14; + static final int TEST_STREAM_NUMBER = 15; + static final int TEST_PPID = 8; + static final long TEST_TTL = 10000L; + static final SocketAddress addr = new TestSocketAddress(); + static final Association assoc = new TestAssociation(1, 1, 1); + + void test(String[] args) { + /* TEST 1 : createOutGoing(SocketAddress,int) */ + MessageInfo info = MessageInfo.createOutgoing(addr, + DEFAULT_STREAM_NUMBER); + checkDefaults(info); + checkGetterSetters(info); + + /* TEST 2 : createOutGoing(Association,SocketAddress,int) */ + info = MessageInfo.createOutgoing(assoc, addr, DEFAULT_STREAM_NUMBER); + checkDefaults(info); + check(info.association().equals(assoc), "incorrect association"); + checkGetterSetters(info); + + /* TEST 3: null values */ + info = MessageInfo.createOutgoing(null, 0); + check(info.address() == null, "address should be null"); + check(info.association() == null, "association should be null"); + info = MessageInfo.createOutgoing(assoc, null, 0); + check(info.address() == null, "address should be null"); + + /* Test 4: IllegalArgumentException */ + testIAE(new Runnable() { + public void run() { MessageInfo.createOutgoing(addr, -1); } }); + testIAE(new Runnable() { + public void run() { MessageInfo.createOutgoing(addr, 65537); } }); + testIAE(new Runnable() { + public void run() { MessageInfo.createOutgoing(null, addr, 0); } }); + testIAE(new Runnable() { + public void run() { MessageInfo.createOutgoing(assoc, addr, -1); } }); + testIAE(new Runnable() { + public void run() { MessageInfo.createOutgoing(assoc, addr, 65537);}}); + + final MessageInfo iaeInfo = MessageInfo.createOutgoing(assoc, addr, 0); + testIAE(new Runnable() { + public void run() { iaeInfo.streamNumber(-1); } }); + testIAE(new Runnable() { + public void run() { iaeInfo.streamNumber(65537); } }); + } + + /* TEST : unordered = false, timeToLive = 0, complete = true, + * payloadProtocolID = 0. */ + void checkDefaults(MessageInfo info) { + check(info.isUnordered() == false, "default unordered value not false"); + check(info.timeToLive() == 0L, "timeToLive should be 0L"); + check(info.isComplete() == true, "default complete value not true"); + check(info.payloadProtocolID() == 0, "default PPID not 0"); + check(info.bytes() == 0, "default bytes value not 0"); + check(info.streamNumber() == DEFAULT_STREAM_NUMBER, + "incorrect default stream number"); + check(info.address().equals(addr), "incorrect address"); + } + + void checkGetterSetters(MessageInfo info) { + check(info.streamNumber(TEST_STREAM_NUMBER).streamNumber() == + TEST_STREAM_NUMBER, "stream number not being set correctly"); + + check(info.complete(false).isComplete() == false, + "complete not being set correctly"); + + check(info.unordered(true).isUnordered() == true, + "unordered not being set correctly"); + + check(info.payloadProtocolID(TEST_PPID).payloadProtocolID() == + TEST_PPID, "PPID not being set correctly"); + + check(info.timeToLive(TEST_TTL).timeToLive() == TEST_TTL, + "TTL not being set correctly"); + } + + void testIAE(Runnable runnable) { + try { + runnable.run(); + fail("IllegalArgumentException should have been thrown"); + } catch(IllegalArgumentException iae) { + pass(); + } + } + + static class TestSocketAddress extends SocketAddress {} + + static class TestAssociation extends Association { + TestAssociation(int assocID, int maxInStreams, int maxOutStreams) { + super(assocID, maxInStreams, maxOutStreams); + } + } + + //--------------------- Infrastructure --------------------------- + boolean debug = true; + volatile int passed = 0, failed = 0; + void pass() {passed++;} + void fail() {failed++; Thread.dumpStack();} + void fail(String msg) {System.err.println(msg); fail();} + void unexpected(Throwable t) {failed++; t.printStackTrace();} + void check(boolean cond) {if (cond) pass(); else fail();} + void check(boolean cond, String failMessage) {if (cond) pass(); + else fail(failMessage);} + void debug(String message) {if(debug) { System.out.println(message); } } + public static void main(String[] args) throws Throwable { + Class k = new Object(){}.getClass().getEnclosingClass(); + try {k.getMethod("instanceMain",String[].class) + .invoke( k.newInstance(), (Object) args);} + catch (Throwable e) {throw e.getCause();}} + public void instanceMain(String[] args) throws Throwable { + try {test(args);} catch (Throwable t) {unexpected(t);} + System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); + if (failed > 0) throw new AssertionError("Some tests failed");} +} diff --git a/test/com/sun/nio/sctp/SctpChannel/Bind.java b/test/com/sun/nio/sctp/SctpChannel/Bind.java new file mode 100644 index 000000000..5a391128e --- /dev/null +++ b/test/com/sun/nio/sctp/SctpChannel/Bind.java @@ -0,0 +1,350 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 4927640 + * @summary Tests the SCTP protocol implementation + * @author chegar + */ + +import java.net.*; +import java.io.*; +import java.util.List; +import java.util.Set; +import java.util.Iterator; +import java.nio.ByteBuffer; +import java.nio.channels.AlreadyBoundException; +import java.nio.channels.AlreadyConnectedException; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.UnsupportedAddressTypeException; +import com.sun.nio.sctp.AssociationChangeNotification; +import com.sun.nio.sctp.AbstractNotificationHandler; +import com.sun.nio.sctp.HandlerResult; +import com.sun.nio.sctp.IllegalUnbindException; +import com.sun.nio.sctp.MessageInfo; +import com.sun.nio.sctp.PeerAddressChangeNotification; +import com.sun.nio.sctp.SctpChannel; +import com.sun.nio.sctp.SctpServerChannel; +import com.sun.nio.sctp.ShutdownNotification; +import static java.lang.System.out; + +/** + * Tests bind, bindAddress, unbindAddress, getLocalAddress, and + * getAllLocalAddresses. + */ +public class Bind { + void test(String[] args) { + if (!Util.isSCTPSupported()) { + out.println("SCTP protocol is not supported"); + out.println("Test cannot be run"); + return; + } + + /* Simply bind tests */ + testBind(); + + /* Test unconnected */ + testBindUnbind(false); + + /* Test connected */ + /* Adding/Removing addresses from a connected association is optional. + * This test can be run on systems that support dynamic address + * reconfiguration */ + //testBindUnbind(true); + } + + void testBind() { + SctpChannel channel = null; + try { + channel = SctpChannel.open(); + + /* TEST 1: empty set if channel is not bound */ + check(channel.getAllLocalAddresses().isEmpty(), + "getAllLocalAddresses returned non empty set for unbound channel"); + + /* TEST 2: null to bind the channel to an automatically assigned + * socket address */ + channel.bind(null); + + /* TEST 3: non empty set if the channel is bound */ + check(!channel.getAllLocalAddresses().isEmpty(), + "getAllLocalAddresses returned empty set for bound channel"); + debug("getAllLocalAddresses on channel bound to the wildcard:\n" + + channel.getAllLocalAddresses()); + + /* TEST 4: AlreadyBoundException if this channel is already bound */ + try { channel.bind(null); } + catch (AlreadyBoundException unused) { pass(); } + catch (IOException ioe) { unexpected(ioe); } + + /* TEST 5: UnsupportedAddressTypeException */ + try { + channel.close(); /* open a new unbound channel for test */ + channel = SctpChannel.open(); + channel.bind(new UnsupportedSocketAddress()); + fail("UnsupportedSocketAddress expected"); + } catch (UnsupportedAddressTypeException unused) { pass(); + } catch (IOException ioe) { unexpected(ioe); } + + /* TEST 6: AlreadyConnectedException */ + try { + channel.close(); /* open a new unbound channel for test */ + channel = SctpChannel.open(); + connectChannel(channel); + channel.bind(null); + fail("AlreadyConnectedException expected"); + } catch (AlreadyConnectedException unused) { pass(); + } catch (IOException ioe) { unexpected(ioe); } + + /* TEST 7: ClosedChannelException - If this channel is closed */ + try { + channel.close(); /* open a new unbound channel for test */ + channel = SctpChannel.open(); + channel.close(); + channel.bind(null); + fail("ClosedChannelException expected"); + } catch (ClosedChannelException unused) { pass(); + } catch (IOException ioe) { unexpected(ioe); } + + /* TEST 8: ClosedChannelException if channel is closed */ + try { + channel.getAllLocalAddresses(); + fail("should have thrown ClosedChannelException"); + } catch (ClosedChannelException cce) { + pass(); + } catch (Exception ioe) { + unexpected(ioe); + } + } catch (IOException ioe) { + unexpected(ioe); + } finally { + try { channel.close(); } + catch (IOException ioe) { unexpected(ioe); } + } + } + + void testBindUnbind(boolean connected) { + SctpChannel channel = null; + SctpChannel peerChannel = null; + + debug("testBindUnbind, connected: " + connected); + try { + channel = SctpChannel.open(); + + List addresses = Util.getAddresses(true, false); + Iterator iterator = addresses.iterator(); + InetSocketAddress a = new InetSocketAddress((InetAddress)iterator.next(), 0); + debug("channel.bind( " + a + ")"); + channel.bind(a); + while (iterator.hasNext()) { + InetAddress ia = (InetAddress)iterator.next(); + debug("channel.bindAddress(" + ia + ")"); + channel.bindAddress(ia); + } + if (debug) {Util.dumpAddresses(channel, out);} + + if (connected) { + /* Test with connected channel */ + peerChannel = connectChannel(channel); + } + + /* TEST 1: bind/unbindAddresses on the system addresses */ + debug("bind/unbindAddresses on the system addresses"); + List addrs = Util.getAddresses(true, false); + for (InetAddress addr : addrs) { + try { + debug("unbindAddress: " + addr); + check(boundAddress(channel, addr), "trying to remove address that is not bound"); + channel.unbindAddress(addr); + if (debug) {Util.dumpAddresses(channel, out);} + check(!boundAddress(channel, addr), "address was not removed"); + + debug("bindAddress: " + addr); + channel.bindAddress(addr); + if (debug) {Util.dumpAddresses(channel, out);} + check(boundAddress(channel, addr), "address is not bound"); + } catch (IOException ioe) { + unexpected(ioe); + } + } + + /* TEST 2: bindAddress - already bound address. */ + InetAddress againAddress = addrs.get(0); + try { + debug("bind already bound address " + againAddress); + channel.bindAddress(againAddress); + } catch (AlreadyBoundException unused) { + debug("Caught AlreadyBoundException - OK"); + pass(); + } catch (IOException ioe) { + unexpected(ioe); + } + + /* TEST 3: bind non local address */ + try { + InetAddress nla = InetAddress.getByName("123.123.123.123"); + debug("bind non local address " + nla); + channel.bindAddress(nla); + } catch (IOException ioe) { + debug("Informative only " + ioe); + } + + /* TEST 4: unbind address that is not bound */ + try { + debug("unbind address that is not bound " + againAddress); + /* remove address first then again */ + channel.unbindAddress(againAddress); + channel.unbindAddress(againAddress); + } catch (IllegalUnbindException unused) { + debug("Caught IllegalUnbindException - OK"); + pass(); + } catch (IOException ioe) { + unexpected(ioe); + } + + /* TEST 5: unbind address that is not bound */ + try { + InetAddress nla = InetAddress.getByName("123.123.123.123"); + debug("unbind address that is not bound " + nla); + channel.unbindAddress(nla); + + } catch (IllegalUnbindException unused) { + debug("Caught IllegalUnbindException - OK"); + pass(); + } catch (IOException ioe) { + unexpected(ioe); + } + + if (connected) { + channel.shutdown(); + + BindNotificationHandler handler = new BindNotificationHandler(); + ByteBuffer buffer = ByteBuffer.allocate(10); + MessageInfo info; + while((info = peerChannel.receive(buffer, null, handler)) != null) { + if (info != null) { + if (info.bytes() == -1) { + debug("peerChannel Reached EOF"); + break; + } + } + } + + while((info = channel.receive(buffer, null, handler)) != null) { + if (info != null) { + if (info.bytes() == -1) { + debug("channel Reached EOF"); + break; + } + } + } + } + } catch (IOException ioe) { + ioe.printStackTrace(); + } finally { + try { if (channel != null) channel.close(); } + catch (IOException ioe) { unexpected(ioe); } + } + } + + boolean boundAddress(SctpChannel channel, InetAddress addr) + throws IOException { + for (SocketAddress boundAddr : channel.getAllLocalAddresses()) { + if (((InetSocketAddress) boundAddr).getAddress().equals(addr)) + return true; + } + return false; + } + + SctpChannel connectChannel(SctpChannel channel) + throws IOException { + debug("connecting channel..."); + try { + SctpServerChannel ssc = SctpServerChannel.open(); + ssc.bind(null); + Set addrs = ssc.getAllLocalAddresses(); + Iterator iterator = addrs.iterator(); + SocketAddress addr = iterator.next(); + debug("using " + addr + "..."); + channel.connect(addr); + SctpChannel peerChannel = ssc.accept(); + ssc.close(); + debug("connected"); + return peerChannel; + } catch (IOException ioe) { + debug("Cannot connect channel"); + unexpected(ioe); + throw ioe; + } + } + + class BindNotificationHandler extends AbstractNotificationHandler + { + @Override + public HandlerResult handleNotification( + AssociationChangeNotification acn, Object unused) + { + debug("AssociationChangeNotification: " + acn); + return HandlerResult.CONTINUE; + } + + @Override + public HandlerResult handleNotification( + PeerAddressChangeNotification pacn, Object unused) + { + debug("PeerAddressChangeNotification: " + pacn); + return HandlerResult.CONTINUE; + } + + @Override + public HandlerResult handleNotification( + ShutdownNotification sn, Object unused) + { + debug("ShutdownNotification: " + sn); + return HandlerResult.CONTINUE; + } + } + + class UnsupportedSocketAddress extends SocketAddress { } + + //--------------------- Infrastructure --------------------------- + boolean debug = true; + volatile int passed = 0, failed = 0; + void pass() {passed++;} + void fail() {failed++; Thread.dumpStack();} + void fail(String msg) {System.err.println(msg); fail();} + void unexpected(Throwable t) {failed++; t.printStackTrace();} + void check(boolean cond) {if (cond) pass(); else fail();} + void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);} + void debug(String message) {if(debug) { System.out.println(message); } } + public static void main(String[] args) throws Throwable { + Class k = new Object(){}.getClass().getEnclosingClass(); + try {k.getMethod("instanceMain",String[].class) + .invoke( k.newInstance(), (Object) args);} + catch (Throwable e) {throw e.getCause();}} + public void instanceMain(String[] args) throws Throwable { + try {test(args);} catch (Throwable t) {unexpected(t);} + System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); + if (failed > 0) throw new AssertionError("Some tests failed");} + +} diff --git a/test/com/sun/nio/sctp/SctpChannel/Connect.java b/test/com/sun/nio/sctp/SctpChannel/Connect.java new file mode 100644 index 000000000..7614cdeb7 --- /dev/null +++ b/test/com/sun/nio/sctp/SctpChannel/Connect.java @@ -0,0 +1,283 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 4927640 + * @summary Tests the SCTP protocol implementation + * @author chegar + */ + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.io.IOException; +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.nio.channels.AlreadyConnectedException; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.ConnectionPendingException; +import java.nio.channels.NoConnectionPendingException; +import java.nio.channels.UnresolvedAddressException; +import java.nio.channels.UnsupportedAddressTypeException; +import com.sun.nio.sctp.SctpChannel; +import com.sun.nio.sctp.SctpServerChannel; +import static java.lang.System.out; +import static java.lang.System.err; + +/** + * Tests connect, finishConnect, isConnectionPending, + * getRemoteAddresses and association. + */ +public class Connect { + final CountDownLatch finishedLatch = new CountDownLatch(1); + + void test(String[] args) { + SocketAddress address = null; + Server server = null; + + if (!Util.isSCTPSupported()) { + out.println("SCTP protocol is not supported"); + out.println("Test cannot be run"); + return; + } + + if (args.length == 2) { + /* requested to connect to a specific address */ + try { + int port = Integer.valueOf(args[1]); + address = new InetSocketAddress(args[0], port); + } catch (NumberFormatException nfe) { + err.println(nfe); + } + } else { + /* start server on local machine, default */ + try { + server = new Server(); + server.start(); + address = server.address(); + debug("Server started and listening on " + address); + } catch (IOException ioe) { + ioe.printStackTrace(); + return; + } + } + + doTest(address); + } + + void doTest(SocketAddress addr) { + SctpChannel channel = null; + final SocketAddress peerAddress = addr; + + try { + channel = SctpChannel.open(); + + /* TEST 0.5 Verify default values for new/unconnected channel */ + check(channel.getRemoteAddresses().isEmpty(), + "non empty set for unconnected channel"); + check(channel.association() == null, + "non-null association for unconnected channel"); + check(!channel.isConnectionPending(), + "should not have a connection pending"); + + /* TEST 1: non-blocking connect */ + channel.configureBlocking(false); + if (channel.connect(peerAddress) != true) { + debug("non-blocking connect did not immediately succeed"); + check(channel.isConnectionPending(), + "should return true for isConnectionPending"); + try { + channel.connect(peerAddress); + fail("should have thrown ConnectionPendingException"); + } catch (ConnectionPendingException cpe) { + pass(); + } catch (IOException ioe) { + unexpected(ioe); + } + channel.configureBlocking(true); + check(channel.finishConnect(), + "finishConnect should have returned true"); + } + + /* TEST 1.5 Verify after connect */ + check(!channel.getRemoteAddresses().isEmpty(), + "empty set for connected channel"); + check(channel.association() != null, + "null association for connected channel"); + check(!channel.isConnectionPending(), + "pending connection for connected channel"); + + /* TEST 2: Verify AlreadyConnectedException thrown */ + try { + channel.connect(peerAddress); + fail("should have thrown AlreadyConnectedException"); + } catch (AlreadyConnectedException unused) { + pass(); + } catch (IOException ioe) { + unexpected(ioe); + } + + /* TEST 3: UnresolvedAddressException */ + channel.close(); + channel = SctpChannel.open(); + InetSocketAddress unresolved = + InetSocketAddress.createUnresolved("xxyyzzabc", 4567); + try { + channel.connect(unresolved); + fail("should have thrown UnresolvedAddressException"); + } catch (UnresolvedAddressException unused) { + pass(); + } catch (IOException ioe) { + unexpected(ioe); + } + + /* TEST 4: UnsupportedAddressTypeException */ + SocketAddress unsupported = new UnsupportedSocketAddress(); + try { + channel.connect(unsupported); + fail("should have thrown UnsupportedAddressTypeException"); + } catch (UnsupportedAddressTypeException unused) { + pass(); + } catch (IOException ioe) { + unexpected(ioe); + } + + /* TEST 5: ClosedChannelException */ + channel.close(); + final SctpChannel closedChannel = channel; + testCCE(new Callable() { + public Void call() throws IOException { + closedChannel.connect(peerAddress); return null; } }); + + /* TEST 5.5 getRemoteAddresses */ + testCCE(new Callable() { + public Void call() throws IOException { + closedChannel.getRemoteAddresses(); return null; } }); + testCCE(new Callable() { + public Void call() throws IOException { + closedChannel.association(); return null; } }); + check(!channel.isConnectionPending(), + "pending connection for closed channel"); + + /* Run some more finishConnect tests */ + + /* TEST 6: NoConnectionPendingException */ + channel = SctpChannel.open(); + try { + channel.finishConnect(); + fail("should have thrown NoConnectionPendingException"); + } catch (NoConnectionPendingException unused) { + pass(); + } catch (IOException ioe) { + unexpected(ioe); + } + + /* TEST 7: ClosedChannelException */ + channel.close(); + final SctpChannel cceChannel = channel; + testCCE(new Callable() { + public Void call() throws IOException { + cceChannel.finishConnect(); return null; } }); + } catch (IOException ioe) { + unexpected(ioe); + } finally { + finishedLatch.countDown(); + try { if (channel != null) channel.close(); } + catch (IOException e) { unexpected(e);} + } + } + + class UnsupportedSocketAddress extends SocketAddress { } + + void testCCE(Callable callable) { + try { + callable.call(); + fail("should have thrown ClosedChannelException"); + } catch (ClosedChannelException cce) { + pass(); + } catch (Exception ioe) { + unexpected(ioe); + } + } + + class Server implements Runnable + { + final InetSocketAddress serverAddr; + private SctpServerChannel ssc; + + public Server() throws IOException { + ssc = SctpServerChannel.open().bind(null); + java.util.Set addrs = ssc.getAllLocalAddresses(); + if (addrs.isEmpty()) + debug("addrs should not be empty"); + + serverAddr = (InetSocketAddress) addrs.iterator().next(); + } + + public void start() { + (new Thread(this, "Server-" + serverAddr.getPort())).start(); + } + + public InetSocketAddress address() { + return serverAddr; + } + + @Override + public void run() { + SctpChannel sc = null; + try { + sc = ssc.accept(); + finishedLatch.await(); + } catch (IOException ioe) { + unexpected(ioe); + } catch (InterruptedException ie) { + unexpected(ie); + } finally { + try { if (ssc != null) ssc.close(); } + catch (IOException ioe) { unexpected(ioe); } + try { if (sc != null) sc.close(); } + catch (IOException ioe) { unexpected(ioe); } + } + } + } + + //--------------------- Infrastructure --------------------------- + boolean debug = true; + volatile int passed = 0, failed = 0; + void pass() {passed++;} + void fail() {failed++; Thread.dumpStack();} + void fail(String msg) {System.err.println(msg); fail();} + void unexpected(Throwable t) {failed++; t.printStackTrace();} + void check(boolean cond) {if (cond) pass(); else fail();} + void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);} + void debug(String message) {if(debug) { System.out.println(message); } } + public static void main(String[] args) throws Throwable { + Class k = new Object(){}.getClass().getEnclosingClass(); + try {k.getMethod("instanceMain",String[].class) + .invoke( k.newInstance(), (Object) args);} + catch (Throwable e) {throw e.getCause();}} + public void instanceMain(String[] args) throws Throwable { + try {test(args);} catch (Throwable t) {unexpected(t);} + System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); + if (failed > 0) throw new AssertionError("Some tests failed");} + +} diff --git a/test/com/sun/nio/sctp/SctpChannel/Receive.java b/test/com/sun/nio/sctp/SctpChannel/Receive.java new file mode 100644 index 000000000..6e906c4c9 --- /dev/null +++ b/test/com/sun/nio/sctp/SctpChannel/Receive.java @@ -0,0 +1,361 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 4927640 + * @summary Tests the SCTP protocol implementation + * @author chegar + */ + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.io.IOException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.nio.ByteBuffer; +import java.nio.channels.NotYetConnectedException; +import java.nio.channels.ClosedChannelException; +import com.sun.nio.sctp.AbstractNotificationHandler; +import com.sun.nio.sctp.AssociationChangeNotification; +import com.sun.nio.sctp.AssociationChangeNotification.AssocChangeEvent; +import com.sun.nio.sctp.HandlerResult; +import com.sun.nio.sctp.IllegalReceiveException; +import com.sun.nio.sctp.MessageInfo; +import com.sun.nio.sctp.Notification; +import com.sun.nio.sctp.SctpChannel; +import com.sun.nio.sctp.SctpServerChannel; +import com.sun.nio.sctp.ShutdownNotification; +import static java.lang.System.out; +import static java.lang.System.err; + +public class Receive { + /* Latches used to synchronize between the client and server so that + * connections without any IO may not be closed without being accepted */ + final CountDownLatch clientFinishedLatch = new CountDownLatch(1); + final CountDownLatch serverFinishedLatch = new CountDownLatch(1); + + /* Used to verify that the ppid is being sent and received correctly */ + static final int PPID = 5; + + void test(String[] args) { + SocketAddress address = null; + Server server; + + + if (!Util.isSCTPSupported()) { + out.println("SCTP protocol is not supported"); + out.println("Test cannot be run"); + return; + } + + if (args.length == 2) { + /* requested to connecct to a specific address */ + try { + int port = Integer.valueOf(args[1]); + address = new InetSocketAddress(args[0], port); + } catch (NumberFormatException nfe) { + err.println(nfe); + } + } else { + /* start server on local machine, default */ + try { + server = new Server(); + server.start(); + address = server.address(); + debug("Server started and listening on " + address); + } catch (IOException ioe) { + ioe.printStackTrace(); + return; + } + } + + doTest(address); + } + + void doTest(SocketAddress peerAddress) { + SctpChannel channel = null; + ByteBuffer buffer = ByteBuffer.allocate(Util.LARGE_BUFFER); + MessageInfo info; + + try { + channel = SctpChannel.open(); + ReceiveNotificationHandler handler = + new ReceiveNotificationHandler(channel); + + /* TEST 1: Verify NotYetConnectedException thrown */ + try { + channel.receive(buffer, null, handler); + fail("should have thrown NotYetConnectedException"); + } catch (NotYetConnectedException unused) { + pass(); + } catch (IOException ioe) { + unexpected(ioe); + } + + channel.connect(peerAddress); + + /* TEST 2: receive small message */ + do { + debug("Test 2: invoking receive"); + info = channel.receive(buffer, null, handler); + if (info == null) { + fail("unexpected null from receive"); + return; + } + } while (!info.isComplete()); + + buffer.flip(); + check(handler.receivedCommUp(), "SCTP_COMM_UP not received"); + check(info != null, "info is null"); + check(info.address() != null, "address is null"); + check(info.association() != null, "association is null"); + check(info.isComplete(), "message is not complete"); + check(info.isUnordered() != true, + "message should not be unordered"); + check(info.streamNumber() >= 0, "invalid stream number"); + check(info.payloadProtocolID() == PPID, "PPID incorrect"); + check(info.bytes() == Util.SMALL_MESSAGE.getBytes("ISO-8859-1"). + length, "bytes received not equal to message length"); + check(info.bytes() == buffer.remaining(), "bytes != remaining"); + check(Util.compare(buffer, Util.SMALL_MESSAGE), + "received message not the same as sent message"); + + buffer.clear(); + + /* TEST 3: receive large message */ + do { + debug("Test 3: invoking receive"); + info = channel.receive(buffer, null, handler); + if (info == null) { + fail("unexpected null from receive"); + return; + } + } while (!info.isComplete()); + + buffer.flip(); + check(info != null, "info is null"); + check(info.address() != null, "address is null"); + check(info.association() != null, "association is null"); + check(info.isComplete(), "message is not complete"); + check(info.isUnordered() != true, + "message should not be unordered"); + check(info.streamNumber() >= 0, "invalid stream number"); + check(info.bytes() == Util.LARGE_MESSAGE.getBytes("ISO-8859-1"). + length, "bytes received not equal to message length"); + check(info.bytes() == buffer.remaining(), "bytes != remaining"); + check(Util.compare(buffer, Util.LARGE_MESSAGE), + "received message not the same as sent message"); + + buffer.clear(); + + /* TEST 4: EOF */ + buffer.clear(); // buffer position 0 + info = channel.receive(buffer,null, handler); + check(info != null, "info is null"); + check(info.bytes() == -1, "should have received EOF"); + check(buffer.position() == 0, "buffer position should be unchanged"); + + /* TEST 5: ClosedChannelException */ + channel.close(); + try { + channel.receive(buffer, null, null); + fail("should have thrown ClosedChannelException"); + } catch (ClosedChannelException cce) { + pass(); + } catch (IOException ioe) { + unexpected(ioe); + } + handler = null; + + /* TEST 6: handler returns RETURN after handling a notification */ + ReceiveNotificationHandler handler2 = + new ReceiveNotificationHandler(null); /* HandlerResult.RETURN */ + channel = SctpChannel.open(peerAddress, 0, 0); + info = channel.receive(buffer, null, handler2); + check(info == null, "channel should return null"); + check(handler2.receivedCommUp(), "SCTP_COMM_UP not received"); + check(buffer.position() == 0, "buffer position should be unchanged"); + + /* TEST 7: Non blocking channel return null if no data */ + channel.configureBlocking(false); + info = channel.receive(buffer, null, null); + check(info == null, "non-blocking channel should return null"); + check(buffer.position() == 0, "buffer position should be unchanged"); + } catch (IOException ioe) { + unexpected(ioe); + } finally { + clientFinishedLatch.countDown(); + try { serverFinishedLatch.await(10L, TimeUnit.SECONDS); } + catch (InterruptedException ie) { unexpected(ie); } + if (channel != null) { + try { channel.close(); } + catch (IOException e) { unexpected (e);} + } + } + } + + class Server implements Runnable + { + final InetSocketAddress serverAddr; + private SctpServerChannel ssc; + + public Server() throws IOException { + ssc = SctpServerChannel.open().bind(null); + java.util.Set addrs = ssc.getAllLocalAddresses(); + if (addrs.isEmpty()) + debug("addrs should not be empty"); + + serverAddr = (InetSocketAddress) addrs.iterator().next(); + } + + public void start() { + (new Thread(this, "Server-" + serverAddr.getPort())).start(); + } + + public InetSocketAddress address() { + return serverAddr; + } + + @Override + public void run() { + try { + SctpChannel sc = ssc.accept(); + + /* send a small message */ + MessageInfo info = MessageInfo.createOutgoing(null, 0) + .payloadProtocolID(PPID); + ByteBuffer buf = ByteBuffer.allocateDirect(Util.SMALL_BUFFER); + buf.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1")); + buf.flip(); + + debug("sending small message: " + buf); + sc.send(buf, info); + + /* send a large message */ + buf = ByteBuffer.allocateDirect(Util.LARGE_BUFFER); + buf.put(Util.LARGE_MESSAGE.getBytes("ISO-8859-1")); + buf.flip(); + + debug("sending large message: " + buf); + sc.send(buf, info); + sc.shutdown(); + debug("shutdown"); + ReceiveNotificationHandler handler = + new ReceiveNotificationHandler(sc); + sc.receive(buf, null, handler); + sc.close(); + + /* accept another socket for the TEST 6 */ + sc = ssc.accept(); + ssc.close(); + + clientFinishedLatch.await(10L, TimeUnit.SECONDS); + serverFinishedLatch.countDown(); + sc.close(); + } catch (IOException ioe) { + unexpected(ioe); + } catch (InterruptedException ie) { + unexpected(ie); + } + } + } + + class ReceiveNotificationHandler extends AbstractNotificationHandler + { + SctpChannel channel; + boolean receivedCommUp; // false + + public ReceiveNotificationHandler(SctpChannel channel) { + this.channel = channel; + } + + public boolean receivedCommUp() { + return receivedCommUp; + } + + @Override + public HandlerResult handleNotification( + Notification notification, Object attachment) { + fail("Unknown notification type"); + return HandlerResult.CONTINUE; + } + + @Override + public HandlerResult handleNotification( + AssociationChangeNotification notification, Object attachment) { + AssocChangeEvent event = notification.event(); + debug("AssociationChangeNotification"); + debug(" Association: " + notification.association()); + debug(" Event: " + event); + + if (event.equals(AssocChangeEvent.COMM_UP)) + receivedCommUp = true; + + if (channel == null) + return HandlerResult.RETURN; + + /* TEST 4: IllegalReceiveException - If the given handler invokes + * the receive method of this channel*/ + ByteBuffer buffer = ByteBuffer.allocate(10); + try { + channel.receive(buffer, null, this); + fail("IllegalReceiveException expected"); + } catch (IllegalReceiveException unused) { + pass(); + } catch (IOException ioe) { + unexpected(ioe); + } + + return HandlerResult.CONTINUE; + } + + @Override + public HandlerResult handleNotification( + ShutdownNotification notification, Object attachment) { + debug("ShutdownNotification"); + debug(" Association: " + notification.association()); + return HandlerResult.CONTINUE; + } + } + //--------------------- Infrastructure --------------------------- + boolean debug = true; + volatile int passed = 0, failed = 0; + void pass() {passed++;} + void fail() {failed++; Thread.dumpStack();} + void fail(String msg) {System.err.println(msg); fail();} + void unexpected(Throwable t) {failed++; t.printStackTrace();} + void check(boolean cond) {if (cond) pass(); else fail();} + void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);} + void debug(String message) {if(debug) { + System.out.println(Thread.currentThread() + " " + message); } } + public static void main(String[] args) throws Throwable { + Class k = new Object(){}.getClass().getEnclosingClass(); + try {k.getMethod("instanceMain",String[].class) + .invoke( k.newInstance(), (Object) args);} + catch (Throwable e) {throw e.getCause();}} + public void instanceMain(String[] args) throws Throwable { + try {test(args);} catch (Throwable t) {unexpected(t);} + System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); + if (failed > 0) throw new AssertionError("Some tests failed");} + +} diff --git a/test/com/sun/nio/sctp/SctpChannel/Send.java b/test/com/sun/nio/sctp/SctpChannel/Send.java new file mode 100644 index 000000000..3cee201d7 --- /dev/null +++ b/test/com/sun/nio/sctp/SctpChannel/Send.java @@ -0,0 +1,419 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 4927640 + * @summary Tests the SCTP protocol implementation + * @author chegar + */ + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.io.IOException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.nio.ByteBuffer; +import java.nio.channels.NotYetConnectedException; +import java.nio.channels.ClosedChannelException; +import com.sun.nio.sctp.AbstractNotificationHandler; +import com.sun.nio.sctp.Association; +import com.sun.nio.sctp.AssociationChangeNotification; +import com.sun.nio.sctp.AssociationChangeNotification.AssocChangeEvent; +import com.sun.nio.sctp.HandlerResult; +import com.sun.nio.sctp.InvalidStreamException; +import com.sun.nio.sctp.MessageInfo; +import com.sun.nio.sctp.Notification; +import com.sun.nio.sctp.SctpChannel; +import com.sun.nio.sctp.SctpServerChannel; +import static java.lang.System.out; +import static java.lang.System.err; + +public class Send { + /* Latches used to synchronize between the client and server so that + * connections without any IO may not be closed without being accepted */ + final CountDownLatch clientFinishedLatch = new CountDownLatch(1); + final CountDownLatch serverFinishedLatch = new CountDownLatch(1); + + SendNotificationHandler handler = new SendNotificationHandler(); + + void test(String[] args) { + SocketAddress address = null; + Server server = null; + + if (!Util.isSCTPSupported()) { + out.println("SCTP protocol is not supported"); + out.println("Test cannot be run"); + return; + } + + if (args.length == 2) { + /* requested to connecct to a specific address */ + try { + int port = Integer.valueOf(args[1]); + address = new InetSocketAddress(args[0], port); + } catch (NumberFormatException nfe) { + err.println(nfe); + } + } else { + /* start server on local machine, default */ + try { + server = new Server(); + server.start(); + address = server.address(); + debug("Server started and listening on " + address); + } catch (IOException ioe) { + ioe.printStackTrace(); + return; + } + } + + doTest(address); + } + + void doTest(SocketAddress peerAddress) { + SctpChannel channel = null; + ByteBuffer buffer = ByteBuffer.allocate(Util.LARGE_BUFFER); + MessageInfo info = MessageInfo.createOutgoing(null, 0); + + try { + channel = SctpChannel.open(); + + /* TEST 1: Verify NotYetConnectedException thrown */ + try { + channel.send(buffer, info); + fail("should have thrown NotYetConnectedException"); + } catch (NotYetConnectedException unused) { + pass(); + } catch (IOException ioe) { + unexpected(ioe); + } + + channel.connect(peerAddress); + /* Receive CommUp */ + channel.receive(buffer, null, handler); + + /* save for TEST 8 */ + Association association = channel.association(); + + /* TEST 2: send small message */ + int streamNumber = 0; + debug("sending on stream number: " + streamNumber); + info = MessageInfo.createOutgoing(null, streamNumber); + buffer.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1")); + buffer.flip(); + int position = buffer.position(); + int remaining = buffer.remaining(); + + debug("sending small message: " + buffer); + int sent = channel.send(buffer, info); + + check(sent == remaining, "sent should be equal to remaining"); + check(buffer.position() == (position + sent), + "buffers position should have been incremented by sent"); + + buffer.clear(); + + /* TEST 3: send large message */ + streamNumber = handler.maxOutStreams() - 1; + debug("sending on stream number: " + streamNumber); + info = MessageInfo.createOutgoing(null, streamNumber); + buffer.put(Util.LARGE_MESSAGE.getBytes("ISO-8859-1")); + buffer.flip(); + position = buffer.position(); + remaining = buffer.remaining(); + + debug("sending large message: " + buffer); + sent = channel.send(buffer, info); + + check(sent == remaining, "sent should be equal to remaining"); + check(buffer.position() == (position + sent), + "buffers position should have been incremented by sent"); + + /* TEST 4: InvalidStreamExcepton */ + streamNumber = handler.maxInStreams; + info = MessageInfo.createOutgoing(null, streamNumber); + buffer.clear(); + buffer.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1")); + buffer.flip(); + position = buffer.position(); + remaining = buffer.remaining(); + + debug("sending on stream number: " + streamNumber); + debug("sending small message: " + buffer); + try { + sent = channel.send(buffer, info); + fail("should have thrown InvalidStreamExcepton"); + } catch (InvalidStreamException ise){ + pass(); + } catch (IOException ioe) { + unexpected(ioe); + } + check(buffer.remaining() == remaining, + "remaining should not be changed"); + check(buffer.position() == position, + "buffers position should not be changed"); + + /* TEST 5: Non blocking send should return zero if there is + insufficient room in the underlying output buffer */ + buffer.clear(); + channel.configureBlocking(false); + info = MessageInfo.createOutgoing(null, 1); + buffer.put(Util.LARGE_MESSAGE.getBytes("ISO-8859-1")); + buffer.flip(); + + int count = 0; // do not loop forever + do { + position = buffer.position(); + remaining = buffer.remaining(); + debug("sending large message: " + buffer); + sent = channel.send(buffer, info); + if (sent == 0) { + check(buffer.remaining() == remaining, + "remaining should not be changed"); + check(buffer.position() == position, + "buffers position should not be changed"); + } + buffer.rewind(); + } while (sent != 0 && count++ < 100); + + /* TEST 6: ClosedChannelException */ + channel.close(); + try { + channel.send(buffer, info); + fail("should have thrown ClosedChannelException"); + } catch (ClosedChannelException cce) { + pass(); + } catch (IOException ioe) { + unexpected(ioe); + } + + /* TEST 7: send without previous receive. + * Verify that send can still throw InvalidStreamExcepton */ + debug("Opening new channel."); + channel = SctpChannel.open(peerAddress, 0, 0); + streamNumber = Short.MAX_VALUE - 1; + info = MessageInfo.createOutgoing(null, streamNumber); + buffer.clear(); + buffer.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1")); + buffer.flip(); + position = buffer.position(); + remaining = buffer.remaining(); + + debug("sending on stream number: " + streamNumber); + debug("sending small message: " + buffer); + try { + sent = channel.send(buffer, info); + fail("should have thrown InvalidStreamExcepton"); + } catch (InvalidStreamException ise){ + pass(); + } catch (IOException ioe) { + unexpected(ioe); + } + check(buffer.remaining() == remaining, + "remaining should not be changed"); + check(buffer.position() == position, + "buffers position should not be changed"); + + /* Receive CommUp */ + channel.receive(buffer, null, handler); + check(handler.receivedCommUp(), "should have received COMM_UP"); + + /* TEST 8: Send to an invalid preferred SocketAddress */ + SocketAddress addr = new InetSocketAddress("123.123.123.123", 3456); + info = MessageInfo.createOutgoing(addr, 0); + debug("sending to " + addr); + debug("sending small message: " + buffer); + try { + sent = channel.send(buffer, info); + fail("Invalid address should have thrown an Exception."); + } catch (Exception e){ + pass(); + debug("OK, caught " + e); + } + } catch (IOException ioe) { + unexpected(ioe); + } finally { + clientFinishedLatch.countDown(); + try { serverFinishedLatch.await(10L, TimeUnit.SECONDS); } + catch (InterruptedException ie) { unexpected(ie); } + if (channel != null) { + try { channel.close(); } + catch (IOException e) { unexpected (e);} + } + } + } + + class Server implements Runnable + { + final InetSocketAddress serverAddr; + private SctpServerChannel ssc; + + public Server() throws IOException { + ssc = SctpServerChannel.open().bind(null); + java.util.Set addrs = ssc.getAllLocalAddresses(); + if (addrs.isEmpty()) + debug("addrs should not be empty"); + + serverAddr = (InetSocketAddress) addrs.iterator().next(); + } + + public void start() { + (new Thread(this, "Server-" + serverAddr.getPort())).start(); + } + + public InetSocketAddress address() { + return serverAddr; + } + + @Override + public void run() { + ByteBuffer buffer = ByteBuffer.allocateDirect(Util.LARGE_BUFFER); + SctpChannel sc1 = null, sc2 = null; + try { + sc1 = ssc.accept(); + + /* receive a small message */ + MessageInfo info; + do { + info = sc1.receive(buffer, null, null); + if (info == null) { + fail("Server: unexpected null from receive"); + return; + } + } while (!info.isComplete()); + + buffer.flip(); + check(info != null, "info is null"); + check(info.streamNumber() == 0, + "message not sent on the correct stream"); + check(info.bytes() == Util.SMALL_MESSAGE.getBytes("ISO-8859-1"). + length, "bytes received not equal to message length"); + check(info.bytes() == buffer.remaining(), "bytes != remaining"); + check(Util.compare(buffer, Util.SMALL_MESSAGE), + "received message not the same as sent message"); + + /* receive a large message */ + buffer.clear(); + do { + info = sc1.receive(buffer, null, null); + if (info == null) { + fail("Server: unexpected null from receive"); + return; + } + } while (!info.isComplete()); + + buffer.flip(); + check(info != null, "info is null"); + check(info.streamNumber() == handler.maxOutStreams() - 1, + "message not sent on the correct stream"); + check(info.bytes() == Util.LARGE_MESSAGE.getBytes("ISO-8859-1"). + length, "bytes received not equal to message length"); + check(info.bytes() == buffer.remaining(), "bytes != remaining"); + check(Util.compare(buffer, Util.LARGE_MESSAGE), + "received message not the same as sent message"); + + /* TEST 7 ++ */ + sc2 = ssc.accept(); + + clientFinishedLatch.await(10L, TimeUnit.SECONDS); + serverFinishedLatch.countDown(); + } catch (IOException ioe) { + unexpected(ioe); + } catch (InterruptedException ie) { + unexpected(ie); + } finally { + try { if (ssc != null) ssc.close(); } + catch (IOException unused) {} + try { if (sc1 != null) sc1.close(); } + catch (IOException unused) {} + try { if (sc2 != null) sc2.close(); } + catch (IOException unused) {} + } + } + } + + class SendNotificationHandler extends AbstractNotificationHandler + { + boolean receivedCommUp; // false + int maxInStreams; + int maxOutStreams; + + public boolean receivedCommUp() { + return receivedCommUp; + } + + public int maxInStreams() { + return maxInStreams; + } + + public int maxOutStreams(){ + return maxOutStreams; + } + + @Override + public HandlerResult handleNotification( + Notification notification, Void attachment) { + fail("Unknown notification type"); + return HandlerResult.CONTINUE; + } + + @Override + public HandlerResult handleNotification( + AssociationChangeNotification notification, Void attachment) { + AssocChangeEvent event = notification.event(); + Association association = notification.association(); + debug("AssociationChangeNotification"); + debug(" Association: " + notification.association()); + debug(" Event: " + event); + + if (event.equals(AssocChangeEvent.COMM_UP)) + receivedCommUp = true; + + this.maxInStreams = association.maxInboundStreams(); + this.maxOutStreams = association.maxOutboundStreams(); + + return HandlerResult.RETURN; + } + } + + //--------------------- Infrastructure --------------------------- + boolean debug = true; + volatile int passed = 0, failed = 0; + void pass() {passed++;} + void fail() {failed++; Thread.dumpStack();} + void fail(String msg) {System.err.println(msg); fail();} + void unexpected(Throwable t) {failed++; t.printStackTrace();} + void check(boolean cond) {if (cond) pass(); else fail();} + void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);} + void debug(String message) {if(debug) { System.out.println(message); } } + public static void main(String[] args) throws Throwable { + Class k = new Object(){}.getClass().getEnclosingClass(); + try {k.getMethod("instanceMain",String[].class) + .invoke( k.newInstance(), (Object) args);} + catch (Throwable e) {throw e.getCause();}} + public void instanceMain(String[] args) throws Throwable { + try {test(args);} catch (Throwable t) {unexpected(t);} + System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); + if (failed > 0) throw new AssertionError("Some tests failed");} + +} diff --git a/test/com/sun/nio/sctp/SctpChannel/Shutdown.java b/test/com/sun/nio/sctp/SctpChannel/Shutdown.java new file mode 100644 index 000000000..92c51adb0 --- /dev/null +++ b/test/com/sun/nio/sctp/SctpChannel/Shutdown.java @@ -0,0 +1,274 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 4927640 + * @summary Tests the SCTP protocol implementation + * @author chegar + */ + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.io.IOException; +import java.util.concurrent.CountDownLatch; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.NotYetConnectedException; +import com.sun.nio.sctp.AbstractNotificationHandler; +import com.sun.nio.sctp.HandlerResult; +import com.sun.nio.sctp.MessageInfo; +import com.sun.nio.sctp.SctpChannel; +import com.sun.nio.sctp.SctpServerChannel; +import com.sun.nio.sctp.ShutdownNotification; +import static java.lang.System.out; +import static java.lang.System.err; + +public class Shutdown { + static CountDownLatch finishedLatch = new CountDownLatch(1); + static CountDownLatch sentLatch = new CountDownLatch(1); + + void test(String[] args) { + SocketAddress address = null; + ShutdownServer server = null; + + if (!Util.isSCTPSupported()) { + out.println("SCTP protocol is not supported"); + out.println("Test cannot be run"); + return; + } + + if (args.length == 2) { + /* requested to connecct to a specific address */ + try { + int port = Integer.valueOf(args[1]); + address = new InetSocketAddress(args[0], port); + } catch (NumberFormatException nfe) { + err.println(nfe); + } + } else { + /* start server on local machine, default */ + try { + server = new ShutdownServer(); + server.start(); + address = server.address(); + debug("Server started and listening on " + address); + } catch (IOException ioe) { + ioe.printStackTrace(); + return; + } + } + + doTest(address); + } + + void doTest(SocketAddress peerAddress) { + SctpChannel channel = null; + ByteBuffer buffer = ByteBuffer.allocate(Util.SMALL_BUFFER); + MessageInfo info; + + try { + channel = SctpChannel.open(); + + /* TEST 1: Verify NotYetConnectedException thrown */ + debug("Test 1: NotYetConnectedException"); + try { + channel.shutdown(); + fail("shutdown not throwing expected NotYetConnectedException"); + } catch (NotYetConnectedException unused) { + pass(); + } catch (IOException ioe) { + unexpected(ioe); + } + + channel.connect(peerAddress); + sentLatch.await(); + channel.shutdown(); + + /* TEST 2: receive data sent before shutdown */ + do { + debug("Test 2: invoking receive"); + info = channel.receive(buffer, null, null); + if (info == null) { + fail("unexpected null from receive"); + return; + } + } while (!info.isComplete()); + + buffer.flip(); + check(info != null, "info is null"); + check(info.bytes() == Util.SMALL_MESSAGE.getBytes("ISO-8859-1"). + length, "bytes received not equal to message length"); + check(info.bytes() == buffer.remaining(), "bytes != remaining"); + check(Util.compare(buffer, Util.SMALL_MESSAGE), + "received message not the same as sent message"); + + buffer.clear(); + + /* TEST 3: receive notifications on the SCTP stack */ + debug("Test 3: receive notifications"); + while ((info = channel.receive(buffer, null, null )) != null && + info.bytes() != -1 ); + + + /* TEST 4: If the channel is already shutdown then invoking this + * method has no effect. */ + debug("Test 4: no-op"); + try { + channel.shutdown(); + pass(); + } catch (IOException ioe) { + unexpected(ioe); + } + + /* TEST 5: Further sends will throw ClosedChannelException */ + debug("Test 5: ClosedChannelException"); + info = MessageInfo.createOutgoing(null, 1); + try { + channel.send(buffer, info); + fail("shutdown not throwing expected ClosedChannelException"); + } catch (ClosedChannelException unused) { + pass(); + } catch (IOException ioe) { + unexpected(ioe); + } + } catch (IOException ioe) { + unexpected(ioe); + } catch (InterruptedException ie) { + unexpected(ie); + }finally { + finishedLatch.countDown(); + try { if (channel != null) channel.close(); } + catch (IOException e) { unexpected(e);} + } + } + + class ShutdownServer implements Runnable + { + final InetSocketAddress serverAddr; + private SctpServerChannel ssc; + + public ShutdownServer() throws IOException { + ssc = SctpServerChannel.open().bind(null); + //serverAddr = (InetSocketAddress)(ssc.getAllLocalAddresses().iterator().next()); + + java.util.Set addrs = ssc.getAllLocalAddresses(); + if (addrs.isEmpty()) + debug("addrs should not be empty"); + + serverAddr = (InetSocketAddress) addrs.iterator().next(); + + } + + public void start() { + (new Thread(this, "ShutdownServer-" + serverAddr.getPort())).start(); + } + + public InetSocketAddress address() { + return serverAddr; + } + + @Override + public void run() { + SctpChannel sc = null; + try { + sc = ssc.accept(); + + /* send a message */ + MessageInfo info = MessageInfo.createOutgoing(null, 1); + ByteBuffer buf = ByteBuffer.allocateDirect(Util.SMALL_BUFFER); + buf.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1")); + buf.flip(); + sc.send(buf, info); + + /* notify client that the data has been sent */ + sentLatch.countDown(); + + /* wait until after the client has finished its tests */ + finishedLatch.await(); + + buf.clear(); + ShutdownNotificationHandler handler = + new ShutdownNotificationHandler(); + BooleanWrapper bool = new BooleanWrapper(); + sc.configureBlocking(false); + sc.receive(buf, bool, handler); + check(bool.booleanValue(), "SHUTDOWN not received on Server"); + + } catch (IOException ioe) { + ioe.printStackTrace(); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } finally { + try { if (ssc != null) ssc.close(); } + catch (IOException ioe) { unexpected(ioe); } + try { if (sc != null) sc.close(); } + catch (IOException ioe) { unexpected(ioe); } + } + } + } + + class BooleanWrapper { + boolean bool; + + boolean booleanValue() { + return bool; + } + + void booleanValue(boolean value) { + bool = value; + } + } + + class ShutdownNotificationHandler extends AbstractNotificationHandler + { + @Override + public HandlerResult handleNotification( + ShutdownNotification sn, BooleanWrapper bool) + { + bool.booleanValue(true); + debug(sn.toString()); + return HandlerResult.RETURN; + } + } + + //--------------------- Infrastructure --------------------------- + boolean debug = true; + volatile int passed = 0, failed = 0; + void pass() {passed++;} + void fail() {failed++; Thread.dumpStack();} + void fail(String msg) {System.err.println(msg); fail();} + void unexpected(Throwable t) {failed++; t.printStackTrace();} + void check(boolean cond) {if (cond) pass(); else fail();} + void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);} + void debug(String message) {if(debug) { System.out.println(message); } } + public static void main(String[] args) throws Throwable { + Class k = new Object(){}.getClass().getEnclosingClass(); + try {k.getMethod("instanceMain",String[].class) + .invoke( k.newInstance(), (Object) args);} + catch (Throwable e) {throw e.getCause();}} + public void instanceMain(String[] args) throws Throwable { + try {test(args);} catch (Throwable t) {unexpected(t);} + System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); + if (failed > 0) throw new AssertionError("Some tests failed");} + +} diff --git a/test/com/sun/nio/sctp/SctpChannel/SocketOptionTests.java b/test/com/sun/nio/sctp/SctpChannel/SocketOptionTests.java new file mode 100644 index 000000000..1e7c9cac3 --- /dev/null +++ b/test/com/sun/nio/sctp/SctpChannel/SocketOptionTests.java @@ -0,0 +1,157 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 4927640 + * @summary Tests the SCTP protocol implementation + * @author chegar + */ + +import java.io.IOException; +import java.util.Set; +import java.util.List; +import java.util.Arrays; +import java.nio.channels.ClosedChannelException; +import com.sun.nio.sctp.SctpChannel; +import com.sun.nio.sctp.SctpSocketOption; +import static com.sun.nio.sctp.SctpStandardSocketOption.*; +import static java.lang.System.out; + +public class SocketOptionTests { + void checkOption(SctpChannel sc, SctpSocketOption name, + T expectedValue) throws IOException { + T value = sc.getOption(name); + check(value.equals(expectedValue), name + ": value (" + value + + ") not as expected (" + expectedValue + ")"); + } + + void optionalSupport(SctpChannel sc, SctpSocketOption name, + T value) { + try { + sc.setOption(name, value); + checkOption(sc, name, value); + } catch (IOException e) { + /* Informational only, not all options have native support */ + out.println(name + " not supported. " + e); + } + } + + void test(String[] args) { + if (!Util.isSCTPSupported()) { + out.println("SCTP protocol is not supported"); + out.println("Test cannot be run"); + return; + } + + try { + SctpChannel sc = SctpChannel.open(); + + /* check supported options */ + Set> options = sc.supportedOptions(); + List> expected = Arrays.>asList( + SCTP_DISABLE_FRAGMENTS, SCTP_EXPLICIT_COMPLETE, + SCTP_FRAGMENT_INTERLEAVE, SCTP_INIT_MAXSTREAMS, + SCTP_NODELAY, SCTP_PRIMARY_ADDR, SCTP_SET_PEER_PRIMARY_ADDR, + SO_SNDBUF, SO_RCVBUF, SO_LINGER); + + for (SctpSocketOption opt: expected) { + if (!options.contains(opt)) + fail(opt.name() + " should be supported"); + } + + InitMaxStreams streams = InitMaxStreams.create(1024, 1024); + sc.setOption(SCTP_INIT_MAXSTREAMS, streams); + checkOption(sc, SCTP_INIT_MAXSTREAMS, streams); + streams = sc.getOption(SCTP_INIT_MAXSTREAMS); + check(streams.maxInStreams() == 1024, "Max in streams: value: " + + streams.maxInStreams() + ", expected 1024 "); + check(streams.maxOutStreams() == 1024, "Max out streams: value: " + + streams.maxOutStreams() + ", expected 1024 "); + + optionalSupport(sc, SCTP_DISABLE_FRAGMENTS, true); + optionalSupport(sc, SCTP_EXPLICIT_COMPLETE, true); + optionalSupport(sc, SCTP_FRAGMENT_INTERLEAVE, 1); + + + //TODO: SCTP_PRIMARY_ADDR + //sc.bind(null); + //connect + //InetSocketAddress addr = new InetSocketAddress(0); + //sc.setOption(SCTP_PRIMARY_ADDR, addr); + + sc.setOption(SCTP_NODELAY, true); + checkOption(sc, SCTP_NODELAY, true); + sc.setOption(SO_SNDBUF, 16*1024); + sc.setOption(SO_RCVBUF, 16*1024); + checkOption(sc, SO_LINGER, -1); /* default should be negative */ + sc.setOption(SO_LINGER, 2000); + checkOption(sc, SO_LINGER, 2000); + + + /* NullPointerException */ + try { + sc.setOption(null, "value"); + fail("NullPointerException not thrown for setOption"); + } catch (NullPointerException unused) { + pass(); + } + try { + sc.getOption(null); + fail("NullPointerException not thrown for getOption"); + } catch (NullPointerException unused) { + pass(); + } + + /* ClosedChannelException */ + sc.close(); + try { + sc.setOption(SCTP_INIT_MAXSTREAMS, streams); + fail("ClosedChannelException not thrown"); + } catch (ClosedChannelException unused) { + pass(); + } + } catch (IOException ioe) { + unexpected(ioe); + } + } + + //--------------------- Infrastructure --------------------------- + boolean debug = true; + volatile int passed = 0, failed = 0; + void pass() {passed++;} + void fail() {failed++; Thread.dumpStack();} + void fail(String msg) {System.err.println(msg); fail();} + void unexpected(Throwable t) {failed++; t.printStackTrace();} + void check(boolean cond) {if (cond) pass(); else fail();} + void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);} + void debug(String message) {if(debug) { System.out.println(message); } } + public static void main(String[] args) throws Throwable { + Class k = new Object(){}.getClass().getEnclosingClass(); + try {k.getMethod("instanceMain",String[].class) + .invoke( k.newInstance(), (Object) args);} + catch (Throwable e) {throw e.getCause();}} + public void instanceMain(String[] args) throws Throwable { + try {test(args);} catch (Throwable t) {unexpected(t);} + System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); + if (failed > 0) throw new AssertionError("Some tests failed");} +} diff --git a/test/com/sun/nio/sctp/SctpChannel/Util.java b/test/com/sun/nio/sctp/SctpChannel/Util.java new file mode 100644 index 000000000..e6be2ae97 --- /dev/null +++ b/test/com/sun/nio/sctp/SctpChannel/Util.java @@ -0,0 +1,138 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import java.net.NetworkInterface; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.InetAddress; +import java.net.Inet4Address; +import java.net.SocketException; +import java.io.IOException; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.util.List; +import java.util.ArrayList; +import java.util.Set; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; +import java.nio.ByteBuffer; +import com.sun.nio.sctp.SctpChannel; +import static java.lang.System.out; + +public class Util { + static final int SMALL_BUFFER = 128; + static final String SMALL_MESSAGE = + "Under the bridge and over the dam, looking for berries, berries for jam"; + + static final int LARGE_BUFFER = 32768; + static final String LARGE_MESSAGE; + + static { + StringBuffer sb = new StringBuffer(LARGE_BUFFER); + for (int i=0; i<460; i++) + sb.append(SMALL_MESSAGE); + + LARGE_MESSAGE = sb.toString(); + } + + static boolean isSCTPSupported() { + try { + SctpChannel c = SctpChannel.open(); + c.close(); + return true; + } catch (IOException ioe) { + ioe.printStackTrace(); + } catch (UnsupportedOperationException e) { + out.println(e); + } + + return false; + } + /** + * Returns a list of all the addresses on the system. + * @param inclLoopback + * if {@code true}, include the loopback addresses + * @param ipv4Only + * it {@code true}, only IPv4 addresses will be included + */ + static List getAddresses(boolean inclLoopback, + boolean ipv4Only) + throws SocketException { + ArrayList list = new ArrayList(); + Enumeration nets = + NetworkInterface.getNetworkInterfaces(); + for (NetworkInterface netInf : Collections.list(nets)) { + Enumeration addrs = netInf.getInetAddresses(); + for (InetAddress addr : Collections.list(addrs)) { + if (!list.contains(addr) && + (inclLoopback ? true : !addr.isLoopbackAddress()) && + (ipv4Only ? (addr instanceof Inet4Address) : true)) { + list.add(addr); + } + } + } + + return list; + } + + static void dumpAddresses(SctpChannel channel, + PrintStream printStream) + throws IOException { + Set addrs = channel.getAllLocalAddresses(); + printStream.println("Local Addresses: "); + for (Iterator it = addrs.iterator(); it.hasNext(); ) { + InetSocketAddress addr = (InetSocketAddress)it.next(); + printStream.println("\t" + addr); + } + } + + /** + * Compare the contents of the given ByteBuffer with the contens of the + * given byte array. true if, and only if, the contents are the same. + */ + static boolean compare(ByteBuffer bb, byte[] message) { + if (message.length != bb.remaining()) { + out.println("Compare failed, byte array length != to buffer remaining"); + return false; + } + + for (int i=0; i assocs = channel.associations(); + check(assocs.size() == 1, "there should be only one association"); + Iterator it = assocs.iterator(); + check(it.hasNext()); + Association assoc = it.next(); + streamNumber = assoc.maxOutboundStreams() - 1; + + debug("sending on stream number: " + streamNumber); + info = MessageInfo.createOutgoing(assoc, null, streamNumber); + buffer.clear(); + buffer.put(Util.LARGE_MESSAGE.getBytes("ISO-8859-1")); + buffer.flip(); + position = buffer.position(); + remaining = buffer.remaining(); + + debug("sending large message: " + buffer); + sent = channel.send(buffer, info); + + check(sent == remaining, "sent should be equal to remaining"); + check(buffer.position() == (position + sent), + "buffers position should have been incremented by sent"); + + /* TEST 4: receive the echoed message */ + buffer.clear(); + info = channel.receive(buffer, null, null); + buffer.flip(); + check(info != null, "info is null"); + check(info.streamNumber() == streamNumber, + "message not sent on the correct stream"); + check(info.bytes() == Util.LARGE_MESSAGE.getBytes("ISO-8859-1"). + length, "bytes received not equal to message length"); + check(info.bytes() == buffer.remaining(), "bytes != remaining"); + check(Util.compare(buffer, Util.LARGE_MESSAGE), + "received message not the same as sent message"); + + + /* TEST 5: InvalidStreamExcepton */ + streamNumber = assoc.maxOutboundStreams() + 1; + info = MessageInfo.createOutgoing(assoc, null, streamNumber); + buffer.clear(); + buffer.put(Util.SMALL_MESSAGE.getBytes("ISO-8859-1")); + buffer.flip(); + position = buffer.position(); + remaining = buffer.remaining(); + + debug("sending on stream number: " + streamNumber); + debug("sending small message: " + buffer); + try { + sent = channel.send(buffer, info); + fail("should have thrown InvalidStreamExcepton"); + } catch (InvalidStreamException ise){ + pass(); + } catch (IOException ioe) { + unexpected(ioe); + } + check(buffer.remaining() == remaining, + "remaining should not be changed"); + check(buffer.position() == position, + "buffers position should not be changed"); + + + /* TEST 5: getRemoteAddresses(Association) */ + channel.getRemoteAddresses(assoc); + + } catch (IOException ioe) { + unexpected(ioe); + } finally { + clientFinishedLatch.countDown(); + try { serverFinishedLatch.await(10L, TimeUnit.SECONDS); } + catch (InterruptedException ie) { unexpected(ie); } + if (channel != null) { + try { channel.close(); } + catch (IOException e) { unexpected (e);} + } + } + } + + class Server implements Runnable + { + final InetSocketAddress serverAddr; + private SctpMultiChannel serverChannel; + + public Server() throws IOException { + serverChannel = SctpMultiChannel.open().bind(null); + java.util.Set addrs = serverChannel.getAllLocalAddresses(); + if (addrs.isEmpty()) + debug("addrs should not be empty"); + + serverAddr = (InetSocketAddress) addrs.iterator().next(); + } + + public void start() { + (new Thread(this, "Server-" + serverAddr.getPort())).start(); + } + + public InetSocketAddress address() { + return serverAddr; + } + + @Override + public void run() { + ByteBuffer buffer = ByteBuffer.allocateDirect(Util.LARGE_BUFFER); + try { + MessageInfo info; + + /* receive a small message */ + do { + info = serverChannel.receive(buffer, null, null); + if (info == null) { + fail("Server: unexpected null from receive"); + return; + } + } while (!info.isComplete()); + + buffer.flip(); + check(info != null, "info is null"); + check(info.streamNumber() == 0, + "message not sent on the correct stream"); + check(info.bytes() == Util.SMALL_MESSAGE.getBytes("ISO-8859-1"). + length, "bytes received not equal to message length"); + check(info.bytes() == buffer.remaining(), "bytes != remaining"); + check(Util.compare(buffer, Util.SMALL_MESSAGE), + "received message not the same as sent message"); + + check(info != null, "info is null"); + Set assocs = serverChannel.associations(); + check(assocs.size() == 1, "there should be only one association"); + Iterator it = assocs.iterator(); + check(it.hasNext()); + Association assoc = it.next(); + + /* echo the message */ + debug("Server: echoing first message"); + buffer.flip(); + int bytes = serverChannel.send(buffer, info); + debug("Server: sent " + bytes + "bytes"); + + /* receive a large message */ + buffer.clear(); + do { + info = serverChannel.receive(buffer, null, null); + if (info == null) { + fail("Server: unexpected null from receive"); + return; + } + } while (!info.isComplete()); + + buffer.flip(); + + check(info.streamNumber() == assoc.maxInboundStreams() - 1, + "message not sent on the correct stream"); + check(info.bytes() == Util.LARGE_MESSAGE.getBytes("ISO-8859-1"). + length, "bytes received not equal to message length"); + check(info.bytes() == buffer.remaining(), "bytes != remaining"); + check(Util.compare(buffer, Util.LARGE_MESSAGE), + "received message not the same as sent message"); + + /* echo the message */ + debug("Server: echoing second message"); + buffer.flip(); + bytes = serverChannel.send(buffer, info); + debug("Server: sent " + bytes + "bytes"); + + clientFinishedLatch.await(10L, TimeUnit.SECONDS); + serverFinishedLatch.countDown(); + } catch (IOException ioe) { + unexpected(ioe); + } catch (InterruptedException ie) { + unexpected(ie); + } finally { + try { if (serverChannel != null) serverChannel.close(); } + catch (IOException unused) {} + } + } + } + + //--------------------- Infrastructure --------------------------- + boolean debug = true; + volatile int passed = 0, failed = 0; + void pass() {passed++;} + void fail() {failed++; Thread.dumpStack();} + void fail(String msg) {System.err.println(msg); fail();} + void unexpected(Throwable t) {failed++; t.printStackTrace();} + void check(boolean cond) {if (cond) pass(); else fail();} + void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);} + void debug(String message) {if(debug) { System.out.println(message); } } + public static void main(String[] args) throws Throwable { + Class k = new Object(){}.getClass().getEnclosingClass(); + try {k.getMethod("instanceMain",String[].class) + .invoke( k.newInstance(), (Object) args);} + catch (Throwable e) {throw e.getCause();}} + public void instanceMain(String[] args) throws Throwable { + try {test(args);} catch (Throwable t) {unexpected(t);} + System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); + if (failed > 0) throw new AssertionError("Some tests failed");} + +} diff --git a/test/com/sun/nio/sctp/SctpMultiChannel/Util.java b/test/com/sun/nio/sctp/SctpMultiChannel/Util.java new file mode 100644 index 000000000..e6be2ae97 --- /dev/null +++ b/test/com/sun/nio/sctp/SctpMultiChannel/Util.java @@ -0,0 +1,138 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import java.net.NetworkInterface; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.InetAddress; +import java.net.Inet4Address; +import java.net.SocketException; +import java.io.IOException; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.util.List; +import java.util.ArrayList; +import java.util.Set; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; +import java.nio.ByteBuffer; +import com.sun.nio.sctp.SctpChannel; +import static java.lang.System.out; + +public class Util { + static final int SMALL_BUFFER = 128; + static final String SMALL_MESSAGE = + "Under the bridge and over the dam, looking for berries, berries for jam"; + + static final int LARGE_BUFFER = 32768; + static final String LARGE_MESSAGE; + + static { + StringBuffer sb = new StringBuffer(LARGE_BUFFER); + for (int i=0; i<460; i++) + sb.append(SMALL_MESSAGE); + + LARGE_MESSAGE = sb.toString(); + } + + static boolean isSCTPSupported() { + try { + SctpChannel c = SctpChannel.open(); + c.close(); + return true; + } catch (IOException ioe) { + ioe.printStackTrace(); + } catch (UnsupportedOperationException e) { + out.println(e); + } + + return false; + } + /** + * Returns a list of all the addresses on the system. + * @param inclLoopback + * if {@code true}, include the loopback addresses + * @param ipv4Only + * it {@code true}, only IPv4 addresses will be included + */ + static List getAddresses(boolean inclLoopback, + boolean ipv4Only) + throws SocketException { + ArrayList list = new ArrayList(); + Enumeration nets = + NetworkInterface.getNetworkInterfaces(); + for (NetworkInterface netInf : Collections.list(nets)) { + Enumeration addrs = netInf.getInetAddresses(); + for (InetAddress addr : Collections.list(addrs)) { + if (!list.contains(addr) && + (inclLoopback ? true : !addr.isLoopbackAddress()) && + (ipv4Only ? (addr instanceof Inet4Address) : true)) { + list.add(addr); + } + } + } + + return list; + } + + static void dumpAddresses(SctpChannel channel, + PrintStream printStream) + throws IOException { + Set addrs = channel.getAllLocalAddresses(); + printStream.println("Local Addresses: "); + for (Iterator it = addrs.iterator(); it.hasNext(); ) { + InetSocketAddress addr = (InetSocketAddress)it.next(); + printStream.println("\t" + addr); + } + } + + /** + * Compare the contents of the given ByteBuffer with the contens of the + * given byte array. true if, and only if, the contents are the same. + */ + static boolean compare(ByteBuffer bb, byte[] message) { + if (message.length != bb.remaining()) { + out.println("Compare failed, byte array length != to buffer remaining"); + return false; + } + + for (int i=0; i addrs = ssc.getAllLocalAddresses(); + if (addrs.isEmpty()) + debug("addrs should not be empty"); + + serverAddr = (InetSocketAddress) addrs.iterator().next(); + + /* TEST 2: null if this channel is in non-blocking mode and no + * association is available to be accepted */ + ssc.configureBlocking(false); + debug("TEST 2: non-blocking mode null"); + try { + SctpChannel sc = ssc.accept(); + check(sc == null, "non-blocking mode should return null"); + } catch (IOException ioe) { + unexpected(ioe); + } finally { + ssc.configureBlocking(true); + } + } + + void start() { + serverThread = new Thread(this, "AcceptServer-" + + serverAddr.getPort()); + serverThread.start(); + } + + InetSocketAddress address() { + return serverAddr; + } + + SctpServerChannel channel() { + return ssc; + } + + Thread thread() { + return serverThread; + } + + @Override + public void run() { + SctpChannel sc = null; + try { + /* TEST 3: accepted channel */ + debug("TEST 3: accepted channel"); + sc = ssc.accept(); + + checkAcceptedChannel(sc); + acceptLatch.countDown(); + + /* TEST 4: ClosedByInterruptException */ + debug("TEST 4: ClosedByInterruptException"); + try { + closeByIntLatch.countDown(); + ssc.accept(); + fail(); + } catch (ClosedByInterruptException unused) { + debug(" caught ClosedByInterruptException"); + pass(); + } + + /* TEST 5: AsynchronousCloseException */ + debug("TEST 5: AsynchronousCloseException"); + /* reset thread interrupt status */ + Thread.currentThread().interrupted(); + + ssc = SctpServerChannel.open().bind(null); + try { + asyncCloseLatch.countDown(); + ssc.accept(); + fail(); + } catch (AsynchronousCloseException unused) { + debug(" caught AsynchronousCloseException"); + pass(); + } + + /* TEST 6: ClosedChannelException */ + debug("TEST 6: ClosedChannelException"); + try { + ssc.accept(); + fail(); + } catch (ClosedChannelException unused) { + debug(" caught ClosedChannelException"); + pass(); + } + ssc = null; + } catch (IOException ioe) { + ioe.printStackTrace(); + } finally { + try { if (ssc != null) ssc.close(); } + catch (IOException ioe) { unexpected(ioe); } + try { if (sc != null) sc.close(); } + catch (IOException ioe) { unexpected(ioe); } + } + } + } + + void checkAcceptedChannel(SctpChannel sc) { + try { + debug("Checking accepted SctpChannel"); + check(sc.association() != null, + "accepted channel should have an association"); + check(!(sc.getRemoteAddresses().isEmpty()), + "accepted channel should be connected"); + check(!(sc.isConnectionPending()), + "accepted channel should not have a connection pending"); + check(sc.isBlocking(), + "accepted channel should be blocking"); + try { sc.connect(new TestSocketAddress()); fail(); } + catch (AlreadyConnectedException unused) { pass(); } + try { sc.bind(new TestSocketAddress()); fail(); } + catch (AlreadyConnectedException unused) { pass(); } + } catch (IOException unused) { fail(); } + } + + static class TestSocketAddress extends SocketAddress {} + + //--------------------- Infrastructure --------------------------- + boolean debug = true; + volatile int passed = 0, failed = 0; + void pass() {passed++;} + void fail() {failed++; Thread.dumpStack();} + void fail(String msg) {err.println(msg); fail();} + void unexpected(Throwable t) {failed++; t.printStackTrace();} + void check(boolean cond) {if (cond) pass(); else fail();} + void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);} + void debug(String message) {if(debug) { out.println(message); } } + void sleep(long millis) { try { Thread.currentThread().sleep(millis); } + catch(InterruptedException ie) { unexpected(ie); }} + void join(Thread thread, long millis) { try { thread.join(millis); } + catch(InterruptedException ie) { unexpected(ie); }} + public static void main(String[] args) throws Throwable { + Class k = new Object(){}.getClass().getEnclosingClass(); + try {k.getMethod("instanceMain",String[].class) + .invoke( k.newInstance(), (Object) args);} + catch (Throwable e) {throw e.getCause();}} + public void instanceMain(String[] args) throws Throwable { + try {test(args);} catch (Throwable t) {unexpected(t);} + out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); + if (failed > 0) throw new AssertionError("Some tests failed");} + +} diff --git a/test/com/sun/nio/sctp/SctpServerChannel/NonBlockingAccept.java b/test/com/sun/nio/sctp/SctpServerChannel/NonBlockingAccept.java new file mode 100644 index 000000000..a63eee67b --- /dev/null +++ b/test/com/sun/nio/sctp/SctpServerChannel/NonBlockingAccept.java @@ -0,0 +1,230 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* @test + * @bug 4927640 + * @summary Tests the SCTP protocol implementation + * @author chegar + */ + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.io.IOException; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.nio.channels.AlreadyConnectedException; +import java.nio.channels.Selector; +import java.nio.channels.SelectionKey; +import com.sun.nio.sctp.SctpChannel; +import com.sun.nio.sctp.SctpServerChannel; +import static java.lang.System.out; +import static java.lang.System.err; + +public class NonBlockingAccept { + static CountDownLatch acceptLatch = new CountDownLatch(1); + static final int SEL_TIMEOUT = 10000; + static final int NUM_TEST_CONNECTIONS = 10; + + void test(String[] args) { + SocketAddress address = null; + NonblockingServer server; + + if (!Util.isSCTPSupported()) { + out.println("SCTP protocol is not supported"); + out.println("Test cannot be run"); + return; + } + + if (args.length == 2) { + /* requested to connecct to a specific address */ + try { + int port = Integer.valueOf(args[1]); + address = new InetSocketAddress(args[0], port); + } catch (NumberFormatException nfe) { + err.println(nfe); + } + } else { + /* start server on local machine, default */ + try { + server = new NonblockingServer(); + server.start(); + address = server.address(); + debug("Server started and listening on " + address); + } catch (IOException ioe) { + ioe.printStackTrace(); + return; + } + } + + doClient(address); + } + + void doClient(SocketAddress peerAddress) { + Set channels = new HashSet(NUM_TEST_CONNECTIONS); + + try { + for (int i=0; i addrs = ssc.getAllLocalAddresses(); + if (addrs.isEmpty()) + debug("addrs should not be empty"); + + serverAddr = (InetSocketAddress) addrs.iterator().next(); + } + + void start() { + serverThread = new Thread(this, "NonblockingServer-" + + serverAddr.getPort()); + serverThread.start(); + } + + InetSocketAddress address () { + return serverAddr; + } + + @Override + public void run() { + Selector acceptSelector = null; + SelectionKey acceptKey = null; + + try { + acceptSelector = Selector.open(); + ssc.configureBlocking(false); + check(ssc.isBlocking() == false, "Should be in non-blocking mode"); + acceptKey = ssc.register(acceptSelector, SelectionKey.OP_ACCEPT); + + int connectionsAccepted = 0; + while (connectionsAccepted < NUM_TEST_CONNECTIONS) { + int keysAdded = acceptSelector.select(SEL_TIMEOUT); + if (keysAdded > 0) { + Set keys = acceptSelector.selectedKeys(); + Iterator i = keys.iterator(); + while(i.hasNext()) { + SelectionKey sk = i.next(); + i.remove(); + SctpServerChannel nextReady = + (SctpServerChannel)sk.channel(); + check(nextReady.equals(ssc), + "channels should be equal"); + check(sk.isAcceptable(), + "key should be acceptable"); + check(!sk.isReadable(), + "key should not be readable"); + check(!sk.isWritable(), + "key should not be writable"); + check(!sk.isConnectable(), + "key should not be connectable"); + SctpChannel acceptsc = nextReady.accept(); + connectionsAccepted++; + debug("Accepted " + connectionsAccepted + " connections"); + check(acceptsc != null, + "Accepted channel should not be null"); + if (acceptsc != null) { + checkAcceptedChannel(acceptsc); + acceptsc.close(); + } + } /* while */ + } /* if */ + } /* while */ + } catch (IOException ioe) { + ioe.printStackTrace(); + } finally { + acceptLatch.countDown(); + if (acceptKey != null) acceptKey.cancel(); + try { if (acceptSelector != null) acceptSelector.close(); } + catch (IOException ioe) { unexpected(ioe); } + try { if (ssc != null) ssc.close(); } + catch (IOException ioe) { unexpected(ioe); } + } + } + } + + void checkAcceptedChannel(SctpChannel sc) { + try { + debug("Checking accepted SctpChannel"); + check(sc.association() != null, + "accepted channel should have an association"); + check(!(sc.getRemoteAddresses().isEmpty()), + "accepted channel should be connected"); + check(!(sc.isConnectionPending()), + "accepted channel should not have a connection pending"); + check(sc.isBlocking(), + "accepted channel should be blocking"); + try { sc.connect(new TestSocketAddress()); fail(); } + catch (AlreadyConnectedException unused) { pass(); } + try { sc.bind(new TestSocketAddress()); fail(); } + catch (AlreadyConnectedException unused) { pass(); } + } catch (IOException unused) { fail(); } + } + + static class TestSocketAddress extends SocketAddress {} + + //--------------------- Infrastructure --------------------------- + boolean debug = true; + volatile int passed = 0, failed = 0; + void pass() {passed++;} + void fail() {failed++; Thread.dumpStack();} + void fail(String msg) {err.println(msg); fail();} + void unexpected(Throwable t) {failed++; t.printStackTrace();} + void check(boolean cond) {if (cond) pass(); else fail();} + void check(boolean cond, String failMessage) {if (cond) pass(); else fail(failMessage);} + void debug(String message) {if(debug) { out.println(message); } } + void sleep(long millis) { try { Thread.currentThread().sleep(millis); } + catch(InterruptedException ie) { unexpected(ie); }} + public static void main(String[] args) throws Throwable { + Class k = new Object(){}.getClass().getEnclosingClass(); + try {k.getMethod("instanceMain",String[].class) + .invoke( k.newInstance(), (Object) args);} + catch (Throwable e) {throw e.getCause();}} + public void instanceMain(String[] args) throws Throwable { + try {test(args);} catch (Throwable t) {unexpected(t);} + out.printf("%nPassed = %d, failed = %d%n%n", passed, failed); + if (failed > 0) throw new AssertionError("Some tests failed");} + +} diff --git a/test/com/sun/nio/sctp/SctpServerChannel/Util.java b/test/com/sun/nio/sctp/SctpServerChannel/Util.java new file mode 100644 index 000000000..e6be2ae97 --- /dev/null +++ b/test/com/sun/nio/sctp/SctpServerChannel/Util.java @@ -0,0 +1,138 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import java.net.NetworkInterface; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.net.InetAddress; +import java.net.Inet4Address; +import java.net.SocketException; +import java.io.IOException; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.util.List; +import java.util.ArrayList; +import java.util.Set; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; +import java.nio.ByteBuffer; +import com.sun.nio.sctp.SctpChannel; +import static java.lang.System.out; + +public class Util { + static final int SMALL_BUFFER = 128; + static final String SMALL_MESSAGE = + "Under the bridge and over the dam, looking for berries, berries for jam"; + + static final int LARGE_BUFFER = 32768; + static final String LARGE_MESSAGE; + + static { + StringBuffer sb = new StringBuffer(LARGE_BUFFER); + for (int i=0; i<460; i++) + sb.append(SMALL_MESSAGE); + + LARGE_MESSAGE = sb.toString(); + } + + static boolean isSCTPSupported() { + try { + SctpChannel c = SctpChannel.open(); + c.close(); + return true; + } catch (IOException ioe) { + ioe.printStackTrace(); + } catch (UnsupportedOperationException e) { + out.println(e); + } + + return false; + } + /** + * Returns a list of all the addresses on the system. + * @param inclLoopback + * if {@code true}, include the loopback addresses + * @param ipv4Only + * it {@code true}, only IPv4 addresses will be included + */ + static List getAddresses(boolean inclLoopback, + boolean ipv4Only) + throws SocketException { + ArrayList list = new ArrayList(); + Enumeration nets = + NetworkInterface.getNetworkInterfaces(); + for (NetworkInterface netInf : Collections.list(nets)) { + Enumeration addrs = netInf.getInetAddresses(); + for (InetAddress addr : Collections.list(addrs)) { + if (!list.contains(addr) && + (inclLoopback ? true : !addr.isLoopbackAddress()) && + (ipv4Only ? (addr instanceof Inet4Address) : true)) { + list.add(addr); + } + } + } + + return list; + } + + static void dumpAddresses(SctpChannel channel, + PrintStream printStream) + throws IOException { + Set addrs = channel.getAllLocalAddresses(); + printStream.println("Local Addresses: "); + for (Iterator it = addrs.iterator(); it.hasNext(); ) { + InetSocketAddress addr = (InetSocketAddress)it.next(); + printStream.println("\t" + addr); + } + } + + /** + * Compare the contents of the given ByteBuffer with the contens of the + * given byte array. true if, and only if, the contents are the same. + */ + static boolean compare(ByteBuffer bb, byte[] message) { + if (message.length != bb.remaining()) { + out.println("Compare failed, byte array length != to buffer remaining"); + return false; + } + + for (int i=0; i