From e7c7c840a53a151cdfc983600855a654b902035a Mon Sep 17 00:00:00 2001 From: marci4 Date: Tue, 29 Jan 2019 23:04:31 +0100 Subject: [PATCH 1/3] Fix issue #847 --- .../org/java_websocket/drafts/Draft_6455.java | 28 ++- .../java_websocket/issues/Issue847Test.java | 188 ++++++++++++++++++ .../org/java_websocket/util/KeyUtils.java | 55 +++++ 3 files changed, 267 insertions(+), 4 deletions(-) create mode 100644 src/test/java/org/java_websocket/issues/Issue847Test.java create mode 100644 src/test/java/org/java_websocket/util/KeyUtils.java diff --git a/src/main/java/org/java_websocket/drafts/Draft_6455.java b/src/main/java/org/java_websocket/drafts/Draft_6455.java index d7b94083f..779ca3d7d 100644 --- a/src/main/java/org/java_websocket/drafts/Draft_6455.java +++ b/src/main/java/org/java_websocket/drafts/Draft_6455.java @@ -476,7 +476,9 @@ private Framedata translateSingleFrame( ByteBuffer buffer ) throws IncompleteExc Opcode optcode = toOpcode( ( byte ) ( b1 & 15 ) ); if( !( payloadlength >= 0 && payloadlength <= 125 ) ) { - payloadlength = translateSingleFramePayloadLength(buffer, optcode, payloadlength ,maxpacketsize, realpacketsize); + TranslatedPayloadMetaData payloadData = translateSingleFramePayloadLength(buffer, optcode, payloadlength ,maxpacketsize, realpacketsize); + payloadlength = payloadData.getPayloadLength(); + realpacketsize = payloadData.getRealPackageSize(); } translateSingleFrameCheckLengthLimit(payloadlength); realpacketsize += ( mask ? 4 : 0 ); @@ -517,12 +519,12 @@ private Framedata translateSingleFrame( ByteBuffer buffer ) throws IncompleteExc * @param oldPayloadlength the old payload length * @param maxpacketsize the max packet size allowed * @param realpacketsize the real packet size - * @return the new payload length + * @return the new payload data containing new payload length and new packet size * @throws InvalidFrameException thrown if a control frame has an invalid length * @throws IncompleteException if the maxpacketsize is smaller than the realpackagesize * @throws LimitExceededException if the payload length is to big */ - private int translateSingleFramePayloadLength(ByteBuffer buffer, Opcode optcode, int oldPayloadlength, int maxpacketsize, int realpacketsize) throws InvalidFrameException, IncompleteException, LimitExceededException { + private TranslatedPayloadMetaData translateSingleFramePayloadLength(ByteBuffer buffer, Opcode optcode, int oldPayloadlength, int maxpacketsize, int realpacketsize) throws InvalidFrameException, IncompleteException, LimitExceededException { int payloadlength = oldPayloadlength; if( optcode == Opcode.PING || optcode == Opcode.PONG || optcode == Opcode.CLOSING ) { log.trace( "Invalid frame: more than 125 octets" ); @@ -546,7 +548,7 @@ private int translateSingleFramePayloadLength(ByteBuffer buffer, Opcode optcode, translateSingleFrameCheckLengthLimit(length); payloadlength = ( int ) length; } - return payloadlength; + return new TranslatedPayloadMetaData(payloadlength, realpacketsize); } /** @@ -1035,4 +1037,22 @@ private long getByteBufferListSize() { } return totalSize; } + + private class TranslatedPayloadMetaData { + private int payloadLength; + private int realPackageSize; + + int getPayloadLength() { + return payloadLength; + } + + int getRealPackageSize() { + return realPackageSize; + } + + TranslatedPayloadMetaData(int newPayloadLength, int newRealPackageSize) { + this.payloadLength = newPayloadLength; + this.realPackageSize = newRealPackageSize; + } + } } diff --git a/src/test/java/org/java_websocket/issues/Issue847Test.java b/src/test/java/org/java_websocket/issues/Issue847Test.java new file mode 100644 index 000000000..f1034020b --- /dev/null +++ b/src/test/java/org/java_websocket/issues/Issue847Test.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2010-2019 Nathan Rajlich + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package org.java_websocket.issues; + +import org.java_websocket.client.WebSocketClient; +import org.java_websocket.drafts.Draft_6455; +import org.java_websocket.framing.BinaryFrame; +import org.java_websocket.handshake.ServerHandshake; +import org.java_websocket.util.Charsetfunctions; +import org.java_websocket.util.KeyUtils; +import org.java_websocket.util.SocketUtil; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Scanner; + +import static org.junit.Assert.fail; + +@RunWith(Parameterized.class) +public class Issue847Test { + + private static Thread thread; + private static ServerSocket serverSocket; + + private static int port; + private static final int NUMBER_OF_TESTS = 20; + + @Parameterized.Parameter + public int size; + + @Parameterized.Parameters + public static Collection data() { + List ret = new ArrayList(NUMBER_OF_TESTS); + for (int i = 1; i <= NUMBER_OF_TESTS+1; i++) ret.add(new Integer[]{(int)Math.round(Math.pow(2, i))}); + return ret; + } + + @BeforeClass + public static void startServer() throws Exception { + port = SocketUtil.getAvailablePort(); + thread = new Thread( + new Runnable() { + public void run() { + try { + serverSocket = new ServerSocket( port ); + serverSocket.setReuseAddress( true ); + while( true ) { + Socket client = null; + try { + client = serverSocket.accept(); + Scanner in = new Scanner( client.getInputStream() ); + String input; + String seckey = ""; + String testCase; + boolean useMask = false; + int size = 0; + OutputStream os = client.getOutputStream(); + while( in.hasNext() ) { + input = in.nextLine(); + if( input.startsWith( "Sec-WebSocket-Key: " ) ) { + seckey = input.split( " " )[1]; + } + //Last + if( input.startsWith( "Upgrade" ) ) { + os.write(Charsetfunctions.asciiBytes("HTTP/1.1 101 Websocket Connection Upgrade\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n" + KeyUtils.getSecKey(seckey) + "\r\n")); + os.flush(); + Thread.sleep(10); + Draft_6455 draft_6455 = new Draft_6455(); + BinaryFrame binaryFrame = new BinaryFrame(); + binaryFrame.setPayload(ByteBuffer.allocate(size)); + binaryFrame.setTransferemasked(useMask); + ByteBuffer byteBuffer = draft_6455.createBinaryFrame(binaryFrame); + byte[] bytes = byteBuffer.array(); + int first = size/2; + os.write(bytes, 0, first); + os.flush(); + Thread.sleep(5); + os.write(bytes, first, bytes.length-first); + os.flush(); + break; + } + if( input.startsWith( "GET " ) ) { + testCase = input.split(" ")[1]; + String[] strings = testCase.split("/"); + useMask = Boolean.valueOf(strings[1]); + size = Integer.valueOf(strings[2]); + } + } + } catch ( IOException e ) { + // + } + } + } catch ( Exception e ) { + e.printStackTrace(); + fail( "There should be no exception" ); + } + } + } ); + thread.start(); + } + + @AfterClass + public static void successTests() throws IOException { + serverSocket.close(); + thread.interrupt(); + } + + @Test(timeout = 5000) + public void testIncrementalFrameUnmasked() throws Exception { + testIncrementalFrame( false, size ); + } + + @Test(timeout = 5000) + public void testIncrementalFrameMsked() throws Exception { + testIncrementalFrame( true, size ); + } + + + private void testIncrementalFrame(boolean useMask, int size ) throws Exception { + final boolean[] threadReturned = { false }; + final WebSocketClient webSocketClient = new WebSocketClient( new URI( "ws://localhost:"+ port + "/" + useMask + "/" + size ) ) { + @Override + public void onOpen( ServerHandshake handshakedata ) { + } + + @Override + public void onMessage( String message ) { + fail( "There should not be a message!" ); + } + public void onMessage( ByteBuffer message ) { + threadReturned[0] = true; + close(); + } + + @Override + public void onClose( int code, String reason, boolean remote ) { + } + + @Override + public void onError( Exception ex ) { + ex.printStackTrace(); + } + }; + Thread finalThread = new Thread( webSocketClient ); + finalThread.start(); + finalThread.join(); + if( !threadReturned[0] ) { + fail( "Error" ); + } + } +} + diff --git a/src/test/java/org/java_websocket/util/KeyUtils.java b/src/test/java/org/java_websocket/util/KeyUtils.java new file mode 100644 index 000000000..eb5bf9a01 --- /dev/null +++ b/src/test/java/org/java_websocket/util/KeyUtils.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2010-2019 Nathan Rajlich + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ + +package org.java_websocket.util; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class KeyUtils { + /** + * Generate a final key from a input string + * + * @param in the input string + * @return a final key + */ + public static String generateFinalKey( String in ) { + String seckey = in.trim(); + String acc = seckey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + MessageDigest sh1; + try { + sh1 = MessageDigest.getInstance( "SHA1" ); + } catch ( NoSuchAlgorithmException e ) { + throw new IllegalStateException( e ); + } + return Base64.encodeBytes( sh1.digest( acc.getBytes() ) ); + } + + public static String getSecKey( String seckey ) { + return "Sec-WebSocket-Accept: " + KeyUtils.generateFinalKey( seckey ) + "\r\n"; + } + +} From 4e1ce77f88b5c1bb11f56f8af2f74d77fcb8f4e3 Mon Sep 17 00:00:00 2001 From: marci4 Date: Thu, 31 Jan 2019 21:49:09 +0100 Subject: [PATCH 2/3] Fix issues found by codacy --- .../java/org/java_websocket/drafts/Draft_6455.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/java_websocket/drafts/Draft_6455.java b/src/main/java/org/java_websocket/drafts/Draft_6455.java index 779ca3d7d..3298bbe02 100644 --- a/src/main/java/org/java_websocket/drafts/Draft_6455.java +++ b/src/main/java/org/java_websocket/drafts/Draft_6455.java @@ -518,14 +518,14 @@ private Framedata translateSingleFrame( ByteBuffer buffer ) throws IncompleteExc * @param optcode the decoded optcode * @param oldPayloadlength the old payload length * @param maxpacketsize the max packet size allowed - * @param realpacketsize the real packet size + * @param oldRealpacketsize the real packet size * @return the new payload data containing new payload length and new packet size * @throws InvalidFrameException thrown if a control frame has an invalid length * @throws IncompleteException if the maxpacketsize is smaller than the realpackagesize * @throws LimitExceededException if the payload length is to big */ - private TranslatedPayloadMetaData translateSingleFramePayloadLength(ByteBuffer buffer, Opcode optcode, int oldPayloadlength, int maxpacketsize, int realpacketsize) throws InvalidFrameException, IncompleteException, LimitExceededException { - int payloadlength = oldPayloadlength; + private TranslatedPayloadMetaData translateSingleFramePayloadLength(ByteBuffer buffer, Opcode optcode, int oldPayloadlength, int maxpacketsize, int oldRealpacketsize) throws InvalidFrameException, IncompleteException, LimitExceededException { + int payloadlength = oldPayloadlength, realpacketsize = oldRealpacketsize; if( optcode == Opcode.PING || optcode == Opcode.PONG || optcode == Opcode.CLOSING ) { log.trace( "Invalid frame: more than 125 octets" ); throw new InvalidFrameException( "more than 125 octets" ); @@ -1042,11 +1042,11 @@ private class TranslatedPayloadMetaData { private int payloadLength; private int realPackageSize; - int getPayloadLength() { + private int getPayloadLength() { return payloadLength; } - int getRealPackageSize() { + private int getRealPackageSize() { return realPackageSize; } From cb3783bc72fce78a4dca3854799b13b6cda716f6 Mon Sep 17 00:00:00 2001 From: marci4 Date: Mon, 4 Feb 2019 22:30:12 +0100 Subject: [PATCH 3/3] Fix code quality issue --- src/main/java/org/java_websocket/drafts/Draft_6455.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/java_websocket/drafts/Draft_6455.java b/src/main/java/org/java_websocket/drafts/Draft_6455.java index 3298bbe02..126f154d9 100644 --- a/src/main/java/org/java_websocket/drafts/Draft_6455.java +++ b/src/main/java/org/java_websocket/drafts/Draft_6455.java @@ -525,7 +525,8 @@ private Framedata translateSingleFrame( ByteBuffer buffer ) throws IncompleteExc * @throws LimitExceededException if the payload length is to big */ private TranslatedPayloadMetaData translateSingleFramePayloadLength(ByteBuffer buffer, Opcode optcode, int oldPayloadlength, int maxpacketsize, int oldRealpacketsize) throws InvalidFrameException, IncompleteException, LimitExceededException { - int payloadlength = oldPayloadlength, realpacketsize = oldRealpacketsize; + int payloadlength = oldPayloadlength, + realpacketsize = oldRealpacketsize; if( optcode == Opcode.PING || optcode == Opcode.PONG || optcode == Opcode.CLOSING ) { log.trace( "Invalid frame: more than 125 octets" ); throw new InvalidFrameException( "more than 125 octets" );