/* * TurnServer, the OpenSource Java Solution for TURN protocol. Maintained by the * Jitsi community (http://jitsi.org). * * Copyright @ 2015 Atlassian Pty Ltd * * 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.jitsi.turnserver.listeners; import java.util.logging.*; import org.ice4j.*; import org.ice4j.attribute.*; import org.ice4j.message.*; import org.ice4j.stack.*; import org.jitsi.turnserver.stack.*; /** * The class that would be handling and responding to incoming Allocation * requests that are Allocation and sends a success or error response. * * @author Aakash Garg */ public class AllocationRequestListener implements RequestListener { /** * The <tt>Logger</tt> used by the <tt>AllocationRequestListener</tt> class * and its instances for logging output. */ private static final Logger logger = Logger .getLogger(AllocationRequestListener.class.getName()); private final TurnStack turnStack; /** * The indicator which determines whether this * <tt>AllocationrequestListener</tt> is currently started. */ private boolean started = false; /** * Creates a new AllocationRequestListener * * @param turnStack */ public AllocationRequestListener(StunStack stunStack) { if (stunStack instanceof TurnStack) { this.turnStack = (TurnStack) stunStack; } else { throw new IllegalArgumentException("This is not a TurnStack!"); } } @Override public void processRequest(StunMessageEvent evt) throws IllegalArgumentException { if (logger.isLoggable(Level.FINER)) { logger.setLevel(Level.FINEST); } Message message = evt.getMessage(); if (message.getMessageType() == Message.ALLOCATE_REQUEST) { logger.finest("Received a Allocation Request from " + evt.getRemoteAddress()); Response response = null; RequestedTransportAttribute requestedTransportAttribute = (RequestedTransportAttribute) message .getAttribute(Attribute.REQUESTED_TRANSPORT); DontFragmentAttribute dontFragmentAttribute = (DontFragmentAttribute) message .getAttribute(Attribute.DONT_FRAGMENT); ReservationTokenAttribute reservationTokenAttribute = (ReservationTokenAttribute) message .getAttribute(Attribute.RESERVATION_TOKEN); LifetimeAttribute lifetimeAttribute = (LifetimeAttribute) message.getAttribute(Attribute.LIFETIME); EvenPortAttribute evenPort = (EvenPortAttribute) message.getAttribute(Attribute.EVEN_PORT); if (lifetimeAttribute == null) { lifetimeAttribute = AttributeFactory .createLifetimeAttribute( (int) (Allocation.DEFAULT_LIFETIME / 1000)); } EvenPortAttribute evenPortAttribute = (EvenPortAttribute) message.getAttribute(Attribute.EVEN_PORT); TransportAddress clientAddress = evt.getRemoteAddress(); TransportAddress serverAddress = evt.getLocalAddress(); Transport transport = serverAddress.getTransport(); FiveTuple fiveTuple = new FiveTuple(clientAddress, serverAddress, transport); Character errorCode = null; if(!this.turnStack.canHaveMoreAllocations()) { errorCode = ErrorCodeAttribute.ALLOCATION_QUOTA_REACHED; } else if (requestedTransportAttribute == null) { errorCode = ErrorCodeAttribute.BAD_REQUEST; } else if (requestedTransportAttribute.getRequestedTransport() == RequestedTransportAttribute.TCP) { if (!this.turnStack.isTCPAllowed()) errorCode = ErrorCodeAttribute.UNSUPPORTED_TRANSPORT_PROTOCOL; else if (reservationTokenAttribute != null) { logger.finest("error : reservation token found in TCP message."); errorCode = ErrorCodeAttribute.UNSUPPORTED_TRANSPORT_PROTOCOL; } else if (evenPort != null) { logger.finest("error : even port found in TCP message."); errorCode = ErrorCodeAttribute.UNSUPPORTED_TRANSPORT_PROTOCOL; } else if (dontFragmentAttribute != null) { logger.finest("error : dont fragment found in TCP message."); errorCode = ErrorCodeAttribute.UNSUPPORTED_TRANSPORT_PROTOCOL; } } else if (requestedTransportAttribute.getRequestedTransport() == RequestedTransportAttribute.UDP && !this.turnStack.isUDPAllowed()) { errorCode = ErrorCodeAttribute.UNSUPPORTED_TRANSPORT_PROTOCOL; logger.finest("UDP not alllowed on Allocation Requests."); } else if (reservationTokenAttribute != null && evenPortAttribute != null) { errorCode = ErrorCodeAttribute.BAD_REQUEST; logger .finest("Both reservation Token and Even PortAttribute are found in Allocation request."); } if (turnStack.getServerAllocation(fiveTuple)!=null) { errorCode = ErrorCodeAttribute.ALLOCATION_MISMATCH; logger.finest("Allocation not found for the "+fiveTuple); } // do other checks here if (errorCode == null) { if(evenPortAttribute==null) { evenPortAttribute = AttributeFactory.createEvenPortAttribute(false); } TransportAddress relayAddress = turnStack.getNewRelayAddress( evenPortAttribute.isRFlag(), serverAddress.getTransport()); /* logger.finest("Added a new Relay Address "+relayAddress); System.out.println("Added a new Relay Address "+relayAddress +" for client "+evt.getRemoteAddress()); */ Allocation allocation = null; synchronized(this) { allocation = new Allocation(relayAddress, fiveTuple, lifetimeAttribute.getLifetime()); this.turnStack.addNewServerAllocation(allocation); // System.out.println("Added a new allocation."); } logger.finest("Added a new Allocation with relay address :" + allocation.getRelayAddress()+" for client " +evt.getRemoteAddress()); response = MessageFactory.createAllocationResponse( (Request) message, allocation.getFiveTuple().getClientTransportAddress(), allocation.getRelayAddress(), (int) allocation.getLifetime()); XorRelayedAddressAttribute relayedXorAddress = AttributeFactory.createXorRelayedAddressAttribute( allocation.getRelayAddress(), evt.getTransactionID().getBytes()); response.putAttribute(relayedXorAddress); LifetimeAttribute lifetime = AttributeFactory.createLifetimeAttribute( (int)(allocation.getLifetime())); response.putAttribute(lifetime); XorMappedAddressAttribute clientXorAddress = AttributeFactory.createXorMappedAddressAttribute( clientAddress, evt.getTransactionID().getBytes()); response.putAttribute(clientXorAddress); if(evenPort!=null) { // TODO : logic for process and creating Reservation Token. byte[] token = {7,7,7,7}; ReservationTokenAttribute reservationToken = AttributeFactory.createReservationTokenAttribute( token); response.putAttribute(reservationToken); if(evenPort.isRFlag()) { TransportAddress relayAddess = allocation.getRelayAddress(); TransportAddress nextAddress = new TransportAddress( relayAddress.getAddress(), relayAddress.getPort()+1, relayAddress.getTransport()); boolean isReserved = this.turnStack.reservePort(nextAddress); if(isReserved) { logger.log( Level.FINEST, nextAddress+" reserved by "+fiveTuple); } else { logger.log( Level.FINEST, nextAddress+" not reserved by "+fiveTuple); } } } } else { System.err.println("Error Code " + (int)errorCode + " on Allocation Request"); logger.finest("Error Code " + (int)errorCode + " on Allocation Request"); response = MessageFactory.createAllocationErrorResponse(errorCode); } try { logger .fine("Trying to send response to " + evt.getRemoteAddress() + " from " + evt.getLocalAddress()); turnStack.sendResponse( evt.getTransactionID().getBytes(), response, evt.getLocalAddress(), evt.getRemoteAddress()); logger.finest("Response sent."); } catch (Exception e) { System.err.println("Failed to send response"); logger.log( Level.INFO, "Failed to send " + response + " through " + evt.getLocalAddress(), e); // try to trigger a 500 response although if this one failed, throw new RuntimeException("Failed to send a response", e); } } else { return; } } /** * Starts this <tt>AllocationRequestListener</tt>. If it is not currently * running, does nothing. */ public void start() { if (!started) { turnStack.addRequestListener(this); started = true; } } /** * Stops this <tt>AllocationRequestListenerr</tt>. A stopped * <tt>AllocationRequestListenerr</tt> can be restarted by calling * {@link #start()} on it. */ public void stop() { turnStack.removeRequestListener(this); started = false; } }