001/** 002 * 003 * Copyright 2003-2005 Jive Software. 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 */ 017 018package org.jivesoftware.smackx.jingle.nat; 019 020import java.net.InetAddress; 021import java.net.NetworkInterface; 022import java.net.SocketException; 023import java.util.Enumeration; 024import java.util.logging.Logger; 025 026import org.jivesoftware.smack.SmackException.NoResponseException; 027import org.jivesoftware.smack.SmackException.NotConnectedException; 028import org.jivesoftware.smack.XMPPConnection; 029import org.jivesoftware.smack.PacketCollector; 030import org.jivesoftware.smack.XMPPException.XMPPErrorException; 031import org.jivesoftware.smack.packet.IQ; 032import org.jivesoftware.smack.provider.IQProvider; 033import org.jivesoftware.smack.provider.ProviderManager; 034import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; 035import org.jivesoftware.smackx.disco.packet.DiscoverInfo; 036import org.xmlpull.v1.XmlPullParser; 037 038/** 039 * RTPBridge IQ Packet used to request and retrieve a RTPBridge Candidates that can be used for a Jingle Media Transmission between two parties that are behind NAT. 040 * This Jingle Bridge has all the needed information to establish a full UDP Channel (Send and Receive) between two parties. 041 * <i>This transport method should be used only if other transport methods are not allowed. Or if you want a more reliable transport.</i> 042 * <p/> 043 * High Level Usage Example: 044 * <p/> 045 * RTPBridge rtpBridge = RTPBridge.getRTPBridge(connection, sessionID); 046 * 047 * @author Thiago Camargo 048 */ 049public class RTPBridge extends IQ { 050 051 private static final Logger LOGGER = Logger.getLogger(RTPBridge.class.getName()); 052 053 private String sid; 054 private String pass; 055 private String ip; 056 private String name; 057 private int portA = -1; 058 private int portB = -1; 059 private String hostA; 060 private String hostB; 061 private BridgeAction bridgeAction = BridgeAction.create; 062 063 private enum BridgeAction { 064 065 create, change, publicip 066 } 067 068 /** 069 * Element name of the packet extension. 070 */ 071 public static final String NAME = "rtpbridge"; 072 073 /** 074 * Element name of the packet extension. 075 */ 076 public static final String ELEMENT_NAME = "rtpbridge"; 077 078 /** 079 * Namespace of the packet extension. 080 */ 081 public static final String NAMESPACE = "http://www.jivesoftware.com/protocol/rtpbridge"; 082 083 static { 084 ProviderManager.addIQProvider(NAME, NAMESPACE, new Provider()); 085 } 086 087 /** 088 * Creates a RTPBridge Instance with defined Session ID 089 * 090 * @param sid 091 */ 092 public RTPBridge(String sid) { 093 this.sid = sid; 094 } 095 096 /** 097 * Creates a RTPBridge Instance with defined Session ID 098 * 099 * @param action 100 */ 101 public RTPBridge(BridgeAction action) { 102 this.bridgeAction = action; 103 } 104 105 /** 106 * Creates a RTPBridge Instance with defined Session ID 107 * 108 * @param sid 109 * @param bridgeAction 110 */ 111 public RTPBridge(String sid, BridgeAction bridgeAction) { 112 this.sid = sid; 113 this.bridgeAction = bridgeAction; 114 } 115 116 /** 117 * Creates a RTPBridge Packet without Session ID 118 */ 119 public RTPBridge() { 120 } 121 122 /** 123 * Get the attributes string 124 */ 125 public String getAttributes() { 126 StringBuilder str = new StringBuilder(); 127 128 if (getSid() != null) 129 str.append(" sid='").append(getSid()).append("'"); 130 131 if (getPass() != null) 132 str.append(" pass='").append(getPass()).append("'"); 133 134 if (getPortA() != -1) 135 str.append(" porta='").append(getPortA()).append("'"); 136 137 if (getPortB() != -1) 138 str.append(" portb='").append(getPortB()).append("'"); 139 140 if (getHostA() != null) 141 str.append(" hosta='").append(getHostA()).append("'"); 142 143 if (getHostB() != null) 144 str.append(" hostb='").append(getHostB()).append("'"); 145 146 return str.toString(); 147 } 148 149 /** 150 * Get the Session ID of the Packet (usually same as Jingle Session ID) 151 * 152 * @return the session ID 153 */ 154 public String getSid() { 155 return sid; 156 } 157 158 /** 159 * Set the Session ID of the Packet (usually same as Jingle Session ID) 160 * 161 * @param sid 162 */ 163 public void setSid(String sid) { 164 this.sid = sid; 165 } 166 167 /** 168 * Get the Host A IP Address 169 * 170 * @return the Host A IP Address 171 */ 172 public String getHostA() { 173 return hostA; 174 } 175 176 /** 177 * Set the Host A IP Address 178 * 179 * @param hostA 180 */ 181 public void setHostA(String hostA) { 182 this.hostA = hostA; 183 } 184 185 /** 186 * Get the Host B IP Address 187 * 188 * @return the Host B IP Address 189 */ 190 public String getHostB() { 191 return hostB; 192 } 193 194 /** 195 * Set the Host B IP Address 196 * 197 * @param hostB 198 */ 199 public void setHostB(String hostB) { 200 this.hostB = hostB; 201 } 202 203 /** 204 * Get Side A receive port 205 * 206 * @return the side A receive prot 207 */ 208 public int getPortA() { 209 return portA; 210 } 211 212 /** 213 * Set Side A receive port 214 * 215 * @param portA 216 */ 217 public void setPortA(int portA) { 218 this.portA = portA; 219 } 220 221 /** 222 * Get Side B receive port 223 * 224 * @return the side B receive port 225 */ 226 public int getPortB() { 227 return portB; 228 } 229 230 /** 231 * Set Side B receive port 232 * 233 * @param portB 234 */ 235 public void setPortB(int portB) { 236 this.portB = portB; 237 } 238 239 /** 240 * Get the RTP Bridge IP 241 * 242 * @return the RTP Bridge IP 243 */ 244 public String getIp() { 245 return ip; 246 } 247 248 /** 249 * Set the RTP Bridge IP 250 * 251 * @param ip 252 */ 253 public void setIp(String ip) { 254 this.ip = ip; 255 } 256 257 /** 258 * Get the RTP Agent Pass 259 * 260 * @return the RTP Agent Pass 261 */ 262 public String getPass() { 263 return pass; 264 } 265 266 /** 267 * Set the RTP Agent Pass 268 * 269 * @param pass 270 */ 271 public void setPass(String pass) { 272 this.pass = pass; 273 } 274 275 /** 276 * Get the name of the Candidate 277 * 278 * @return the name of the Candidate 279 */ 280 public String getName() { 281 return name; 282 } 283 284 /** 285 * Set the name of the Candidate 286 * 287 * @param name 288 */ 289 public void setName(String name) { 290 this.name = name; 291 } 292 293 /** 294 * Get the Child Element XML of the Packet 295 * 296 * @return the Child Element XML of the Packet 297 */ 298 public String getChildElementXML() { 299 StringBuilder str = new StringBuilder(); 300 str.append("<" + ELEMENT_NAME + " xmlns='" + NAMESPACE + "' sid='").append(sid).append("'>"); 301 302 if (bridgeAction.equals(BridgeAction.create)) 303 str.append("<candidate/>"); 304 else if (bridgeAction.equals(BridgeAction.change)) 305 str.append("<relay ").append(getAttributes()).append(" />"); 306 else 307 str.append("<publicip ").append(getAttributes()).append(" />"); 308 309 str.append("</" + ELEMENT_NAME + ">"); 310 return str.toString(); 311 } 312 313 /** 314 * IQProvider for RTP Bridge packets. 315 * Parse receive RTPBridge packet to a RTPBridge instance 316 * 317 * @author Thiago Rocha 318 */ 319 public static class Provider implements IQProvider { 320 321 public Provider() { 322 super(); 323 } 324 325 public IQ parseIQ(XmlPullParser parser) throws Exception { 326 327 boolean done = false; 328 329 int eventType; 330 String elementName; 331 332 if (!parser.getNamespace().equals(RTPBridge.NAMESPACE)) 333 throw new Exception("Not a RTP Bridge packet"); 334 335 RTPBridge iq = new RTPBridge(); 336 337 for (int i = 0; i < parser.getAttributeCount(); i++) { 338 if (parser.getAttributeName(i).equals("sid")) 339 iq.setSid(parser.getAttributeValue(i)); 340 } 341 342 // Start processing sub-elements 343 while (!done) { 344 eventType = parser.next(); 345 elementName = parser.getName(); 346 347 if (eventType == XmlPullParser.START_TAG) { 348 if (elementName.equals("candidate")) { 349 for (int i = 0; i < parser.getAttributeCount(); i++) { 350 if (parser.getAttributeName(i).equals("ip")) 351 iq.setIp(parser.getAttributeValue(i)); 352 else if (parser.getAttributeName(i).equals("pass")) 353 iq.setPass(parser.getAttributeValue(i)); 354 else if (parser.getAttributeName(i).equals("name")) 355 iq.setName(parser.getAttributeValue(i)); 356 else if (parser.getAttributeName(i).equals("porta")) 357 iq.setPortA(Integer.parseInt(parser.getAttributeValue(i))); 358 else if (parser.getAttributeName(i).equals("portb")) 359 iq.setPortB(Integer.parseInt(parser.getAttributeValue(i))); 360 } 361 } 362 else if (elementName.equals("publicip")) { 363 for (int i = 0; i < parser.getAttributeCount(); i++) { 364 if (parser.getAttributeName(i).equals("ip")) 365 iq.setIp(parser.getAttributeValue(i)); 366 } 367 } 368 } 369 else if (eventType == XmlPullParser.END_TAG) { 370 if (parser.getName().equals(RTPBridge.ELEMENT_NAME)) { 371 done = true; 372 } 373 } 374 } 375 return iq; 376 } 377 } 378 379 /** 380 * Get a new RTPBridge Candidate from the server. 381 * If a error occurs or the server don't support RTPBridge Service, null is returned. 382 * 383 * @param connection 384 * @param sessionID 385 * @return the new RTPBridge 386 * @throws NotConnectedException 387 */ 388 public static RTPBridge getRTPBridge(XMPPConnection connection, String sessionID) throws NotConnectedException { 389 390 if (!connection.isConnected()) { 391 return null; 392 } 393 394 RTPBridge rtpPacket = new RTPBridge(sessionID); 395 rtpPacket.setTo(RTPBridge.NAME + "." + connection.getServiceName()); 396 397 PacketCollector collector = connection.createPacketCollectorAndSend(rtpPacket); 398 399 RTPBridge response = (RTPBridge) collector.nextResult(); 400 401 // Cancel the collector. 402 collector.cancel(); 403 404 return response; 405 } 406 407 /** 408 * Check if the server support RTPBridge Service. 409 * 410 * @param connection 411 * @return true if the server supports the RTPBridge service 412 * @throws XMPPErrorException 413 * @throws NoResponseException 414 * @throws NotConnectedException 415 */ 416 public static boolean serviceAvailable(XMPPConnection connection) throws NoResponseException, 417 XMPPErrorException, NotConnectedException { 418 419 if (!connection.isConnected()) { 420 return false; 421 } 422 423 LOGGER.fine("Service listing"); 424 425 ServiceDiscoveryManager disco = ServiceDiscoveryManager 426 .getInstanceFor(connection); 427// DiscoverItems items = disco.discoverItems(connection.getServiceName()); 428// Iterator iter = items.getItems(); 429// while (iter.hasNext()) { 430// DiscoverItems.Item item = (DiscoverItems.Item) iter.next(); 431// if (item.getEntityID().startsWith("rtpbridge.")) { 432// return true; 433// } 434// } 435 436 DiscoverInfo discoInfo = disco.discoverInfo(connection.getServiceName()); 437 for (DiscoverInfo.Identity identity : discoInfo.getIdentities()) { 438 if ((identity.getName() != null) && (identity.getName().startsWith("rtpbridge"))) { 439 return true; 440 } 441 } 442 443 return false; 444 } 445 446 /** 447 * Check if the server support RTPBridge Service. 448 * 449 * @param connection 450 * @return the RTPBridge 451 * @throws NotConnectedException 452 */ 453 public static RTPBridge relaySession(XMPPConnection connection, String sessionID, String pass, TransportCandidate proxyCandidate, TransportCandidate localCandidate) throws NotConnectedException { 454 455 if (!connection.isConnected()) { 456 return null; 457 } 458 459 RTPBridge rtpPacket = new RTPBridge(sessionID, RTPBridge.BridgeAction.change); 460 rtpPacket.setTo(RTPBridge.NAME + "." + connection.getServiceName()); 461 rtpPacket.setType(Type.SET); 462 463 rtpPacket.setPass(pass); 464 rtpPacket.setPortA(localCandidate.getPort()); 465 rtpPacket.setPortB(proxyCandidate.getPort()); 466 rtpPacket.setHostA(localCandidate.getIp()); 467 rtpPacket.setHostB(proxyCandidate.getIp()); 468 469 // LOGGER.debug("Relayed to: " + candidate.getIp() + ":" + candidate.getPort()); 470 471 PacketCollector collector = connection.createPacketCollectorAndSend(rtpPacket); 472 473 RTPBridge response = (RTPBridge) collector.nextResult(); 474 475 // Cancel the collector. 476 collector.cancel(); 477 478 return response; 479 } 480 481 /** 482 * Get Public Address from the Server. 483 * 484 * @param xmppConnection 485 * @return public IP String or null if not found 486 * @throws NotConnectedException 487 */ 488 public static String getPublicIP(XMPPConnection xmppConnection) throws NotConnectedException { 489 490 if (!xmppConnection.isConnected()) { 491 return null; 492 } 493 494 RTPBridge rtpPacket = new RTPBridge(RTPBridge.BridgeAction.publicip); 495 rtpPacket.setTo(RTPBridge.NAME + "." + xmppConnection.getServiceName()); 496 rtpPacket.setType(Type.SET); 497 498 // LOGGER.debug("Relayed to: " + candidate.getIp() + ":" + candidate.getPort()); 499 500 PacketCollector collector = xmppConnection.createPacketCollectorAndSend(rtpPacket); 501 502 RTPBridge response = (RTPBridge) collector.nextResult(); 503 504 // Cancel the collector. 505 collector.cancel(); 506 507 if(response == null) return null; 508 509 if (response.getIp() == null || response.getIp().equals("")) return null; 510 511 Enumeration<NetworkInterface> ifaces = null; 512 try { 513 ifaces = NetworkInterface.getNetworkInterfaces(); 514 } 515 catch (SocketException e) { 516 e.printStackTrace(); 517 } 518 while (ifaces!=null&&ifaces.hasMoreElements()) { 519 520 NetworkInterface iface = ifaces.nextElement(); 521 Enumeration<InetAddress> iaddresses = iface.getInetAddresses(); 522 523 while (iaddresses.hasMoreElements()) { 524 InetAddress iaddress = iaddresses.nextElement(); 525 if (!iaddress.isLoopbackAddress()) 526 if (iaddress.getHostAddress().indexOf(response.getIp()) >= 0) 527 return null; 528 529 } 530 } 531 532 return response.getIp(); 533 } 534 535}