All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.aoindustries.aoserv.client.DNSZone Maven / Gradle / Ivy

There is a newer version: 1.92.0
Show newest version
/*
 * aoserv-client - Java client for the AOServ platform.
 * Copyright (C) 2001-2013, 2014, 2016  AO Industries, Inc.
 *     [email protected]
 *     7262 Bull Pen Cir
 *     Mobile, AL 36695
 *
 * This file is part of aoserv-client.
 *
 * aoserv-client is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * aoserv-client 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with aoserv-client.  If not, see .
 */
package com.aoindustries.aoserv.client;

import com.aoindustries.io.CompressedDataInputStream;
import com.aoindustries.io.CompressedDataOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

/**
 * A DNSZone is one domain hosted in the name servers.  It can have
 * any number of DNSRecords.  Please see DNSTLD for
 * domain restrictions.
 *
 * @see  DNSTLD
 * @see  DNSRecord
 *
 * @author  AO Industries, Inc.
 */
final public class DNSZone extends CachedObjectStringKey implements Removable, Dumpable {

	static final int
		COLUMN_ZONE=0,
		COLUMN_PACKAGE=2
	;
	static final String COLUMN_ZONE_name= "zone";

	public static final int DEFAULT_TTL = 43200;

	/**
	 * The maximum number of characters allowed in a line.
	 */
	public static final int MAX_LINE_LENGTH = 255;

	/**
	 * The zone that is in charge of the API.
	 */
	public static final String API_ZONE="aoindustries.com.";

	/**
	 * The hostmaster that is placed in a newly created DNSZone.
	 */
	public static final String DEFAULT_HOSTMASTER="hostmaster."+API_ZONE;

	/**
	 * The default priority for new MX records.
	 */
	public static final int DEFAULT_MX_PRIORITY = 10;

	private String file;
	String packageName;
	private String hostmaster;
	private long serial;
	private int ttl;

	public int addDNSRecord(
		String domain,
		DNSType type,
		int priority,
		int weight,
		int port,
		String destination,
		int ttl
	) throws IOException, SQLException {
		return table.connector.getDnsRecords().addDNSRecord(this, domain, type, priority, weight, port, destination, ttl);
	}

	@Override
	public void dump(PrintWriter out) throws SQLException, IOException {
		printZoneFile(out);
	}

	public DNSType[] getAllowedDNSTypes() throws IOException, SQLException {
		DNSTypeTable tt=table.connector.getDnsTypes();
		if(isArpa()) {
			DNSType[] types={
				tt.get(DNSType.NS),
				tt.get(DNSType.PTR)
			};
			return types;
		} else {
			DNSType[] types={
				tt.get(DNSType.A),
				tt.get(DNSType.AAAA),
				tt.get(DNSType.CNAME),
				tt.get(DNSType.MX),
				tt.get(DNSType.NS),
				tt.get(DNSType.SRV),
				tt.get(DNSType.TXT)
			};
			return types;
		}
	}

	public static String getArpaZoneForIPAddress(String ip, String netmask) throws IllegalArgumentException {
		if(!IPAddress.isValidIPAddress(ip)) throw new IllegalArgumentException("Invalid IP address: "+ip);
		if(netmask.equals("255.255.255.0")) {
			int pos = ip.indexOf('.');
			int oct1 = Integer.parseInt(ip.substring(0, pos));
			int pos2 = ip.indexOf('.', pos+1);
			int oct2 = Integer.parseInt(ip.substring(pos+1, pos2));
			pos = ip.indexOf('.', pos2+1);
			int oct3 = Integer.parseInt(ip.substring(pos2+1, pos));
			return oct3+"."+oct2+"."+oct1+".in-addr.arpa";
		} else if(netmask.equals("255.255.255.128")) {
			// Hurricane Electric compatible
			int pos = ip.indexOf('.');
			int oct1 = Integer.parseInt(ip.substring(0, pos));
			int pos2 = ip.indexOf('.', pos+1);
			int oct2 = Integer.parseInt(ip.substring(pos+1, pos2));
			pos = ip.indexOf('.', pos2+1);
			int oct3 = Integer.parseInt(ip.substring(pos2+1, pos));
			int oct4 = Integer.parseInt(ip.substring(pos+1));
			return "subnet"+(oct4&128)+"."+oct3+"."+oct2+"."+oct1+".in-addr.arpa";
		} else throw new IllegalArgumentException("Unsupported netmask: "+netmask);
	}

