]> sjero.net Git - linphone/blob - p2pproxy/src/org/linphone/p2pproxy/core/stun/StunClient.java
2892902de6dcef9065c4fd3db44c92ea1927a74e
[linphone] / p2pproxy / src / org / linphone / p2pproxy / core / stun / StunClient.java
1 /*
2 p2pproxy Copyright (C) 2007  Jehan Monnier ()
3
4 StunClient.java - .
5
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.
10
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.
15
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.
19  */
20 package org.linphone.p2pproxy.core.stun;
21
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;
29 import java.net.URI;
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;
51
52 public class StunClient {
53         private static Logger mLog = Logger.getLogger(StunClient.class);
54         private List<InetSocketAddress> mStunServerList;
55    JxtaNetworkManager mJxtaNetworkManager;
56    
57    private int SO_TIME_OUT = 300;
58    
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);
65       }
66       mStunServerList = lAddressList;
67    }
68    StunClient(List<InetSocketAddress> aStunServerList) {
69       mStunServerList = aStunServerList;
70    }
71    public StunClient(JxtaNetworkManager aJxtaNetworkManager) throws P2pProxyException {
72       //need to acquire stun server address()
73       mJxtaNetworkManager = aJxtaNetworkManager;
74       try {
75          mStunServerList = acquireStunServerAddress();
76       } catch (Exception e) {
77          throw new P2pProxyException(e);
78       }
79    }   
80    public List<InetSocketAddress> getStrunServerList() {
81            return mStunServerList;
82    }
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()));
89       }
90       return lSocketAddressList;
91    }
92    
93    public AddressInfo computeAddressInfo(DatagramSocket lLocalSocket) throws PortUnreachableException, P2pProxyException {
94       AddressInfo lAddressInfo = new AddressInfo((InetSocketAddress) lLocalSocket.getLocalSocketAddress()); 
95       InetSocketAddress lCurrentMediaServerAddress = null; 
96       try {
97               DiscoveryInfo lDiscoveryInfo = new DiscoveryInfo((InetSocketAddress) lLocalSocket.getLocalSocketAddress()); 
98               //1 bind request 
99               bindRequest(lDiscoveryInfo,lLocalSocket,lLocalSocket,null,lCurrentMediaServerAddress = mStunServerList.get(0));
100                    //2 bind request
101                    if (mStunServerList.size() > 1) {
102                    //open new socket
103                    DatagramSocket lDatagramSocket = new DatagramSocket();
104                    bindRequest(lDiscoveryInfo,lDatagramSocket, lLocalSocket, lDiscoveryInfo.getPublicSocketAddress(),lCurrentMediaServerAddress = mStunServerList.get(1));
105                    lDatagramSocket.close();
106                    }
107                    //analyse
108                     
109                    lAddressInfo.setPublicAddress(lDiscoveryInfo.getPublicSocketAddress());
110                    
111         } catch (PortUnreachableException pex) {
112                 MediaResoureUnreachableException lExeption = new MediaResoureUnreachableException(pex);
113                 lExeption.setRourceAddress(lCurrentMediaServerAddress.getAddress().getHostAddress()+":"+lCurrentMediaServerAddress.getPort());
114                 throw lExeption;
115         } catch (Exception e) {
116                 throw new P2pProxyException(e);
117         }
118            return lAddressInfo;
119    }
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;
123            while (true) {
124                    try {
125                            aLocalSocket.setReuseAddress(true);
126                            aLocalSocket.connect(aStunAddress);
127                            aLocalSocket.setSoTimeout(lSoTimeOut);
128
129                            MessageHeader sendMH = new MessageHeader(MessageHeader.MessageHeaderType.BindingRequest);
130                            sendMH.generateTransactionID();
131
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()));
138                                    try {
139                                            lResponseAddress.setPort(aResponseAddress.getPort());
140                                            sendMH.addMessageAttribute(lResponseAddress);
141                                    } catch (MessageAttributeException e) {
142                                            mLog.info("Cannot set Response address ["+lResponseAddress+"]");
143                                    }
144                            }
145
146                            byte[] data = sendMH.getBytes();
147                            DatagramPacket send = new DatagramPacket(data, data.length);
148                            aLocalSocket.send(send);
149
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());
156                            }
157
158                            MappedAddress lMappedAddress = (MappedAddress) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.MappedAddress);
159                            ErrorCode ec = (ErrorCode) receiveMH.getMessageAttribute(MessageAttribute.MessageAttributeType.ErrorCode);
160                            if (ec != null) {
161                                    aDiscoveryInfo.setError(ec.getResponseCode(), ec.getReason());
162                                    throw new P2pProxyException("Message header contains an Errorcode message attribute. ["+ec+"]");
163                            }
164                            if ((lMappedAddress == null)) {
165                                    throw new P2pProxyException("Response does not contain a Mapped Address message attribute.");
166
167                            } else {
168                                    if (aLocalSocket.getLocalSocketAddress().equals(aResponseSocket.getLocalSocketAddress())) {
169                                            aDiscoveryInfo.setPublicSocketAddress(new InetSocketAddress(lMappedAddress.getAddress().getInetAddress(),lMappedAddress.getPort()));
170                                    } else {
171                                            aDiscoveryInfo.setFullCone();
172                                    }
173                            }
174                            return;
175
176                    } catch (PortUnreachableException pex ) {
177                            throw pex;
178                    }
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;
186                            } else {
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();
191                                    } else {
192                                            aDiscoveryInfo.setSymmetric();
193                                    }
194                                    if (mLog.isInfoEnabled()) mLog.info("Node is not capable of UDP communication.");
195                                    return ;
196                            }
197                    } 
198            }
199    }
200 }