/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.vysper.xmpp.extension.xep0065_socks;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.vysper.xml.fragment.XMLElement;
import org.apache.vysper.xmpp.addressing.Entity;
import org.apache.vysper.xmpp.addressing.EntityImpl;
import org.apache.vysper.xmpp.modules.core.base.handler.DefaultIQHandler;
import org.apache.vysper.xmpp.protocol.NamespaceURIs;
import org.apache.vysper.xmpp.server.ServerRuntimeContext;
import org.apache.vysper.xmpp.server.SessionContext;
import org.apache.vysper.xmpp.server.response.ServerErrorResponses;
import org.apache.vysper.xmpp.stanza.IQStanza;
import org.apache.vysper.xmpp.stanza.IQStanzaType;
import org.apache.vysper.xmpp.stanza.Stanza;
import org.apache.vysper.xmpp.stanza.StanzaBuilder;
import org.apache.vysper.xmpp.stanza.StanzaErrorCondition;
import org.apache.vysper.xmpp.stanza.StanzaErrorType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Implementation of <a href="http://xmpp.org/extensions/xep-0045.html">XEP-0045 Multi-user chat</a>.
*
*
* @author The Apache MINA Project (dev@mina.apache.org)
*/
public class Socks5IqHandler extends DefaultIQHandler {
private static InetAddress DEFAULT_ADDRESS;
static {
try {
DEFAULT_ADDRESS = InetAddress.getByAddress(new byte[]{0,0,0,0});
} catch (UnknownHostException ignore) {;}
}
final Logger logger = LoggerFactory.getLogger(Socks5IqHandler.class);
private Entity jid;
private InetSocketAddress proxyAddress;
private Socks5ConnectionsRegistry connections;
public Socks5IqHandler(Entity jid, InetSocketAddress proxyAddress, Socks5ConnectionsRegistry connections) {
this.jid = jid;
this.proxyAddress = proxyAddress;
this.connections = connections;
}
@Override
protected boolean verifyInnerElement(Stanza stanza) {
return verifyInnerElementWorker(stanza, "query") && verifyInnerNamespace(stanza, NamespaceURIs.XEP0065_SOCKS5_BYTESTREAMS);
}
@Override
protected Stanza handleGet(IQStanza stanza, ServerRuntimeContext serverRuntimeContext, SessionContext sessionContext) {
/*
C: <iq from='requester@example.com/foo'
id='uj2c15z9'
to='streamer.example.com'
type='get'>
<query xmlns='http://jabber.org/protocol/bytestreams'/>
</iq>
S: <iq from='streamer.example.com'
id='uj2c15z9'
to='requester@example.com/foo'
type='result'>
<query xmlns='http://jabber.org/protocol/bytestreams'>
<streamhost
host='24.24.24.1'
jid='streamer.example.com'
port='7625'/>
</query>
</iq>
*/
StanzaBuilder builder = StanzaBuilder.createIQStanza(stanza.getTo(), stanza.getFrom(), IQStanzaType.RESULT, stanza.getID())
.startInnerElement("query", NamespaceURIs.XEP0065_SOCKS5_BYTESTREAMS)
.startInnerElement("streamhost", NamespaceURIs.XEP0065_SOCKS5_BYTESTREAMS);
// if an address is provided on the proxy address, use that, else use the JID
if(DEFAULT_ADDRESS.equals(proxyAddress.getAddress())) {
builder.addAttribute("host", jid.getFullQualifiedName());
} else {
builder.addAttribute("host", proxyAddress.getHostName());
}
builder.addAttribute("jid", jid.getFullQualifiedName())
.addAttribute("port", Integer.toString(proxyAddress.getPort()));
return builder.build();
}
@Override
protected Stanza handleSet(IQStanza stanza, ServerRuntimeContext serverRuntimeContext, SessionContext sessionContext) {
/*
C: <iq from='requester@example.com/foo'
id='oqx6t1c9'
to='streamer.example.com'
type='set'>
<query xmlns='http://jabber.org/protocol/bytestreams'
sid='vxf9n471bn46'>
<activate>target@example.org/bar</activate>
</query>
</iq>
S: <iq from='streamer.example.com'
id='oqx6t1c9'
to='requester@example.com/foo'
type='result'/>
SHA1 Hash of: (SID + Requester JID + Target JID)
*/
try {
XMLElement queryElm = stanza.getSingleInnerElementsNamed("query", NamespaceURIs.XEP0065_SOCKS5_BYTESTREAMS);
XMLElement activateElm = queryElm.getSingleInnerElementsNamed("activate", NamespaceURIs.XEP0065_SOCKS5_BYTESTREAMS);
String sid = queryElm.getAttributeValue("sid");
Entity target = EntityImpl.parse(activateElm.getInnerText().getText());
Entity requester = stanza.getFrom();
String hash = DigestUtils.shaHex(sid + requester.getFullQualifiedName() + target.getFullQualifiedName());
if(connections.activate(hash)) {
Stanza result = StanzaBuilder.createIQStanza(jid, requester, IQStanzaType.RESULT, stanza.getID()).build();
return result;
} else {
throw new RuntimeException("Pair not found");
}
} catch(Exception e) {
return ServerErrorResponses.getStanzaError(StanzaErrorCondition.BAD_REQUEST, stanza, StanzaErrorType.CANCEL, null, null, null);
}
}
}