/** * Creates a default SIPResponse message for this request. Note You must add the necessary tags to * outgoing responses if need be. For efficiency, this method does not clone the incoming request. * If you want to modify the outgoing response, be sure to clone the incoming request as the * headers are shared and any modification to the headers of the outgoing response will result in * a modification of the incoming request. Tag fields are just copied from the incoming request. * Contact headers are removed from the incoming request. Added by Jeff Keyser. Route headers are * not added to the response. * * @param statusCode Status code for the response. * @param reasonPhrase Reason phrase for this response. * @return A SIPResponse with the status and reason supplied, and a copy of all the original * headers from this request except the ones that are not supposed to be part of the response * . */ public SIPResponse createResponse(int statusCode, String reasonPhrase) { SIPResponse newResponse; Iterator headerIterator; SIPHeader nextHeader; newResponse = new SIPResponse(); try { newResponse.setStatusCode(statusCode); } catch (ParseException ex) { throw new IllegalArgumentException("Bad code " + statusCode); } if (reasonPhrase != null) newResponse.setReasonPhrase(reasonPhrase); else newResponse.setReasonPhrase(SIPResponse.getReasonPhrase(statusCode)); headerIterator = getHeaders(); while (headerIterator.hasNext()) { nextHeader = (SIPHeader) headerIterator.next(); if (nextHeader instanceof From || nextHeader instanceof To || nextHeader instanceof ViaList || nextHeader instanceof CallID || (nextHeader instanceof RecordRouteList && mustCopyRR(statusCode)) || nextHeader instanceof CSeq // We just copy TimeStamp for all headers (not just 100). || nextHeader instanceof TimeStamp) { try { newResponse.attachHeader((SIPHeader) nextHeader.clone(), false); } catch (SIPDuplicateHeaderException e) { e.printStackTrace(); } } } if (MessageFactoryImpl.getDefaultServerHeader() != null) { newResponse.setHeader(MessageFactoryImpl.getDefaultServerHeader()); } if (newResponse.getStatusCode() == 100) { // Trying is never supposed to have the tag parameter set. newResponse.getTo().removeParameter("tag"); } ServerHeader server = MessageFactoryImpl.getDefaultServerHeader(); if (server != null) { newResponse.setHeader(server); } return newResponse; }
/** * Gets invoked by the parser as a callback on successful message parsing (i.e. no parser errors). * * @param sipMessage Message to process (this calls the application for processing the message). * <p>Jvb: note that this code is identical to TCPMessageChannel, refactor some day */ public void processMessage(SIPMessage sipMessage) throws Exception { try { if (sipMessage.getFrom() == null || sipMessage.getTo() == null || sipMessage.getCallId() == null || sipMessage.getCSeq() == null || sipMessage.getViaHeaders() == null) { if (logger.isLoggingEnabled()) { String badmsg = sipMessage.encode(); logger.logError("bad message " + badmsg); logger.logError(">>> Dropped Bad Msg"); } return; } sipMessage.setRemoteAddress(this.peerAddress); sipMessage.setRemotePort(this.getPeerPort()); sipMessage.setLocalAddress(this.getMessageProcessor().getIpAddress()); sipMessage.setLocalPort(this.getPort()); // Issue 3: https://telestax.atlassian.net/browse/JSIP-3 sipMessage.setPeerPacketSourceAddress(this.peerAddress); sipMessage.setPeerPacketSourcePort(this.peerPort); ViaList viaList = sipMessage.getViaHeaders(); // For a request // first via header tells where the message is coming from. // For response, this has already been recorded in the outgoing // message. if (sipMessage instanceof SIPRequest) { Via v = (Via) viaList.getFirst(); // the peer address and tag it appropriately. Hop hop = sipStack.addressResolver.resolveAddress(v.getHop()); this.peerProtocol = v.getTransport(); // if(peerPortAdvertisedInHeaders <= 0) { int hopPort = v.getPort(); if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { logger.logDebug( "hop port = " + hopPort + " for request " + sipMessage + " for this channel " + this + " key " + key); } if (hopPort <= 0) { // if port is 0 we assume the default port for TCP this.peerPortAdvertisedInHeaders = 5060; } else { this.peerPortAdvertisedInHeaders = hopPort; } if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { logger.logDebug( "3.Storing peerPortAdvertisedInHeaders = " + peerPortAdvertisedInHeaders + " for this channel " + this + " key " + key); } // } // may be needed to reconnect, when diff than peer address if (peerAddressAdvertisedInHeaders == null) { peerAddressAdvertisedInHeaders = hop.getHost(); if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { logger.logDebug( "3.Storing peerAddressAdvertisedInHeaders = " + peerAddressAdvertisedInHeaders + " for this channel " + this + " key " + key); } } try { if (mySock != null) { // selfrouting makes socket = null // https://jain-sip.dev.java.net/issues/show_bug.cgi?id=297 this.peerAddress = mySock.getInetAddress(); } // Check to see if the received parameter matches // the peer address and tag it appropriately. // JvB: dont do this. It is both costly and incorrect // Must set received also when it is a FQDN, regardless // whether // it resolves to the correct IP address // InetAddress sentByAddress = // InetAddress.getByName(hop.getHost()); // JvB: if sender added 'rport', must always set received boolean hasRPort = v.hasParameter(Via.RPORT); if (!hasRPort && v.getPort() != peerPort) { // https://github.com/RestComm/jain-sip/issues/79 if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { logger.logDebug( "setting rport since viaPort " + v.getPort() + " different than peerPacketSourcePort " + peerPort + " so that the response can be routed back"); } hasRPort = true; } if (hasRPort || !hop.getHost().equals(this.peerAddress.getHostAddress())) { v.setParameter(Via.RECEIVED, this.peerAddress.getHostAddress()); } // @@@ hagai // JvB: technically, may only do this when Via already // contains // rport v.setParameter(Via.RPORT, Integer.toString(this.peerPort)); } catch (java.text.ParseException ex) { InternalErrorHandler.handleException(ex); } // Use this for outgoing messages as well. if (!this.isCached && mySock != null) { // self routing makes // mySock=null // https://jain-sip.dev.java.net/issues/show_bug.cgi?id=297 this.isCached = true; int remotePort = ((java.net.InetSocketAddress) mySock.getRemoteSocketAddress()).getPort(); String key = IOHandler.makeKey(mySock.getInetAddress(), remotePort); if (this.messageProcessor instanceof NioTcpMessageProcessor) { // https://java.net/jira/browse/JSIP-475 don't use iohandler in case of NIO // communications of the socket will leak in the iohandler sockettable ((NioTcpMessageProcessor) this.messageProcessor) .nioHandler.putSocket(key, mySock.getChannel()); } else { sipStack.ioHandler.putSocket(key, mySock); } // since it can close the socket it needs to be after the mySock usage otherwise // it the socket will be disconnected and NPE will be thrown in some edge cases ((ConnectionOrientedMessageProcessor) this.messageProcessor).cacheMessageChannel(this); } } // Foreach part of the request header, fetch it and process it long receptionTime = System.currentTimeMillis(); // if (sipMessage instanceof SIPRequest) { // This is a request - process the request. SIPRequest sipRequest = (SIPRequest) sipMessage; // Create a new sever side request processor for this // message and let it handle the rest. if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG)) { logger.logDebug("----Processing Message---"); } if (logger.isLoggingEnabled(ServerLogger.TRACE_MESSAGES)) { sipStack.serverLogger.logMessage( sipMessage, this.getPeerHostPort().toString(), this.messageProcessor.getIpAddress().getHostAddress() + ":" + this.messageProcessor.getPort(), false, receptionTime); } // Check for reasonable size - reject message // if it is too long. if (sipStack.getMaxMessageSize() > 0 && sipRequest.getSize() + (sipRequest.getContentLength() == null ? 0 : sipRequest.getContentLength().getContentLength()) > sipStack.getMaxMessageSize()) { SIPResponse sipResponse = sipRequest.createResponse(SIPResponse.MESSAGE_TOO_LARGE); byte[] resp = sipResponse.encodeAsBytes(this.getTransport()); this.sendMessage(resp, false); throw new Exception("Message size exceeded"); } String sipVersion = ((SIPRequest) sipMessage).getRequestLine().getSipVersion(); if (!sipVersion.equals("SIP/2.0")) { SIPResponse versionNotSupported = ((SIPRequest) sipMessage) .createResponse(Response.VERSION_NOT_SUPPORTED, "Bad SIP version " + sipVersion); this.sendMessage(versionNotSupported.encodeAsBytes(this.getTransport()), false); throw new Exception("Bad version "); } String method = ((SIPRequest) sipMessage).getMethod(); String cseqMethod = ((SIPRequest) sipMessage).getCSeqHeader().getMethod(); if (!method.equalsIgnoreCase(cseqMethod)) { SIPResponse sipResponse = sipRequest.createResponse(SIPResponse.BAD_REQUEST); byte[] resp = sipResponse.encodeAsBytes(this.getTransport()); this.sendMessage(resp, false); throw new Exception("Bad CSeq method" + sipMessage + " method " + method); } // Stack could not create a new server request interface. // maybe not enough resources. ServerRequestInterface sipServerRequest = sipStack.newSIPServerRequest(sipRequest, this); if (sipServerRequest != null) { try { sipServerRequest.processRequest(sipRequest, this); } finally { if (sipServerRequest instanceof SIPTransaction) { SIPServerTransaction sipServerTx = (SIPServerTransaction) sipServerRequest; if (!sipServerTx.passToListener()) ((SIPTransaction) sipServerRequest).releaseSem(); } } } else { if (sipStack.sipMessageValve == null) { // Allow message valves to nullify messages without error SIPResponse response = sipRequest.createResponse(Response.SERVICE_UNAVAILABLE); RetryAfter retryAfter = new RetryAfter(); // Be a good citizen and send a decent response code back. try { retryAfter.setRetryAfter((int) (10 * (Math.random()))); response.setHeader(retryAfter); this.sendMessage(response); } catch (Exception e) { // IGNore } if (logger.isLoggingEnabled()) logger.logWarning("Dropping message -- could not acquire semaphore"); } } } else { SIPResponse sipResponse = (SIPResponse) sipMessage; // JvB: dont do this // if (sipResponse.getStatusCode() == 100) // sipResponse.getTo().removeParameter("tag"); try { sipResponse.checkHeaders(); } catch (ParseException ex) { if (logger.isLoggingEnabled()) logger.logError("Dropping Badly formatted response message >>> " + sipResponse); return; } // This is a response message - process it. // Check the size of the response. // If it is too large dump it silently. if (sipStack.getMaxMessageSize() > 0 && sipResponse.getSize() + (sipResponse.getContentLength() == null ? 0 : sipResponse.getContentLength().getContentLength()) > sipStack.getMaxMessageSize()) { if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG)) logger.logDebug("Message size exceeded"); return; } ServerResponseInterface sipServerResponse = sipStack.newSIPServerResponse(sipResponse, this); if (sipServerResponse != null) { try { if (sipServerResponse instanceof SIPClientTransaction && !((SIPClientTransaction) sipServerResponse).checkFromTag(sipResponse)) { if (logger.isLoggingEnabled()) logger.logError("Dropping response message with invalid tag >>> " + sipResponse); return; } sipServerResponse.processResponse(sipResponse, this); } finally { if (sipServerResponse instanceof SIPTransaction && !((SIPTransaction) sipServerResponse).passToListener()) { // Note that the semaphore is released in event // scanner if the // request is actually processed by the Listener. ((SIPTransaction) sipServerResponse).releaseSem(); } } } else { logger.logWarning( "Application is blocked -- could not acquire semaphore -- dropping response"); } } } finally { } }