📄 serverdialback.java
字号:
sb.append("\"/>");
connection.deliverRawText(sb.toString());
if (!valid) {
// Close the underlying connection
connection.close();
}
return valid;
}
catch (Exception e) {
Log.warn("Error verifying key of remote server: " + hostname, e);
// Send a <remote-connection-failed/> stream error condition
// and terminate both the XML stream and the underlying
// TCP connection
connection.deliverRawText(new StreamError(
StreamError.Condition.remote_connection_failed).toXML());
// Close the underlying connection
connection.close();
return false;
}
}
}
}
private boolean isHostUnknown(String recipient) {
boolean host_unknown = !serverName.equals(recipient);
// If the recipient does not match the serverName then check if it matches a subdomain. This
// trick is useful when subdomains of this server are registered in the DNS so remote
// servers may establish connections directly to a subdomain of this server
if (host_unknown && recipient.contains(serverName)) {
host_unknown = !routingTable.hasComponentRoute(new JID(recipient));
}
return host_unknown;
}
/**
* Verifies the key with the Authoritative Server.
*/
private boolean verifyKey(String key, String streamID, String recipient, String hostname,
String host, int port) throws IOException, XmlPullParserException,
RemoteConnectionFailedException {
XMPPPacketReader reader;
Writer writer = null;
// Establish a TCP connection back to the domain name asserted by the Originating Server
Log.debug("ServerDialback: RS - Trying to connect to Authoritative Server: " + hostname + ":" + port +
"(DNS lookup: " + host + ":" + port + ")");
// Connect to the Authoritative server
Socket socket = new Socket();
socket.connect(new InetSocketAddress(host, port), RemoteServerManager.getSocketTimeout());
// Set a read timeout
socket.setSoTimeout(RemoteServerManager.getSocketTimeout());
Log.debug("ServerDialback: RS - Connection to AS: " + hostname + ":" + port + " successful");
try {
reader = new XMPPPacketReader();
reader.setXPPFactory(FACTORY);
reader.getXPPParser().setInput(new InputStreamReader(socket.getInputStream(),
CHARSET));
// Get a writer for sending the open stream tag
writer =
new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(),
CHARSET));
// Send the Authoritative Server a stream header
StringBuilder stream = new StringBuilder();
stream.append("<stream:stream");
stream.append(" xmlns:stream=\"http://etherx.jabber.org/streams\"");
stream.append(" xmlns=\"jabber:server\"");
stream.append(" xmlns:db=\"jabber:server:dialback\">");
writer.write(stream.toString());
writer.flush();
// Get the answer from the Authoritative Server
XmlPullParser xpp = reader.getXPPParser();
for (int eventType = xpp.getEventType(); eventType != XmlPullParser.START_TAG;) {
eventType = xpp.next();
}
if ("jabber:server:dialback".equals(xpp.getNamespace("db"))) {
Log.debug("ServerDialback: RS - Asking AS to verify dialback key for id" + streamID);
// Request for verification of the key
StringBuilder sb = new StringBuilder();
sb.append("<db:verify");
sb.append(" from=\"").append(recipient).append("\"");
sb.append(" to=\"").append(hostname).append("\"");
sb.append(" id=\"").append(streamID).append("\">");
sb.append(key);
sb.append("</db:verify>");
writer.write(sb.toString());
writer.flush();
try {
Element doc = reader.parseDocument().getRootElement();
if ("db".equals(doc.getNamespacePrefix()) && "verify".equals(doc.getName())) {
if (!streamID.equals(doc.attributeValue("id"))) {
// Include the invalid-id stream error condition in the response
writer.write(new StreamError(StreamError.Condition.invalid_id).toXML());
writer.flush();
// Thrown an error so <remote-connection-failed/> stream error
// condition is sent to the Originating Server
throw new RemoteConnectionFailedException("Invalid ID");
}
else if (isHostUnknown(doc.attributeValue("to"))) {
// Include the host-unknown stream error condition in the response
writer.write(
new StreamError(StreamError.Condition.host_unknown).toXML());
writer.flush();
// Thrown an error so <remote-connection-failed/> stream error
// condition is sent to the Originating Server
throw new RemoteConnectionFailedException("Host unknown");
}
else if (!hostname.equals(doc.attributeValue("from"))) {
// Include the invalid-from stream error condition in the response
writer.write(
new StreamError(StreamError.Condition.invalid_from).toXML());
writer.flush();
// Thrown an error so <remote-connection-failed/> stream error
// condition is sent to the Originating Server
throw new RemoteConnectionFailedException("Invalid From");
}
else {
boolean valid = "valid".equals(doc.attributeValue("type"));
Log.debug("ServerDialback: RS - Key was " + (valid ? "" : "NOT ") +
"VERIFIED by the Authoritative Server for: " +
hostname);
return valid;
}
}
else {
Log.debug("ServerDialback: db:verify answer was: " + doc.asXML());
}
}
catch (DocumentException e) {
Log.error("An error occured connecting to the Authoritative Server", e);
// Thrown an error so <remote-connection-failed/> stream error condition is
// sent to the Originating Server
throw new RemoteConnectionFailedException("Error connecting to the Authoritative Server");
}
}
else {
// Include the invalid-namespace stream error condition in the response
writer.write(new StreamError(StreamError.Condition.invalid_namespace).toXML());
writer.flush();
// Thrown an error so <remote-connection-failed/> stream error condition is
// sent to the Originating Server
throw new RemoteConnectionFailedException("Invalid namespace");
}
}
finally {
try {
Log.debug("ServerDialback: RS - Closing connection to Authoritative Server: " + hostname);
// Close the stream
StringBuilder sb = new StringBuilder();
sb.append("</stream:stream>");
writer.write(sb.toString());
writer.flush();
// Close the TCP connection
socket.close();
}
catch (IOException ioe) {
// Do nothing
}
}
return false;
}
/**
* Verifies the key sent by a Receiving Server. This server will be acting as the
* Authoritative Server when executing this method. The remote server may have established
* a new connection to the Authoritative Server (i.e. this server) for verifying the key
* or it may be reusing an existing incoming connection.
*
* @param doc the Element that contains the key to verify.
* @param connection the connection to use for sending the verification result
* @return true if the key was verified.
*/
public static boolean verifyReceivedKey(Element doc, Connection connection) {
String verifyFROM = doc.attributeValue("from");
String verifyTO = doc.attributeValue("to");
String key = doc.getTextTrim();
String id = doc.attributeValue("id");
Log.debug("ServerDialback: AS - Verifying key for host: " + verifyFROM + " id: " + id);
// TODO If the value of the 'to' address does not match a recognized hostname,
// then generate a <host-unknown/> stream error condition
// TODO If the value of the 'from' address does not match the hostname
// represented by the Receiving Server when opening the TCP connection, then
// generate an <invalid-from/> stream error condition
// Verify the received key
// Created the expected key based on the received ID value and the shared secret
String expectedKey = AuthFactory.createDigest(id, getSecretkey());
boolean verified = expectedKey.equals(key);
// Send the result of the key verification
StringBuilder sb = new StringBuilder();
sb.append("<db:verify");
sb.append(" from=\"").append(verifyTO).append("\"");
sb.append(" to=\"").append(verifyFROM).append("\"");
sb.append(" type=\"");
sb.append(verified ? "valid" : "invalid");
sb.append("\" id=\"").append(id).append("\"/>");
connection.deliverRawText(sb.toString());
Log.debug("ServerDialback: AS - Key was: " + (verified ? "VALID" : "INVALID") + " for host: " +
verifyFROM +
" id: " +
id);
return verified;
}
/**
* Returns the secret key that was randomly generated. When running inside of a cluster
* the key will be unique to all cluster nodes.
*
* @return the secret key that was randomly generated.
*/
private static String getSecretkey() {
String key = "secretKey";
Lock lock = CacheFactory.getLock(key, secretKeyCache);
try {
lock.lock();
String secret = secretKeyCache.get(key);
if (secret == null) {
secret = StringUtils.randomString(10);
secretKeyCache.put(key, secret);
}
return secret;
}
finally {
lock.unlock();
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -