outgoingserversession.java

来自「基于Jabber协议的即时消息服务器」· Java 代码 · 共 679 行 · 第 1/3 页

JAVA
679
字号
                            // Skip the opening stream sent by the server
                            for (int eventType = xpp.getEventType();
                                 eventType != XmlPullParser.START_TAG;) {
                                eventType = xpp.next();
                            }

                            // SASL authentication was successful so create new
                            // OutgoingServerSession
                            String id = xpp.getAttributeValue("", "id");
                            StreamID streamID = new BasicStreamIDFactory().createStreamID(id);
                            OutgoingServerSession session = new OutgoingServerSession(domain,
                                    connection, new OutgoingServerSocketReader(reader), streamID);
                            connection.init(session);
                            // Set the hostname as the address of the session
                            session.setAddress(new JID(null, hostname, null));
                            // Set that the session was created using TLS+SASL (no server dialback)
                            session.usingServerDialback = false;
                            return session;
                        }
                        else {
                            Log.debug("OS - Error, EXTERNAL SASL authentication with " + hostname +
                                    " failed");
                            return null;
                        }
                    }
                }
                Log.debug("OS - Error, EXTERNAL SASL was not offered by " + hostname);
            }
            else {
                Log.debug("OS - Error, no SASL mechanisms were offered by " + hostname);
            }
        }
        else {
            Log.debug("OS - Error, <proceed> was not received");
        }
        return null;
    }

    private static boolean doExternalAuthentication(String domain, SocketConnection connection,
            XMPPPacketReader reader) throws DocumentException, IOException, XmlPullParserException {

        StringBuilder sb = new StringBuilder();
        sb.append("<auth xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" mechanism=\"EXTERNAL\">");
        sb.append(StringUtils.encodeBase64(domain));
        sb.append("</auth>");
        connection.deliverRawText(sb.toString());

        Element response = reader.parseDocument().getRootElement();
        if (response != null && "success".equals(response.getName())) {
            return true;
        }
        return false;
    }

    OutgoingServerSession(String serverName, Connection connection,
            OutgoingServerSocketReader socketReader, StreamID streamID) {
        super(serverName, connection, streamID);
        this.socketReader = socketReader;
        socketReader.setSession(this);
    }

    public void process(Packet packet) throws UnauthorizedException, PacketException {
        try {
            String senderDomain = packet.getFrom().getDomain();
            if (!getAuthenticatedDomains().contains(senderDomain)) {
                synchronized (senderDomain.intern()) {
                    if (!getAuthenticatedDomains().contains(senderDomain) &&
                            !authenticateSubdomain(senderDomain, packet.getTo().getDomain())) {
                        // Return error since sender domain was not validated by remote server
                        returnErrorToSender(packet);
                        return;
                    }
                }
            }

            if (conn != null && !conn.isClosed()) {
                conn.deliver(packet);
            }
        }
        catch (Exception e) {
            Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
        }
    }

    /**
     * Authenticates a subdomain of this server with the specified remote server over an exsiting
     * outgoing connection. If the existing session was using server dialback then a new db:result
     * is going to be sent to the remote server. But if the existing session was TLS+SASL based
     * then just assume that the subdomain was authenticated by the remote server.
     *
     * @param domain the local subdomain to authenticate with the remote server.
     * @param hostname the hostname of the remote server.
     * @return True if the subdomain was authenticated by the remote server.
     */
    private boolean authenticateSubdomain(String domain, String hostname) {
        if (!usingServerDialback) {
            // Using SASL so just assume that the domain was validated
            // (note: this may not be correct)
            addAuthenticatedDomain(domain);
            return true;
        }
        ServerDialback method = new ServerDialback(getConnection(), domain);
        if (method.authenticateDomain(socketReader, domain, hostname, getStreamID().getID())) {
            // Add the validated domain as an authenticated domain
            addAuthenticatedDomain(domain);
            return true;
        }
        return false;
    }

    private void returnErrorToSender(Packet packet) {
        RoutingTable routingTable = XMPPServer.getInstance().getRoutingTable();
        try {
            if (packet instanceof IQ) {
                IQ reply = new IQ();
                reply.setID(packet.getID());
                reply.setTo(packet.getFrom());
                reply.setFrom(packet.getTo());
                reply.setChildElement(((IQ) packet).getChildElement().createCopy());
                reply.setError(PacketError.Condition.remote_server_not_found);
                ChannelHandler route = routingTable.getRoute(reply.getTo());
                if (route != null) {
                    route.process(reply);
                }
            }
            else if (packet instanceof Presence) {
                Presence reply = new Presence();
                reply.setID(packet.getID());
                reply.setTo(packet.getFrom());
                reply.setFrom(packet.getTo());
                reply.setError(PacketError.Condition.remote_server_not_found);
                ChannelHandler route = routingTable.getRoute(reply.getTo());
                if (route != null) {
                    route.process(reply);
                }
            }
            else if (packet instanceof Message) {
                Message reply = new Message();
                reply.setID(packet.getID());
                reply.setTo(packet.getFrom());
                reply.setFrom(packet.getTo());
                reply.setType(((Message)packet).getType());
                reply.setThread(((Message)packet).getThread());
                reply.setError(PacketError.Condition.remote_server_not_found);
                ChannelHandler route = routingTable.getRoute(reply.getTo());
                if (route != null) {
                    route.process(reply);
                }
            }
        }
        catch (UnauthorizedException e) {
            // Do nothing
        }
        catch (Exception e) {
            Log.warn("Error returning error to sender. Original packet: " + packet, e);
        }
    }

    /**
     * Returns a collection with all the domains, subdomains and virtual hosts that where
     * authenticated. The remote server will accept packets sent from any of these domains,
     * subdomains and virtual hosts.
     *
     * @return domains, subdomains and virtual hosts that where validated.
     */
    public Collection<String> getAuthenticatedDomains() {
        return Collections.unmodifiableCollection(authenticatedDomains);
    }

    /**
     * Adds a new authenticated domain, subdomain or virtual host to the list of
     * authenticated domains for the remote server. The remote server will accept packets
     * sent from this new authenticated domain.
     *
     * @param domain the new authenticated domain, subdomain or virtual host to add.
     */
    public void addAuthenticatedDomain(String domain) {
        authenticatedDomains.add(domain);
    }

    /**
     * Removes an authenticated domain from the list of authenticated domains. The remote
     * server will no longer be able to accept packets sent from the removed domain, subdomain or
     * virtual host.
     *
     * @param domain the domain, subdomain or virtual host to remove from the list of
     *               authenticated domains.
     */
    public void removeAuthenticatedDomain(String domain) {
        authenticatedDomains.remove(domain);
    }

    /**
     * Returns the list of hostnames related to the remote server. This tracking is useful for
     * reusing the same session for the same remote server even if the server has many names.
     *
     * @return the list of hostnames related to the remote server.
     */
    public Collection<String> getHostnames() {
        return Collections.unmodifiableCollection(hostnames);
    }

    /**
     * Adds a new hostname to the list of known hostnames of the remote server. This tracking is
     * useful for reusing the same session for the same remote server even if the server has
     * many names.
     *
     * @param hostname the new known name of the remote server
     */
    private void addHostname(String hostname) {
        if (hostnames.add(hostname)) {
            // Register the outgoing session in the SessionManager. If the session
            // was already registered nothing happens
            sessionManager.registerOutgoingServerSession(hostname, this);
            // Add a new route for this new session
            XMPPServer.getInstance().getRoutingTable().addRoute(new JID(hostname), this);
        }
    }

    public String getAvailableStreamFeatures() {
        // Nothing special to add
        return null;
    }
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?