1 17 18 package org.apache.james.dnsserver; 19 20 import org.apache.avalon.framework.activity.Initializable; 21 import org.apache.avalon.framework.activity.Disposable; 22 import org.apache.avalon.framework.configuration.Configurable; 23 import org.apache.avalon.framework.configuration.Configuration; 24 import org.apache.avalon.framework.configuration.ConfigurationException; 25 import org.apache.avalon.framework.logger.AbstractLogEnabled; 26 import org.xbill.DNS.Cache; 27 import org.xbill.DNS.Credibility; 28 import org.xbill.DNS.DClass; 29 import org.xbill.DNS.ExtendedResolver; 30 import org.xbill.DNS.FindServer; 31 import org.xbill.DNS.Lookup; 32 import org.xbill.DNS.Message; 33 import org.xbill.DNS.MXRecord; 34 import org.xbill.DNS.ARecord; 35 import org.xbill.DNS.Name; 36 import org.xbill.DNS.Rcode; 37 import org.xbill.DNS.Record; 38 import org.xbill.DNS.Resolver; 39 import org.xbill.DNS.RRset; 40 import org.xbill.DNS.SetResponse; 41 import org.xbill.DNS.TextParseException; 42 import org.xbill.DNS.Type; 43 44 import java.net.InetAddress ; 45 import java.net.UnknownHostException ; 46 import java.util.*; 47 48 52 public class DNSServer 53 extends AbstractLogEnabled 54 implements Configurable, Initializable, Disposable, 55 org.apache.james.services.DNSServer, DNSServerMBean { 56 57 61 private Resolver resolver; 62 63 67 private Cache cache; 68 69 72 private int dnsCredibility; 73 74 77 private List dnsServers = new ArrayList(); 78 79 82 private Comparator mxComparator = new MXRecordComparator(); 83 84 87 public void configure( final Configuration configuration ) 88 throws ConfigurationException { 89 90 final boolean autodiscover = 91 configuration.getChild( "autodiscover" ).getValueAsBoolean( true ); 92 93 if (autodiscover) { 94 getLogger().info("Autodiscovery is enabled - trying to discover your system's DNS Servers"); 95 String [] serversArray = FindServer.servers(); 96 if (serversArray != null) { 97 for ( int i = 0; i < serversArray.length; i++ ) { 98 dnsServers.add(serversArray[ i ]); 99 getLogger().info("Adding autodiscovered server " + serversArray[i]); 100 } 101 } 102 } 103 104 final Configuration serversConfiguration = configuration.getChild( "servers" ); 106 final Configuration[] serverConfigurations = 107 serversConfiguration.getChildren( "server" ); 108 109 for ( int i = 0; i < serverConfigurations.length; i++ ) { 110 dnsServers.add( serverConfigurations[ i ].getValue() ); 111 } 112 113 if (dnsServers.isEmpty()) { 114 getLogger().info("No DNS servers have been specified or found by autodiscovery - adding 127.0.0.1"); 115 dnsServers.add("127.0.0.1"); 116 } 117 118 final boolean authoritative = 119 configuration.getChild( "authoritative" ).getValueAsBoolean( false ); 120 dnsCredibility = authoritative ? Credibility.AUTH_ANSWER : Credibility.NONAUTH_ANSWER; 123 } 124 125 128 public void initialize() 129 throws Exception { 130 131 getLogger().debug("DNSServer init..."); 132 133 if (dnsServers.isEmpty()) { 135 try { 136 dnsServers.add( InetAddress.getLocalHost().getHostName() ); 137 } catch ( UnknownHostException ue ) { 138 dnsServers.add( "127.0.0.1" ); 139 } 140 } 141 142 final String [] serversArray = (String [])dnsServers.toArray(new String [0]); 144 145 if (getLogger().isInfoEnabled()) { 146 for(int c = 0; c < serversArray.length; c++) { 147 getLogger().info("DNS Server is: " + serversArray[c]); 148 } 149 } 150 151 try { 152 resolver = new ExtendedResolver( serversArray ); 153 Lookup.setDefaultResolver(resolver); 154 } catch (UnknownHostException uhe) { 155 getLogger().fatalError("DNS service could not be initialized. The DNS servers specified are not recognized hosts.", uhe); 156 throw uhe; 157 } 158 159 cache = new Cache (DClass.IN); 160 161 getLogger().debug("DNSServer ...init end"); 162 } 163 164 169 public String [] getDNSServers() { 170 return (String [])dnsServers.toArray(new String [0]); 171 } 172 173 182 public Collection findMXRecords(String hostname) { 183 Record answers[] = lookup(hostname, Type.MX); 184 List servers = new ArrayList(); 185 try { 186 if (answers == null) { 187 return servers; 188 } 189 190 MXRecord mxAnswers[] = new MXRecord[answers.length]; 191 for (int i = 0; i < answers.length; i++) { 192 mxAnswers[i] = (MXRecord)answers[i]; 193 } 194 195 Arrays.sort(mxAnswers, mxComparator); 196 197 for (int i = 0; i < mxAnswers.length; i++) { 198 servers.add(mxAnswers[i].getTarget ().toString ()); 199 getLogger().debug(new StringBuffer ("Found MX record ").append(mxAnswers[i].getTarget ().toString ()).toString()); 200 } 201 return Collections.unmodifiableCollection(servers); 202 } finally { 203 if (servers.size () == 0) { 206 StringBuffer logBuffer = 207 new StringBuffer (128) 208 .append("Couldn't resolve MX records for domain ") 209 .append(hostname) 210 .append("."); 211 getLogger().info(logBuffer.toString()); 212 try { 213 getByName(hostname); 214 servers.add(hostname); 215 } catch (UnknownHostException uhe) { 216 logBuffer = new StringBuffer (128) 220 .append("Couldn't resolve IP address for host ") 221 .append(hostname) 222 .append("."); 223 getLogger().error(logBuffer.toString()); 224 } 225 } 226 } 227 } 228 229 238 public Record[] lookup(String name, int type) { 239 return rawDNSLookup(name,false,type); 240 } 241 242 249 private Record[] rawDNSLookup(String namestr, boolean querysent, int type) { 250 Name name = null; 251 try { 252 name = Name.fromString(namestr, Name.root); 253 } catch (TextParseException tpe) { 254 getLogger().error("Couldn't parse name " + namestr, tpe); 256 return null; 257 } 258 int dclass = DClass.IN; 259 260 SetResponse cached = cache.lookupRecords(name, type, dnsCredibility); 261 if (cached.isSuccessful()) { 262 getLogger().debug(new StringBuffer (256) 263 .append("Retrieving MX record for ") 264 .append(name).append(" from cache") 265 .toString()); 266 267 return processSetResponse(cached); 268 } 269 else if (cached.isNXDOMAIN() || cached.isNXRRSET()) { 270 return null; 271 } 272 else if (querysent) { 273 return null; 274 } 275 else { 276 getLogger().debug(new StringBuffer (256) 277 .append("Looking up MX record for ") 278 .append(name) 279 .toString()); 280 Record question = Record.newRecord(name, type, dclass); 281 Message query = Message.newQuery(question); 282 Message response = null; 283 284 try { 285 response = resolver.send(query); 286 } 287 catch (Exception ex) { 288 getLogger().warn("Query error!", ex); 289 return null; 290 } 291 292 int rcode = response.getHeader().getRcode(); 293 if (rcode == Rcode.NOERROR || rcode == Rcode.NXDOMAIN) { 294 cached = cache.addMessage(response); 295 if (cached != null && cached.isSuccessful()) { 296 return processSetResponse(cached); 297 } 298 } 299 300 if (rcode != Rcode.NOERROR) { 301 return null; 302 } 303 304 return rawDNSLookup(namestr, true, type); 305 } 306 } 307 308 private Record[] processSetResponse(SetResponse sr) { 309 Record [] answers; 310 int answerCount = 0, n = 0; 311 312 RRset [] rrsets = sr.answers(); 313 answerCount = 0; 314 for (int i = 0; i < rrsets.length; i++) { 315 answerCount += rrsets[i].size(); 316 } 317 318 answers = new Record[answerCount]; 319 320 for (int i = 0; i < rrsets.length; i++) { 321 Iterator iter = rrsets[i].rrs(); 322 while (iter.hasNext()) { 323 Record r = (Record)iter.next(); 324 answers[n++] = r; 325 } 326 } 327 return answers; 328 } 329 330 347 private static class MXRecordComparator implements Comparator { 348 private final static Random random = new Random(); 349 public int compare (Object a, Object b) { 350 int pa = ((MXRecord)a).getPriority(); 351 int pb = ((MXRecord)b).getPriority(); 352 return (pa == pb) ? (512 - random.nextInt(1024)) : pa - pb; 353 } 354 } 355 356 373 public Iterator getSMTPHostAddresses(final String domainName) { 374 return new Iterator() { 375 private Iterator mxHosts = findMXRecords(domainName).iterator(); 376 private Iterator addresses = null; 377 378 public boolean hasNext() { 379 386 if ((addresses == null || !addresses.hasNext()) && mxHosts.hasNext()) do { 387 final String nextHostname = (String )mxHosts.next(); 388 InetAddress [] addrs = null; 389 try { 390 addrs = getAllByName(nextHostname); 391 } catch (UnknownHostException uhe) { 392 StringBuffer logBuffer = new StringBuffer (128) 396 .append("Couldn't resolve IP address for discovered host ") 397 .append(nextHostname) 398 .append("."); 399 getLogger().error(logBuffer.toString()); 400 } 401 final InetAddress [] ipAddresses = addrs; 402 403 addresses = new Iterator() { 404 int i = 0; 405 406 public boolean hasNext() { 407 return ipAddresses != null && i < ipAddresses.length; 408 } 409 410 public Object next() { 411 return new org.apache.mailet.HostAddress(nextHostname, "smtp://" + ipAddresses[i++].getHostAddress()); 412 } 413 414 public void remove() { 415 throw new UnsupportedOperationException ("remove not supported by this iterator"); 416 } 417 }; 418 } while (!addresses.hasNext() && mxHosts.hasNext()); 419 420 return addresses != null && addresses.hasNext(); 421 } 422 423 public Object next() { 424 return addresses != null ? addresses.next() : null; 425 } 426 427 public void remove() { 428 throw new UnsupportedOperationException ("remove not supported by this iterator"); 429 } 430 }; 431 } 432 433 448 449 private static String allowIPLiteral(String host) { 450 if ((host.charAt(host.length() - 1) == '.')) { 451 String possible_ip_literal = host.substring(0, host.length() - 1); 452 if (org.xbill.DNS.Address.isDottedQuad(possible_ip_literal)) { 453 host = possible_ip_literal; 454 } 455 } 456 return host; 457 } 458 459 462 public static InetAddress getByName(String host) throws UnknownHostException { 463 return org.xbill.DNS.Address.getByName(allowIPLiteral(host)); 464 } 465 466 469 public static InetAddress [] getAllByName(String host) throws UnknownHostException { 470 return org.xbill.DNS.Address.getAllByName(allowIPLiteral(host)); 471 } 472 473 491 492 493 494 503 504 private class MxSorter implements Iterator { 505 private int priorListPriority = Integer.MIN_VALUE; 506 private ArrayList equiPriorityList = new ArrayList(); 507 private Record[] mxRecords; 508 private Random rnd = new Random (); 509 510 520 521 private MxSorter(String domainName) { 522 mxRecords = lookup(domainName, Type.MX); 523 if (mxRecords == null || mxRecords.length == 0) { 524 Record[] aRecords = lookup(domainName, Type.A); 526 if(aRecords != null && aRecords.length > 0) { 527 equiPriorityList.add(domainName); 528 } 529 } 530 } 531 532 542 private void createPriorityList(){ 543 int leastPriorityFound = Integer.MAX_VALUE; 544 548 for (int i = 0; i < mxRecords.length; i++) { 549 MXRecord thisRecord = (MXRecord)mxRecords[i]; 550 int thisRecordPriority = thisRecord.getPriority(); 551 if (thisRecordPriority > priorListPriority) { 552 if (thisRecordPriority < leastPriorityFound) { 553 equiPriorityList.clear(); 554 leastPriorityFound = thisRecordPriority; 555 equiPriorityList.add(thisRecord.getTarget().toString()); 556 } else if (thisRecordPriority == leastPriorityFound) { 557 equiPriorityList.add(thisRecord.getTarget().toString()); 558 } 559 } 560 } 561 priorListPriority = leastPriorityFound; 562 } 563 564 public boolean hasNext(){ 565 if (equiPriorityList.size() > 0){ 566 return true; 567 }else if (mxRecords != null && mxRecords.length > 0){ 568 createPriorityList(); 569 return equiPriorityList.size() > 0; 570 } else{ 571 return false; 572 } 573 } 574 575 public Object next(){ 576 if (hasNext()){ 577 578 579 int getIndex = rnd.nextInt(equiPriorityList.size()); 580 Object returnElement = equiPriorityList.get(getIndex); 581 equiPriorityList.remove(getIndex); 582 return returnElement; 583 }else{ 584 throw new NoSuchElementException(); 585 } 586 } 587 588 public void remove () { 589 throw new UnsupportedOperationException ("remove not supported by this iterator"); 590 } 591 } 592 593 594 603 public void dispose() 604 { 605 cache.setCleanInterval (-1); 608 } 609 } 610 | Popular Tags |