From d611a2daccd77872b10f02a13c6194c0b59d4bc7 Mon Sep 17 00:00:00 2001 From: jehan Date: Wed, 15 Oct 2008 20:14:38 +0000 Subject: [PATCH] prepare stun client git-svn-id: svn+ssh://svn.savannah.nongnu.org/linphone/trunk@94 3f6dc0c8-ddfe-455d-9043-3cd528dc4637 --- .../p2pproxy/core/stun/AddressInfo.java | 7 +- .../p2pproxy/core/stun/DiscoveryInfo.java | 110 ++++++++++++++++++ .../p2pproxy/core/stun/StunClient.java | 100 +++++++++++++++- 3 files changed, 213 insertions(+), 4 deletions(-) create mode 100644 p2pproxy/src/org/linphone/p2pproxy/core/stun/DiscoveryInfo.java diff --git a/p2pproxy/src/org/linphone/p2pproxy/core/stun/AddressInfo.java b/p2pproxy/src/org/linphone/p2pproxy/core/stun/AddressInfo.java index cddf55965..6db120128 100644 --- a/p2pproxy/src/org/linphone/p2pproxy/core/stun/AddressInfo.java +++ b/p2pproxy/src/org/linphone/p2pproxy/core/stun/AddressInfo.java @@ -39,7 +39,7 @@ public class AddressInfo { AddressInfo(InetSocketAddress aPrivateAddress) { mPrivateAddress = aPrivateAddress; } - public Mode getType() { + public Mode getMode() { return mMode; } public void setMode(Mode aMode) { @@ -49,6 +49,9 @@ public class AddressInfo { return mPrivateAddress; } public InetSocketAddress getPublicAddress() { - return mPrivateAddress; + return mPublicAddress; + } + public void setPublicAddress(InetSocketAddress aPublicAddress) { + mPublicAddress = aPublicAddress; } } diff --git a/p2pproxy/src/org/linphone/p2pproxy/core/stun/DiscoveryInfo.java b/p2pproxy/src/org/linphone/p2pproxy/core/stun/DiscoveryInfo.java new file mode 100644 index 000000000..07e2e261c --- /dev/null +++ b/p2pproxy/src/org/linphone/p2pproxy/core/stun/DiscoveryInfo.java @@ -0,0 +1,110 @@ +/* + * This file is part of JSTUN. + * + * Copyright (c) 2005 Thomas King - All rights + * reserved. + * + * This software is licensed under either the GNU Public License (GPL), + * or the Apache 2.0 license. Copies of both license agreements are + * included in this distribution. + */ + +package org.linphone.p2pproxy.core.stun; + +import java.net.*; + +public class DiscoveryInfo { + private boolean error = false; + private int errorResponseCode = 0; + private String errorReason; + private boolean blockedUDP = false; + private boolean fullCone = false; + private boolean symmetric = false; + private InetSocketAddress mTestSocketAddress; + private InetSocketAddress mPublicSocketAddress; + + public DiscoveryInfo(InetSocketAddress aTestSocketAddress) { + mTestSocketAddress = aTestSocketAddress; + } + + public boolean isError() { + return error; + } + + public void setError(int responseCode, String reason) { + this.error = true; + this.errorResponseCode = responseCode; + this.errorReason = reason; + } + + + + public boolean isBlockedUDP() { + if (error) return false; + return blockedUDP; + } + + public void setBlockedUDP() { + this.blockedUDP = true; + } + + public boolean isFullCone() { + if (error) return false; + return fullCone; + } + + public void setFullCone() { + this.fullCone = true; + } + + public boolean isSymmetric() { + if (error) return false; + return symmetric; + } + + public void setSymmetric() { + this.symmetric = true; + } + + public InetSocketAddress getLocalSocketAddress() { + return mTestSocketAddress; + } + + public InetSocketAddress getPublicSocketAddress() { + return mPublicSocketAddress; + } + public void setPublicSocketAddress(InetSocketAddress address) { + mPublicSocketAddress = address; + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append("Network interface: "); + try { + sb.append(NetworkInterface.getByInetAddress(mTestSocketAddress.getAddress()).getName()); + } catch (SocketException se) { + sb.append("unknown"); + } + sb.append("\n"); + sb.append("Local Socket address: "); + sb.append(mTestSocketAddress); + sb.append("\n"); + if (error) { + sb.append(errorReason + " - Responsecode: " + errorResponseCode); + return sb.toString(); + } + sb.append("Result: "); + if (blockedUDP) sb.append("Firewall blocks UDP.\n"); + if (fullCone) sb.append("Full Cone NAT handles connections.\n"); + if (symmetric) sb.append("Symmetric Cone NAT handles connections.\n"); + sb.append("Public IP address: "); + if (mPublicSocketAddress != null) { + sb.append(mPublicSocketAddress.toString()); + } else { + sb.append("unknown"); + } + sb.append("\n"); + return sb.toString(); + } + +} diff --git a/p2pproxy/src/org/linphone/p2pproxy/core/stun/StunClient.java b/p2pproxy/src/org/linphone/p2pproxy/core/stun/StunClient.java index 6bfbdb7e3..008494b25 100644 --- a/p2pproxy/src/org/linphone/p2pproxy/core/stun/StunClient.java +++ b/p2pproxy/src/org/linphone/p2pproxy/core/stun/StunClient.java @@ -20,19 +20,39 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. package org.linphone.p2pproxy.core.stun; import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; import java.net.InetSocketAddress; +import java.net.SocketException; +import java.net.SocketTimeoutException; import java.net.URI; +import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; +import org.apache.log4j.Logger; import org.linphone.p2pproxy.api.P2pProxyException; import org.linphone.p2pproxy.core.JxtaNetworkManager; import org.linphone.p2pproxy.core.P2pProxyAdvertisementNotFoundException; import org.linphone.p2pproxy.core.sipproxy.NetworkResourceAdvertisement; +import org.linphone.p2pproxy.test.StunServerTester; + +import de.javawi.jstun.attribute.ChangeRequest; +import de.javawi.jstun.attribute.ChangedAddress; +import de.javawi.jstun.attribute.ErrorCode; +import de.javawi.jstun.attribute.MappedAddress; +import de.javawi.jstun.attribute.MessageAttribute; +import de.javawi.jstun.attribute.MessageAttributeParsingException; +import de.javawi.jstun.header.MessageHeader; +import de.javawi.jstun.header.MessageHeaderParsingException; +import de.javawi.jstun.util.UtilityException; public class StunClient { - private List mStunServerList; + private static Logger mLog = Logger.getLogger(StunClient.class); + private List mStunServerList; JxtaNetworkManager mJxtaNetworkManager; + private DiscoveryInfo mDiscoveryInfo; + private int SO_TIME_OUT = 300; StunClient(List aStunServerList) { mStunServerList = aStunServerList; @@ -56,5 +76,81 @@ public class StunClient { return lSocketAddressList; } - + public AddressInfo computeAddressInfo(DatagramSocket lLocalSocket) throws P2pProxyException { + //1 bind request + try { + //1 bind request + bindRequest(lLocalSocket,lLocalSocket, mStunServerList.get(0)); + //open new socket + } catch (Exception e) { + throw new P2pProxyException(e); + } + return null; + } + private void bindRequest(DatagramSocket aLocalSocket, DatagramSocket aResponseSocket,InetSocketAddress aStunAddress) throws UtilityException, SocketException, UnknownHostException, IOException, MessageAttributeParsingException, MessageHeaderParsingException, P2pProxyException { + int timeSinceFirstTransmission = 0; + int lSoTimeOut = SO_TIME_OUT; + while (true) { + try { + aLocalSocket.setReuseAddress(true); + aLocalSocket.connect(aStunAddress); + aLocalSocket.setSoTimeout(lSoTimeOut); + + MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest); + sendMH.generateTransactionID(); + + ChangeRequest changeRequest = new ChangeRequest(); + sendMH.addMessageAttribute(changeRequest); + + byte[] data = sendMH.getBytes(); + DatagramPacket send = new DatagramPacket(data, data.length); + aLocalSocket.send(send); + + MessageHeader receiveMH = new MessageHeader(); + while (!(receiveMH.equalTransactionID(sendMH))) { + DatagramPacket receive = new DatagramPacket(new byte[200], 200); + aResponseSocket.receive(receive); + receiveMH = MessageHeader.parseHeader(receive.getData()); + receiveMH.parseAttributes(receive.getData()); + } + + MappedAddress lMappedAddress = (MappedAddress) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.MappedAddress); + ErrorCode ec = (ErrorCode) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.ErrorCode); + if (ec != null) { + mDiscoveryInfo.setError(ec.getResponseCode(), ec.getReason()); + throw new P2pProxyException("Message header contains an Errorcode message attribute. ["+ec+"]"); + } + if ((lMappedAddress == null)) { + throw new P2pProxyException("Response does not contain a Mapped Address message attribute."); + + } else { + if (aLocalSocket.getLocalSocketAddress().equals(aResponseSocket.getLocalSocketAddress())) { + mDiscoveryInfo.setPublicSocketAddress(new InetSocketAddress(lMappedAddress.getAddress().getInetAddress(),lMappedAddress.getPort())); + } else { + mDiscoveryInfo.setFullCone(); + } + } + return; + + } catch (SocketTimeoutException ste) { + if (timeSinceFirstTransmission < 7900) { + if (mLog.isInfoEnabled()) mLog.info("Socket timeout while receiving the response."); + timeSinceFirstTransmission += lSoTimeOut; + int timeoutAddValue = (timeSinceFirstTransmission * 2); + if (timeoutAddValue > 1600) timeoutAddValue = 1600; + lSoTimeOut = timeoutAddValue; + } else { + // node is not capable of udp communication + if (mLog.isInfoEnabled()) mLog.info("Socket timeout while receiving the response. Maximum retry limit exceed. Give up."); + if (aLocalSocket.getLocalSocketAddress().equals(aResponseSocket.getLocalSocketAddress())) { + mDiscoveryInfo.setBlockedUDP(); + } else { + mDiscoveryInfo.setSymmetric(); + } + if (mLog.isInfoEnabled()) mLog.info("Node is not capable of UDP communication."); + return ; + } + } + } + } }