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

com.aoindustries.aoserv.master.IpAddressHandler Maven / Gradle / Ivy

There is a newer version: 1.91.8
Show newest version
/*
 * aoserv-master - Master server for the AOServ Platform.
 * Copyright (C) 2001-2013, 2014, 2015, 2017, 2018, 2019, 2020  AO Industries, Inc.
 *     [email protected]
 *     7262 Bull Pen Cir
 *     Mobile, AL 36695
 *
 * This file is part of aoserv-master.
 *
 * aoserv-master 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-master 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-master.  If not, see .
 */
package com.aoindustries.aoserv.master;

import com.aoindustries.aoserv.client.account.Account;
import com.aoindustries.aoserv.client.dns.Zone;
import com.aoindustries.aoserv.client.master.User;
import com.aoindustries.aoserv.client.net.DeviceId;
import com.aoindustries.aoserv.client.net.IpAddress;
import com.aoindustries.aoserv.client.schema.Table;
import com.aoindustries.aoserv.master.dns.DnsService;
import com.aoindustries.dbc.DatabaseConnection;
import com.aoindustries.net.DomainName;
import com.aoindustries.net.InetAddress;
import com.aoindustries.validation.ValidationException;
import java.io.IOException;
import java.sql.SQLException;
import org.apache.commons.lang3.NotImplementedException;

/**
 * The IPAddressHandler handles all the accesses to the net.IpAddress table.
 *
 * @author  AO Industries, Inc.
 */
final public class IpAddressHandler {

	public static void checkAccessIpAddress(DatabaseConnection conn, RequestSource source, String action, int ipAddressId) throws IOException, SQLException {
		User mu = MasterServer.getUser(conn, source.getCurrentAdministrator());
		if(mu!=null) {
			if(MasterServer.getUserHosts(conn, source.getCurrentAdministrator()).length!=0) {
				NetHostHandler.checkAccessHost(conn, source, action, getHostForIpAddress(conn, ipAddressId));
			}
		} else {
			PackageHandler.checkAccessPackage(conn, source, action, getPackageForIpAddress(conn, ipAddressId));
		}
	}

	public static boolean isDhcpAddress(DatabaseConnection conn, int ipAddress) throws IOException, SQLException {
		return conn.executeBooleanQuery(
			"select \"isDhcp\" from net.\"IpAddress\" where id=?",
			ipAddress
		);
	}

	@SuppressWarnings("deprecation")
	public static DomainName getUnassignedHostname(
		DatabaseConnection conn,
		int ipAddress
	) throws IOException, SQLException {
		try {
			final InetAddress inetAddress = getInetAddressForIpAddress(conn, ipAddress);
			switch(inetAddress.getAddressFamily()) {
				case INET : {
					String ip = inetAddress.toString();
					int pos=ip.lastIndexOf('.');
					final String octet=ip.substring(pos+1);
					int host = getHostForIpAddress(conn, ipAddress);
					final String net;
					if(ip.startsWith("66.160.183.")) net = "net1.";
					else if(ip.startsWith("64.62.174.")) net = "net2.";
					else if(ip.startsWith("64.71.144.")) net = "net3.";
					else net = "";
					final String farm=NetHostHandler.getFarmForHost(conn, host);
					String hostname="unassigned"+octet+"."+net+farm+'.'+Zone.API_ZONE;
					while(hostname.endsWith(".")) hostname = hostname.substring(0, hostname.length()-1);
					return DomainName.valueOf(hostname);
				}
				case INET6 : {
					throw new NotImplementedException();
				}
				default :
					throw new AssertionError();
			}
		} catch(ValidationException e) {
			throw new SQLException(e.getLocalizedMessage(), e);
		}
	}

