/************************************************************************* * Copyright 2009-2014 Eucalyptus Systems, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. * * Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta * CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need * additional information or have any questions. * * This file may incorporate work covered under the following copyright * and permission notice: * * Software License Agreement (BSD License) * * Copyright (c) 2008, Regents of the University of California * All rights reserved. * * Redistribution and use of this software in source and binary forms, * with or without modification, are permitted provided that the * following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. USERS OF THIS SOFTWARE ACKNOWLEDGE * THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE LICENSED MATERIAL, * COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS SOFTWARE, * AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING * IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA, * SANTA BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY, * WHICH IN THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION, * REPLACEMENT OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO * IDENTIFIED, OR WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT * NEEDED TO COMPLY WITH ANY SUCH LICENSES OR RIGHTS. * * This file may incorporate work covered under the following copyright * and permission notice: * * Copyright (c) 1999-2004, Brian Wellington. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. ************************************************************************/ package com.eucalyptus.cloud.ws; import static com.eucalyptus.util.dns.DnsResolvers.DnsRequest; import java.io.IOException; import java.net.InetAddress; import java.net.Socket; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.log4j.Logger; import org.xbill.DNS.CNAMERecord; import org.xbill.DNS.Credibility; import org.xbill.DNS.DNAMERecord; import org.xbill.DNS.ExtendedFlags; import org.xbill.DNS.Flags; import org.xbill.DNS.Header; import org.xbill.DNS.Message; import org.xbill.DNS.Name; import org.xbill.DNS.NameTooLongException; import org.xbill.DNS.OPTRecord; import org.xbill.DNS.Opcode; import org.xbill.DNS.RRset; import org.xbill.DNS.Rcode; import org.xbill.DNS.Record; import org.xbill.DNS.Section; import org.xbill.DNS.SetResponse; import org.xbill.DNS.TSIG; import org.xbill.DNS.TSIGRecord; import org.xbill.DNS.Type; import com.eucalyptus.dns.Cache; import com.eucalyptus.system.Threads; import com.eucalyptus.util.Pair; import com.eucalyptus.util.dns.DnsResolvers; import com.google.common.base.Function; import com.google.common.base.Optional; public class ConnectionHandler extends Thread { private static Logger LOG = Logger.getLogger( ConnectionHandler.class ); static final int FLAG_DNSSECOK = 1; static final int FLAG_SIGONLY = 2; Map caches = new ConcurrentHashMap(); public ConnectionHandler() { super( Threads.threadUniqueName( "dns-connection-handler" ) ); } byte [] generateReply(Message query, byte [] in, int length, Socket s) throws IOException { Header header; boolean badversion; int maxLength; boolean sigonly; SetResponse sr; int flags = 0; header = query.getHeader(); if (header.getRcode() != Rcode.NOERROR) return errorMessage(query, Rcode.FORMERR); if (header.getOpcode() != Opcode.QUERY) return errorMessage(query, Rcode.NOTIMP); Record queryRecord = query.getQuestion(); TSIGRecord queryTSIG = query.getTSIG(); TSIG tsig = null; OPTRecord queryOPT = query.getOPT(); if (queryOPT != null && queryOPT.getVersion() > 0) badversion = true; if (s != null) maxLength = 65535; else if (queryOPT != null) maxLength = Math.max(queryOPT.getPayloadSize(), 512); else maxLength = 512; if (queryOPT != null && (queryOPT.getFlags() & ExtendedFlags.DO) != 0) flags = FLAG_DNSSECOK; Message response = new Message(query.getHeader().getID()); response.getHeader().setFlag(Flags.QR); if (query.getHeader().getFlag(Flags.RD)) response.getHeader().setFlag(Flags.RD); if(queryRecord != null) { response.addRecord(queryRecord, Section.QUESTION); Name name = queryRecord.getName(); int type = queryRecord.getType(); int dclass = queryRecord.getDClass(); if (!Type.isRR(type) && type != Type.ANY) return errorMessage(query, Rcode.NOTIMP); byte rcode = addAnswer(response, name, type, dclass, 0, flags); if (rcode != Rcode.NOERROR && rcode != Rcode.NXDOMAIN) { if(rcode == Rcode.REFUSED) return errorMessage(query, Rcode.REFUSED); else return errorMessage(query, Rcode.SERVFAIL); } if (queryOPT != null) { int optflags = (flags == FLAG_DNSSECOK) ? ExtendedFlags.DO : 0; OPTRecord opt = new OPTRecord((short)4096, rcode, (byte)0, optflags); response.addRecord(opt, Section.ADDITIONAL); } } response.setTSIG(tsig, Rcode.NOERROR, queryTSIG); return response.toWire(maxLength); } byte addAnswer( final Message response, Name name, int type, int dclass, int iterations, int flags) { SetResponse sr = null; byte rcode = Rcode.NOERROR; if (iterations > 6) return Rcode.NOERROR; if (type == Type.SIG || type == Type.RRSIG) { type = Type.ANY; flags |= FLAG_SIGONLY; } try { sr = DnsResolvers.findRecords( response, new DnsRequest() { @Override public Record getQuery() { return response.getQuestion( ); } @Override public InetAddress getLocalAddress() { return ConnectionHandler.getLocalInetAddress(); } @Override public InetAddress getRemoteAddress() { return ConnectionHandler.getRemoteInetAddress(); } } ); if ( sr == null ) { return Rcode.SERVFAIL; } else { if (sr.isDelegation()) { RRset nsRecords = sr.getNS(); addRRset(nsRecords.getName(), response, nsRecords, Section.AUTHORITY, flags); }else if (sr.isCNAME()) { CNAMERecord cname = sr.getCNAME(); RRset rrset = new RRset(cname); addRRset(name, response, rrset, Section.ANSWER, flags); } else if (sr.isDNAME()) { DNAMERecord dname = sr.getDNAME(); RRset rrset = new RRset(dname); addRRset(name, response, rrset, Section.ANSWER, flags); Name newname; try { newname = name.fromDNAME(dname); } catch (NameTooLongException e) { return Rcode.YXDOMAIN; } if(newname != null) { rrset = new RRset(new CNAMERecord(name, dclass, 0, newname)); addRRset(name, response, rrset, Section.ANSWER, flags); } } if ( sr.isSuccessful( ) ) { if (type == Type.AAAA) response.getHeader().setFlag(Flags.AA); return Rcode.NOERROR; } else if ( sr.isNXDOMAIN( )) { response.getHeader().setRcode(Rcode.NXDOMAIN); return Rcode.NXDOMAIN; } else if (response.getHeader().getRcode() == Rcode.REFUSED) { return Rcode.REFUSED; } else return Rcode.SERVFAIL; } } catch ( Exception ex ) { Logger.getLogger( DnsResolvers.class ).error( ex ); return Rcode.SERVFAIL; } } private final void addCacheNS(Message response, Cache cache, Name name) { SetResponse sr = cache.lookupRecords(name, Type.NS, Credibility.HINT); if (!sr.isDelegation()) return; RRset nsRecords = sr.getNS(); Iterator it = nsRecords.rrs(); while (it.hasNext()) { Record r = (Record) it.next(); response.addRecord(r, Section.AUTHORITY); } } void addRRset(Name name, Message response, RRset rrset, int section, int flags) { for (int s = 1; s <= section; s++) if (response.findRRset(name, rrset.getType(), s)) return; if ((flags & FLAG_SIGONLY) == 0) { Iterator it = rrset.rrs(); while (it.hasNext()) { Record r = (Record) it.next(); if (r.getName().isWild() && !name.isWild()) r = r.withName(name); response.addRecord(r, section); } } if ((flags & (FLAG_SIGONLY | FLAG_DNSSECOK)) != 0) { Iterator it = rrset.sigs(); while (it.hasNext()) { Record r = (Record) it.next(); if (r.getName().isWild() && !name.isWild()) r = r.withName(name); response.addRecord(r, section); } } } byte [] buildErrorMessage(Header header, int rcode, Record question) { Message response = new Message(); response.setHeader(header); for (int i = 0; i < 4; i++) response.removeAllRecords(i); if (rcode == Rcode.SERVFAIL) response.addRecord(question, Section.QUESTION); header.setRcode(rcode); return response.toWire(); } public byte [] errorMessage(Message query, int rcode) { return buildErrorMessage(query.getHeader(), rcode, query.getQuestion()); } public byte [] formerrMessage(byte [] in) { Header header; try { if(in != null) header = new Header(in); else header = new Header(); } catch (IOException e) { return null; } return buildErrorMessage(header, Rcode.FORMERR, null); } public Cache getCache(int dclass) { Cache c = (Cache) caches.get(new Integer(dclass)); if (c == null) { c = new Cache(dclass); caches.put(new Integer(dclass), c); } return c; } private static final ThreadLocal<Pair<InetAddress,InetAddress>> localAndRemoteInetAddresses = new ThreadLocal<>(); private static InetAddress getInetAddress( final Function<Pair<InetAddress,InetAddress>,InetAddress> extractor ) { return Optional.fromNullable( localAndRemoteInetAddresses.get( ) ).transform( extractor ).orNull( ); } static InetAddress getLocalInetAddress( ) { return getInetAddress( Pair.<InetAddress,InetAddress>left( ) ); } static InetAddress getRemoteInetAddress( ) { return getInetAddress( Pair.<InetAddress, InetAddress>right() ); } static void setLocalAndRemoteInetAddresses( InetAddress local, InetAddress remote ) { ConnectionHandler.localAndRemoteInetAddresses.set( Pair.pair( local, remote ) ); } static void clearInetAddresses( ) { ConnectionHandler.localAndRemoteInetAddresses.remove( ); } }