• Openfire S2S 经验分享


    1、各Openfire服务器得设置不同的域名,即 ofProperty表xmpp.domain属性,如设置 192.168.1.46等。

    2、Dialback提供一种弱身份验证的方式,要使用这种方式可以将 ofproperty表中“xmpp.server.tls.enabled” 设置为false,并将“xmpp.server.dialback.enabled”设置为true。

    3、建立到对方的路由:

      LocalOutgoingServerSession类 authenticateDomain方法:

    View Code
     1 public static boolean authenticateDomain(String domain, String hostname) {
     2     OutgoingServerSession session;           
     3     ......
     4     session = createOutgoingSession(domain, hostname, port);
     5     if (session != null) {
     6         // Add the validated domain as an authenticated domain
     7         session.addAuthenticatedDomain(domain);
     8         // Add the new hostname to the list of names that the server may have
     9         session.addHostname(hostname);
    10         // Notify the SessionManager that a new session has been created
    11         sessionManager.outgoingServerSessionCreated((LocalOutgoingServerSession) session);
    12         return true;
    13     }
    14     ......
    15 }    

       3.1 Socket探测对方5269端口是否开放。

           LocalOutgoingServerSession类 createOutgoingSession方法:

    View Code
     1 private static LocalOutgoingServerSession createOutgoingSession(String domain, String hostname,
     2             int port) {
     3         boolean useTLS = JiveGlobals.getBooleanProperty("xmpp.server.tls.enabled", true);
     4         RemoteServerConfiguration configuration = RemoteServerManager.getConfiguration(hostname);
     5         if (configuration != null) {
     6             // TODO Use the specific TLS configuration for this remote server
     7             //useTLS = configuration.isTLSEnabled();
     8         }
     9 
    10         // Connect to remote server using XMPP 1.0 (TLS + SASL EXTERNAL or TLS + server dialback or server dialback)
    11         SocketConnection connection = null;
    12         String realHostname = null;
    13         int realPort = port;
    14         Socket socket = new Socket();
    15         try {
    16             // Get the real hostname to connect to using DNS lookup of the specified hostname
    17             DNSUtil.HostAddress address = DNSUtil.resolveXMPPServerDomain(hostname, port);
    18             realHostname = address.getHost();
    19             realPort = address.getPort();
    20             Log.debug("LocalOutgoingServerSession: OS - Trying to connect to " + hostname + ":" + port +
    21                     "(DNS lookup: " + realHostname + ":" + realPort + ")");
    22             // Establish a TCP connection to the Receiving Server
    23             socket.connect(new InetSocketAddress(realHostname, realPort),
    24                     RemoteServerManager.getSocketTimeout());
    25             Log.debug("LocalOutgoingServerSession: OS - Plain connection to " + hostname + ":" + port + " successful");
    26         }
    27         catch (Exception e) {
    28             Log.error("Error trying to connect to remote server: " + hostname +
    29                     "(DNS lookup: " + realHostname + ":" + realPort + ")", e);
    30             return null;
    31         }

         3.2 尝试server dialback(XMPP 1.0),建立信任通道。
             3.2.1 LocalOutgoingServerSession类 createOutgoingSession方法:

    View Code
     1 // Check if we are going to try server dialback (XMPP 1.0)
     2                     else if (ServerDialback.isEnabled() && features.element("dialback") != null) {
     3                         Log.debug("LocalOutgoingServerSession: OS - About to try connecting using server dialback XMPP 1.0 with: " + hostname);
     4                         ServerDialback method = new ServerDialback(connection, domain);
     5                         OutgoingServerSocketReader newSocketReader = new OutgoingServerSocketReader(reader);                        
     6                         if (method.authenticateDomain(newSocketReader, domain, hostname, id)) {
     7                             Log.debug("LocalOutgoingServerSession: OS - SERVER DIALBACK XMPP 1.0 with " + hostname + " was successful");                            
     8                             StreamID streamID = new BasicStreamIDFactory().createStreamID(id);
     9                             LocalOutgoingServerSession session = new LocalOutgoingServerSession(domain, connection, newSocketReader, streamID);
    10                             connection.init(session);
    11                             // Set the hostname as the address of the session
    12                             session.setAddress(new JID(null, hostname, null));                            
    13                             return session;
    14                         }
    15                         else {
    16                             Log.debug("LocalOutgoingServerSession: OS - Error, SERVER DIALBACK with " + hostname + " failed");
    17                         }
    18                     }
    19                     

            3.2.2 ServerDialback类 authenticateDomain方法:

    View Code
     1 public boolean authenticateDomain(OutgoingServerSocketReader socketReader, String domain,
     2             String hostname, String id) {
     3         String key = AuthFactory.createDigest(id, getSecretkey());
     4         Log.debug("ServerDialback: OS - Sent dialback key to host: " + hostname + " id: " + id + " from domain: " +
     5                 domain);
     6 
     7         synchronized (socketReader) {
     8             // Send a dialback key to the Receiving Server
     9             StringBuilder sb = new StringBuilder();
    10             sb.append("<db:result");
    11             sb.append(" from=\"").append(domain).append("\"");
    12             sb.append(" to=\"").append(hostname).append("\">");
    13             sb.append(key);
    14             sb.append("</db:result>");
    15             connection.deliverRawText(sb.toString());
    16 
    17             // Process the answer from the Receiving Server
    18             try {
    19                 Element doc = socketReader.getElement(RemoteServerManager.getSocketTimeout(),
    20                         TimeUnit.MILLISECONDS);
    21                 if (doc == null) {
    22                     Log.debug("ServerDialback: OS - Time out waiting for answer in validation from: " + hostname +
    23                             " id: " +
    24                             id +
    25                             " for domain: " +
    26                             domain);
    27                     return false;
    28                 }
    29                 else if ("db".equals(doc.getNamespacePrefix()) && "result".equals(doc.getName())) {
    30                     boolean success = "valid".equals(doc.attributeValue("type"));
    31                     Log.debug("ServerDialback: OS - Validation " + (success ? "GRANTED" : "FAILED") + " from: " +
    32                             hostname +
    33                             " id: " +
    34                             id +
    35                             " for domain: " +
    36                             domain);
    37                     return success;
    38                 }
    39                 else {
    40                     Log.debug("ServerDialback: OS - Unexpected answer in validation from: " + hostname + " id: " +
    41                             id +
    42                             " for domain: " +
    43                             domain +
    44                             " answer:" +
    45                             doc.asXML());
    46                     return false;
    47                 }
    48             }
    49             catch (InterruptedException e) {
    50                 Log.debug("ServerDialback: OS - Validation FAILED from: " + hostname +
    51                         " id: " +
    52                         id +
    53                         " for domain: " +
    54                         domain, e);
    55                 return false;
    56             }
    57         }
    58     }

    4、加入路由表的serversCache缓存。如 session.addHostname(hostname);

        LocalOutgoingServerSession类 addHostname方法:

    View Code
    1 public void addHostname(String hostname) {
    2         synchronized (hostnames) {
    3             hostnames.add(hostname);
    4         }
    5         // Add a new route for this new session
    6         XMPPServer.getInstance().getRoutingTable().addServerRoute(new JID(null, hostname, nulltrue), this);
    7     }

        RoutingTableImpl类 addServerRoute(JID route, LocalOutgoingServerSession destination)方法:

    View Code
     1 public void addServerRoute(JID route, LocalOutgoingServerSession destination) {
     2         String address = route.getDomain();
     3         localRoutingTable.addRoute(address, destination);
     4         Lock lock = CacheFactory.getLock(address, serversCache);
     5         try {
     6             lock.lock();
     7             serversCache.put(address, server.getNodeID().toByteArray());
     8         }
     9         finally {
    10             lock.unlock();            
    11         }
    12     }


    5、包再次路由,发往对方。

        5.1 OutgoingSessionPromise类 sendPacket(Packet packet)方法:        

    View Code
     1 private void sendPacket(Packet packet) throws Exception {
     2             // Create a connection to the remote server from the domain where the packet has been sent
     3             boolean created;
     4             // Make sure that only one cluster node is creating the outgoing connection
     5             // TODO: Evaluate why removing the oss part causes nasty s2s and lockup issues.
     6             Lock lock = CacheFactory.getLock(domain+"oss", serversCache);
     7             try {
     8                 lock.lock();
     9                 created = LocalOutgoingServerSession
    10                         .authenticateDomain(packet.getFrom().getDomain(), packet.getTo().getDomain());
    11             } finally {
    12                 lock.unlock();
    13             }
    14             if (created) {
    15                 if (!routingTable.hasServerRoute(packet.getTo())) {
    16                     throw new Exception("Route created but not found!!!");
    17                 }
    18                 // A connection to the remote server was created so get the route and send the packet
    19                 routingTable.routePacket(packet.getTo(), packet, false);
    20             }
    21             else {
    22                 throw new Exception("Failed to create connection to remote server");
    23             }
    24         }

        5.2 RoutingTableImpl类 routePacket(JID jid, Packet packet, boolean fromServer)方法:

    View Code
     1 // Packet sent to remote server
     2             byte[] nodeID = serversCache.get(jid.getDomain());
     3             if (nodeID != null) {
     4                 if (server.getNodeID().equals(nodeID)) {
     5                     // This is a route to a remote server connected from this node
     6                     try {
     7                         localRoutingTable.getRoute(jid.getDomain()).process(packet);
     8                         routed = true;
     9                     } catch (UnauthorizedException e) {
    10                         Log.error(e);
    11                     }
    12                 }
    13                 else {
    14                     // This is a route to a remote server connected from other node
    15                     if (remotePacketRouter != null) {
    16                         routed = remotePacketRouter.routePacket(nodeID, jid, packet);
    17                     }
    18                 }
    19             }
    20             else {
    21                 // Return a promise of a remote session. This object will queue packets pending
    22                 // to be sent to remote servers
    23                 OutgoingSessionPromise.getInstance().process(packet);
    24                 routed = true;
    25             }

          5.3 通过SocketConnection类的deliver(Packet packet)发出:

    View Code
     1 public void deliver(Packet packet) throws UnauthorizedException, PacketException {
     2         if (isClosed()) {
     3             backupDeliverer.deliver(packet);
     4         }
     5         else {
     6             boolean errorDelivering = false;
     7             boolean allowedToWrite = false;
     8             try {
     9                 requestWriting();
    10                 allowedToWrite = true;
    11                 xmlSerializer.write(packet.getElement());
    12                 if (flashClient) {
    13                     writer.write('\0');
    14                 }
    15                 xmlSerializer.flush();
    16             }
    17             catch (Exception e) {
    18                 Log.debug("Error delivering packet" + "\n" + this.toString(), e);
    19                 errorDelivering = true;
    20             }
    21             finally {
    22                 if (allowedToWrite) {
    23                     releaseWriting();
    24                 }
    25             }
    26             if (errorDelivering) {
    27                 close();
    28                 // Retry sending the packet again. Most probably if the packet is a
    29                 // Message it will be stored offline
    30                 backupDeliverer.deliver(packet);
    31             }
    32             else {
    33                 session.incrementServerPacketCount();
    34             }
    35         }
    36     }

      

    6、对方Openfire服务器在ServerSocketReader类的 packetReceived(Packet packet) 接收,并响应回包。

    View Code
     1 private void packetReceived(Packet packet) throws PacketRejectedException {
     2         if (packet.getTo() == null || packet.getFrom() == null) {
     3             Log.debug("Closing IncomingServerSession due to packet with no TO or FROM: " +
     4                     packet.toXML());
     5             // Send a stream error saying that the packet includes no TO or FROM
     6             StreamError error = new StreamError(StreamError.Condition.improper_addressing);
     7             connection.deliverRawText(error.toXML());
     8             // Close the underlying connection
     9             connection.close();
    10             open = false;
    11             throw new PacketRejectedException("Packet with no TO or FROM attributes");
    12         }
    13         else if (!((LocalIncomingServerSession) session).isValidDomain(packet.getFrom().getDomain())) {
    14             Log.debug("Closing IncomingServerSession due to packet with invalid domain: " +
    15                     packet.toXML());
    16             // Send a stream error saying that the packet includes an invalid FROM
    17             StreamError error = new StreamError(StreamError.Condition.invalid_from);
    18             connection.deliverRawText(error.toXML());
    19             // Close the underlying connection
    20             connection.close();
    21             open = false;
    22             throw new PacketRejectedException("Packet with no TO or FROM attributes");
    23         }
    24     }
  • 相关阅读:
    Zookeeper ZAB 协议分析
    Docker技术快速精通指南
    Oracle闪回技术详解
    怎样打造一个分布式数据库
    使用js冒泡实现点击空白处关闭弹窗
    也谈谈我对Docker的简单理解
    Docker技术快速精通指南
    Oracle优化网上常见的5个错误观点
    使用Spring AOP实现MySQL读写分离
    RESTEASY ,从学会使用到了解原理。
  • 原文地址:https://www.cnblogs.com/huazai8204/p/2295669.html
Copyright © 2020-2023  润新知