	public static void moveIpAddress(
		DatabaseConnection conn,
		RequestSource source,
		InvalidateList invalidateList,
		int ipAddress,
		int toHost
	) throws IOException, SQLException {
		checkAccessIpAddress(conn, source, "moveIpAddress", ipAddress);
		NetHostHandler.checkAccessHost(conn, source, "moveIpAddress", toHost);
		int fromHost = getHostForIpAddress(conn, ipAddress);
		NetHostHandler.checkAccessHost(conn, source, "moveIpAddress", fromHost);

		Account.Name account = getAccountForIpAddress(conn, ipAddress);

		// Update net.IpAddress
		int toDevice = conn.executeIntQuery(
			"select id from net.\"Device\" where server=? and \"deviceId\"=?",
			toHost,
			DeviceId.ETH0
		);
		conn.executeUpdate(
			"update net.\"IpAddress\" set device=? where id=?",
			toDevice,
			ipAddress
		);

		// Notify all clients of the update
		invalidateList.addTable(
			conn,
			Table.TableID.IP_ADDRESSES,
			account,
			fromHost,
			false
		);
		invalidateList.addTable(
			conn,
			Table.TableID.IP_ADDRESSES,
			account,
			toHost,
			false
		);
	}

	/**
	 * Sets the IP address for a DHCP-enabled IP address.
	 */
	public static void setDhcpAddressDestination(
		DatabaseConnection conn,
		RequestSource source,
		InvalidateList invalidateList,
		int dhcpAddress,
		InetAddress inetAddress
	) throws IOException, SQLException {
		checkAccessIpAddress(conn, source, "setIPAddressDHCPAddress", dhcpAddress);
		if(!isDhcpAddress(conn, dhcpAddress)) throw new SQLException("net.IpAddress is not DHCP-enabled: " + dhcpAddress);

		Account.Name account = getAccountForIpAddress(conn, dhcpAddress);
		int host = getHostForIpAddress(conn, dhcpAddress);

		// Update the table
		conn.executeUpdate("update net.\"IpAddress\" set \"inetAddress\"=?::\"com.aoindustries.net\".\"InetAddress\" where id=?", inetAddress, dhcpAddress);

		// Notify all clients of the update
		invalidateList.addTable(
			conn,
			Table.TableID.IP_ADDRESSES,
			account,
			host,
			false
		);

		// Update any DNS records that follow this IP address
		MasterServer.getService(DnsService.class).updateDhcpDnsRecords(conn, invalidateList, dhcpAddress, inetAddress);
	}

	/**
	 * Sets the hostname for an IP address.
	 */
	public static void setIpAddressHostname(
		DatabaseConnection conn,
		RequestSource source,
		InvalidateList invalidateList,
		int ipAddress,
		DomainName hostname
	) throws IOException, SQLException {
		checkAccessIpAddress(conn, source, "setIPAddressHostname", ipAddress);
		MasterServer.checkAccessHostname(conn, source, "setIPAddressHostname", hostname.toString());

		setIpAddressHostname(conn, invalidateList, ipAddress, hostname);
	}

	/**
	 * Sets the hostname for an IP address.
	 */
	public static void setIpAddressHostname(
		DatabaseConnection conn,
		InvalidateList invalidateList,
		int ipAddress,
		DomainName hostname
	) throws IOException, SQLException {
		// Can't set the hostname on a disabled package
		//String packageName=getPackageForIPAddress(conn, ipAddress);
		//if(PackageHandler.isPackageDisabled(conn, packageName)) throw new SQLException("Unable to set hostname for an IP address, package disabled: "+packageName);

		InetAddress ip = getInetAddressForIpAddress(conn, ipAddress);
		if(
			ip.isLoopback()
			|| ip.isUnspecified()
		) throw new SQLException("Not allowed to set the hostname for "+ip);

		// Update the table
		conn.executeUpdate("update net.\"IpAddress\" set hostname=? where id=?", hostname.toString(), ipAddress);

		// Notify all clients of the update
		invalidateList.addTable(
			conn,
			Table.TableID.IP_ADDRESSES,
			getAccountForIpAddress(conn, ipAddress),
			getHostForIpAddress(conn, ipAddress),
			false
		);

		// Update any reverse DNS matchins this IP address
		MasterServer.getService(DnsService.class).updateReverseDnsIfExists(conn, invalidateList, ip, hostname);
	}

	public static void setIpAddressMonitoringEnabled(
		DatabaseConnection conn,
		RequestSource source,
		InvalidateList invalidateList,
		int ipAddress,
		boolean monitoringEnabled
	) throws IOException, SQLException {
		checkAccessIpAddress(conn, source, "setIPAddressMonitoringEnabled", ipAddress);

		setIpAddressMonitoringEnabled(conn, invalidateList, ipAddress, monitoringEnabled);
	}

