001/** 002 * 003 * Copyright the original author or authors 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smackx.jingleold.nat; 018 019import java.net.InetAddress; 020import java.net.UnknownHostException; 021import java.util.List; 022import java.util.logging.Level; 023import java.util.logging.Logger; 024 025/** 026 * ICE Transport candidate. 027 * 028 * A candidate represents the possible transport for data interchange between 029 * the two endpoints. 030 * 031 * @author Thiago Camargo 032 */ 033public final class ICECandidate extends TransportCandidate implements Comparable<ICECandidate> { 034 035 private static final Logger LOGGER = Logger.getLogger(ICECandidate.class.getName()); 036 037 private String id; // An identification 038 039 private String username; 040 041 private int preference; 042 043 private Protocol proto; 044 045 private Channel channel; 046 047 private int network; 048 049 private Type type; 050 051 public enum Type { 052 relay, srflx, prflx, local, host 053 } 054 055 public ICECandidate() { 056 super(); 057 } 058 059 /** 060 * Constructor with the basic elements of a transport definition. 061 * 062 * @param ip the IP address to use as a local address 063 * @param generation used to keep track of the candidates 064 * @param network used for diagnostics (used when the machine has 065 * several NICs) 066 * @param password user name, as it is used in ICE 067 * @param port the port at the candidate IP address 068 * @param username user name, as it is used in ICE 069 * @param preference preference for this transportElement, as it is used 070 * in ICE 071 * @param type type as defined in ICE-12 072 */ 073 public ICECandidate(String ip, int generation, int network, 074 String password, int port, String username, 075 int preference, Type type) { 076 super(ip, port, generation); 077 078 proto = Protocol.UDP; 079 channel = Channel.MYRTPVOICE; 080 081 this.network = network; 082 this.password = password; 083 this.username = username; 084 this.preference = preference; 085 this.type = type; 086 } 087 088 /** 089 * Get the ID. 090 * 091 * @return the id 092 */ 093 public String getId() { 094 return id; 095 } 096 097 /** 098 * Set the ID. 099 * 100 * @param id the id to set 101 */ 102 public void setId(String id) { 103 this.id = id; 104 } 105 106 /** 107 * Get the protocol used for the transmission. 108 * 109 * @return the protocol used for transmission 110 */ 111 public Protocol getProto() { 112 return proto; 113 } 114 115 /** 116 * Set the protocol for the transmission. 117 * 118 * @param proto the protocol to use 119 */ 120 public void setProto(Protocol proto) { 121 this.proto = proto; 122 } 123 124 /** 125 * Get the network interface used for this connection. 126 * 127 * @return the interface number 128 */ 129 public int getNetwork() { 130 return network; 131 } 132 133 /** 134 * Set the interface for this connection. 135 * 136 * @param network the interface number 137 */ 138 public void setNetwork(int network) { 139 this.network = network; 140 } 141 142 /** 143 * Get the username for this transportElement in ICE. 144 * 145 * @return a username string 146 */ 147 public String getUsername() { 148 return username; 149 } 150 151 /** 152 * Get the channel. 153 * 154 * @return the channel associated 155 */ 156 public Channel getChannel() { 157 return channel; 158 } 159 160 /** 161 * Set the channel for this transportElement. 162 * 163 * @param channel the new channel 164 */ 165 public void setChannel(Channel channel) { 166 this.channel = channel; 167 } 168 169 /** 170 * Set the username for this transportElement in ICE. 171 * 172 * @param username the username used in ICE 173 */ 174 public void setUsername(String username) { 175 this.username = username; 176 } 177 178 /** 179 * Get the preference number for this transportElement. 180 * 181 * @return the preference for this transportElement 182 */ 183 public int getPreference() { 184 return preference; 185 } 186 187 /** 188 * Set the preference order for this transportElement. 189 * 190 * @param preference a number identifying the preference (as defined in 191 * ICE) 192 */ 193 public void setPreference(int preference) { 194 this.preference = preference; 195 } 196 197 /** 198 * Get the Candidate Type. 199 * 200 * @return candidate Type 201 */ 202 public Type getType() { 203 return type; 204 } 205 206 /** 207 * Set the Candidate Type. 208 * 209 * @param type candidate type. 210 */ 211 public void setType(Type type) { 212 this.type = type; 213 } 214 215 /** 216 * Check if a transport candidate is usable. The transport resolver should 217 * check if the transport candidate the other endpoint has provided is 218 * usable. 219 * 220 * ICE Candidate can check connectivity using UDP echo Test. 221 */ 222 @SuppressWarnings("UnusedVariable") 223 @Override 224 public void check(final List<TransportCandidate> localCandidates) { 225 // TODO candidate is being checked trigger 226 // candidatesChecking.add(cand); 227 228 final ICECandidate checkingCandidate = this; 229 230 Thread checkThread = new Thread(new Runnable() { 231 @Override 232 public void run() { 233 234 final TestResult result = new TestResult(); 235 236 // Media Proxy don't have Echo features. 237 // If its a relayed candidate we assumed that is NOT Valid while other candidates still being checked. 238 // The negotiator MUST add then in the correct situations 239 if (getType().equals(Type.relay)) { 240 triggerCandidateChecked(false); 241 return; 242 } 243 244 ResultListener resultListener = new ResultListener() { 245 @Override 246 public void testFinished(TestResult testResult, TransportCandidate candidate) { 247 if (testResult.isReachable() && checkingCandidate.equals(candidate)) { 248 result.setResult(true); 249 LOGGER.fine("Candidate reachable: " + candidate.getIp() + ":" + candidate.getPort() + " from " + getIp() + ":" + getPort()); 250 } 251 } 252 }; 253 254 for (TransportCandidate candidate : localCandidates) { 255 CandidateEcho echo = candidate.getCandidateEcho(); 256 if (echo != null) { 257 if (candidate instanceof ICECandidate) { 258 ICECandidate iceCandidate = (ICECandidate) candidate; 259 if (iceCandidate.getType().equals(getType())) { 260 try { 261 echo.addResultListener(resultListener); 262 InetAddress address = InetAddress.getByName(getIp()); 263 echo.testASync(checkingCandidate, getPassword()); 264 } 265 catch (UnknownHostException e) { 266 LOGGER.log(Level.WARNING, "exception", e); 267 } 268 } 269 } 270 } 271 } 272 273 for (int i = 0; i < 10 && !result.isReachable(); i++) 274 try { 275 LOGGER.severe("ICE Candidate retry #" + i); 276 Thread.sleep(400); 277 } 278 catch (InterruptedException e) { 279 LOGGER.log(Level.WARNING, "exception", e); 280 } 281 282 for (TransportCandidate candidate : localCandidates) { 283 CandidateEcho echo = candidate.getCandidateEcho(); 284 if (echo != null) { 285 echo.removeResultListener(resultListener); 286 } 287 } 288 289 triggerCandidateChecked(result.isReachable()); 290 291 // TODO candidate is being checked trigger 292 // candidatesChecking.remove(cand); 293 } 294 }, "Transport candidate check"); 295 296 checkThread.setName("Transport candidate test"); 297 checkThread.start(); 298 } 299 300 @Override 301 public boolean equals(Object obj) { 302 if (this == obj) { 303 return true; 304 } 305 if (!super.equals(obj)) { 306 return false; 307 } 308 if (getClass() != obj.getClass()) { 309 return false; 310 } 311 312 final ICECandidate other = (ICECandidate) obj; 313 if (getChannel() == null) { 314 if (other.getChannel() != null) { 315 return false; 316 } 317 } 318 else if (!getChannel().equals(other.getChannel())) { 319 return false; 320 } 321 if (getId() == null) { 322 if (other.getId() != null) { 323 return false; 324 } 325 } 326 else if (!getId().equals(other.getId())) { 327 return false; 328 } 329 if (getNetwork() != other.getNetwork()) { 330 return false; 331 } 332 if (getPassword() == null) { 333 if (other.getPassword() != null) { 334 return false; 335 } 336 } 337 else if (!getPassword().equals(other.password)) { 338 return false; 339 } 340 if (getPreference() != other.getPreference()) { 341 return false; 342 } 343 if (getProto() == null) { 344 if (other.getProto() != null) { 345 return false; 346 } 347 } 348 else if (!getProto().equals(other.getProto())) { 349 return false; 350 } 351 if (getUsername() == null) { 352 if (other.getUsername() != null) { 353 return false; 354 } 355 } 356 else if (!getUsername().equals(other.getUsername())) { 357 return false; 358 } 359 360 if (getIp() == null) { 361 if (other.getIp() != null) { 362 return false; 363 } 364 } 365 else if (!getIp().equals(other.getIp())) { 366 return false; 367 } 368 369 if (getPort() != other.getPort()) { 370 return false; 371 } 372 373 if (getType() == null) { 374 if (other.getType() != null) { 375 return false; 376 } 377 } 378 else if (!getType().equals(other.getType())) { 379 return false; 380 } 381 382 return true; 383 } 384 385 @Override 386 public int hashCode() { 387 int res = 37; 388 res = 37 * res + (getChannel() == null ? 0 : getChannel().hashCode()); 389 res = 37 * res + (getId() == null ? 0 : getId().hashCode()); 390 res = 37 * res + getNetwork(); 391 res = 37 * res + (getPassword() == null ? 0 : getPassword().hashCode()); 392 res = 37 * res + getPreference(); 393 res = 37 * res + (getProto() == null ? 0 : getProto().hashCode()); 394 res = 37 * res + (getUsername() == null ? 0 : getUsername().hashCode()); 395 res = 37 * res + (getIp() == null ? 0 : getIp().hashCode()); 396 res = 37 * res + getPort(); 397 res = 37 * res + (getType() == null ? 0 : getType().hashCode()); 398 return res; 399 } 400 401 @Override 402 public boolean isNull() { 403 if (super.isNull()) { 404 return true; 405 } 406 else if (getProto().isNull()) { 407 return true; 408 } 409 else if (getChannel().isNull()) { 410 return true; 411 } 412 return false; 413 } 414 415 /** 416 * Compare the to other Transport candidate. 417 * 418 * @param arg another Transport candidate 419 * @return a negative integer, zero, or a positive integer as this 420 * object is less than, equal to, or greater than the specified 421 * object 422 */ 423 @Override 424 public int compareTo(ICECandidate arg) { 425 if (getPreference() < arg.getPreference()) { 426 return -1; 427 } else if (getPreference() > arg.getPreference()) { 428 return 1; 429 } 430 return 0; 431 } 432 433} 434