1 11 12 package org.jivesoftware.messenger.server; 13 14 import org.dom4j.DocumentException; 15 import org.dom4j.Element; 16 import org.dom4j.io.XPPPacketReader; 17 import org.jivesoftware.messenger.*; 18 import org.jivesoftware.messenger.auth.AuthFactory; 19 import org.jivesoftware.messenger.net.DNSUtil; 20 import org.jivesoftware.messenger.net.SocketConnection; 21 import org.jivesoftware.messenger.spi.BasicStreamIDFactory; 22 import org.jivesoftware.util.JiveGlobals; 23 import org.jivesoftware.util.Log; 24 import org.jivesoftware.util.StringUtils; 25 import org.xmlpull.v1.XmlPullParser; 26 import org.xmlpull.v1.XmlPullParserException; 27 import org.xmlpull.v1.XmlPullParserFactory; 28 import org.xmpp.packet.JID; 29 import org.xmpp.packet.StreamError; 30 31 import javax.net.SocketFactory; 32 import java.io.*; 33 import java.net.Socket ; 34 import java.net.UnknownHostException ; 35 import java.util.concurrent.TimeUnit ; 36 37 58 class ServerDialback { 59 62 protected static String CHARSET = "UTF-8"; 63 66 private static final String secretKey = StringUtils.randomString(10); 67 68 private static XmlPullParserFactory FACTORY = null; 69 70 static { 71 try { 72 FACTORY = XmlPullParserFactory.newInstance(); 73 } 74 catch (XmlPullParserException e) { 75 Log.error("Error creating a parser factory", e); 76 } 77 } 78 79 private Connection connection; 80 private String serverName; 81 private SessionManager sessionManager = SessionManager.getInstance(); 82 private RoutingTable routingTable = XMPPServer.getInstance().getRoutingTable(); 83 84 105 ServerDialback(Connection connection, String serverName) { 106 this.connection = connection; 107 this.serverName = serverName; 108 } 109 110 ServerDialback() { 111 } 112 113 122 public OutgoingServerSession createOutgoingSession(String domain, String hostname, int port) { 123 String realHostname = null; 124 try { 125 Log.debug("OS - Trying to connect to " + hostname + ":" + port); 127 DNSUtil.HostAddress address = DNSUtil.resolveXMPPServerDomain(hostname); 129 realHostname = address.getHost(); 130 Socket socket = SocketFactory.getDefault().createSocket(realHostname, port); 131 Log.debug("OS - Connection to " + hostname + ":" + port + " successfull"); 132 connection = 133 new SocketConnection(XMPPServer.getInstance().getPacketDeliverer(), socket, 134 false); 135 StringBuilder stream = new StringBuilder (); 138 stream.append("<stream:stream"); 139 stream.append(" xmlns:stream=\"http://etherx.jabber.org/streams\""); 140 stream.append(" xmlns=\"jabber:server\""); 141 stream.append(" xmlns:db=\"jabber:server:dialback\">"); 142 connection.deliverRawText(stream.toString()); 143 stream = null; 144 145 XPPPacketReader reader = new XPPPacketReader(); 146 reader.setXPPFactory(XmlPullParserFactory.newInstance()); 147 reader.getXPPParser().setInput(new InputStreamReader(socket.getInputStream(), 148 CHARSET)); 149 XmlPullParser xpp = reader.getXPPParser(); 151 for (int eventType = xpp.getEventType(); eventType != XmlPullParser.START_TAG;) { 152 eventType = xpp.next(); 153 } 154 if ("jabber:server:dialback".equals(xpp.getNamespace("db"))) { 155 String id = xpp.getAttributeValue("", "id"); 156 OutgoingServerSocketReader socketReader = new OutgoingServerSocketReader(reader); 157 if (authenticateDomain(socketReader, domain, hostname, id)) { 158 StreamID streamID = new BasicStreamIDFactory().createStreamID(id); 160 OutgoingServerSession session = new OutgoingServerSession(domain, connection, 161 socketReader, streamID); 162 connection.init(session); 163 session.setAddress(new JID(null, hostname, null)); 165 return session; 166 } 167 else { 168 connection.close(); 170 } 171 } 172 else { 173 Log.debug("OS - Invalid namespace in packet: " + xpp.getText()); 174 StreamError error = new StreamError(StreamError.Condition.invalid_namespace); 176 StringBuilder sb = new StringBuilder (); 177 sb.append(error.toXML()); 178 connection.deliverRawText(sb.toString()); 179 connection.close(); 181 } 182 } 183 catch (UnknownHostException e) { 184 Log.debug("Error connecting to the remote server: " + hostname + "(DNS lookup: " + 185 realHostname + 186 ")", 187 e); 188 if (connection != null) { 190 connection.close(); 191 } 192 } 193 catch (Exception e) { 194 Log.error("Error creating outgoing session to remote server: " + hostname + 195 "(DNS lookup: " + 196 realHostname + 197 ")", 198 e); 199 if (connection != null) { 201 connection.close(); 202 } 203 } 204 return null; 205 } 206 207 221 public boolean authenticateDomain(OutgoingServerSocketReader socketReader, String domain, 222 String hostname, String id) { 223 String key = AuthFactory.createDigest(id, secretKey); 224 Log.debug("OS - Sent dialback key to host: " + hostname + " id: " + id + " from domain: " + 225 domain); 226 227 synchronized (socketReader) { 228 StringBuilder sb = new StringBuilder (); 230 sb.append("<db:result"); 231 sb.append(" from=\"" + domain + "\""); 232 sb.append(" to=\"" + hostname + "\">"); 233 sb.append(key); 234 sb.append("</db:result>"); 235 connection.deliverRawText(sb.toString()); 236 sb = null; 237 238 try { 240 Element doc = socketReader.getElement(JiveGlobals.getIntProperty("xmpp.server.read.timeout", 60000), 241 TimeUnit.MILLISECONDS); 242 if (doc == null) { 243 Log.debug("OS - Time out waiting for answer in validation from: " + hostname + 244 " id: " + 245 id + 246 " for domain: " + 247 domain); 248 return false; 249 } 250 else if ("db".equals(doc.getNamespacePrefix()) && "result".equals(doc.getName())) { 251 boolean success = "valid".equals(doc.attributeValue("type")); 252 Log.debug("OS - Validation " + (success ? "GRANTED" : "FAILED") + " from: " + 253 hostname + 254 " id: " + 255 id + 256 " for domain: " + 257 domain); 258 return success; 259 } 260 else { 261 Log.debug("OS - Unexpected answer in validation from: " + hostname + " id: " + 262 id + 263 " for domain: " + 264 domain + 265 " answer:" + 266 doc.asXML()); 267 return false; 268 } 269 } 270 catch (InterruptedException e) { 271 Log.debug("OS - Validation FAILED from: " + hostname + 272 " id: " + 273 id + 274 " for domain: " + 275 domain, e); 276 return false; 277 } 278 } 279 } 280 281 298 public IncomingServerSession createIncomingSession(XPPPacketReader reader) throws IOException, 299 XmlPullParserException { 300 XmlPullParser xpp = reader.getXPPParser(); 301 StringBuilder sb; 302 StreamError error; 303 if ("jabber:server:dialback".equals(xpp.getNamespace("db"))) { 304 305 StreamID streamID = sessionManager.nextStreamID(); 306 307 sb = new StringBuilder (); 308 sb.append("<stream:stream"); 309 sb.append(" xmlns:stream=\"http://etherx.jabber.org/streams\""); 310 sb.append(" xmlns=\"jabber:server\" xmlns:db=\"jabber:server:dialback\""); 311 sb.append(" id=\""); 312 sb.append(streamID.toString()); 313 sb.append("\">"); 314 connection.deliverRawText(sb.toString()); 315 316 try { 317 Element doc = reader.parseDocument().getRootElement(); 318 if ("db".equals(doc.getNamespacePrefix()) && "result".equals(doc.getName())) { 319 if (validateRemoteDomain(doc, streamID)) { 320 String hostname = doc.attributeValue("from"); 321 IncomingServerSession session = sessionManager. 323 createIncomingServerSession(connection, streamID); 324 session.setAddress(new JID(null, hostname, null)); 326 session.addValidatedDomain(hostname); 328 return session; 329 } 330 } 331 else if ("db".equals(doc.getNamespacePrefix()) && "verify".equals(doc.getName())) { 332 verifyReceivedKey(doc, connection); 336 connection.close(); 338 String verifyFROM = doc.attributeValue("from"); 339 String id = doc.attributeValue("id"); 340 Log.debug("AS - Connection closed for host: " + verifyFROM + " id: " + id); 341 sb = null; 342 return null; 343 } 344 else { 345 error = new StreamError(StreamError.Condition.invalid_xml); 347 sb = new StringBuilder (); 348 sb.append(error.toXML()); 349 connection.deliverRawText(sb.toString()); 350 connection.close(); 352 return null; 353 } 354 } 355 catch (Exception e) { 356 Log.error("An error occured while creating a server session", e); 357 connection.close(); 359 return null; 360 } 361 362 } 363 else { 364 error = new StreamError(StreamError.Condition.invalid_namespace); 366 sb = new StringBuilder (); 367 sb.append(error.toXML()); 368 connection.deliverRawText(sb.toString()); 369 connection.close(); 371 return null; 372 } 373 return null; 374 } 375 376 389 public boolean validateRemoteDomain(Element doc, StreamID streamID) { 390 StreamError error; 391 StringBuilder sb; 392 String recipient = doc.attributeValue("to"); 393 String hostname = doc.attributeValue("from"); 394 Log.debug("RS - Received dialback key from host: " + hostname + " to: " + recipient); 395 if (!RemoteServerManager.canAccess(hostname)) { 396 error = new StreamError(StreamError.Condition.host_unknown); 398 sb = new StringBuilder (); 399 sb.append(error.toXML()); 400 connection.deliverRawText(sb.toString()); 401 connection.close(); 403 Log.debug("RS - Error, hostname is not allowed to establish a connection to " + 404 "this server: " + 405 recipient); 406 return false; 407 } 408 else if (isHostUnknown(recipient)) { 409 error = new StreamError(StreamError.Condition.host_unknown); 411 sb = new StringBuilder (); 412 sb.append(error.toXML()); 413 connection.deliverRawText(sb.toString()); 414 connection.close(); 416 Log.debug("RS - Error, hostname not recognized: " + recipient); 417 return false; 418 } 419 else { 420 if (sessionManager.getIncomingServerSession(hostname) != null) { 421 error = new StreamError(StreamError.Condition.not_authorized); 423 sb = new StringBuilder (); 424 sb.append(error.toXML()); 425 connection.deliverRawText(sb.toString()); 426 connection.close(); 428 Log.debug("RS - Error, incoming connection already exists from: " + hostname); 429 return false; 430 } 431 else { 432 String key = doc.getTextTrim(); 433 434 DNSUtil.HostAddress address = DNSUtil.resolveXMPPServerDomain(hostname); 435 436 try { 437 boolean valid = verifyKey(key, streamID.toString(), recipient, hostname, 438 address.getHost(), address.getPort()); 439 440 Log.debug("RS - Sending key verification result to OS: " + hostname); 441 sb = new StringBuilder (); 442 sb.append("<db:result"); 443 sb.append(" from=\"" + recipient + "\""); 444 sb.append(" to=\"" + hostname + "\""); 445 sb.append(" type=\""); 446 sb.append(valid ? "valid" : "invalid"); 447 sb.append("\"/>"); 448 connection.deliverRawText(sb.toString()); 449 450 if (!valid) { 451 connection.close(); 453 } 454 return valid; 455 } 456 catch (Exception e) { 457 Log.warn("Error verifying key", e); 458 error = 462 new StreamError(StreamError.Condition.remote_connection_failed); 463 sb = new StringBuilder (); 464 sb.append(error.toXML()); 465 connection.deliverRawText(sb.toString()); 466 connection.close(); 468 return false; 469 } 470 } 471 } 472 } 473 474 private boolean isHostUnknown(String recipient) { 475 boolean host_unknown = !serverName.equals(recipient); 476 if (host_unknown && recipient.contains(serverName)) { 480 try { 481 routingTable.getRoute(new JID(recipient)); 482 host_unknown = false; 483 } 484 catch (NoSuchRouteException e) { 485 host_unknown = true; 486 } 487 } 488 return host_unknown; 489 } 490 491 494 private boolean verifyKey(String key, String streamID, String recipient, String hostname, 495 String host, int port) throws IOException, XmlPullParserException, 496 RemoteConnectionFailedException { 497 XPPPacketReader reader = null; 498 Writer writer = null; 499 StreamError error; 500 Log.debug("RS - Trying to connect to Authoritative Server: " + hostname + ":" + port); 502 Socket socket = SocketFactory.getDefault().createSocket(host, port); 503 socket.setSoTimeout(JiveGlobals.getIntProperty("xmpp.server.read.timeout", 60000)); 505 Log.debug("RS - Connection to AS: " + hostname + ":" + port + " successfull"); 506 try { 507 reader = new XPPPacketReader(); 508 reader.setXPPFactory(FACTORY); 509 510 reader.getXPPParser().setInput(new InputStreamReader(socket.getInputStream(), 511 CHARSET)); 512 writer = 514 new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), 515 CHARSET)); 516 StringBuilder stream = new StringBuilder (); 518 stream.append("<stream:stream"); 519 stream.append(" xmlns:stream=\"http://etherx.jabber.org/streams\""); 520 stream.append(" xmlns=\"jabber:server\""); 521 stream.append(" xmlns:db=\"jabber:server:dialback\">"); 522 writer.write(stream.toString()); 523 writer.flush(); 524 stream = null; 525 526 XmlPullParser xpp = reader.getXPPParser(); 528 for (int eventType = xpp.getEventType(); eventType != XmlPullParser.START_TAG;) { 529 eventType = xpp.next(); 530 } 531 if ("jabber:server:dialback".equals(xpp.getNamespace("db"))) { 532 Log.debug("RS - Asking AS to verify dialback key for id" + streamID); 533 StringBuilder sb = new StringBuilder (); 535 sb.append("<db:verify"); 536 sb.append(" from=\"" + recipient + "\""); 537 sb.append(" to=\"" + hostname + "\""); 538 sb.append(" id=\"" + streamID + "\">"); 539 sb.append(key); 540 sb.append("</db:verify>"); 541 writer.write(sb.toString()); 542 writer.flush(); 543 sb = null; 544 545 try { 546 Element doc = reader.parseDocument().getRootElement(); 547 if ("db".equals(doc.getNamespacePrefix()) && "verify".equals(doc.getName())) { 548 if (!streamID.equals(doc.attributeValue("id"))) { 549 error = new StreamError(StreamError.Condition.invalid_id); 551 sb = new StringBuilder (); 552 sb.append(error.toXML()); 553 writer.write(sb.toString()); 554 writer.flush(); 555 throw new RemoteConnectionFailedException("Invalid ID"); 558 } 559 else if (isHostUnknown(doc.attributeValue("to"))) { 560 error = new StreamError(StreamError.Condition.host_unknown); 562 sb = new StringBuilder (); 563 sb.append(error.toXML()); 564 writer.write(sb.toString()); 565 writer.flush(); 566 throw new RemoteConnectionFailedException("Host unknown"); 569 } 570 else if (!hostname.equals(doc.attributeValue("from"))) { 571 error = new StreamError(StreamError.Condition.invalid_from); 573 sb = new StringBuilder (); 574 sb.append(error.toXML()); 575 writer.write(sb.toString()); 576 writer.flush(); 577 throw new RemoteConnectionFailedException("Invalid From"); 580 } 581 else { 582 boolean valid = "valid".equals(doc.attributeValue("type")); 583 Log.debug("RS - Key was " + (valid ? "" : "NOT ") + 584 "VERIFIED by the Authoritative Server for: " + 585 hostname); 586 return valid; 587 } 588 } 589 else { 590 Log.debug("db:verify answer was: " + doc.asXML()); 591 } 592 } 593 catch (DocumentException e) { 594 Log.error("An error occured connecting to the Authoritative Server", e); 595 throw new RemoteConnectionFailedException("Error connecting to the Authoritative Server"); 598 } 599 600 } 601 else { 602 error = new StreamError(StreamError.Condition.invalid_namespace); 604 StringBuilder sb = new StringBuilder (); 605 sb.append(error.toXML()); 606 writer.write(sb.toString()); 607 writer.flush(); 608 throw new RemoteConnectionFailedException("Invalid namespace"); 611 } 612 } 613 finally { 614 try { 615 Log.debug("RS - Closing connection to Authoritative Server: " + hostname); 616 StringBuilder sb = new StringBuilder (); 618 sb.append("</stream:stream>"); 619 writer.write(sb.toString()); 620 writer.flush(); 621 socket.close(); 623 } 624 catch (IOException ioe) { 625 } 626 } 627 return false; 628 } 629 630 640 public static boolean verifyReceivedKey(Element doc, Connection connection) { 641 String verifyFROM = doc.attributeValue("from"); 642 String verifyTO = doc.attributeValue("to"); 643 String key = doc.getTextTrim(); 644 String id = doc.attributeValue("id"); 645 Log.debug("AS - Verifying key for host: " + verifyFROM + " id: " + id); 646 647 653 String expectedKey = AuthFactory.createDigest(id, secretKey); 656 boolean verified = expectedKey.equals(key); 657 658 StringBuilder sb = new StringBuilder (); 660 sb.append("<db:verify"); 661 sb.append(" from=\"" + verifyTO + "\""); 662 sb.append(" to=\"" + verifyFROM + "\""); 663 sb.append(" type=\""); 664 sb.append(verified ? "valid" : "invalid"); 665 sb.append("\" id=\"" + id + "\"/>"); 666 connection.deliverRawText(sb.toString()); 667 Log.debug("AS - Key was: " + (verified ? "VALID" : "INVALID") + " for host: " + 668 verifyFROM + 669 " id: " + 670 id); 671 return verified; 672 } 673 } 674 675 | Popular Tags |