	public static void setIpAddressMonitoringEnabled(
		DatabaseConnection conn,
		InvalidateList invalidateList,
		int ipAddress,
		boolean monitoringEnabled
	) throws IOException, SQLException {
		// Update the table
		// TODO: Add row when first enabled or column set to non-default
		// TODO: Remove row when disabled and other columns match defaults
		conn.executeUpdate("update \"net.monitoring\".\"IpAddressMonitoring\" set enabled=? where id=?", monitoringEnabled, ipAddress);

		// Notify all clients of the update
		invalidateList.addTable(
			conn,
			Table.TableID.IP_ADDRESSES,
			getAccountForIpAddress(conn, ipAddress),
			getHostForIpAddress(conn, ipAddress),
			false
		);
	}

	/**
	 * Sets the Package owner of an net.IpAddress.
	 */
	public static void setIpAddressPackage(
		DatabaseConnection conn,
		RequestSource source,
		InvalidateList invalidateList,
		int ipAddress,
		Account.Name newPackage
	) throws IOException, SQLException {
		checkAccessIpAddress(conn, source, "setIPAddressPackage", ipAddress);
		PackageHandler.checkAccessPackage(conn, source, "setIPAddressPackage", newPackage);

		setIpAddressPackage(conn, invalidateList, ipAddress, newPackage);
	}

	/**
	 * Sets the Package owner of an net.IpAddress.
	 */
	public static void setIpAddressPackage(
		DatabaseConnection conn,
		InvalidateList invalidateList,
		int ipAddress,
		Account.Name newPackage
	) throws IOException, SQLException {
		Account.Name oldAccounting = getAccountForIpAddress(conn, ipAddress);
		Account.Name newAccounting = PackageHandler.getAccountForPackage(conn, newPackage);
		int host = getHostForIpAddress(conn, ipAddress);

		// Make sure that the IP Address is not in use
		int count=conn.executeIntQuery(
			  "select\n"
			+ "  count(*)\n"
			+ "from\n"
			+ "  net.\"Bind\"\n"
			+ "where\n"
			+ "  \"ipAddress\"=?",
			ipAddress
		);
		if(count!=0) throw new SQLException("Unable to set Package, net.IpAddress in use by "+count+(count==1?" row":" rows")+" in net.Bind: "+ipAddress);

		// Update the table
		conn.executeUpdate("update net.\"IpAddress\" set package=(select id from billing.\"Package\" where name=?), \"isAvailable\"=false where id=?", newPackage, ipAddress);

		// Notify all clients of the update
		invalidateList.addTable(
			conn,
			Table.TableID.IP_ADDRESSES,
			InvalidateList.getAccountCollection(oldAccounting, newAccounting),
			host,
			false
		);
	}

	public static int getSharedHttpdIpAddress(DatabaseConnection conn, int linuxServer) throws IOException, SQLException {
		return conn.executeIntQuery(
			"select\n"
			+ "  coalesce(\n"
			+ "    (\n"
			+ "      select\n"
			+ "        ia.id\n"
			+ "      from\n"
			+ "        net.\"IpAddress\" ia,\n"
			+ "        net.\"Device\" nd\n"
			+ "        left join net.\"Bind\" nb on nd.server=nb.server and nb.port in (80, 443) and nb.net_protocol=?::\"com.aoindustries.net\".\"Protocol\"\n"
			+ "        left join web.\"HttpdBind\" hb on nb.id=hb.net_bind\n"
			+ "        left join web.\"HttpdServer\" hs on hb.httpd_server=hs.id\n"
			+ "      where\n"
			+ "        ia.\"isOverflow\"\n"
			+ "        and ia.device=nd.id\n"
			+ "        and nd.server=?\n"
			+ "        and (\n"
			+ "          nb.\"ipAddress\" is null\n"
			+ "          or ia.id=nb.\"ipAddress\"\n"
			+ "        )\n"
			+ "      order by\n"
			+ "        (\n"
			+ "          select\n"
			+ "            count(*)\n"
			+ "          from\n"
			+ "            net.\"Bind\" nb2,\n"
			+ "            web.\"VirtualHost\" hsb2\n"
			+ "          where\n"
			+ "            nb2.server=?\n"
			+ "            and nb2.\"ipAddress\"=ia.id\n"
			+ "            and (\n"
			+ "              nb2.port=80\n"
			+ "              or nb2.port=443\n"
			+ "            ) and nb2.id=hsb2.httpd_bind\n"
			+ "        )\n"
			+ "      limit 1\n"
			+ "    ), -1\n"
			+ "  )",
			com.aoindustries.net.Protocol.TCP.name(),
			linuxServer,
			linuxServer
		);
	}

