From da7a9769953d75d14f38dbf48c49893e82a3c6cb Mon Sep 17 00:00:00 2001 From: Damian Minkov Date: Tue, 6 Mar 2012 07:14:14 +0000 Subject: [PATCH] Adds security timeout event, triggered whenever we tried and couldn't establish security connection (zrtp sends initial hello packets and don't receive response). Adds initial implementation of paranoia mode, if enabled and the other party didn't indicate encryption we fail the call. --- .../impl/gui/main/call/CallManager.java | 14 +++- .../impl/gui/main/call/CallPeerAdapter.java | 21 +++++ .../impl/gui/main/call/CallPeerRenderer.java | 7 ++ .../gui/main/call/OneToOneCallPeerPanel.java | 10 +++ .../call/conference/ConferenceFocusPanel.java | 10 +++ .../call/conference/ConferencePeerPanel.java | 10 +++ .../transform/zrtp/SecurityEventManager.java | 2 + .../impl/protocol/jabber/CallGTalkImpl.java | 23 ++++++ .../impl/protocol/jabber/CallJabberImpl.java | 22 ++++++ .../jabber/CallPeerMediaHandlerGTalkImpl.java | 30 ++++++++ .../CallPeerMediaHandlerJabberImpl.java | 30 ++++++++ .../sip/CallPeerMediaHandlerSipImpl.java | 21 ++++- .../impl/protocol/sip/CallSipImpl.java | 36 +++++++++ .../OperationSetBasicTelephonySipImpl.java | 76 +++++++++++++++++-- .../sip/ProtocolProviderServiceSipImpl.java | 22 ++++++ .../impl/protocol/sip/sdp/SdpUtils.java | 5 ++ .../NotificationManager.java | 14 +++- .../service/neomedia/event/SrtpListener.java | 8 ++ .../service/protocol/AbstractCallPeer.java | 30 ++++++++ .../protocol/ProtocolProviderFactory.java | 6 ++ .../event/CallPeerSecurityListener.java | 11 +++ .../event/CallPeerSecurityTimeoutEvent.java | 39 ++++++++++ .../protocol/media/CallPeerMediaHandler.java | 30 ++++++++ .../protocol/media/MediaAwareCallPeer.java | 13 ++++ 24 files changed, 481 insertions(+), 9 deletions(-) create mode 100644 src/net/java/sip/communicator/service/protocol/event/CallPeerSecurityTimeoutEvent.java diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java b/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java index 029dc3262..3f917bce6 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java +++ b/src/net/java/sip/communicator/impl/gui/main/call/CallManager.java @@ -91,8 +91,18 @@ public void run() receivedCallDialog.setVisible(true); - final String peerName - = sourceCall.getCallPeers().next().getDisplayName(); + Iterator peerIterator = + sourceCall.getCallPeers(); + + if(!peerIterator.hasNext()) + { + if (receivedCallDialog.isVisible()) + receivedCallDialog.setVisible(false); + + return; + } + + final String peerName = peerIterator.next().getDisplayName(); final Date callDate = new Date(); sourceCall.addCallChangeListener(new CallChangeAdapter() diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallPeerAdapter.java b/src/net/java/sip/communicator/impl/gui/main/call/CallPeerAdapter.java index 371d43cbd..6be7282b4 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/CallPeerAdapter.java +++ b/src/net/java/sip/communicator/impl/gui/main/call/CallPeerAdapter.java @@ -8,8 +8,10 @@ import java.beans.*; +import net.java.sip.communicator.service.neomedia.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; +import net.java.sip.communicator.service.protocol.media.*; /** * The CallPeerAdapter is an adapter that implements all common @@ -188,6 +190,25 @@ public void securityOff(CallPeerSecurityOffEvent securityOffEvent) renderer.securityOff(securityOffEvent); } + /** + * The handler for the security event received. The security event + * represents a timeout trying to establish a secure connection. + * Most probably the other peer doesn't support it. + * + * @param securityTimeoutEvent + * the security timeout event received + */ + public void securityTimeout( + CallPeerSecurityTimeoutEvent securityTimeoutEvent) + { + CallPeer peer = (CallPeer) securityTimeoutEvent.getSource(); + + if (!peer.equals(callPeer)) + return; + + renderer.securityTimeout(securityTimeoutEvent); + } + /** * Adds a ConferenceMemberPanel to this container when a * ConferenceMember has been added to the corresponding conference. diff --git a/src/net/java/sip/communicator/impl/gui/main/call/CallPeerRenderer.java b/src/net/java/sip/communicator/impl/gui/main/call/CallPeerRenderer.java index 365d124ba..d9155368a 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/CallPeerRenderer.java +++ b/src/net/java/sip/communicator/impl/gui/main/call/CallPeerRenderer.java @@ -77,6 +77,13 @@ public interface CallPeerRenderer */ public void securityOff(CallPeerSecurityOffEvent evt); + /** + * Indicates that the security is timeouted, is not supported by the + * other end. + * @param evt Details about the event that caused this message. + */ + public void securityTimeout(CallPeerSecurityTimeoutEvent evt); + /** * Sets the call peer adapter that manages all related listeners. * diff --git a/src/net/java/sip/communicator/impl/gui/main/call/OneToOneCallPeerPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/OneToOneCallPeerPanel.java index a25f7dda4..30821781c 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/OneToOneCallPeerPanel.java +++ b/src/net/java/sip/communicator/impl/gui/main/call/OneToOneCallPeerPanel.java @@ -719,6 +719,16 @@ public void securityOff(CallPeerSecurityOffEvent evt) } } + /** + * Indicates that the security is timeouted, is not supported by the + * other end. + * @param evt Details about the event that caused this message. + */ + public void securityTimeout(CallPeerSecurityTimeoutEvent evt) + { + + } + /** * Sets the call peer adapter managing all related listeners. * @param adapter the adapter to set diff --git a/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceFocusPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceFocusPanel.java index fbc0dcc0d..951df44d4 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceFocusPanel.java +++ b/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferenceFocusPanel.java @@ -341,6 +341,16 @@ public void securityOff(CallPeerSecurityOffEvent evt) } } + /** + * Indicates that the security is timeouted, is not supported by the + * other end. + * @param evt Details about the event that caused this message. + */ + public void securityTimeout(CallPeerSecurityTimeoutEvent evt) + { + + } + /** * Sets the call peer adapter that manages all related listeners. * diff --git a/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferencePeerPanel.java b/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferencePeerPanel.java index 0b032ff40..3e8adc1e9 100644 --- a/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferencePeerPanel.java +++ b/src/net/java/sip/communicator/impl/gui/main/call/conference/ConferencePeerPanel.java @@ -236,6 +236,16 @@ public void securityOff(CallPeerSecurityOffEvent evt) } } + /** + * Indicates that the security is timeouted, is not supported by the + * other end. + * @param evt Details about the event that caused this message. + */ + public void securityTimeout(CallPeerSecurityTimeoutEvent evt) + { + + } + /** * Sets the mute status icon to the status panel. * diff --git a/src/net/java/sip/communicator/impl/neomedia/transform/zrtp/SecurityEventManager.java b/src/net/java/sip/communicator/impl/neomedia/transform/zrtp/SecurityEventManager.java index e965546e1..b54a1d31e 100644 --- a/src/net/java/sip/communicator/impl/neomedia/transform/zrtp/SecurityEventManager.java +++ b/src/net/java/sip/communicator/impl/neomedia/transform/zrtp/SecurityEventManager.java @@ -350,6 +350,8 @@ public void zrtpNotSuppOther() logger.debug(sessionTypeToString(sessionType) + ": Other party does not support ZRTP key negotiation protocol," + " no secure calls possible."); + + securityListener.securityTimeout(sessionType); } /** diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallGTalkImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallGTalkImpl.java index 1d9a56230..2a2bf02f3 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/CallGTalkImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallGTalkImpl.java @@ -11,6 +11,7 @@ import org.jivesoftware.smack.packet.*; import net.java.sip.communicator.impl.protocol.jabber.extensions.gtalk.*; +import net.java.sip.communicator.impl.protocol.jabber.extensions.jingle.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.service.protocol.media.*; @@ -127,6 +128,28 @@ public CallPeerGTalkImpl processGTalkInitiate(SessionIQ sessionIQ) //before notifying about this call, make sure that it looks alright callPeer.processSessionInitiate(sessionIQ); + // if paranoia is set, to accept the call we need to know that + // the other party has support for media encryption + if(getProtocolProvider().getAccountID().getAccountPropertyBoolean( + ProtocolProviderFactory.MODE_PARANOIA, false) + && callPeer.getMediaHandler().getAdvertisedEncryptionMethods().length + == 0) + { + //send an error response; + String reasonText = "Encryption required!"; + SessionIQ errResp = GTalkPacketFactory.createSessionTerminate( + sessionIQ.getTo(), + sessionIQ.getFrom(), + sessionIQ.getID(), + Reason.SECURITY_ERROR, + reasonText); + + callPeer.setState(CallPeerState.FAILED, reasonText); + getProtocolProvider().getConnection().sendPacket(errResp); + + return null; + } + if( callPeer.getState() == CallPeerState.FAILED) return null; diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java index 8aaf171dd..a08174797 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallJabberImpl.java @@ -184,6 +184,28 @@ public CallPeerJabberImpl processSessionInitiate(JingleIQ jingleIQ) //before notifying about this call, make sure that it looks alright callPeer.processSessionInitiate(jingleIQ); + // if paranoia is set, to accept the call we need to know that + // the other party has support for media encryption + if(getProtocolProvider().getAccountID().getAccountPropertyBoolean( + ProtocolProviderFactory.MODE_PARANOIA, false) + && callPeer.getMediaHandler().getAdvertisedEncryptionMethods().length + == 0) + { + //send an error response; + String reasonText = "Encryption required!"; + JingleIQ errResp = JinglePacketFactory.createSessionTerminate( + jingleIQ.getTo(), + jingleIQ.getFrom(), + jingleIQ.getSID(), + Reason.SECURITY_ERROR, + reasonText); + + callPeer.setState(CallPeerState.FAILED, reasonText); + getProtocolProvider().getConnection().sendPacket(errResp); + + return null; + } + if( callPeer.getState() == CallPeerState.FAILED) return null; diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerGTalkImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerGTalkImpl.java index 2e2907e7d..a3ddcf50d 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerGTalkImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerGTalkImpl.java @@ -148,6 +148,21 @@ else if(ext.getNamespace().equals( } } + EncryptionPacketExtension encryptionPacketExtension + = offer.getFirstChildOfType(EncryptionPacketExtension.class); + if(encryptionPacketExtension != null) + { + ZrtpHashPacketExtension zrtpHashPacketExtension = + encryptionPacketExtension.getFirstChildOfType( + ZrtpHashPacketExtension.class); + + if(zrtpHashPacketExtension != null + && zrtpHashPacketExtension.getValue() != null) + { + addAdvertisedEncryptionMethod(SrtpControlType.ZRTP); + } + } + for(MediaType mediaType : MediaType.values()) { if(!(isAudio && mediaType == MediaType.AUDIO) && @@ -339,6 +354,21 @@ public void processAnswer(RtpDescriptionPacketExtension answer) { List lst = answer.getPayloadTypes(); + EncryptionPacketExtension encryptionPacketExtension + = answer.getFirstChildOfType(EncryptionPacketExtension.class); + if(encryptionPacketExtension != null) + { + ZrtpHashPacketExtension zrtpHashPacketExtension = + encryptionPacketExtension.getFirstChildOfType( + ZrtpHashPacketExtension.class); + + if(zrtpHashPacketExtension != null + && zrtpHashPacketExtension.getValue() != null) + { + addAdvertisedEncryptionMethod(SrtpControlType.ZRTP); + } + } + boolean masterStreamSet = true; for(MediaType mediaType : MediaType.values()) { diff --git a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java index f4ac19eb6..d510e7aa6 100644 --- a/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/jabber/CallPeerMediaHandlerJabberImpl.java @@ -398,6 +398,21 @@ public void processOffer(List offer) localContentMap.put(content.getName(), ourContent); atLeastOneValidDescription = true; + + EncryptionPacketExtension encryptionPacketExtension + = description.getFirstChildOfType(EncryptionPacketExtension.class); + if(encryptionPacketExtension != null) + { + ZrtpHashPacketExtension zrtpHashPacketExtension = + encryptionPacketExtension.getFirstChildOfType( + ZrtpHashPacketExtension.class); + + if(zrtpHashPacketExtension != null + && zrtpHashPacketExtension.getValue() != null) + { + addAdvertisedEncryptionMethod(SrtpControlType.ZRTP); + } + } } if (!atLeastOneValidDescription) @@ -1178,6 +1193,21 @@ private void processContent(ContentPacketExtension content, boolean modify, } } + EncryptionPacketExtension encryptionPacketExtension + = description.getFirstChildOfType(EncryptionPacketExtension.class); + if(encryptionPacketExtension != null) + { + ZrtpHashPacketExtension zrtpHashPacketExtension = + encryptionPacketExtension.getFirstChildOfType( + ZrtpHashPacketExtension.class); + + if(zrtpHashPacketExtension != null + && zrtpHashPacketExtension.getValue() != null) + { + addAdvertisedEncryptionMethod(SrtpControlType.ZRTP); + } + } + // create the corresponding stream... initStream(content.getName(), connector, dev, supportedFormats.get(0), target, direction, rtpExtensions, diff --git a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandlerSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandlerSipImpl.java index 8da7ef854..3fa3a370d 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandlerSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/CallPeerMediaHandlerSipImpl.java @@ -665,7 +665,7 @@ && getPeer().getCall().isSipZrtpAttribute()) String helloHash = zcontrol.getHelloHash(); if(helloHash != null && helloHash.length() > 0) - md.setAttribute("zrtp-hash", helloHash); + md.setAttribute(SdpUtils.ZRTP_HASH_ATTR, helloHash); } catch (SdpException ex) @@ -870,6 +870,8 @@ private synchronized void processAnswer(SessionDescription answer) this.setCallInfoURL(SdpUtils.getCallInfoURL(answer)); boolean masterStreamSet = false; + boolean hasZrtp = false; + boolean hasSdes = false; List seenMediaTypes = new ArrayList(); for (MediaDescription mediaDescription : remoteDescriptions) { @@ -1012,6 +1014,8 @@ private synchronized void processAnswer(SessionDescription answer) it.remove(); } } + + hasSdes = true; } } @@ -1034,10 +1038,25 @@ private synchronized void processAnswer(SessionDescription answer) } } + try + { + hasZrtp = mediaDescription.getAttribute( + SdpUtils.ZRTP_HASH_ATTR) != null; + } + catch (SdpParseException e) + { + logger.error("received an unparsable sdp attribute", e); + } + // create the corresponding stream... initStream(connector, dev, supportedFormats.get(0), target, direction, rtpExtensions, masterStream); } + + if(hasSdes) + addAdvertisedEncryptionMethod(SrtpControlType.SDES); + if(hasZrtp) + addAdvertisedEncryptionMethod(SrtpControlType.ZRTP); } /** diff --git a/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java index a6cb38681..29c919bbf 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/CallSipImpl.java @@ -258,6 +258,8 @@ private CallPeerSipImpl createCallPeerFor( mediaDirections.put(MediaType.AUDIO, MediaDirection.INACTIVE); mediaDirections.put(MediaType.VIDEO, MediaDirection.INACTIVE); + boolean hasZrtp = false; + boolean hasSdes = false; //this check is not mandatory catch all to skip if a problem exists try { @@ -279,6 +281,9 @@ private CallPeerSipImpl createCallPeerFor( MediaType mediaType = SdpUtils.getMediaType(mediaDescription); + hasZrtp = hasZrtp || mediaDescription.getAttribute( + SdpUtils.ZRTP_HASH_ATTR) != null; + if(mediaType.equals(MediaType.VIDEO)) { MediaDirection videoDirection = @@ -293,6 +298,25 @@ else if(mediaType.equals(MediaType.AUDIO)) mediaDirections.put(MediaType.AUDIO, audioDirection); } + + @SuppressWarnings("unchecked") + Vector attrs = + mediaDescription.getAttributes(true); + for (Attribute a : attrs) + { + try + { + if (a.getName().equals("crypto")) + { + hasSdes = true; + } + } + catch (SdpParseException e) + { + logger.error( + "received an unparsable sdp attribute", e); + } + } } } } @@ -309,6 +333,18 @@ else if(mediaType.equals(MediaType.AUDIO)) this, mediaDirections); } + + if(hasZrtp) + { + callPeer.getMediaHandler().addAdvertisedEncryptionMethod( + SrtpControlType.ZRTP); + } + + if(hasSdes) + { + callPeer.getMediaHandler().addAdvertisedEncryptionMethod( + SrtpControlType.SDES); + } } return callPeer; diff --git a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java index 4bff06e2e..cc909dcec 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/OperationSetBasicTelephonySipImpl.java @@ -190,7 +190,7 @@ public Call createCall(Contact callee, CallGroup group) * for use by other OperationSets willing to initialize * Calls and willing to control their establishment in ways * different than {@link #createOutgoingCall(Address, - * javax.sip.message.Message)}. + * javax.sip.message.Message,CallGroup)}. * * @return a new outgoing Call with no peers in it * @throws OperationFailedException if initializing the new outgoing @@ -623,12 +623,29 @@ && isRemoteControlNotification(request)) || (responseStatusCodeRange == 5) || (responseStatusCodeRange == 6)) { - logger.error("Received error: " + response.getStatusCode() - + " " + response.getReasonPhrase()); + String reason = response.getReasonPhrase(); + + WarningHeader warningHeader + = (WarningHeader)response.getHeader(WarningHeader.NAME); + if(warningHeader != null) + { + reason = warningHeader.getText(); + + logger.error("Received error: " + response.getStatusCode() + + " " + response.getReasonPhrase() + + " " + warningHeader.getText() + + "-" + warningHeader.getAgent() + + "-" + warningHeader.getName()); + } + else + { + logger.error("Received error: " + response.getStatusCode() + + " " + response.getReasonPhrase()); + } if (callPeer != null) callPeer.setState(CallPeerState.FAILED, - response.getReasonPhrase()); + reason); processed = true; } @@ -1053,7 +1070,56 @@ private void processInvite(SipProvider sourceProvider, //this is a brand new call (not a transferred one) CallSipImpl call = new CallSipImpl(this); - call.processInvite(sourceProvider, serverTransaction); + MediaAwareCallPeer peer = + call.processInvite(sourceProvider, serverTransaction); + + if(getProtocolProvider().getAccountID() + .getAccountPropertyBoolean( + ProtocolProviderFactory.MODE_PARANOIA, false) + && peer.getMediaHandler() + .getAdvertisedEncryptionMethods().length == 0) + { + // if in paranoia mode and we don't find any encryption + // fail peer/call send error with warning explaining why + + peer.setState( + CallPeerState.FAILED, + "Encryption required!", + Response.SESSION_NOT_ACCEPTABLE); + + // 606 Not acceptable + // warning header : encryption required + WarningHeader warning = null; + try + { + //399 Miscellaneous warning + warning = protocolProvider.getHeaderFactory() + .createWarningHeader( + protocolProvider.getAccountID().getService() + , 399, "Encryption required!"); + } + catch(InvalidArgumentException e) + { + logger.error("Cannot create warning header", e); + } + catch(ParseException e) + { + logger.error("Cannot create warning header", e); + } + + try + { + protocolProvider.sayError(serverTransaction, + Response.SESSION_NOT_ACCEPTABLE, + warning); + } + catch(OperationFailedException e) + { + logger.error("Cannot send 606 error!", e); + } + + return; + } // checks for auto answering of call, if no further processing // is needed return diff --git a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java index a36fb852c..ad1b254cc 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/ProtocolProviderServiceSipImpl.java @@ -1938,6 +1938,25 @@ public void sendAck(ClientTransaction clientTransaction) */ public void sayError(ServerTransaction serverTransaction, int errorCode) throws OperationFailedException + { + sayError(serverTransaction, errorCode, null); + } + + /** + * Send an error response with the errorCode code using + * serverTransaction. + * + * @param serverTransaction the transaction that we'd like to send an error + * response in. + * @param errorCode the code that the response should have. + * + * @throws OperationFailedException if we failed constructing or sending a + * SIP Message. + */ + public void sayError(ServerTransaction serverTransaction, + int errorCode, + Header header) + throws OperationFailedException { Request request = serverTransaction.getRequest(); Response errorResponse = null; @@ -1946,6 +1965,9 @@ public void sayError(ServerTransaction serverTransaction, int errorCode) errorResponse = getMessageFactory().createResponse( errorCode, request); + if(header != null) + errorResponse.setHeader(header); + //we used to be adding a To tag here and we shouldn't. 3261 says: //"Dialogs are created through [...] non-failure responses". and //we are using this method for failure responses only. diff --git a/src/net/java/sip/communicator/impl/protocol/sip/sdp/SdpUtils.java b/src/net/java/sip/communicator/impl/protocol/sip/sdp/SdpUtils.java index 990488a1e..90267431d 100644 --- a/src/net/java/sip/communicator/impl/protocol/sip/sdp/SdpUtils.java +++ b/src/net/java/sip/communicator/impl/protocol/sip/sdp/SdpUtils.java @@ -50,6 +50,11 @@ public class SdpUtils */ private static final String EXTMAP_ATTR = "extmap"; + /** + * The name of the SDP attribute that defines zrtp hello hash. + */ + public static final String ZRTP_HASH_ATTR = "zrtp-hash"; + /** * Parses the specified sdp String into a * SessionDescription and returns it; diff --git a/src/net/java/sip/communicator/plugin/notificationwiring/NotificationManager.java b/src/net/java/sip/communicator/plugin/notificationwiring/NotificationManager.java index 9dfc84b84..2f2f10075 100644 --- a/src/net/java/sip/communicator/plugin/notificationwiring/NotificationManager.java +++ b/src/net/java/sip/communicator/plugin/notificationwiring/NotificationManager.java @@ -670,7 +670,7 @@ public static NotificationData fireNotification(String eventType) /** * Stops all sounds for the given event type. * - * @param notificationData the event type for which we should stop sounds. One of + * @param data the event type for which we should stop sounds. One of * the static event types defined in this class. */ public static void stopSound(NotificationData data) @@ -1200,6 +1200,18 @@ public void securityOn(CallPeerSecurityOnEvent evt) public void securityOff(CallPeerSecurityOffEvent securityOffEvent) {} + /** + * The handler for the security event received. The security event + * represents a timeout trying to establish a secure connection. + * Most probably the other peer doesn't support it. + * + * @param securityTimeoutEvent + * the security timeout event received + */ + public void securityTimeout( + CallPeerSecurityTimeoutEvent securityTimeoutEvent) + {} + /** * Processes the received security message. * @param event the event we received diff --git a/src/net/java/sip/communicator/service/neomedia/event/SrtpListener.java b/src/net/java/sip/communicator/service/neomedia/event/SrtpListener.java index 6671eb2f1..aa82bb388 100644 --- a/src/net/java/sip/communicator/service/neomedia/event/SrtpListener.java +++ b/src/net/java/sip/communicator/service/neomedia/event/SrtpListener.java @@ -50,4 +50,12 @@ public void securityTurnedOn( int sessionType, public void securityMessageReceived(String message, String i18nMessage, int severity); + + /** + * Indicates that the other party has timeouted replying to our + * offer to secure the connection. + * + * @param sessionType the type of the call session - audio or video. + */ + public void securityTimeout(int sessionType); } diff --git a/src/net/java/sip/communicator/service/protocol/AbstractCallPeer.java b/src/net/java/sip/communicator/service/protocol/AbstractCallPeer.java index 8f0c042e9..797dea858 100644 --- a/src/net/java/sip/communicator/service/protocol/AbstractCallPeer.java +++ b/src/net/java/sip/communicator/service/protocol/AbstractCallPeer.java @@ -334,6 +334,36 @@ protected void fireCallPeerSecurityOffEvent(CallPeerSecurityOffEvent evt) } } + /** + * Constructs a CallPeerSecurityStatusEvent using this call peer as + * source, setting it to be of type eventType and the corresponding + * oldValue and newValue. + * + * @param evt the event object with details to pass on to the consumers + */ + protected void fireCallPeerSecurityTimeoutEvent( + CallPeerSecurityTimeoutEvent evt) + { + lastSecurityEvent = evt; + + if (logger.isDebugEnabled()) + logger.debug("Dispatching a CallPeerSecurityStatusEvent event to " + + callPeerSecurityListeners.size() + +" listeners. event is: " + evt.toString()); + + List listeners = null; + synchronized (callPeerSecurityListeners) + { + listeners = new ArrayList( + callPeerSecurityListeners); + } + + for(CallPeerSecurityListener listener : listeners) + { + listener.securityTimeout(evt); + } + } + /** * Constructs a CallPeerSecurityStatusEvent using this call peer as * source, setting it to be of type eventType and the corresponding diff --git a/src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java b/src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java index 111e12d32..74fd0d1f1 100644 --- a/src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java +++ b/src/net/java/sip/communicator/service/protocol/ProtocolProviderFactory.java @@ -403,6 +403,12 @@ public abstract class ProtocolProviderFactory */ public static final String KEEP_ALIVE_INTERVAL = "KEEP_ALIVE_INTERVAL"; + /** + * Paranoia mode when turned on requires all calls to be secure and + * indicated as such. + */ + public static final String MODE_PARANOIA = "MODE_PARANOIA"; + /** * The BundleContext containing (or to contain) the service * registration of this factory. diff --git a/src/net/java/sip/communicator/service/protocol/event/CallPeerSecurityListener.java b/src/net/java/sip/communicator/service/protocol/event/CallPeerSecurityListener.java index e55289a04..f48d17d13 100755 --- a/src/net/java/sip/communicator/service/protocol/event/CallPeerSecurityListener.java +++ b/src/net/java/sip/communicator/service/protocol/event/CallPeerSecurityListener.java @@ -43,6 +43,17 @@ public void securityOn( public void securityOff( CallPeerSecurityOffEvent securityEvent); + /** + * The handler for the security event received. The security event + * represents a timeout trying to establish a secure connection. + * Most probably the other peer doesn't support it. + * + * @param securityTimeoutEvent + * the security timeout event received + */ + public void securityTimeout( + CallPeerSecurityTimeoutEvent securityTimeoutEvent); + /** * The handler of the security message event. * diff --git a/src/net/java/sip/communicator/service/protocol/event/CallPeerSecurityTimeoutEvent.java b/src/net/java/sip/communicator/service/protocol/event/CallPeerSecurityTimeoutEvent.java new file mode 100644 index 000000000..63f546e05 --- /dev/null +++ b/src/net/java/sip/communicator/service/protocol/event/CallPeerSecurityTimeoutEvent.java @@ -0,0 +1,39 @@ +/* + * Jitsi, the OpenSource Java VoIP and Instant Messaging client. + * + * Distributable under LGPL license. + * See terms of license at gnu.org. + */ +package net.java.sip.communicator.service.protocol.event; + +import net.java.sip.communicator.service.protocol.*; + +/** + * The CallPeerSecurityTimeoutEvent is triggered whenever a + * communication with a given peer cannot be established, the peer + * did not answer our tries to secure the connection. + * + * @author Damian Minkov + */ +public class CallPeerSecurityTimeoutEvent + extends CallPeerSecurityStatusEvent +{ + /** + * Serial version UID. + */ + private static final long serialVersionUID = 0L; + + /** + * The event constructor + * + * @param callPeer the call peer associated with this event + * @param sessionType the type of the session, either + * {@link CallPeerSecurityStatusEvent#AUDIO_SESSION} or + * {@link CallPeerSecurityStatusEvent#VIDEO_SESSION} + */ + public CallPeerSecurityTimeoutEvent( CallPeer callPeer, + int sessionType) + { + super(callPeer, sessionType); + } +} diff --git a/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java b/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java index 146bcc4c6..cc8b3e154 100644 --- a/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java +++ b/src/net/java/sip/communicator/service/protocol/media/CallPeerMediaHandler.java @@ -88,6 +88,13 @@ public abstract class CallPeerMediaHandler< private MediaDirection audioDirectionUserPreference = MediaDirection.SENDRECV; + /** + * List of advertised encryption methods. Indicated before establishing the + * call. + */ + private List advertisedEncryptionMethods = + new ArrayList(); + /** * A reference to the CallPeer instance that this handler is managing media * streams for. @@ -722,6 +729,29 @@ public boolean isSecure() return true; } + /** + * Returns the advertised methods for securing the call, + * this are the methods like SDES, ZRTP that are + * indicated in the initial session initialization. Missing here doesn't + * mean the other party don't support it. + * @return the advertised encryption methods. + */ + public SrtpControlType[] getAdvertisedEncryptionMethods() + { + return advertisedEncryptionMethods.toArray( + new SrtpControlType[advertisedEncryptionMethods.size()]); + } + + /** + * Adds encryption method to the list of advertised secure methods. + * @param encryptionMethod the method to add. + */ + public void addAdvertisedEncryptionMethod(SrtpControlType encryptionMethod) + { + if(!advertisedEncryptionMethods.contains(encryptionMethod)) + advertisedEncryptionMethods.add(encryptionMethod); + } + /** * Passes multiStreamData to the video stream that we are using * in this media handler (if any) so that the underlying SRTP lib could diff --git a/src/net/java/sip/communicator/service/protocol/media/MediaAwareCallPeer.java b/src/net/java/sip/communicator/service/protocol/media/MediaAwareCallPeer.java index 5305988d8..283872040 100644 --- a/src/net/java/sip/communicator/service/protocol/media/MediaAwareCallPeer.java +++ b/src/net/java/sip/communicator/service/protocol/media/MediaAwareCallPeer.java @@ -686,6 +686,19 @@ public void securityTurnedOn(int sessionType, String cipher, fireCallPeerSecurityOnEvent(evt); } + /** + * Indicates that the other party has timeouted replying to our + * offer to secure the connection. + * + * @param sessionType the type of the call session - audio or video. + */ + public void securityTimeout(int sessionType) + { + CallPeerSecurityTimeoutEvent evt = + new CallPeerSecurityTimeoutEvent(this, sessionType); + fireCallPeerSecurityTimeoutEvent(evt); + } + /** * Sets the security status to OFF for this call peer. *