package com.juick.components.s2s; import com.juick.components.XMPPServer; import com.juick.xmpp.Iq; import com.juick.xmpp.JID; import com.juick.xmpp.Message; import com.juick.xmpp.Presence; import com.juick.xmpp.utils.XmlUtils; import org.xmlpull.v1.XmlPullParser; import javax.net.ssl.SSLException; import javax.net.ssl.SSLSocket; import java.io.EOFException; import java.io.IOException; import java.net.Socket; import java.net.SocketException; import java.util.ArrayList; import java.util.List; import java.util.UUID; /** * @author ugnich */ public class ConnectionIn extends Connection implements Runnable { final public List from = new ArrayList<>(); public long tsRemoteData = 0; public long packetsRemote = 0; JuickBot bot; public ConnectionIn(XMPPServer xmpp, JuickBot bot, Socket socket) throws Exception { super(xmpp); this.bot = bot; this.socket = socket; streamID = UUID.randomUUID().toString(); restartParser(); } @Override public void run() { logger.info("stream from ? {} start", streamID); try { parser.next(); // stream:stream updateTsRemoteData(); if (!parser.getName().equals("stream") || !parser.getNamespace("stream").equals(NS_STREAM)) { // || !parser.getAttributeValue(null, "version").equals("1.0") // || !parser.getAttributeValue(null, "to").equals(Main.HOSTNAME)) { throw new Exception("stream from ? " + streamID + " invalid first packet"); } boolean xmppversionnew = parser.getAttributeValue(null, "version") != null; String from = parser.getAttributeValue(null, "from"); if (xmpp.bannedHosts.contains(from)) { closeConnection(); return; } sendOpenStream(from, xmppversionnew); while (parser.next() != XmlPullParser.END_DOCUMENT) { updateTsRemoteData(); if (parser.getEventType() != XmlPullParser.START_TAG) { continue; } logParser(); packetsRemote++; String tag = parser.getName(); if (tag.equals("result") && parser.getNamespace().equals(NS_DB)) { String dfrom = parser.getAttributeValue(null, "from"); String to = parser.getAttributeValue(null, "to"); logger.info("stream from {} to {} {} asking for dialback", dfrom, to, streamID); if (dfrom.endsWith(xmpp.HOSTNAME) && (dfrom.equals(xmpp.HOSTNAME) || dfrom.endsWith("." + xmpp.HOSTNAME))) { break; } if (to != null && to.equals(xmpp.HOSTNAME)) { String dbKey = XmlUtils.getTagText(parser); updateTsRemoteData(); xmpp.startDialback(dfrom, streamID, dbKey); } else { throw new HostUnknownException("stream from " + dfrom + " " + streamID + " invalid to " + to); } } else if (tag.equals("verify") && parser.getNamespace().equals(NS_DB)) { String vfrom = parser.getAttributeValue(null, "from"); String vto = parser.getAttributeValue(null, "to"); String vid = parser.getAttributeValue(null, "id"); String vkey = XmlUtils.getTagText(parser); updateTsRemoteData(); boolean valid = false; if (vfrom != null && vto != null && vid != null && vkey != null) { String dialbackKey = xmpp.getConnectionOut(vfrom, false).dbKey; valid = vkey.equals(dialbackKey); } if (valid) { sendStanza(""); logger.info("stream from {} {} dialback verify valid", vfrom, streamID); } else { sendStanza(""); logger.warn("stream from {} {} dialback verify invalid", vfrom, streamID); } } else if (tag.equals("presence") && checkFromTo(parser)) { Presence p = Presence.parse(parser, null); if (p != null && (p.type == null || !p.type.equals(Presence.Type.error))) { bot.incomingPresence(p); } } else if (tag.equals("message") && checkFromTo(parser)) { updateTsRemoteData(); Message msg = Message.parse(parser, xmpp.childParsers); if (msg != null && (msg.type == null || !msg.type.equals(Message.Type.error))) { logger.info("stream {}: {}", streamID, msg); if (!bot.incomingMessage(msg)) { xmpp.getRouter().sendStanza(msg.toString()); } } } else if (tag.equals("iq") && checkFromTo(parser)) { updateTsRemoteData(); String type = parser.getAttributeValue(null, "type"); String xml = XmlUtils.parseToString(parser, true); if (type == null || !type.equals(Iq.Type.error)) { logger.info("stream {}: {}", streamID, xml); xmpp.getRouter().sendStanza(xml); } } else if (sc != null && !isSecured() && tag.equals("starttls")) { logger.info("stream {} securing", streamID); sendStanza(""); try { socket = sc.getSocketFactory().createSocket(socket, socket.getInetAddress().getHostAddress(), socket.getPort(), true); ((SSLSocket) socket).setUseClientMode(false); ((SSLSocket) socket).startHandshake(); setSecured(true); logger.info("stream {} secured", streamID); restartParser(); } catch (SSLException sex) { logger.warn("stream {} ssl error {}", streamID, sex); sendStanza(""); xmpp.removeConnectionIn(this); closeConnection(); } } else if (isSecured() && tag.equals("stream") && parser.getNamespace().equals(NS_STREAM)) { sendOpenStream(null, true); } else { String unhandledStanza = XmlUtils.parseToString(parser, true); logger.warn("Unhandled stanza from {}: {}", streamID, unhandledStanza); } } logger.warn("stream {} finished", streamID); xmpp.removeConnectionIn(this); closeConnection(); } catch (EOFException | SocketException ex) { logger.info("stream {} closed (dirty)", streamID); xmpp.removeConnectionIn(this); closeConnection(); } catch (HostUnknownException e) { logger.warn(e.getMessage()); } catch (Exception e) { logger.warn("stream {} error {}", streamID, e); xmpp.removeConnectionIn(this); closeConnection(); } } void updateTsRemoteData() { tsRemoteData = System.currentTimeMillis(); } void sendOpenStream(String from, boolean xmppversionnew) throws IOException { String openStream = ""; if (xmppversionnew) { openStream += ""; if (sc != null && !isSecured() && !xmpp.brokenSSLhosts.contains(from)) { openStream += ""; } openStream += ""; } sendStanza(openStream); } public void sendDialbackResult(String sfrom, String type) { try { sendStanza(""); if (type.equals("valid")) { from.add(sfrom); logger.info("stream from {} {} ready", sfrom, streamID); } } catch (IOException e) { logger.warn("stream from {} {} error: {}", sfrom, streamID, e); } } boolean checkFromTo(XmlPullParser parser) throws Exception { String cfrom = parser.getAttributeValue(null, "from"); String cto = parser.getAttributeValue(null, "to"); if (cfrom != null && cto != null && !cfrom.isEmpty() && !cto.isEmpty()) { JID jidto = new JID(cto); if (jidto.Host != null && jidto.Username != null && jidto.Host.equals(xmpp.HOSTNAME) && jidto.Username.matches("^[a-zA-Z0-9\\-]{2,16}$")) { JID jidfrom = new JID(cfrom); int size = from.size(); for (int i = 0; i < size; i++) { if (from.get(i).equals(jidfrom.Host)) { return true; } } } } return false; } class HostUnknownException extends Exception { public HostUnknownException(String message) { super(message); } } }