	@Override
	Object getColumnImpl(int i) {
		switch(i) {
			case COLUMN_ZONE: return pkey;
			case 1: return file;
			case COLUMN_PACKAGE: return packageName;
			case 3: return hostmaster;
			case 4: return serial;
			case 5: return ttl;
			default: throw new IllegalArgumentException("Invalid index: "+i);
		}
	}

	public static long getCurrentSerial() {
		Calendar cal=Calendar.getInstance();
		return
			cal.get(Calendar.YEAR)*1000000L
			+ (cal.get(Calendar.MONTH)+1)*10000
			+ cal.get(Calendar.DATE)*100
			+ 01
		;
	}

	public List getDNSRecords() throws IOException, SQLException {
		return table.connector.getDnsRecords().getDNSRecords(this);
	}

	public List getDNSRecords(String domain, DNSType type) throws IOException, SQLException {
		return table.connector.getDnsRecords().getDNSRecords(this, domain, type);
	}

	public String getFile() {
		return file;
	}

	public String getHostmaster() {
		return hostmaster;
	}

	public Package getPackage() throws SQLException, IOException {
		Package obj=table.connector.getPackages().get(packageName);
		if(obj==null) throw new SQLException("Unable to find Package: "+packageName);
		return obj;
	}

	public long getSerial() {
		return serial;
	}

	public int getTTL() {
		return ttl;
	}

	@Override
	public SchemaTable.TableID getTableID() {
		return SchemaTable.TableID.DNS_ZONES;
	}

	public String getZone() {
		return pkey;
	}

	public String getZoneFile() throws SQLException, IOException {
		ByteArrayOutputStream bout=new ByteArrayOutputStream();
		PrintWriter out=new PrintWriter(bout);
		printZoneFile(out);
		out.flush();
		return new String(bout.toByteArray());
	}

	@Override
	public void init(ResultSet result) throws SQLException {
		pkey=result.getString(1);
		file=result.getString(2);
		packageName=result.getString(3);
		hostmaster=result.getString(4);
		serial=result.getLong(5);
		ttl=result.getInt(6);
	}

	public boolean isArpa() {
		return pkey.length()>13 && pkey.substring(pkey.length()-13).equals(".in-addr.arpa");
	}

	/**
	 * Checks that the line is not too long, prints the line, and clears the buffer.
	 */
	private static void printLine(StringBuilder line, PrintWriter out) {
		if(line.length()>MAX_LINE_LENGTH) throw new IllegalStateException("Line too long: "+line);
		out.print(line);
		out.print('\n');
		line.setLength(0);
	}

