JingleS5BTransportManager.java

  1. /**
  2.  *
  3.  * Copyright 2017 Paul Schaub
  4.  *
  5.  * Licensed under the Apache License, Version 2.0 (the "License");
  6.  * you may not use this file except in compliance with the License.
  7.  * You may obtain a copy of the License at
  8.  *
  9.  *     http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.jivesoftware.smackx.jingle.transports.jingle_s5b;

  18. import java.util.ArrayList;
  19. import java.util.Iterator;
  20. import java.util.List;
  21. import java.util.WeakHashMap;
  22. import java.util.logging.Level;
  23. import java.util.logging.Logger;

  24. import org.jivesoftware.smack.SmackException;
  25. import org.jivesoftware.smack.XMPPConnection;
  26. import org.jivesoftware.smack.XMPPException;
  27. import org.jivesoftware.smack.packet.IQ;

  28. import org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamManager;
  29. import org.jivesoftware.smackx.bytestreams.socks5.Socks5Proxy;
  30. import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
  31. import org.jivesoftware.smackx.jingle.JingleSession;
  32. import org.jivesoftware.smackx.jingle.element.Jingle;
  33. import org.jivesoftware.smackx.jingle.element.JingleAction;
  34. import org.jivesoftware.smackx.jingle.element.JingleContent;
  35. import org.jivesoftware.smackx.jingle.provider.JingleContentProviderManager;
  36. import org.jivesoftware.smackx.jingle.transports.JingleTransportManager;
  37. import org.jivesoftware.smackx.jingle.transports.JingleTransportSession;
  38. import org.jivesoftware.smackx.jingle.transports.jingle_s5b.elements.JingleS5BTransport;
  39. import org.jivesoftware.smackx.jingle.transports.jingle_s5b.provider.JingleS5BTransportProvider;

  40. import org.jxmpp.jid.FullJid;
  41. import org.jxmpp.jid.Jid;

  42. /**
  43.  * Manager for Jingle SOCKS5 Bytestream transports (XEP-0261).
  44.  */
  45. public final class JingleS5BTransportManager extends JingleTransportManager<JingleS5BTransport> {

  46.     private static final Logger LOGGER = Logger.getLogger(JingleS5BTransportManager.class.getName());

  47.     private static final WeakHashMap<XMPPConnection, JingleS5BTransportManager> INSTANCES = new WeakHashMap<>();

  48.     private List<Bytestream.StreamHost> localStreamHosts = null;
  49.     private List<Bytestream.StreamHost> availableStreamHosts = null;

  50.     private static boolean useLocalCandidates = true;
  51.     private static boolean useExternalCandidates = true;

  52.     private JingleS5BTransportManager(XMPPConnection connection) {
  53.         super(connection);
  54.         JingleContentProviderManager.addJingleContentTransportProvider(getNamespace(), new JingleS5BTransportProvider());
  55.     }

  56.     public static synchronized JingleS5BTransportManager getInstanceFor(XMPPConnection connection) {
  57.         JingleS5BTransportManager manager = INSTANCES.get(connection);
  58.         if (manager == null) {
  59.             manager = new JingleS5BTransportManager(connection);
  60.             INSTANCES.put(connection, manager);
  61.         }
  62.         return manager;
  63.     }

  64.     @Override
  65.     public String getNamespace() {
  66.         return JingleS5BTransport.NAMESPACE_V1;
  67.     }

  68.     @Override
  69.     public JingleTransportSession<JingleS5BTransport> transportSession(JingleSession jingleSession) {
  70.         return new JingleS5BTransportSession(jingleSession);
  71.     }

  72.     private List<Bytestream.StreamHost> queryAvailableStreamHosts() throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException {
  73.         Socks5BytestreamManager s5m = Socks5BytestreamManager.getBytestreamManager(getConnection());
  74.         List<Jid> proxies = s5m.determineProxies();
  75.         return determineStreamHostInfo(proxies);
  76.     }

  77.     private List<Bytestream.StreamHost> queryLocalStreamHosts() {
  78.         return Socks5BytestreamManager.getBytestreamManager(getConnection())
  79.                 .getLocalStreamHost();
  80.     }

  81.     public List<Bytestream.StreamHost> getAvailableStreamHosts() throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException {
  82.         if (availableStreamHosts == null) {
  83.             availableStreamHosts = queryAvailableStreamHosts();
  84.         }
  85.         return availableStreamHosts;
  86.     }

  87.     public List<Bytestream.StreamHost> getLocalStreamHosts() {
  88.         if (localStreamHosts == null) {
  89.             localStreamHosts = queryLocalStreamHosts();
  90.         }
  91.         return localStreamHosts;
  92.     }

  93.     public List<Bytestream.StreamHost> determineStreamHostInfo(List<Jid> proxies) {
  94.         XMPPConnection connection = getConnection();
  95.         List<Bytestream.StreamHost> streamHosts = new ArrayList<>();

  96.         Iterator<Jid> iterator = proxies.iterator();
  97.         while (iterator.hasNext()) {
  98.             Jid proxy = iterator.next();
  99.             Bytestream request = new Bytestream();
  100.             request.setType(IQ.Type.get);
  101.             request.setTo(proxy);
  102.             try {
  103.                 Bytestream response = connection.sendIqRequestAndWaitForResponse(request);
  104.                 streamHosts.addAll(response.getStreamHosts());
  105.             }
  106.             catch (Exception e) {
  107.                 iterator.remove();
  108.             }
  109.         }

  110.         return streamHosts;
  111.     }


  112.     @Override
  113.     public void authenticated(XMPPConnection connection, boolean resumed) {
  114.         if (!resumed) try {
  115.             Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy();
  116.             if (!socks5Proxy.isRunning()) {
  117.                 socks5Proxy.start();
  118.             }
  119.             localStreamHosts = queryLocalStreamHosts();
  120.             availableStreamHosts = queryAvailableStreamHosts();
  121.         } catch (InterruptedException | SmackException.NoResponseException | SmackException.NotConnectedException | XMPPException.XMPPErrorException e) {
  122.             LOGGER.log(Level.WARNING, "Could not query available StreamHosts: " + e, e);
  123.         }
  124.     }

  125.     public Jingle createCandidateUsed(FullJid recipient, FullJid initiator, String sessionId, JingleContent.Senders contentSenders,
  126.                                       JingleContent.Creator contentCreator, String contentName, String streamId,
  127.                                       String candidateId) {
  128.         Jingle.Builder jb = Jingle.builder(connection());
  129.         jb.setSessionId(sessionId).setInitiator(initiator).setAction(JingleAction.transport_info);

  130.         JingleContent.Builder cb = JingleContent.getBuilder();
  131.         cb.setName(contentName).setCreator(contentCreator).setSenders(contentSenders);

  132.         JingleS5BTransport.Builder tb = JingleS5BTransport.getBuilder();
  133.         tb.setCandidateUsed(candidateId).setStreamId(streamId);

  134.         Jingle jingle = jb.addJingleContent(cb.setTransport(tb.build()).build()).build();
  135.         jingle.setFrom(getConnection().getUser().asFullJidOrThrow());
  136.         jingle.setTo(recipient);

  137.         return jingle;
  138.     }

  139.     public Jingle createCandidateError(FullJid remote, FullJid initiator, String sessionId, JingleContent.Senders senders, JingleContent.Creator creator, String name, String streamId) {
  140.         Jingle.Builder jb = Jingle.builder(connection());
  141.         jb.setSessionId(sessionId).setInitiator(initiator).setAction(JingleAction.transport_info);

  142.         JingleContent.Builder cb = JingleContent.getBuilder();
  143.         cb.setName(name).setCreator(creator).setSenders(senders);

  144.         JingleS5BTransport.Builder tb = JingleS5BTransport.getBuilder();
  145.         tb.setCandidateError().setStreamId(streamId);

  146.         Jingle jingle = jb.addJingleContent(cb.setTransport(tb.build()).build()).build();
  147.         jingle.setFrom(getConnection().getUser().asFullJidOrThrow());
  148.         jingle.setTo(remote);

  149.         return jingle;
  150.     }

  151.     public Jingle createProxyError(FullJid remote, FullJid initiator, String sessionId,
  152.                                    JingleContent.Senders senders, JingleContent.Creator creator,
  153.                                    String name, String streamId) {
  154.         Jingle.Builder jb = Jingle.builder(connection());
  155.         jb.setSessionId(sessionId).setAction(JingleAction.transport_info).setInitiator(initiator);

  156.         JingleContent.Builder cb = JingleContent.getBuilder();
  157.         cb.setSenders(senders).setCreator(creator).setName(name);

  158.         JingleS5BTransport.Builder tb = JingleS5BTransport.getBuilder();
  159.         tb.setStreamId(sessionId).setProxyError().setStreamId(streamId);

  160.         Jingle jingle = jb.addJingleContent(cb.setTransport(tb.build()).build()).build();
  161.         jingle.setTo(remote);
  162.         jingle.setFrom(getConnection().getUser().asFullJidOrThrow());
  163.         return jingle;
  164.     }

  165.     public Jingle createCandidateActivated(FullJid remote, FullJid initiator, String sessionId,
  166.                                            JingleContent.Senders senders, JingleContent.Creator creator,
  167.                                            String name, String streamId, String candidateId) {
  168.         Jingle.Builder jb = Jingle.builder(connection());
  169.         jb.setInitiator(initiator).setSessionId(sessionId).setAction(JingleAction.transport_info);

  170.         JingleContent.Builder cb = JingleContent.getBuilder();
  171.         cb.setName(name).setCreator(creator).setSenders(senders);

  172.         JingleS5BTransport.Builder tb = JingleS5BTransport.getBuilder();
  173.         tb.setStreamId(streamId).setCandidateActivated(candidateId);

  174.         Jingle jingle = jb.addJingleContent(cb.setTransport(tb.build()).build()).build();
  175.         jingle.setFrom(getConnection().getUser().asFullJidOrThrow());
  176.         jingle.setTo(remote);
  177.         return jingle;
  178.     }

  179.     public static void setUseLocalCandidates(boolean localCandidates) {
  180.         JingleS5BTransportManager.useLocalCandidates = localCandidates;
  181.     }

  182.     public static void setUseExternalCandidates(boolean externalCandidates) {
  183.         JingleS5BTransportManager.useExternalCandidates = externalCandidates;
  184.     }

  185.     public static boolean isUseLocalCandidates() {
  186.         return useLocalCandidates;
  187.     }

  188.     public static boolean isUseExternalCandidates() {
  189.         return useExternalCandidates;
  190.     }
  191. }