private void handleAllocationRequest() throws StunException, IOException, InterruptedException { StunMessageEvent event = eventsReceivedByTurnServer.poll(5, TimeUnit.SECONDS); TransportAddress localAddress = event.getLocalAddress(); TransportAddress remoteAddress = event.getRemoteAddress(); Request request = (Request) event.getMessage(); assertThat(request.getMessageType(), is(Message.ALLOCATE_REQUEST)); Response allocationResponse = MessageFactory.createAllocationResponse( request, remoteAddress, new TransportAddress(localAddress.getHostAddress(), 2222, UDP), 100); sendResponse(remoteAddress, request, allocationResponse); }
/** * Returns the result of applying XOR on the specified attribute's address. The method may be used * for both encoding and decoding XorMappedAddresses. * * @param address the address on which XOR should be applied * @param transactionID the transaction id to use for the XOR * @return the XOR-ed address. */ public static TransportAddress applyXor(TransportAddress address, byte[] transactionID) { byte[] addressBytes = address.getAddressBytes(); char port = (char) address.getPort(); char portModifier = (char) ((transactionID[0] << 8 & 0x0000FF00) | (transactionID[1] & 0x000000FF)); port ^= portModifier; for (int i = 0; i < addressBytes.length; i++) addressBytes[i] ^= transactionID[i]; TransportAddress xoredAdd; try { xoredAdd = new TransportAddress(addressBytes, port, Transport.UDP); } catch (UnknownHostException e) { // shouldn't happen so just throw an illegal arg throw new IllegalArgumentException(e); } return xoredAdd; }
/** * Determines whether a specific {@code DatagramPacket} is accepted by {@link #channelDataSocket} * (i.e. whether {@code channelDataSocket} understands {@code packet} and {@code packet} is meant * to be received by {@code channelDataSocket}). * * @param packet the {@code DatagramPacket} which is to be checked whether it is accepted by * {@code channelDataSocket} * @return {@code true} if {@code channelDataSocket} accepts {@code packet} (i.e. {@code * channelDataSocket} understands {@code packet} and {@code p} is meant to be received by * {@code channelDataSocket}); otherwise, {@code false} */ private boolean isChannelData(DatagramPacket packet) { // Is it from our TURN server? if (!serverAddress.equals(packet.getSocketAddress())) { return false; } int packetLength = packet.getLength(); if (packetLength < (CHANNELDATA_CHANNELNUMBER_LENGTH + CHANNELDATA_LENGTH_LENGTH)) { return false; } byte[] pData = packet.getData(); int pOffset = packet.getOffset(); /* * The first two bits should be 0b01 because of the current channel number range 0x4000 - 0x7FFE. But 0b10 and 0b11 * which are currently reserved and may be used in the future to extend the range of channel numbers. */ if ((pData[pOffset] & 0xC0) == 0) { return false; } pOffset += CHANNELDATA_CHANNELNUMBER_LENGTH; packetLength -= CHANNELDATA_CHANNELNUMBER_LENGTH; int length = ((pData[pOffset++] << 8) | (pData[pOffset] & 0xFF)); int padding = ((length % 4) > 0) ? 4 - (length % 4) : 0; /* * The Length field specifies the length in bytes of the Application Data field. The Length field does not include * the padding that is sometimes present in the data of the DatagramPacket. */ return length == packetLength - padding - CHANNELDATA_LENGTH_LENGTH || length == packetLength - CHANNELDATA_LENGTH_LENGTH; }
/** The listening thread's run method. */ @Override public void run() { DatagramPacket packet = null; while (this.running) { try { IceSocketWrapper localSock; synchronized (sockLock) { if (!running) return; localSock = this.sock; } /* * Make sure localSock's receiveBufferSize is taken into * account including after it gets changed. */ int receiveBufferSize = 1500; /* if(localSock.getTCPSocket() != null) { receiveBufferSize = localSock.getTCPSocket(). getReceiveBufferSize(); } else if(localSock.getUDPSocket() != null) { receiveBufferSize = localSock.getUDPSocket(). getReceiveBufferSize(); } */ if (packet == null) { packet = new DatagramPacket(new byte[receiveBufferSize], receiveBufferSize); } else { byte[] packetData = packet.getData(); if ((packetData == null) || (packetData.length < receiveBufferSize)) { packet.setData(new byte[receiveBufferSize], 0, receiveBufferSize); } else { /* * XXX Tell the packet it is large enough because the * socket will not look at the length of the data array * property and will just respect the length property. */ packet.setLength(receiveBufferSize); } } localSock.receive(packet); // get lost if we are no longer running. if (!running) return; logger.finest("received datagram"); RawMessage rawMessage = new RawMessage( packet.getData(), packet.getLength(), new TransportAddress( packet.getAddress(), packet.getPort(), listenAddress.getTransport()), listenAddress); messageQueue.add(rawMessage); } catch (SocketException ex) { if (running) { logger.log( Level.WARNING, "Connector died: " + listenAddress + " -> " + remoteAddress, ex); stop(); // Something wrong has happened errorHandler.handleFatalError( this, "A socket exception was thrown" + " while trying to receive a message.", ex); } else { // The exception was most probably caused by calling // this.stop(). } } catch (ClosedChannelException cce) { logger.log(Level.WARNING, "A net access point has gone useless:", cce); stop(); errorHandler.handleFatalError( this, "ClosedChannelException occurred while listening" + " for messages!", cce); } catch (IOException ex) { logger.log(Level.WARNING, "A net access point has gone useless:", ex); errorHandler.handleError(ex.getMessage(), ex); // do not stop the thread; } catch (Throwable ex) { logger.log(Level.WARNING, "A net access point has gone useless:", ex); stop(); errorHandler.handleFatalError( this, "Unknown error occurred while listening for messages!", ex); } } }