	private static void printRecord(String linePrefix, StringBuilder line, PrintWriter out, String domain, int ttl, int recordTtl, String type, int priority, int weight, int port, String destination) {
		line.append(linePrefix);
		line.append(domain);
		int count=Math.max(1, 24-domain.length());
		for(int d=0;d= (char)0x7f from the destination
				line.append('"');
				for(int d=0, dlen=destination.length(); d=' '
						&& ch<(char)0x7f
					) line.append(ch);
				}
				line.append('"');
				printLine(line, out);
			} else {
				// Begin parenthesis
				line.append('(');
				printLine(line, out);
				// Double-quote TXT types and filter " and anything < (space) or >= (char)0x7f from the destination
				final int charsPerLine = MAX_LINE_LENGTH-(linePrefix.length()+2);
				for(int lineStart=0; lineStart=' '
							&& ch<(char)0x7f
						) line.append(ch);
					}
					line.append('"');
					printLine(line, out);
				}
				// End parenthesis
				line.append(linePrefix);
				line.append(')');
				printLine(line, out);
			}
		} else {
			line.append(destination);
			printLine(line, out);
		}
	}

	public void printZoneFile(PrintWriter out) throws SQLException, IOException {
		StringBuilder line = new StringBuilder(); // Buffers each line to ensure not too long
		List records=getDNSRecords();
		line.append("$TTL    ").append(ttl);
		printLine(line, out);
		if(!isArpa()) {
			line.append("$ORIGIN ").append(pkey);
			printLine(line, out);
		}
		line.append("@                       ").append(ttl).append(" IN   SOA     ");
		// Find the first nameserver
		DNSRecord firstNS=null;
		for(DNSRecord record : records) {
			if(record.getType().getType().equals(DNSType.NS)) {
				firstNS=record;
				break;
			}
		}
		line.append(firstNS==null ? "ns1.aoindustries.com." : firstNS.getDestination());
		line.append("   ").append(hostmaster).append(" (");
		printLine(line, out);
		line.append("                                ").append(serial).append(" ; serial");
		printLine(line, out);
		line.append("                                3600    ; refresh");
		printLine(line, out);
		line.append("                                600     ; retry");
		printLine(line, out);
		line.append("                                1814400 ; expiry");
		printLine(line, out);
		line.append("                                300     ; minimum");
		printLine(line, out);
		line.append("                                )");
		printLine(line, out);
		if(firstNS==null) {
			// Add the default nameservers because named will refuse to start without them
			line.append("; No name servers configured, using the defaults");
			printLine(line, out);
			printRecord("", line, out, "@", ttl, DNSRecord.NO_TTL, DNSType.NS, DNSRecord.NO_PRIORITY, DNSRecord.NO_WEIGHT, DNSRecord.NO_PORT, "ns1.aoindustries.com.");
			printRecord("", line, out, "@", ttl, DNSRecord.NO_TTL, DNSType.NS, DNSRecord.NO_PRIORITY, DNSRecord.NO_WEIGHT, DNSRecord.NO_PORT, "ns2.aoindustries.com.");
			printRecord("", line, out, "@", ttl, DNSRecord.NO_TTL, DNSType.NS, DNSRecord.NO_PRIORITY, DNSRecord.NO_WEIGHT, DNSRecord.NO_PORT, "ns3.aoindustries.com.");
			printRecord("", line, out, "@", ttl, DNSRecord.NO_TTL, DNSType.NS, DNSRecord.NO_PRIORITY, DNSRecord.NO_WEIGHT, DNSRecord.NO_PORT, "ns4.aoindustries.com.");
		}
		int len=records.size();
		for(int c=0;c getCannotRemoveReasons() {
		List reasons=new ArrayList<>();
		if(pkey.equals(API_ZONE)) reasons.add(new CannotRemoveReason("Not allowed to remove the API Zone: "+API_ZONE));
		return reasons;
	}

	@Override
	public void remove() throws IOException, SQLException {
		table.connector.requestUpdateIL(true, AOServProtocol.CommandID.REMOVE, SchemaTable.TableID.DNS_ZONES, pkey);
	}

	public void setTTL(int ttl) throws IOException, SQLException {
		table.connector.requestUpdateIL(true, AOServProtocol.CommandID.SET_DNS_ZONE_TTL, pkey, ttl);
		this.ttl=ttl;
	}

	@Override
	public void write(CompressedDataOutputStream out, AOServProtocol.Version version) throws IOException {
		out.writeUTF(pkey);
		out.writeUTF(file);
		out.writeUTF(packageName);
		out.writeUTF(hostmaster);
		out.writeLong(serial);
		if(version.compareTo(AOServProtocol.Version.VERSION_1_0_A_127)>=0) out.writeCompressedInt(ttl);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy