diff --git a/CHANGELOG.md b/CHANGELOG.md index 63de06783..2ca5d8fb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. [#14](https://github.com/tzaeschke/phtree-cpp/pull/14), [#15](https://github.com/tzaeschke/phtree-cpp/pull/15), [#17](https://github.com/tzaeschke/phtree-cpp/pull/17) +- BREAKING CHANGE:`ScionService` instances created via `Scion.newXYZ` + will not be considered by `Scion.defaultService()`. Also, `DatagramChannel.getService()` + does not create a service if none exists. + [#18](https://github.com/tzaeschke/phtree-cpp/pull/18) ### Fixed - Fixed: SCMP problem when pinging local AS. diff --git a/doc/Design.md b/doc/Design.md index be2b6efce..54c601e14 100644 --- a/doc/Design.md +++ b/doc/Design.md @@ -15,6 +15,35 @@ Also, when roaming, the provider may not actually support SCION. In this case th device would need to connect to some kind of gateway to connect to either a daemon or at least a topology server + control server. +## Server / Client + +Server functionality, such as `open()` and `receive()` -> `send(RequestPath)`, should _not_ require +any access to a daemon or control service. The main reason is that it ensures efficiency +by completely avoiding any type of interaction with daemon/CS. +Interaction with daemon/CS should not be denied but it should not occur during default operations. + +Client functionality will usually require a `ScionService`. If none is provided during `open()`, +a service will internally be requested via `Scion.defaultService()`. +While explicit initialization is usually preferable as a design choice, this implicit +initialization was implemented to allow usage of `open()` without additional arguments. +This allows being much closer to the native Java `DatagramChannel` API. + +An alternative would be to push the the delayed/implicit initialization into the `ScionService` +class. We would always have a `ScionService` instance, but it wouldn't initially connect to +a daemon or control service. The disadvantage is that creation of a `ScionService` would have +to fail "late", i.e. we can create it but only when we use it would be know whether it +is actually able to connect to something. + +## ScionService + +The `ScionService` implements all interactions with a local daemon or with a control service. +When a `ScionService` is required internally (e.g. by `DatagramChannel`) and none has been +specified, a `ScionService` will be requested (and if none has been created yet, created) via +`Scion.defaultService()`. + +A `ScionService` created via specific factory methods (`Scion.newServiceWithDNS`, etc) will _not_ be +used or returned by `Scion.defaultService()`. + ## Library dependencies etc - Use Maven (instead of Gradle or something else): Maven is still the most used framework and arguable the best ( diff --git a/src/main/java/org/scion/AbstractDatagramChannel.java b/src/main/java/org/scion/AbstractDatagramChannel.java index 75e441baf..88743f05c 100644 --- a/src/main/java/org/scion/AbstractDatagramChannel.java +++ b/src/main/java/org/scion/AbstractDatagramChannel.java @@ -46,7 +46,12 @@ abstract class AbstractDatagramChannel> imp private Consumer errorListener; protected AbstractDatagramChannel(ScionService service) throws IOException { - this.channel = java.nio.channels.DatagramChannel.open(); + this(service, DatagramChannel.open()); + } + + protected AbstractDatagramChannel( + ScionService service, java.nio.channels.DatagramChannel channel) { + this.channel = channel; this.service = service; } @@ -54,6 +59,7 @@ protected synchronized void configureBlocking(boolean block) throws IOException channel.configureBlocking(block); } + // `protected` because it should not be visible in ScmpChannel API. protected synchronized boolean isBlocking() { return channel.isBlocking(); } @@ -77,24 +83,15 @@ public synchronized void setPathPolicy(PathPolicy pathPolicy) throws IOException } } - public synchronized ScionService getService() { - // Late and implicit initialization is questionable but probably the best choice here. - // 1) Under any conceivable circumstances there should only be one service instance required, - // so defaultService() should yield the 'correct' instance. - // 2) defaultService() should also be 'correct' in the sense that no other instance should ever - // be required. The dedicated newServiceXYZ() methods should be unnecessary. - // - // Why do we do this? It allows us to use DatagramChannel.open() in all situations, - // independent of whether we are a client (requires Service) or a server (should not use - // Service). + protected synchronized ScionService getOrCreateService() { if (service == null) { - service = Scion.defaultService(); + service = ScionService.defaultService(); } return this.service; } - public synchronized void setService(ScionService service) { - this.service = service; + public synchronized ScionService getService() { + return this.service; } protected DatagramChannel channel() { @@ -150,7 +147,7 @@ public synchronized C connect(SocketAddress addr) throws IOException { throw new IllegalArgumentException( "connect() requires an InetSocketAddress or a ScionSocketAddress."); } - return connect(pathPolicy.filter(getService().getPaths((InetSocketAddress) addr))); + return connect(pathPolicy.filter(getOrCreateService().getPaths((InetSocketAddress) addr))); } /** @@ -185,7 +182,7 @@ public synchronized Path getCurrentPath() { return path; } - protected void setPath(RequestPath path) { + protected synchronized void setPath(RequestPath path) { this.path = path; } @@ -362,7 +359,7 @@ protected void buildHeaderNoRefresh( channel.connect(connection); } - srcIA = getService().getLocalIsdAs(); + srcIA = getOrCreateService().getLocalIsdAs(); // Get external host address. This must be done *after* refreshing the path! InetSocketAddress srcSocketAddress = (InetSocketAddress) channel.getLocalAddress(); srcAddress = srcSocketAddress.getAddress().getAddress(); @@ -392,7 +389,7 @@ protected RequestPath ensureUpToDate(RequestPath path) throws IOException { private RequestPath updatePath(RequestPath path) throws IOException { // expired, get new path - RequestPath newPath = pathPolicy.filter(getService().getPaths(path)); + RequestPath newPath = pathPolicy.filter(getOrCreateService().getPaths(path)); if (isConnected) { // equal to !isBound at this point if (!newPath.getFirstHopAddress().equals(this.connection)) { diff --git a/src/main/java/org/scion/DatagramChannel.java b/src/main/java/org/scion/DatagramChannel.java index 1a408f51b..9bc1efcc0 100644 --- a/src/main/java/org/scion/DatagramChannel.java +++ b/src/main/java/org/scion/DatagramChannel.java @@ -29,22 +29,24 @@ public class DatagramChannel extends AbstractDatagramChannel private final ByteBuffer bufferReceive; private final ByteBuffer bufferSend; - protected DatagramChannel() throws IOException { - this(null); - } - - protected DatagramChannel(ScionService service) throws IOException { - super(service); + protected DatagramChannel(ScionService service, java.nio.channels.DatagramChannel channel) + throws IOException { + super(service, channel); this.bufferReceive = ByteBuffer.allocateDirect(getOption(StandardSocketOptions.SO_RCVBUF)); this.bufferSend = ByteBuffer.allocateDirect(getOption(StandardSocketOptions.SO_SNDBUF)); } public static DatagramChannel open() throws IOException { - return new DatagramChannel(); + return open(null); } public static DatagramChannel open(ScionService service) throws IOException { - return new DatagramChannel(service); + return open(service, java.nio.channels.DatagramChannel.open()); + } + + public static DatagramChannel open( + ScionService service, java.nio.channels.DatagramChannel channel) throws IOException { + return new DatagramChannel(service, channel); } // TODO we return `void` here. If we implement SelectableChannel @@ -85,7 +87,9 @@ public synchronized void send(ByteBuffer srcBuffer, SocketAddress destination) if (!(destination instanceof InetSocketAddress)) { throw new IllegalArgumentException("Address must be of type InetSocketAddress."); } - send(srcBuffer, getPathPolicy().filter(getService().getPaths((InetSocketAddress) destination))); + send( + srcBuffer, + getPathPolicy().filter(getOrCreateService().getPaths((InetSocketAddress) destination))); } /** diff --git a/src/main/java/org/scion/ScionService.java b/src/main/java/org/scion/ScionService.java index 5f45c9101..ab86840e8 100644 --- a/src/main/java/org/scion/ScionService.java +++ b/src/main/java/org/scion/ScionService.java @@ -121,11 +121,6 @@ protected ScionService(String addressOrHost, Mode mode) { } throw e; } - synchronized (LOCK) { - if (DEFAULT == null) { - DEFAULT = this; - } - } } /** @@ -171,10 +166,9 @@ static ScionService defaultService() { DEFAULT = new ScionService(daemonHost + ":" + daemonPort, Mode.DAEMON); return DEFAULT; } catch (ScionRuntimeException e) { - // Ignore + throw new ScionRuntimeException( + "Could not connect to daemon, DNS or bootstrap resource.", e); } - - throw new ScionRuntimeException("Could not connect to daemon, DNS or bootstrap resource."); } } @@ -229,6 +223,10 @@ public DatagramChannel openChannel() throws IOException { return DatagramChannel.open(this); } + public DatagramChannel openChannel(java.nio.channels.DatagramChannel channel) throws IOException { + return DatagramChannel.open(this, channel); + } + Daemon.ASResponse getASInfo() { Daemon.ASRequest request = Daemon.ASRequest.newBuilder().setIsdAs(0).build(); Daemon.ASResponse response; diff --git a/src/test/java/org/scion/PackageVisibilityHelper.java b/src/test/java/org/scion/PackageVisibilityHelper.java index fd6c01fc3..e2c3c103e 100644 --- a/src/test/java/org/scion/PackageVisibilityHelper.java +++ b/src/test/java/org/scion/PackageVisibilityHelper.java @@ -15,10 +15,12 @@ package org.scion; import com.google.protobuf.ByteString; +import com.google.protobuf.Timestamp; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.nio.ByteBuffer; +import java.time.Instant; import java.util.List; import org.scion.internal.InternalConstants; import org.scion.internal.ScionHeaderParser; @@ -69,10 +71,24 @@ public static RequestPath createDummyPath( Daemon.Interface.newBuilder() .setAddress(Daemon.Underlay.newBuilder().setAddress(firstHopString).build()) .build(); - Daemon.Path path = Daemon.Path.newBuilder().setRaw(bs).setInterface(inter).build(); + Timestamp ts = Timestamp.newBuilder().setSeconds(Instant.now().getEpochSecond() + 100).build(); + Daemon.Path path = + Daemon.Path.newBuilder().setRaw(bs).setInterface(inter).setExpiration(ts).build(); return RequestPath.create(path, dstIsdAs, dstHost, dstPort); } + public static ResponsePath createDummyResponsePath( + byte[] raw, + long srcIsdAs, + byte[] srcIP, + int srcPort, + long dstIsdAs, + byte[] dstIP, + int dstPort, + InetSocketAddress firstHop) { + return ResponsePath.create(raw, srcIsdAs, srcIP, srcPort, dstIsdAs, dstIP, dstPort, firstHop); + } + public static RequestPath createRequestPath110_112( Daemon.Path.Builder builder, long dstIsdAs, diff --git a/src/test/java/org/scion/api/DatagramChannelApiServerTest.java b/src/test/java/org/scion/api/DatagramChannelApiServerTest.java new file mode 100644 index 000000000..469eec30e --- /dev/null +++ b/src/test/java/org/scion/api/DatagramChannelApiServerTest.java @@ -0,0 +1,112 @@ +// Copyright 2023 ETH Zurich +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.scion.api; + +import static org.junit.jupiter.api.Assertions.*; + +import java.io.IOException; +import java.net.*; +import java.nio.ByteBuffer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.scion.*; +import org.scion.testutil.ExamplePacket; +import org.scion.testutil.MockDNS; +import org.scion.testutil.MockDaemon; +import org.scion.testutil.MockDatagramChannel; + +/** + * Test that typical "server" operations do not require a ScionService. + * + *

This is service-less operation is required for servers that should not use (or may not have) + * access to a daemon or control service. Service-less operation is mainly implemented to ensure + * good server performance by avoiding costly network calls to daemons etc. + */ +class DatagramChannelApiServerTest { + + private static final int dummyPort = 44444; + + @BeforeEach + public void beforeEach() throws IOException { + MockDaemon.closeDefault(); + MockDNS.clear(); + ScionService.closeDefault(); + } + + @AfterEach + public void afterEach() throws IOException { + MockDaemon.closeDefault(); + MockDNS.clear(); + ScionService.closeDefault(); + } + + @Test + void open_withoutService() throws IOException { + // check that open() (without service argument) does not internally require a ScionService. + try (DatagramChannel channel = DatagramChannel.open()) { + assertNull(channel.getService()); + } + } + + @Test + void bind_withoutService() throws IOException { + // check that open() (without service argument) does not internally require a ScionService. + try (DatagramChannel channel = DatagramChannel.open()) { + channel.bind(new InetSocketAddress("127.0.0.1", 12345)); + assertNull(channel.getService()); + } + } + + @Test + void send_withoutService() throws IOException { + // check that send(ResponsePath) does not internally require a ScionService. + try (DatagramChannel channel = DatagramChannel.open()) { + assertNull(channel.getService()); + ResponsePath path = + PackageVisibilityHelper.createDummyResponsePath( + new byte[0], + 1, + new byte[4], + 1, + 1, + new byte[4], + 1, + new InetSocketAddress("127.0.0.1", 1)); + Path p2 = channel.send(ByteBuffer.allocate(0), path); + assertNull(channel.getService()); + assertEquals(path, p2); + } + } + + @Test + void receive_withoutService() throws IOException { + // check that receive() does not internally require a ScionService. + SocketAddress addr = new InetSocketAddress("127.0.0.1", 12345); + MockDatagramChannel mock = MockDatagramChannel.open(); + mock.setReceiveCallback( + buf -> { + buf.put(ExamplePacket.PACKET_BYTES_SERVER_E2E_PING); + return addr; + }); + try (DatagramChannel channel = DatagramChannel.open(null, mock)) { + assertNull(channel.getService()); + ByteBuffer buffer = ByteBuffer.allocate(100); + Path path = channel.receive(buffer); + assertNull(channel.getService()); + assertEquals(addr, path.getFirstHopAddress()); + } + } +} diff --git a/src/test/java/org/scion/api/DatagramChannelApiTest.java b/src/test/java/org/scion/api/DatagramChannelApiTest.java index 42fa15b6c..9d24c83b6 100644 --- a/src/test/java/org/scion/api/DatagramChannelApiTest.java +++ b/src/test/java/org/scion/api/DatagramChannelApiTest.java @@ -18,10 +18,7 @@ import com.google.protobuf.Timestamp; import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.net.StandardSocketOptions; -import java.net.UnknownHostException; +import java.net.*; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.NotYetConnectedException; @@ -278,16 +275,30 @@ void isConnected_Path() throws IOException { } @Test - void getService() throws IOException { + void getService_default() throws IOException { + ScionService service1 = Scion.defaultService(); + ScionService service2 = Scion.newServiceWithDaemon(MockDaemon.DEFAULT_ADDRESS_STR); try (DatagramChannel channel = DatagramChannel.open()) { - assertEquals(Scion.defaultService(), channel.getService()); - ScionService service1 = channel.getService(); - ScionService service2 = Scion.newServiceWithDaemon(MockDaemon.DEFAULT_ADDRESS_STR); - channel.setService(service2); + assertNull(channel.getService()); + + // trigger service initialization in channel + RequestPath path = PackageVisibilityHelper.createDummyPath(); + channel.send(ByteBuffer.allocate(0), path); + assertNotEquals(service2, channel.getService()); + assertEquals(service1, channel.getService()); + } + service2.close(); + } + + @Test + void getService_non_default() throws IOException { + ScionService service1 = Scion.defaultService(); + ScionService service2 = Scion.newServiceWithDaemon(MockDaemon.DEFAULT_ADDRESS_STR); + try (DatagramChannel channel = DatagramChannel.open(service2)) { assertEquals(service2, channel.getService()); assertNotEquals(service1, channel.getService()); - service2.close(); } + service2.close(); } @Test @@ -516,7 +527,7 @@ void getCurrentPath() { // send should NOT set a path channel.send(buffer, addr); - // TODO assertNull(channel.getCurrentPath()); + assertNull(channel.getCurrentPath()); } catch (IOException e) { throw new RuntimeException(e); } diff --git a/src/test/java/org/scion/api/DatagramChannelErrorHandlingTest.java b/src/test/java/org/scion/api/DatagramChannelErrorHandlingTest.java new file mode 100644 index 000000000..6feb1d0a7 --- /dev/null +++ b/src/test/java/org/scion/api/DatagramChannelErrorHandlingTest.java @@ -0,0 +1,88 @@ +// Copyright 2023 ETH Zurich +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.scion.api; + +import static org.junit.jupiter.api.Assertions.*; + +import java.io.IOException; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.List; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Test; +import org.scion.*; +import org.scion.testutil.PingPongHelper; + +/** Test path switching (changing first hop) on DatagramChannel. */ +class DatagramChannelErrorHandlingTest { + + private final PathPolicy alternatingPolicy = + new PathPolicy() { + private int count = 0; + + @Override + public RequestPath filter(List paths) { + return paths.get(count++ % 2); + } + }; + + @AfterAll + public static void afterAll() { + // Defensive clean up + ScionService.closeDefault(); + } + + @Test + void testDummyChannel() throws IOException { + // java.nio.channels.DatagramChannel channel2 = new TestChannel(); + // DatagramChannel channel = null; + // try { + // channel = Scion.defaultService().openChannel(channel2); + // } finally { + // channel.close(); + // } + } + + @Test + void test() { + // PingPongHelper.Server serverFn = PingPongHelper::defaultServer; + // PingPongHelper.Client clientFn = this::client; + // PingPongHelper pph = new PingPongHelper(1, 2, 10); + // pph.runPingPong(serverFn, clientFn, false); + // assertEquals(2 * 10, MockNetwork.getForwardCount(0)); + // assertEquals(2 * 10, MockNetwork.getForwardCount(1)); + // assertEquals(2 * 2 * 10, MockNetwork.getAndResetForwardCount()); + } + + private void client(DatagramChannel channel, Path serverAddress, int id) throws IOException { + String message = PingPongHelper.MSG + "-" + id; + ByteBuffer sendBuf = ByteBuffer.wrap(message.getBytes()); + + // Use a path policy that alternates between 1st and 2nd path + // -> setPathPolicy() sets a new path! + channel.setPathPolicy(alternatingPolicy); + channel.write(sendBuf); + + // System.out.println("CLIENT: Receiving ... (" + channel.getLocalAddress() + ")"); + ByteBuffer response = ByteBuffer.allocate(512); + int len = channel.read(response); + assertEquals(message.length(), len); + + response.flip(); + String pong = Charset.defaultCharset().decode(response).toString(); + assertEquals(message, pong); + } +} diff --git a/src/test/java/org/scion/api/DatagramChannelPacketValidationTest.java b/src/test/java/org/scion/api/DatagramChannelPacketValidationTest.java index 7a3e57183..68d84870c 100644 --- a/src/test/java/org/scion/api/DatagramChannelPacketValidationTest.java +++ b/src/test/java/org/scion/api/DatagramChannelPacketValidationTest.java @@ -23,6 +23,7 @@ import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.AfterAll; @@ -117,7 +118,7 @@ private void receive_validationFails_isBlocking_noThrow(boolean throwBad, boolea throws IOException, InterruptedException { barrier = new CountDownLatch(1); Thread serverThread = startServer(throwBad, isBlocking); - barrier.await(); // Wait for thread to start + barrier.await(1, TimeUnit.SECONDS); // Wait for thread to start // client - send bad message for (int i = 0; i < 10; i++) { diff --git a/src/test/java/org/scion/api/ScionTest.java b/src/test/java/org/scion/api/ScionTest.java index fa48620f9..b6843be79 100644 --- a/src/test/java/org/scion/api/ScionTest.java +++ b/src/test/java/org/scion/api/ScionTest.java @@ -181,6 +181,7 @@ void newServiceWithDNS() throws IOException { assertFalse(paths.isEmpty()); assertEquals(1, MockNetwork.getTopoServer().getAndResetCallCount()); assertEquals(1, MockNetwork.getControlServer().getAndResetCallCount()); + assertNotEquals(Scion.defaultService(), ss); } finally { MockNetwork.stopTiny(); } @@ -199,6 +200,7 @@ void newServiceWithBootstrapServer() throws IOException { assertFalse(paths.isEmpty()); assertEquals(1, MockNetwork.getTopoServer().getAndResetCallCount()); assertEquals(1, MockNetwork.getControlServer().getAndResetCallCount()); + assertNotEquals(Scion.defaultService(), ss); } finally { MockNetwork.stopTiny(); } diff --git a/src/test/java/org/scion/testutil/MockDatagramChannel.java b/src/test/java/org/scion/testutil/MockDatagramChannel.java new file mode 100644 index 000000000..4615b7b74 --- /dev/null +++ b/src/test/java/org/scion/testutil/MockDatagramChannel.java @@ -0,0 +1,184 @@ +// Copyright 2024 ETH Zurich +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package org.scion.testutil; + +import java.io.IOException; +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.channels.MembershipKey; +import java.nio.channels.spi.SelectorProvider; +import java.util.Set; +import java.util.function.Function; +import org.scion.ScionSocketOptions; + +public class MockDatagramChannel extends java.nio.channels.DatagramChannel { + private boolean isOpen = true; + private boolean isConnected = false; + private boolean isBlocking = false; + private SocketAddress bindAddress; + private SocketAddress connectAddress; + + private Function receiveCallback = + byteBuffer -> { + throw new UnsupportedOperationException(); + }; + + public static MockDatagramChannel open() throws IOException { + return new MockDatagramChannel(); + } + + protected MockDatagramChannel() { + super(SelectorProvider.provider()); + } + + public void setReceiveCallback(Function cb) { + receiveCallback = cb; + } + + @Override + public java.nio.channels.DatagramChannel bind(SocketAddress socketAddress) throws IOException { + bindAddress = socketAddress; + return this; + } + + @Override + public java.nio.channels.DatagramChannel setOption(SocketOption socketOption, T t) + throws IOException { + // if (ScionSocketOptions.SN_API_THROW_PARSER_FAILURE.equals(option)) { + // cfgReportFailedValidation = (Boolean) t; + // } else if (ScionSocketOptions.SN_PATH_EXPIRY_MARGIN.equals(option)) { + // cfgExpirationSafetyMargin = (Integer) t; + // } else if (StandardSocketOptions.SO_RCVBUF.equals(option)) { + // // TODO resize buf + // channel.setOption(option, t); + // } else if (StandardSocketOptions.SO_SNDBUF.equals(option)) { + // // TODO resize buf + // channel.setOption(option, t); + // } else { + // channel.setOption(option, t); + // } + // return (C) this; + throw new UnsupportedOperationException(); + } + + @Override + public T getOption(SocketOption option) throws IOException { + if (ScionSocketOptions.SN_API_THROW_PARSER_FAILURE.equals(option)) { + return (T) (Boolean) false; // cfgReportFailedValidation; + } else if (ScionSocketOptions.SN_PATH_EXPIRY_MARGIN.equals(option)) { + return (T) (Integer) 10; // cfgExpirationSafetyMargin; + } else if (StandardSocketOptions.SO_RCVBUF.equals(option)) { + return (T) (Integer) 1000; + } else if (StandardSocketOptions.SO_SNDBUF.equals(option)) { + return (T) (Integer) 1000; + } + throw new UnsupportedOperationException(); + } + + @Override + public Set> supportedOptions() { + throw new UnsupportedOperationException(); + } + + @Override + public DatagramSocket socket() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isConnected() { + return isConnected; + } + + @Override + public java.nio.channels.DatagramChannel connect(SocketAddress socketAddress) throws IOException { + connectAddress = socketAddress; + isConnected = true; + return this; + } + + @Override + public java.nio.channels.DatagramChannel disconnect() throws IOException { + connectAddress = null; + isConnected = false; + return this; + } + + @Override + public SocketAddress getRemoteAddress() throws IOException { + return connectAddress; + } + + @Override + public SocketAddress receive(ByteBuffer byteBuffer) throws IOException { + return receiveCallback.apply(byteBuffer); + } + + @Override + public int send(ByteBuffer byteBuffer, SocketAddress socketAddress) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public int read(ByteBuffer byteBuffer) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public long read(ByteBuffer[] byteBuffers, int i, int i1) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public int write(ByteBuffer byteBuffer) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public long write(ByteBuffer[] byteBuffers, int i, int i1) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public SocketAddress getLocalAddress() throws IOException { + return bindAddress; + } + + @Override + public MembershipKey join(InetAddress inetAddress, NetworkInterface networkInterface) + throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public MembershipKey join( + InetAddress inetAddress, NetworkInterface networkInterface, InetAddress inetAddress1) + throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + protected void implCloseSelectableChannel() throws IOException { + isConnected = false; + isOpen = false; + connectAddress = null; + bindAddress = null; + } + + @Override + protected void implConfigureBlocking(boolean b) throws IOException { + this.isBlocking = b; + } +}