	public static Account.Name getPackageForIpAddress(DatabaseConnection conn, int ipAddress) throws IOException, SQLException {
		return conn.executeObjectQuery(ObjectFactories.accountNameFactory,
			"select\n"
			+ "  pk.name\n"
			+ "from\n"
			+ "  net.\"IpAddress\" ia\n"
			+ "  inner join billing.\"Package\" pk on ia.package=pk.id\n"
			+ "where\n"
			+ "  ia.id=?",
			ipAddress
		);
	}

	public static Account.Name getAccountForIpAddress(DatabaseConnection conn, int ipAddress) throws IOException, SQLException {
		return conn.executeObjectQuery(ObjectFactories.accountNameFactory,
			"select\n"
			+ "  pk.accounting\n"
			+ "from\n"
			+ "  net.\"IpAddress\" ia\n"
			+ "  inner join billing.\"Package\" pk on ia.package=pk.id\n"
			+ "where\n"
			+ "  ia.id=?",
			ipAddress
		);
	}

	public static int getHostForIpAddress(DatabaseConnection conn, int ipAddress) throws IOException, SQLException {
		return conn.executeIntQuery("select nd.server from net.\"IpAddress\" ia, net.\"Device\" nd where ia.id=? and ia.device=nd.id", ipAddress);
	}

	public static InetAddress getInetAddressForIpAddress(DatabaseConnection conn, int ipAddress) throws IOException, SQLException {
		return conn.executeObjectQuery(ObjectFactories.inetAddressFactory,
			"select host(\"inetAddress\") from net.\"IpAddress\" where id=?",
			ipAddress
		);
	}

	public static int getWildcardIpAddress(DatabaseConnection conn) throws IOException, SQLException {
		return conn.executeIntQuery("select id from net.\"IpAddress\" where \"inetAddress\"=?::\"com.aoindustries.net\".\"InetAddress\"", IpAddress.WILDCARD_IP); // No limit, must always be 1 row and error otherwise
	}

	public static int getLoopbackIpAddress(DatabaseConnection conn, int host) throws IOException, SQLException {
		return conn.executeIntQuery(
			"select\n"
			+ "  ia.id\n"
			+ "from\n"
			+ "  net.\"IpAddress\" ia,\n"
			+ "  net.\"Device\" nd\n"
			+ "where\n"
			+ "  ia.\"inetAddress\"=?::\"com.aoindustries.net\".\"InetAddress\"\n"
			+ "  and ia.device=nd.id\n"
			+ "  and nd.server=?\n"
			+ "limit 1",
			IpAddress.LOOPBACK_IP,
			host
		);
	}

	public static void releaseIpAddress(
		DatabaseConnection conn,
		InvalidateList invalidateList,
		int ipAddress
	) throws IOException, SQLException {
		setIpAddressHostname(
			conn,
			invalidateList,
			ipAddress,
			getUnassignedHostname(conn, ipAddress)
		);

		conn.executeUpdate(
			"update net.\"IpAddress\" set \"isAvailable\"=true, \"isOverflow\"=false where id=?",
			ipAddress
		);
		conn.executeUpdate(
			"update \"net.monitoring\".\"IpAddressMonitoring\" set enabled=true, \"pingMonitorEnabled\"=false, \"checkBlacklistsOverSmtp\"=false, \"verifyDnsPtr\"=true, \"verifyDnsA\"=false where id=?",
			ipAddress
		);
		invalidateList.addTable(conn,
			Table.TableID.IP_ADDRESSES,
			getAccountForIpAddress(conn, ipAddress),
			getHostForIpAddress(conn, ipAddress),
			false
		);
	}

	private IpAddressHandler() {
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy