use of org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamSession in project Smack by igniterealtime.
the class Socks5BytestreamRequest method accept.
/**
* Accepts the SOCKS5 Bytestream initialization request and returns the socket to send/receive
* data.
* <p>
* Before accepting the SOCKS5 Bytestream request you can set timeouts by invoking
* {@link #setTotalConnectTimeout(int)} and {@link #setMinimumConnectTimeout(int)}.
*
* @return the socket to send/receive data
* @throws InterruptedException if the current thread was interrupted while waiting
* @throws XMPPErrorException if there was an XMPP error returned.
* @throws NotConnectedException if the XMPP connection is not connected.
* @throws CouldNotConnectToAnyProvidedSocks5Host if no connection to any provided stream host could be established
* @throws NoSocks5StreamHostsProvided if no stream host was provided.
*/
@Override
public Socks5BytestreamSession accept() throws InterruptedException, XMPPErrorException, CouldNotConnectToAnyProvidedSocks5Host, NotConnectedException, NoSocks5StreamHostsProvided {
Collection<StreamHost> streamHosts = this.bytestreamRequest.getStreamHosts();
Map<StreamHost, Exception> streamHostsExceptions = new HashMap<>();
// throw exceptions if request contains no stream hosts
if (streamHosts.size() == 0) {
cancelRequest(streamHostsExceptions);
}
StreamHost selectedHost = null;
Socket socket = null;
String digest = Socks5Utils.createDigest(this.bytestreamRequest.getSessionID(), this.bytestreamRequest.getFrom(), this.manager.getConnection().getUser());
/*
* determine timeout for each connection attempt; each SOCKS5 proxy has the same amount of
* time so that the first does not consume the whole timeout
*/
int timeout = Math.max(getTotalConnectTimeout() / streamHosts.size(), getMinimumConnectTimeout());
for (StreamHost streamHost : streamHosts) {
String address = streamHost.getAddress() + ":" + streamHost.getPort();
// check to see if this address has been blacklisted
int failures = getConnectionFailures(address);
if (connectionFailureThreshold > 0 && failures >= connectionFailureThreshold) {
continue;
}
// establish socket
try {
// build SOCKS5 client
final Socks5Client socks5Client = new Socks5Client(streamHost, digest);
// connect to SOCKS5 proxy with a timeout
socket = socks5Client.getSocket(timeout);
// set selected host
selectedHost = streamHost;
break;
} catch (TimeoutException | IOException | SmackException | XMPPException e) {
streamHostsExceptions.put(streamHost, e);
incrementConnectionFailures(address);
}
}
// throw exception if connecting to all SOCKS5 proxies failed
if (selectedHost == null || socket == null) {
cancelRequest(streamHostsExceptions);
}
// send used-host confirmation
Bytestream response = createUsedHostResponse(selectedHost);
this.manager.getConnection().sendStanza(response);
return new Socks5BytestreamSession(socket, selectedHost.getJID().equals(this.bytestreamRequest.getFrom()));
}
use of org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamSession in project ecf by eclipse.
the class Socks5BytestreamRequest method accept.
/**
* Accepts the SOCKS5 Bytestream initialization request and returns the socket to send/receive
* data.
* <p>
* Before accepting the SOCKS5 Bytestream request you can set timeouts by invoking
* {@link #setTotalConnectTimeout(int)} and {@link #setMinimumConnectTimeout(int)}.
*
* @return the socket to send/receive data
* @throws XMPPException if connection to all SOCKS5 proxies failed or if stream is invalid.
* @throws InterruptedException if the current thread was interrupted while waiting
*/
public Socks5BytestreamSession accept() throws XMPPException, InterruptedException {
Collection<StreamHost> streamHosts = this.bytestreamRequest.getStreamHosts();
// throw exceptions if request contains no stream hosts
if (streamHosts.size() == 0) {
cancelRequest();
}
StreamHost selectedHost = null;
Socket socket = null;
String digest = Socks5Utils.createDigest(this.bytestreamRequest.getSessionID(), this.bytestreamRequest.getFrom(), this.manager.getConnection().getUser());
/*
* determine timeout for each connection attempt; each SOCKS5 proxy has the same amount of
* time so that the first does not consume the whole timeout
*/
int timeout = Math.max(getTotalConnectTimeout() / streamHosts.size(), getMinimumConnectTimeout());
for (StreamHost streamHost : streamHosts) {
String address = streamHost.getAddress() + ":" + streamHost.getPort();
// check to see if this address has been blacklisted
int failures = getConnectionFailures(address);
if (CONNECTION_FAILURE_THRESHOLD > 0 && failures >= CONNECTION_FAILURE_THRESHOLD) {
continue;
}
// establish socket
try {
// build SOCKS5 client
final Socks5Client socks5Client = new Socks5Client(streamHost, digest);
// connect to SOCKS5 proxy with a timeout
socket = socks5Client.getSocket(timeout);
// set selected host
selectedHost = streamHost;
break;
} catch (TimeoutException e) {
incrementConnectionFailures(address);
} catch (IOException e) {
incrementConnectionFailures(address);
} catch (XMPPException e) {
incrementConnectionFailures(address);
}
}
// throw exception if connecting to all SOCKS5 proxies failed
if (selectedHost == null || socket == null) {
cancelRequest();
}
// send used-host confirmation
Bytestream response = createUsedHostResponse(selectedHost);
this.manager.getConnection().sendPacket(response);
return new Socks5BytestreamSession(socket, selectedHost.getJID().equals(this.bytestreamRequest.getFrom()));
}
use of org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamSession in project Smack by igniterealtime.
the class Socks5BytestreamManager method establishSession.
/**
* Establishes a SOCKS5 Bytestream with the given user using the given session ID and returns
* the Socket to send/receive data to/from the user.
*
* @param targetJID the JID of the user a SOCKS5 Bytestream should be established
* @param sessionID the session ID for the SOCKS5 Bytestream request
* @return the Socket to send/receive data to/from the user
* @throws IOException if the bytestream could not be established
* @throws InterruptedException if the current thread was interrupted while waiting
* @throws XMPPException if an XMPP protocol error was received.
* @throws NotConnectedException if the XMPP connection is not connected.
* @throws NoResponseException if there was no response from the remote entity.
* @throws SmackMessageException if there was an error.
* @throws FeatureNotSupportedException if a requested feature is not supported by the remote entity.
*/
@Override
public Socks5BytestreamSession establishSession(Jid targetJID, String sessionID) throws IOException, InterruptedException, XMPPException, NoResponseException, NotConnectedException, SmackMessageException, FeatureNotSupportedException {
XMPPConnection connection = connection();
XMPPErrorException discoveryException = null;
// check if target supports SOCKS5 Bytestream
if (!supportsSocks5(targetJID)) {
throw new FeatureNotSupportedException("SOCKS5 Bytestream", targetJID);
}
List<Jid> proxies = new ArrayList<>();
// determine SOCKS5 proxies from XMPP-server
try {
proxies.addAll(determineProxies());
} catch (XMPPErrorException e) {
// don't abort here, just remember the exception thrown by determineProxies()
// determineStreamHostInfos() will at least add the local Socks5 proxy (if enabled)
discoveryException = e;
}
// determine address and port of each proxy
List<StreamHost> streamHosts = determineStreamHostInfos(proxies);
if (streamHosts.isEmpty()) {
if (discoveryException != null) {
throw discoveryException;
} else {
throw new SmackException.SmackMessageException("no SOCKS5 proxies available");
}
}
// compute digest
String digest = Socks5Utils.createDigest(sessionID, connection.getUser(), targetJID);
// prioritize last working SOCKS5 proxy if exists
if (this.proxyPrioritizationEnabled && this.lastWorkingProxy != null) {
StreamHost selectedStreamHost = null;
for (StreamHost streamHost : streamHosts) {
if (streamHost.getJID().equals(this.lastWorkingProxy)) {
selectedStreamHost = streamHost;
break;
}
}
if (selectedStreamHost != null) {
streamHosts.remove(selectedStreamHost);
streamHosts.add(0, selectedStreamHost);
}
}
Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy();
try {
// add transfer digest to local proxy to make transfer valid
socks5Proxy.addTransfer(digest);
// create initiation packet
Bytestream initiation = createBytestreamInitiation(sessionID, targetJID, streamHosts);
// send initiation packet
Stanza response = connection.createStanzaCollectorAndSend(initiation).nextResultOrThrow(getTargetResponseTimeout());
// extract used stream host from response
StreamHostUsed streamHostUsed = ((Bytestream) response).getUsedHost();
StreamHost usedStreamHost = initiation.getStreamHost(streamHostUsed.getJID());
if (usedStreamHost == null) {
throw new SmackException.SmackMessageException("Remote user responded with unknown host");
}
// build SOCKS5 client
Socks5Client socks5Client = new Socks5ClientForInitiator(usedStreamHost, digest, connection, sessionID, targetJID);
// establish connection to proxy
Socket socket = socks5Client.getSocket(getProxyConnectionTimeout());
// remember last working SOCKS5 proxy to prioritize it for next request
this.lastWorkingProxy = usedStreamHost.getJID();
// negotiation successful, return the output stream
return new Socks5BytestreamSession(socket, usedStreamHost.getJID().equals(connection.getUser()));
} catch (TimeoutException e) {
throw new IOException("Timeout while connecting to SOCKS5 proxy", e);
} finally {
// remove transfer digest if output stream is returned or an exception
// occurred
socks5Proxy.removeTransfer(digest);
}
}
use of org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamSession in project Smack by igniterealtime.
the class JingleS5BTransportSession method handleCandidateActivate.
public IQ handleCandidateActivate(Jingle jingle) {
LOGGER.log(Level.INFO, "handleCandidateActivate");
Socks5BytestreamSession bs = new Socks5BytestreamSession(ourChoice.socket, ourChoice.candidate.getJid().asBareJid().equals(jingleSession.getRemote().asBareJid()));
callback.onSessionInitiated(bs);
return IQ.createResultIQ(jingle);
}
use of org.jivesoftware.smackx.bytestreams.socks5.Socks5BytestreamSession in project Smack by igniterealtime.
the class JingleS5BTransportSession method connectIfReady.
/**
* Determine, which candidate (ours/theirs) is the nominated one.
* Connect to this candidate. If it is a proxy and it is ours, activate it and connect.
* If its a proxy and it is theirs, wait for activation.
* If it is not a proxy, just connect.
*/
private void connectIfReady() {
JingleContent content = jingleSession.getContents().get(0);
if (ourChoice == null || theirChoice == null) {
// Not yet ready.
LOGGER.log(Level.INFO, "Not ready.");
return;
}
if (ourChoice == CANDIDATE_FAILURE && theirChoice == CANDIDATE_FAILURE) {
LOGGER.log(Level.INFO, "Failure.");
jingleSession.onTransportMethodFailed(getNamespace());
return;
}
LOGGER.log(Level.INFO, "Ready.");
// Determine nominated candidate.
UsedCandidate nominated;
if (ourChoice != CANDIDATE_FAILURE && theirChoice != CANDIDATE_FAILURE) {
if (ourChoice.candidate.getPriority() > theirChoice.candidate.getPriority()) {
nominated = ourChoice;
} else if (ourChoice.candidate.getPriority() < theirChoice.candidate.getPriority()) {
nominated = theirChoice;
} else {
nominated = jingleSession.isInitiator() ? ourChoice : theirChoice;
}
} else if (ourChoice != CANDIDATE_FAILURE) {
nominated = ourChoice;
} else {
nominated = theirChoice;
}
if (nominated == theirChoice) {
LOGGER.log(Level.INFO, "Their choice, so our proposed candidate is used.");
boolean isProxy = nominated.candidate.getType() == JingleS5BTransportCandidate.Type.proxy;
try {
nominated = connectToOurCandidate(nominated.candidate);
} catch (InterruptedException | IOException | XMPPException | SmackException | TimeoutException e) {
LOGGER.log(Level.INFO, "Could not connect to our candidate.", e);
// TODO: Proxy-Error
return;
}
if (isProxy) {
LOGGER.log(Level.INFO, "Is external proxy. Activate it.");
Bytestream activate = new Bytestream(ourProposal.getStreamId());
activate.setMode(null);
activate.setType(IQ.Type.set);
activate.setTo(nominated.candidate.getJid());
activate.setToActivate(jingleSession.getRemote());
activate.setFrom(jingleSession.getLocal());
try {
jingleSession.getConnection().sendIqRequestAndWaitForResponse(activate);
} catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException e) {
LOGGER.log(Level.WARNING, "Could not activate proxy.", e);
return;
}
LOGGER.log(Level.INFO, "Send candidate-activate.");
Jingle candidateActivate = transportManager().createCandidateActivated(jingleSession.getRemote(), jingleSession.getInitiator(), jingleSession.getSessionId(), content.getSenders(), content.getCreator(), content.getName(), nominated.transport.getStreamId(), nominated.candidate.getCandidateId());
try {
jingleSession.getConnection().sendIqRequestAndWaitForResponse(candidateActivate);
} catch (InterruptedException | XMPPException.XMPPErrorException | SmackException.NotConnectedException | SmackException.NoResponseException e) {
LOGGER.log(Level.WARNING, "Could not send candidate-activated", e);
return;
}
}
LOGGER.log(Level.INFO, "Start transmission.");
Socks5BytestreamSession bs = new Socks5BytestreamSession(nominated.socket, !isProxy);
callback.onSessionInitiated(bs);
} else // Our choice
{
LOGGER.log(Level.INFO, "Our choice, so their candidate was used.");
boolean isProxy = nominated.candidate.getType() == JingleS5BTransportCandidate.Type.proxy;
if (!isProxy) {
LOGGER.log(Level.INFO, "Direct connection.");
Socks5BytestreamSession bs = new Socks5BytestreamSession(nominated.socket, true);
callback.onSessionInitiated(bs);
} else {
LOGGER.log(Level.INFO, "Our choice was their external proxy. wait for candidate-activate.");
}
}
}
Aggregations