2 p2pproxy Copyright (C) 2007 Jehan Monnier ()
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 package org.linphone.p2pproxy.core.stun;
22 import java.io.IOException;
23 import java.net.DatagramPacket;
24 import java.net.DatagramSocket;
25 import java.net.InetSocketAddress;
26 import java.net.PortUnreachableException;
27 import java.net.SocketException;
28 import java.net.SocketTimeoutException;
30 import java.net.UnknownHostException;
31 import java.util.ArrayList;
32 import java.util.List;
33 import org.apache.log4j.Logger;
34 import org.linphone.p2pproxy.api.P2pProxyException;
35 import org.linphone.p2pproxy.core.JxtaNetworkManager;
36 import org.linphone.p2pproxy.core.P2pProxyAdvertisementNotFoundException;
37 import org.linphone.p2pproxy.core.media.MediaResourceService;
38 import org.linphone.p2pproxy.core.media.MediaResoureUnreachableException;
39 import org.linphone.p2pproxy.core.sipproxy.NetworkResourceAdvertisement;
40 import de.javawi.jstun.attribute.ChangeRequest;
41 import de.javawi.jstun.attribute.ErrorCode;
42 import de.javawi.jstun.attribute.MappedAddress;
43 import de.javawi.jstun.attribute.MessageAttribute;
44 import de.javawi.jstun.attribute.MessageAttributeException;
45 import de.javawi.jstun.attribute.MessageAttributeParsingException;
46 import de.javawi.jstun.attribute.ResponseAddress;
47 import de.javawi.jstun.header.MessageHeader;
48 import de.javawi.jstun.header.MessageHeaderParsingException;
49 import de.javawi.jstun.util.Address;
50 import de.javawi.jstun.util.UtilityException;
52 public class StunClient {
53 private static Logger mLog = Logger.getLogger(StunClient.class);
54 private List<InetSocketAddress> mStunServerList;
55 JxtaNetworkManager mJxtaNetworkManager;
57 private int SO_TIME_OUT = 300;
59 public StunClient(String[] aStunServerList) {
60 List<InetSocketAddress> lAddressList = new ArrayList<InetSocketAddress>();
61 for (String lStunInstance:aStunServerList) {
62 URI lUri = URI.create(lStunInstance);
63 InetSocketAddress lInetSocketAddress = new InetSocketAddress(lUri.getHost(),lUri.getPort());
64 lAddressList.add(lInetSocketAddress);
66 mStunServerList = lAddressList;
68 StunClient(List<InetSocketAddress> aStunServerList) {
69 mStunServerList = aStunServerList;
71 public StunClient(JxtaNetworkManager aJxtaNetworkManager) throws P2pProxyException {
72 //need to acquire stun server address()
73 mJxtaNetworkManager = aJxtaNetworkManager;
75 mStunServerList = acquireStunServerAddress();
76 } catch (Exception e) {
77 throw new P2pProxyException(e);
80 public List<InetSocketAddress> getStrunServerList() {
81 return mStunServerList;
83 private List<InetSocketAddress> acquireStunServerAddress() throws P2pProxyAdvertisementNotFoundException, InterruptedException, IOException {
84 List<NetworkResourceAdvertisement> lStunServerAdv = (List<NetworkResourceAdvertisement>) mJxtaNetworkManager.getAdvertisementList(null, MediaResourceService.ADV_NAME, true);
85 List<InetSocketAddress> lSocketAddressList = new ArrayList<InetSocketAddress>(lStunServerAdv.size());
86 for (NetworkResourceAdvertisement lNetworkResourceAdvertisement: lStunServerAdv) {
87 URI lServerUri = URI.create(lNetworkResourceAdvertisement.getAddress());
88 lSocketAddressList.add(new InetSocketAddress(lServerUri.getHost(),lServerUri.getPort()));
90 return lSocketAddressList;
93 public AddressInfo computeAddressInfo(DatagramSocket lLocalSocket) throws PortUnreachableException, P2pProxyException {
94 AddressInfo lAddressInfo = new AddressInfo((InetSocketAddress) lLocalSocket.getLocalSocketAddress());
95 InetSocketAddress lCurrentMediaServerAddress = null;
97 DiscoveryInfo lDiscoveryInfo = new DiscoveryInfo((InetSocketAddress) lLocalSocket.getLocalSocketAddress());
99 bindRequest(lDiscoveryInfo,lLocalSocket,lLocalSocket,null,lCurrentMediaServerAddress = mStunServerList.get(0));
101 if (mStunServerList.size() > 1) {
103 DatagramSocket lDatagramSocket = new DatagramSocket();
104 bindRequest(lDiscoveryInfo,lDatagramSocket, lLocalSocket, lDiscoveryInfo.getPublicSocketAddress(),lCurrentMediaServerAddress = mStunServerList.get(1));
105 lDatagramSocket.close();
109 lAddressInfo.setPublicAddress(lDiscoveryInfo.getPublicSocketAddress());
111 } catch (PortUnreachableException pex) {
112 MediaResoureUnreachableException lExeption = new MediaResoureUnreachableException(pex);
113 lExeption.setRourceAddress(lCurrentMediaServerAddress.getAddress().getHostAddress()+":"+lCurrentMediaServerAddress.getPort());
115 } catch (Exception e) {
116 throw new P2pProxyException(e);
120 private void bindRequest(DiscoveryInfo aDiscoveryInfo,DatagramSocket aLocalSocket, DatagramSocket aResponseSocket,InetSocketAddress aResponseAddress, InetSocketAddress aStunAddress) throws UtilityException, SocketException, UnknownHostException, IOException, MessageAttributeParsingException, MessageHeaderParsingException, P2pProxyException {
121 int timeSinceFirstTransmission = 0;
122 int lSoTimeOut = SO_TIME_OUT;
125 aLocalSocket.setReuseAddress(true);
126 aLocalSocket.connect(aStunAddress);
127 aLocalSocket.setSoTimeout(lSoTimeOut);
129 MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
130 sendMH.generateTransactionID();
132 ChangeRequest changeRequest = new ChangeRequest();
133 sendMH.addMessageAttribute(changeRequest);
134 if (!((InetSocketAddress)aLocalSocket.getLocalSocketAddress()).equals((InetSocketAddress)aResponseSocket.getLocalSocketAddress()) && aResponseAddress != null) {
135 // add response address
136 ResponseAddress lResponseAddress = new ResponseAddress();
137 lResponseAddress.setAddress(new Address(aResponseAddress.getAddress().getHostAddress()));
139 lResponseAddress.setPort(aResponseAddress.getPort());
140 sendMH.addMessageAttribute(lResponseAddress);
141 } catch (MessageAttributeException e) {
142 mLog.info("Cannot set Response address ["+lResponseAddress+"]");
146 byte[] data = sendMH.getBytes();
147 DatagramPacket send = new DatagramPacket(data, data.length);
148 aLocalSocket.send(send);
150 MessageHeader receiveMH = new MessageHeader();
151 while (!(receiveMH.equalTransactionID(sendMH))) {
152 DatagramPacket receive = new DatagramPacket(new byte[200], 200);
153 aResponseSocket.receive(receive);
154 receiveMH = MessageHeader.parseHeader(receive.getData());
155 receiveMH.parseAttributes(receive.getData());
158 MappedAddress lMappedAddress = (MappedAddress) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.MappedAddress);
159 ErrorCode ec = (ErrorCode) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.ErrorCode);
161 aDiscoveryInfo.setError(ec.getResponseCode(), ec.getReason());
162 throw new P2pProxyException("Message header contains an Errorcode message attribute. ["+ec+"]");
164 if ((lMappedAddress == null)) {
165 throw new P2pProxyException("Response does not contain a Mapped Address message attribute.");
168 if (aLocalSocket.getLocalSocketAddress().equals(aResponseSocket.getLocalSocketAddress())) {
169 aDiscoveryInfo.setPublicSocketAddress(new InetSocketAddress(lMappedAddress.getAddress().getInetAddress(),lMappedAddress.getPort()));
171 aDiscoveryInfo.setFullCone();
176 } catch (PortUnreachableException pex ) {
179 catch (SocketTimeoutException ste) {
180 if (timeSinceFirstTransmission < 7900) {
181 if (mLog.isInfoEnabled()) mLog.info("Socket timeout while receiving the response.");
182 timeSinceFirstTransmission += lSoTimeOut;
183 int timeoutAddValue = (timeSinceFirstTransmission * 2);
184 if (timeoutAddValue > 1600) timeoutAddValue = 1600;
185 lSoTimeOut = timeoutAddValue;
187 // node is not capable of udp communication
188 if (mLog.isInfoEnabled()) mLog.info("Socket timeout while receiving the response. Maximum retry limit exceed. Give up.");
189 if (aLocalSocket.getLocalSocketAddress().equals(aResponseSocket.getLocalSocketAddress())) {
190 aDiscoveryInfo.setBlockedUDP();
192 aDiscoveryInfo.setSymmetric();
194 if (mLog.isInfoEnabled()) mLog.info("Node is not capable of UDP communication.");