/** * Performs strict router fix according to RFC3261 section 16.6 step 6 * * <p>pre: top route header in request has no 'lr' parameter in URI post: request-URI added as * last route header, new req-URI = top-route-URI */ public void fixStrictRouting(SIPRequest req) { RouteList routes = req.getRouteHeaders(); Route first = (Route) routes.getFirst(); SipUri firstUri = (SipUri) first.getAddress().getURI(); routes.removeFirst(); // Add request-URI as last Route entry AddressImpl addr = new AddressImpl(); addr.setAddess(req.getRequestURI()); // don't clone it Route route = new Route(addr); routes.add(route); // as last one req.setRequestURI(firstUri); if (sipStack.isLoggingEnabled()) { sipStack.getStackLogger().logDebug("post: fixStrictRouting" + req); } }
/* * (non-Javadoc) * * @see gov.nist.javax.sip.clientauthutils.AuthenticationHelper#attachAuthenticationHeaders(javax.sip.message.Request) */ public void setAuthenticationHeaders(Request request) { SIPRequest sipRequest = (SIPRequest) request; String callId = sipRequest.getCallId().getCallId(); request.removeHeader(AuthorizationHeader.NAME); Collection<AuthorizationHeader> authHeaders = this.cachedCredentials.getCachedAuthorizationHeaders(callId); if (authHeaders == null) { sipStack.getStackLogger().logDebug("Could not find authentication headers for " + callId); return; } for (AuthorizationHeader authHeader : authHeaders) { request.addHeader(authHeader); } }
/** * Return addresses for default proxy to forward the request to. The list is organized in the * following priority. If the requestURI refers directly to a host, the host and port information * are extracted from it and made the next hop on the list. If the default route has been * specified, then it is used to construct the next element of the list. <code> * RouteHeader firstRoute = (RouteHeader) req.getHeader( RouteHeader.NAME ); * if (firstRoute!=null) { * URI uri = firstRoute.getAddress().getURI(); * if (uri.isSIPUri()) { * SipURI nextHop = (SipURI) uri; * if ( nextHop.hasLrParam() ) { * // OK, use it * } else { * nextHop = fixStrictRouting( req ); <--- Here, make the modifications as per RFC3261 * } * } else { * // error: non-SIP URI not allowed in Route headers * throw new SipException( "Request has Route header with non-SIP URI" ); * } * } else if (outboundProxy!=null) { * // use outbound proxy for nextHop * } else if ( req.getRequestURI().isSipURI() ) { * // use request URI for nextHop * } * * </code> * * @param request is the sip request to route. */ public Hop getNextHop(Request request) throws SipException { SIPRequest sipRequest = (SIPRequest) request; RequestLine requestLine = sipRequest.getRequestLine(); if (requestLine == null) { return defaultRoute; } javax.sip.address.URI requestURI = requestLine.getUri(); if (requestURI == null) throw new IllegalArgumentException("Bad message: Null requestURI"); RouteList routes = sipRequest.getRouteHeaders(); /* * In case the topmost Route header contains no 'lr' parameter (which * means the next hop is a strict router), the implementation will * perform 'Route Information Postprocessing' as described in RFC3261 * section 16.6 step 6 (also known as "Route header popping"). That is, * the following modifications will be made to the request: * * The implementation places the Request-URI into the Route header field * as the last value. * * The implementation then places the first Route header field value * into the Request-URI and removes that value from the Route header * field. * * Subsequently, the request URI will be used as next hop target */ if (routes != null) { // to send the request through a specified hop the application is // supposed to prepend the appropriate Route header which. Route route = (Route) routes.getFirst(); URI uri = route.getAddress().getURI(); if (uri.isSipURI()) { SipURI sipUri = (SipURI) uri; if (!sipUri.hasLrParam()) { fixStrictRouting(sipRequest); if (sipStack.isLoggingEnabled()) sipStack.getStackLogger().logDebug("Route post processing fixed strict routing"); } Hop hop = createHop(sipUri, request); if (sipStack.isLoggingEnabled()) sipStack.getStackLogger().logDebug("NextHop based on Route:" + hop); return hop; } else { throw new SipException("First Route not a SIP URI"); } } else if (requestURI.isSipURI() && ((SipURI) requestURI).getMAddrParam() != null) { Hop hop = createHop((SipURI) requestURI, request); if (sipStack.isLoggingEnabled()) sipStack .getStackLogger() .logDebug("Using request URI maddr to route the request = " + hop.toString()); // JvB: don't remove it! // ((SipURI) requestURI).removeParameter("maddr"); return hop; } else if (defaultRoute != null) { if (sipStack.isLoggingEnabled()) sipStack .getStackLogger() .logDebug("Using outbound proxy to route the request = " + defaultRoute.toString()); return defaultRoute; } else if (requestURI.isSipURI()) { Hop hop = createHop((SipURI) requestURI, request); if (hop != null && sipStack.isLoggingEnabled()) sipStack.getStackLogger().logDebug("Used request-URI for nextHop = " + hop.toString()); else if (sipStack.isLoggingEnabled()) { sipStack.getStackLogger().logDebug("returning null hop -- loop detected"); } return hop; } else { // The internal router should never be consulted for non-sip URIs. InternalErrorHandler.handleException( "Unexpected non-sip URI", this.sipStack.getStackLogger()); return null; } }
/** * Send an array of bytes. * * @param receiverAddress -- inet address * @param contactPort -- port to connect to. * @param transport -- tcp or udp. * @param retry -- retry to connect if the other end closed connection * @throws IOException -- if there is an IO exception sending message. */ public Socket sendBytes( InetAddress senderAddress, InetAddress receiverAddress, int contactPort, String transport, byte[] bytes, boolean retry) throws IOException { int retry_count = 0; int max_retry = retry ? 2 : 1; // Server uses TCP transport. TCP client sockets are cached int length = bytes.length; if (sipStack.isLoggingEnabled()) { sipStack.logWriter.logDebug( "sendBytes " + transport + " inAddr " + receiverAddress.getHostAddress() + " port = " + contactPort + " length = " + length); } if (transport.compareToIgnoreCase(TCP) == 0) { String key = makeKey(receiverAddress, contactPort); // This should be in a synchronized block ( reported by // Jayashenkhar ( lucent ). try { boolean retval = this.ioSemaphore.tryAcquire( 10000, TimeUnit.MILLISECONDS); // TODO - make this a stack config parameter? if (!retval) { throw new IOException("Could not acquire IO Semaphore after 10 second -- giving up "); } } catch (InterruptedException ex) { throw new IOException("exception in aquiring sem"); } Socket clientSock = getSocket(key); try { while (retry_count < max_retry) { if (clientSock == null) { if (sipStack.isLoggingEnabled()) { sipStack.logWriter.logDebug("inaddr = " + receiverAddress); sipStack.logWriter.logDebug("port = " + contactPort); } // note that the IP Address for stack may not be // assigned. // sender address is the address of the listening point. // in version 1.1 all listening points have the same IP // address (i.e. that of the stack). In version 1.2 // the IP address is on a per listening point basis. clientSock = sipStack .getNetworkLayer() .createSocket(receiverAddress, contactPort, senderAddress); OutputStream outputStream = clientSock.getOutputStream(); writeChunks(outputStream, bytes, length); putSocket(key, clientSock); break; } else { try { OutputStream outputStream = clientSock.getOutputStream(); writeChunks(outputStream, bytes, length); break; } catch (IOException ex) { if (sipStack.isLoggingEnabled()) sipStack.logWriter.logException(ex); // old connection is bad. // remove from our table. removeSocket(key); try { clientSock.close(); } catch (Exception e) { } clientSock = null; retry_count++; } } } } finally { ioSemaphore.release(); } if (clientSock == null) { throw new IOException("Could not connect to " + receiverAddress + ":" + contactPort); } else return clientSock; // Added by Daniel J. Martinez Manzano <*****@*****.**> // Copied and modified from the former section for TCP } else if (transport.compareToIgnoreCase(TLS) == 0) { String key = makeKey(receiverAddress, contactPort); try { boolean retval = this.ioSemaphore.tryAcquire(10000, TimeUnit.MILLISECONDS); if (!retval) throw new IOException("Timeout aquiring IO SEM"); } catch (InterruptedException ex) { throw new IOException("exception in aquiring sem"); } Socket clientSock = getSocket(key); try { while (retry_count < max_retry) { if (clientSock == null) { if (sipStack.isLoggingEnabled()) { sipStack.logWriter.logDebug("inaddr = " + receiverAddress); sipStack.logWriter.logDebug("port = " + contactPort); } if (!sipStack.useTlsAccelerator) { clientSock = sipStack .getNetworkLayer() .createSSLSocket(receiverAddress, contactPort, senderAddress); } else { clientSock = sipStack .getNetworkLayer() .createSocket(receiverAddress, contactPort, senderAddress); } OutputStream outputStream = clientSock.getOutputStream(); writeChunks(outputStream, bytes, length); putSocket(key, clientSock); break; } else { try { OutputStream outputStream = clientSock.getOutputStream(); writeChunks(outputStream, bytes, length); break; } catch (IOException ex) { if (sipStack.isLoggingEnabled()) sipStack.logWriter.logException(ex); // old connection is bad. // remove from our table. removeSocket(key); try { clientSock.close(); } catch (Exception e) { } clientSock = null; retry_count++; } } } } finally { ioSemaphore.release(); } if (clientSock == null) { throw new IOException("Could not connect to " + receiverAddress + ":" + contactPort); } else return clientSock; } else { // This is a UDP transport... DatagramSocket datagramSock = sipStack.getNetworkLayer().createDatagramSocket(); datagramSock.connect(receiverAddress, contactPort); DatagramPacket dgPacket = new DatagramPacket(bytes, 0, length, receiverAddress, contactPort); datagramSock.send(dgPacket); datagramSock.close(); return null; } }
/** * Generates an authorisation header in response to wwwAuthHeader. * * @param method method of the request being authenticated * @param uri digest-uri * @param requestBody the body of the request. * @param authHeader the challenge that we should respond to * @param userCredentials username and pass * @return an authorisation header in response to authHeader. * @throws OperationFailedException if auth header was malformated. */ private AuthorizationHeader getAuthorization( String method, String uri, String requestBody, WWWAuthenticateHeader authHeader, UserCredentialHash userCredentials) { String response = null; // JvB: authHeader.getQop() is a quoted _list_ of qop values // (e.g. "auth,auth-int") Client is supposed to pick one String qopList = authHeader.getQop(); String qop = (qopList != null) ? "auth" : null; String nc_value = "00000001"; String cnonce = "xyz"; response = MessageDigestAlgorithm.calculateResponse( authHeader.getAlgorithm(), userCredentials.getHashUserDomainPassword(), authHeader.getNonce(), nc_value, // JvB added cnonce, // JvB added method, uri, requestBody, qop, sipStack.getStackLogger()); // jvb changed AuthorizationHeader authorization = null; try { if (authHeader instanceof ProxyAuthenticateHeader) { authorization = headerFactory.createProxyAuthorizationHeader(authHeader.getScheme()); } else { authorization = headerFactory.createAuthorizationHeader(authHeader.getScheme()); } authorization.setUsername(userCredentials.getUserName()); authorization.setRealm(authHeader.getRealm()); authorization.setNonce(authHeader.getNonce()); authorization.setParameter("uri", uri); authorization.setResponse(response); if (authHeader.getAlgorithm() != null) { authorization.setAlgorithm(authHeader.getAlgorithm()); } if (authHeader.getOpaque() != null) { authorization.setOpaque(authHeader.getOpaque()); } // jvb added if (qop != null) { authorization.setQop(qop); authorization.setCNonce(cnonce); authorization.setNonceCount(Integer.parseInt(nc_value)); } authorization.setResponse(response); } catch (ParseException ex) { throw new RuntimeException("Failed to create an authorization header!"); } return authorization; }
/* * (non-Javadoc) * * @see gov.nist.javax.sip.clientauthutils.AuthenticationHelper#handleChallenge(javax.sip.message.Response, * javax.sip.ClientTransaction, javax.sip.SipProvider) */ public ClientTransaction handleChallenge( Response challenge, ClientTransaction challengedTransaction, SipProvider transactionCreator, int cacheTime) throws SipException, NullPointerException { try { if (sipStack.isLoggingEnabled()) { sipStack.getStackLogger().logDebug("handleChallenge: " + challenge); } SIPRequest challengedRequest = ((SIPRequest) challengedTransaction.getRequest()); Request reoriginatedRequest = null; /* * If the challenged request is part of a Dialog and the * Dialog is confirmed the re-originated request should be * generated as an in-Dialog request. */ if (challengedRequest.getToTag() != null || challengedTransaction.getDialog() == null || challengedTransaction.getDialog().getState() != DialogState.CONFIRMED) { reoriginatedRequest = (Request) challengedRequest.clone(); } else { /* * Re-originate the request by consulting the dialog. In particular * the route set could change between the original request and the * in-dialog challenge. */ reoriginatedRequest = challengedTransaction.getDialog().createRequest(challengedRequest.getMethod()); Iterator<String> headerNames = challengedRequest.getHeaderNames(); while (headerNames.hasNext()) { String headerName = headerNames.next(); if (reoriginatedRequest.getHeader(headerName) != null) { ListIterator<Header> iterator = reoriginatedRequest.getHeaders(headerName); while (iterator.hasNext()) { reoriginatedRequest.addHeader(iterator.next()); } } } } // remove the branch id so that we could use the request in a new // transaction removeBranchID(reoriginatedRequest); if (challenge == null || reoriginatedRequest == null) { throw new NullPointerException("A null argument was passed to handle challenge."); } ListIterator authHeaders = null; if (challenge.getStatusCode() == Response.UNAUTHORIZED) { authHeaders = challenge.getHeaders(WWWAuthenticateHeader.NAME); } else if (challenge.getStatusCode() == Response.PROXY_AUTHENTICATION_REQUIRED) { authHeaders = challenge.getHeaders(ProxyAuthenticateHeader.NAME); } else { throw new IllegalArgumentException("Unexpected status code "); } if (authHeaders == null) { throw new IllegalArgumentException( "Could not find WWWAuthenticate or ProxyAuthenticate headers"); } // Remove all authorization headers from the request (we'll re-add them // from cache) reoriginatedRequest.removeHeader(AuthorizationHeader.NAME); reoriginatedRequest.removeHeader(ProxyAuthorizationHeader.NAME); // rfc 3261 says that the cseq header should be augmented for the new // request. do it here so that the new dialog (created together with // the new client transaction) takes it into account. // Bug report - Fredrik Wickstrom CSeqHeader cSeq = (CSeqHeader) reoriginatedRequest.getHeader((CSeqHeader.NAME)); try { cSeq.setSeqNumber(cSeq.getSeqNumber() + 1l); } catch (InvalidArgumentException ex) { throw new SipException("Invalid CSeq -- could not increment : " + cSeq.getSeqNumber()); } /* Resolve this to the next hop based on the previous lookup. If we are not using * lose routing (RFC2543) then just attach hop as a maddr param. */ if (challengedRequest.getRouteHeaders() == null) { Hop hop = ((SIPClientTransaction) challengedTransaction).getNextHop(); SipURI sipUri = (SipURI) reoriginatedRequest.getRequestURI(); sipUri.setMAddrParam(hop.getHost()); if (hop.getPort() != -1) sipUri.setPort(hop.getPort()); } ClientTransaction retryTran = transactionCreator.getNewClientTransaction(reoriginatedRequest); WWWAuthenticateHeader authHeader = null; SipURI requestUri = (SipURI) challengedTransaction.getRequest().getRequestURI(); while (authHeaders.hasNext()) { authHeader = (WWWAuthenticateHeader) authHeaders.next(); String realm = authHeader.getRealm(); AuthorizationHeader authorization = null; String sipDomain; if (this.accountManager instanceof SecureAccountManager) { UserCredentialHash credHash = ((SecureAccountManager) this.accountManager) .getCredentialHash(challengedTransaction, realm); URI uri = reoriginatedRequest.getRequestURI(); sipDomain = credHash.getSipDomain(); authorization = this.getAuthorization( reoriginatedRequest.getMethod(), uri.toString(), (reoriginatedRequest.getContent() == null) ? "" : new String(reoriginatedRequest.getRawContent()), authHeader, credHash); } else { UserCredentials userCreds = ((AccountManager) this.accountManager).getCredentials(challengedTransaction, realm); sipDomain = userCreds.getSipDomain(); if (userCreds == null) throw new SipException("Cannot find user creds for the given user name and realm"); // we haven't yet authenticated this realm since we were // started. authorization = this.getAuthorization( reoriginatedRequest.getMethod(), reoriginatedRequest.getRequestURI().toString(), (reoriginatedRequest.getContent() == null) ? "" : new String(reoriginatedRequest.getRawContent()), authHeader, userCreds); } sipStack .getStackLogger() .logDebug("Created authorization header: " + authorization.toString()); if (cacheTime != 0) cachedCredentials.cacheAuthorizationHeader(sipDomain, authorization, cacheTime); reoriginatedRequest.addHeader(authorization); } if (sipStack.isLoggingEnabled()) { sipStack.getStackLogger().logDebug("Returning authorization transaction." + retryTran); } return retryTran; } catch (SipException ex) { throw ex; } catch (Exception ex) { sipStack.getStackLogger().logError("Unexpected exception ", ex); throw new SipException("Unexpected exception ", ex); } }