19 package org.sleuthkit.datamodel;
 
   21 import com.google.common.cache.Cache;
 
   22 import com.google.common.cache.CacheBuilder;
 
   23 import java.sql.PreparedStatement;
 
   24 import java.sql.ResultSet;
 
   25 import java.sql.SQLException;
 
   26 import java.sql.Statement;
 
   27 import java.util.ArrayList;
 
   28 import java.util.List;
 
   29 import java.util.Objects;
 
   30 import java.util.Optional;
 
   31 import java.util.logging.Level;
 
   32 import java.util.logging.Logger;
 
   33 import java.util.regex.Pattern;
 
   42         private static final Logger LOGGER = Logger.getLogger(
HostAddressManager.class.getName());
 
   45         private final static byte DEFAULT_MAPPING_CACHE_VALUE = 1;
 
   52         private final Cache<Long, Byte> recentHostNameAndIpMappingCache = CacheBuilder.newBuilder().maximumSize(200000).build();
 
   60         private final Cache<String, HostAddress> recentHostAddressCache = CacheBuilder.newBuilder().maximumSize(200000).build();
 
   68         private final Cache<String, Boolean> hostAddressUsageCache = CacheBuilder.newBuilder().maximumSize(200000).build();
 
   93                 try (CaseDbConnection connection = this.db.getConnection()) {
 
  113                 HostAddress hostAddress = recentHostAddressCache.getIfPresent(createRecentHostAddressKey(type, address));
 
  114                 if (Objects.nonNull(hostAddress)) {
 
  115                         return Optional.of(hostAddress);
 
  118                 if (type.equals(HostAddress.HostAddressType.DNS_AUTO)) {
 
  119                         addressType = getDNSType(address);
 
  121                 String normalizedAddress = getNormalizedAddress(address);
 
  122                 String queryString = 
"SELECT * FROM tsk_host_addresses" 
  123                                 + 
" WHERE address = ?  AND address_type = ?";                   
 
  125                         PreparedStatement query = connection.getPreparedStatement(queryString, Statement.NO_GENERATED_KEYS);
 
  126                         query.clearParameters();
 
  127                         query.setString(1, normalizedAddress.toLowerCase());
 
  128                         query.setInt(2, addressType.getId());
 
  129                         try (ResultSet rs = query.executeQuery()) {
 
  131                                         return Optional.empty();        
 
  133                                         HostAddress 
newHostAddress = 
new HostAddress(db, rs.getLong(
"id"), HostAddressType.fromID(rs.getInt(
"address_type")), rs.getString(
"address"));
 
  134                                         recentHostAddressCache.put(createRecentHostAddressKey(newHostAddress.getAddressType(), normalizedAddress), newHostAddress);
 
  135                                         return Optional.of(newHostAddress);                                     
 
  138                 } 
catch (SQLException ex) {
 
  139                         throw new TskCoreException(String.format(
"Error getting host address with type = %s and address = %s", type.getName(), address), ex);
 
  151         private String createRecentHostAddressKey(HostAddressType type, String address) {
 
  152                 return createRecentHostAddressKey(type.getId(), address);
 
  163         private String createRecentHostAddressKey(
int typeId, String address) {
 
  164                 return typeId + 
"#" + address.toLowerCase();
 
  180                 CaseDbConnection connection = this.db.getConnection();
 
  187                         if (hostAddress.isPresent()) {
 
  188                                 return hostAddress.get();
 
  211                         addressType = getDNSType(address);
 
  214                 String normalizedAddress = getNormalizedAddress(address);
 
  218                         long parentObjId = 0;
 
  219                         int objTypeId = TskData.ObjectType.HOST_ADDRESS.getObjectType();
 
  221                         long objId = db.addObject(parentObjId, objTypeId, connection);
 
  223                         String hostAddressInsertSQL = 
"INSERT INTO tsk_host_addresses(id, address_type, address) VALUES (?, ?, ?)"; 
 
  224                         PreparedStatement preparedStatement = connection.getPreparedStatement(hostAddressInsertSQL, Statement.RETURN_GENERATED_KEYS);
 
  226                         preparedStatement.clearParameters();
 
  227                         preparedStatement.setLong(1, objId);
 
  228                         preparedStatement.setInt(2, addressType.getId());
 
  229                         preparedStatement.setString(3, normalizedAddress.toLowerCase());
 
  231                         connection.executeUpdate(preparedStatement);
 
  232                         HostAddress hostAddress =  
new HostAddress(db, objId, addressType, normalizedAddress);
 
  233                         recentHostAddressCache.put(createRecentHostAddressKey(addressType, normalizedAddress), hostAddress);
 
  235                 } 
catch (SQLException ex) {
 
  236                         throw new TskCoreException(String.format(
"Error adding host address of type = %s, with address = %s", type.getName(), address), ex);
 
  252                 String insertSQL = db.getInsertOrIgnoreSQL(
" INTO tsk_host_address_map(host_id, addr_obj_id, source_obj_id, time) " 
  253                                 + 
" VALUES(?, ?, ?, ?) ");
 
  256                 try (CaseDbConnection connection = this.db.getConnection()) {
 
  258                         PreparedStatement preparedStatement = connection.getPreparedStatement(insertSQL, Statement.NO_GENERATED_KEYS);
 
  260                         preparedStatement.clearParameters();
 
  261                         preparedStatement.setLong(1, host.getHostId());
 
  262                         preparedStatement.setLong(2, hostAddress.getId());
 
  263                         preparedStatement.setLong(3, source.getId());
 
  265                                 preparedStatement.setLong(4, time);
 
  267                                 preparedStatement.setNull(4, java.sql.Types.BIGINT);
 
  270                         connection.executeUpdate(preparedStatement);
 
  271                 } 
catch (SQLException ex) {
 
  272                         LOGGER.log(Level.SEVERE, null, ex);
 
  273                         throw new TskCoreException(String.format(
"Error adding host address mapping for host name = %s,  with address = %s", host.getName(), hostAddress.getAddress()), ex);
 
  288                 String queryString = 
"SELECT addr_obj_id FROM tsk_host_address_map " 
  289                                 + 
" WHERE host_id = " + host.getHostId();
 
  291                 List<HostAddress> addresses = 
new ArrayList<>();
 
  294                 try (CaseDbConnection connection = this.db.getConnection();
 
  295                                 Statement s = connection.createStatement();
 
  296                                 ResultSet rs = connection.executeQuery(s, queryString)) {
 
  303                 } 
catch (SQLException ex) {
 
  304                         throw new TskCoreException(String.format(
"Error getting host addresses for host " + host.getName()), ex);
 
  321                 try (CaseDbConnection connection = this.db.getConnection()) {
 
  339                 String queryString = 
"SELECT * FROM tsk_host_addresses" 
  340                                 + 
" WHERE id = " + id;
 
  342                 try (Statement s = connection.createStatement();
 
  343                                 ResultSet rs = connection.executeQuery(s, queryString)) {
 
  346                                 throw new TskCoreException(String.format(
"No address found with id = %d", 
id));
 
  348                                 long objId = rs.getLong(
"id");
 
  349                                 int type = rs.getInt(
"address_type");
 
  350                                 String address = rs.getString(
"address");
 
  351                                 HostAddress hostAddress = 
new HostAddress(db, objId, HostAddress.HostAddressType.fromID(type), address);
 
  352                                 recentHostAddressCache.put(createRecentHostAddressKey(type, address), hostAddress);
 
  355                 } 
catch (SQLException ex) {
 
  356                         throw new TskCoreException(String.format(
"Error getting host address with id = %d", 
id), ex);
 
  373                 try (CaseDbConnection connection = this.db.getConnection()) {
 
  375                 } 
catch (SQLException ex) {
 
  376                         throw new TskCoreException(String.format(
"Error adding host DNS address mapping for DNS name = %s, and IP address = %s", dnsNameAddress.getAddress(), ipAddress.getAddress()), ex);
 
  397                 if (Objects.isNull(caseDbTransaction)) {
 
  398                         throw new TskCoreException(String.format(
"Error adding host DNS address mapping for DNS name = %s, and IP address = %s, null caseDbTransaction passed to addHostNameAndIpMapping", dnsNameAddress.
getAddress(), ipAddress.
getAddress()));
 
  402                 } 
catch (SQLException ex) {
 
  403                         throw new TskCoreException(String.format(
"Error adding host DNS address mapping for DNS name = %s, and IP address = %s", dnsNameAddress.
getAddress(), ipAddress.
getAddress()), ex);
 
  421                         throw new TskCoreException(
"IllegalArguments passed to addHostNameAndIpMapping: A host name address is expected.");
 
  423                 if ((ipAddress.getAddressType() != HostAddress.HostAddressType.IPV4) && (ipAddress.getAddressType() != HostAddress.HostAddressType.IPV6)) {
 
  424                         throw new TskCoreException(
"IllegalArguments passed to addHostNameAndIpMapping:An IPv4/IPv6 address is expected.");
 
  426                 if (Objects.isNull(connection)) {
 
  427                         throw new TskCoreException(
"IllegalArguments passed to addHostNameAndIpMapping: null connection passed to addHostNameAndIpMapping");
 
  430                 String insertSQL = db.getInsertOrIgnoreSQL(
" INTO tsk_host_address_dns_ip_map(dns_address_id, ip_address_id, source_obj_id, time) " 
  431                                 + 
" VALUES(?, ?, ?, ?) ");
 
  433                 PreparedStatement preparedStatement = connection.getPreparedStatement(insertSQL, Statement.NO_GENERATED_KEYS);
 
  435                 preparedStatement.clearParameters();
 
  436                 preparedStatement.setLong(1, dnsNameAddress.getId());
 
  437                 preparedStatement.setLong(2, ipAddress.getId());
 
  438                 preparedStatement.setLong(3, source.getId());
 
  440                         preparedStatement.setLong(4, time);
 
  442                         preparedStatement.setNull(4, java.sql.Types.BIGINT);
 
  444                 connection.executeUpdate(preparedStatement);
 
  445                 recentHostNameAndIpMappingCache.put(ipAddress.getId(), DEFAULT_MAPPING_CACHE_VALUE);
 
  446                 recentHostNameAndIpMappingCache.put(dnsNameAddress.getId(), DEFAULT_MAPPING_CACHE_VALUE);
 
  463                 Byte isPresent = recentHostNameAndIpMappingCache.getIfPresent(addressObjectId);
 
  465                 if (Objects.nonNull(isPresent)) {
 
  469                 String queryString = 
"SELECT count(*) as mappingCount FROM tsk_host_address_dns_ip_map WHERE ip_address_id = ? OR dns_address_id = ? ";
 
  472                 try (CaseDbConnection connection = this.db.getConnection();
 
  473                                 PreparedStatement ps = connection.getPreparedStatement(queryString, Statement.NO_GENERATED_KEYS);) {
 
  474                         ps.clearParameters();
 
  475                         ps.setLong(1, addressObjectId);
 
  476                         ps.setLong(2, addressObjectId);
 
  477                         try (ResultSet rs = ps.executeQuery()) {
 
  481                                         boolean status = rs.getLong(
"mappingCount") > 0;
 
  483                                                 recentHostNameAndIpMappingCache.put(addressObjectId, DEFAULT_MAPPING_CACHE_VALUE);
 
  488                 } 
catch (SQLException ex) {
 
  489                         throw new TskCoreException(
"Error looking up host address / Ip mapping for address = " + addressObjectId, ex);
 
  510                 HostAddress hostAddress = recentHostAddressCache.getIfPresent(createRecentHostAddressKey(type, address));
 
  511                 if (Objects.nonNull(hostAddress)) {
 
  512                         return Optional.of(hostAddress.
getId());
 
  517                         addressType = getDNSType(address);
 
  519                 String normalizedAddress = getNormalizedAddress(address);
 
  521                 String queryString = 
"SELECT id, address_type, address FROM tsk_host_addresses" 
  522                                 + 
" WHERE address = ?  AND address_type = ?"; 
 
  525                 try (CaseDbConnection connection = this.db.getConnection();
 
  526                                 PreparedStatement query = connection.getPreparedStatement(queryString, Statement.NO_GENERATED_KEYS);) {
 
  527                         query.clearParameters();
 
  528                         query.setString(1, normalizedAddress.toLowerCase());
 
  529                         query.setInt(2, addressType.getId());
 
  530                         try (ResultSet rs = query.executeQuery()) {
 
  532                                         return Optional.empty();        
 
  534                                         long objId = rs.getLong(
"id");
 
  535                                         int addrType = rs.getInt(
"address_type");
 
  536                                         String addr = rs.getString(
"address");
 
  538                                         recentHostAddressCache.put(createRecentHostAddressKey(addrType, normalizedAddress), hostAddr);                                  
 
  539                                         return Optional.of(objId);
 
  542                 } 
catch (SQLException ex) {
 
  543                         throw new TskCoreException(String.format(
"Error getting host address with type = %s and address = %s", type.getName(), address), ex);
 
  559                 String queryString = 
"SELECT ip_address_id FROM tsk_host_address_dns_ip_map as map " 
  560                                 + 
" JOIN tsk_host_addresses as addresses " 
  561                                 + 
" ON map.dns_address_id = addresses.id " 
  563                                 + 
" AND addresses.address = ?";
 
  566                 try (CaseDbConnection connection = this.db.getConnection()) {
 
  567                         List<HostAddress> IpAddresses = 
new ArrayList<>();
 
  568                         PreparedStatement query = connection.getPreparedStatement(queryString, Statement.NO_GENERATED_KEYS);
 
  569                         query.clearParameters();
 
  570                         query.setString(1, hostname.toLowerCase());
 
  571                         try (ResultSet rs = query.executeQuery()) {
 
  573                                         long ipAddressObjId = rs.getLong(
"ip_address_id");
 
  575                                         recentHostNameAndIpMappingCache.put(ipAddressObjId, DEFAULT_MAPPING_CACHE_VALUE);
 
  579                 } 
catch (SQLException ex) {
 
  580                         throw new TskCoreException(String.format(
"Error getting host addresses for host name: " + hostname), ex);
 
  595         List<HostAddress> getHostNameByIp(String ipAddress) 
throws TskCoreException {
 
  596                 String queryString = 
"SELECT dns_address_id FROM tsk_host_address_dns_ip_map as map " 
  597                                 + 
" JOIN tsk_host_addresses as addresses " 
  598                                 + 
" ON map.ip_address_id = addresses.id " 
  601                                 + 
" AND addresses.address = ?";
 
  604                 try (CaseDbConnection connection = this.db.getConnection()) {
 
  605                         List<HostAddress> dnsNames = 
new ArrayList<>();
 
  606                         PreparedStatement query = connection.getPreparedStatement(queryString, Statement.NO_GENERATED_KEYS);
 
  607                         query.clearParameters();
 
  608                         query.setString(1, ipAddress.toLowerCase());
 
  609                         try (ResultSet rs = query.executeQuery()) {
 
  611                                         long dnsAddressId = rs.getLong(
"dns_address_id");
 
  613                                         recentHostNameAndIpMappingCache.put(dnsAddressId, DEFAULT_MAPPING_CACHE_VALUE);
 
  617                 } 
catch (SQLException ex) {
 
  618                         throw new TskCoreException(String.format(
"Error getting host addresses for IP address: " + ipAddress), ex);
 
  632                 String key = content.getDataSource().getId() + 
"#" + hostAddress.getId() + 
"#" + content.getId();
 
  633                 Boolean cachedValue = hostAddressUsageCache.getIfPresent(key);
 
  634                 if (null != cachedValue) {
 
  638                 final String insertSQL = db.getInsertOrIgnoreSQL(
" INTO tsk_host_address_usage(addr_obj_id, obj_id, data_source_obj_id) " 
  639                                 + 
" VALUES(" + hostAddress.getId() + 
", " + content.getId() + 
", " + content.getDataSource().getId() + 
") ");
 
  642                 try (CaseDbConnection connection = this.db.getConnection();
 
  643                                 Statement s = connection.createStatement()) {
 
  644                         connection.executeUpdate(s, insertSQL);
 
  645                         hostAddressUsageCache.put(key, 
true);
 
  646                 } 
catch (SQLException ex) {
 
  647                         throw new TskCoreException(String.format(
"Error associating host address %s with artifact with id %d", hostAddress.getAddress(), content.getId()), ex);
 
  653         private final String ADDRESS_USAGE_QUERY = 
"SELECT addresses.id as id, addresses.address_type as address_type, addresses.address as address " 
  654                         + 
" FROM tsk_host_address_usage as usage " 
  655                         + 
" JOIN tsk_host_addresses as addresses " 
  656                         + 
" ON usage.addr_obj_id = addresses.id ";
 
  668                 String queryString = ADDRESS_USAGE_QUERY
 
  669                                 + 
" WHERE usage.obj_id = " + content.getId();
 
  671                 return getHostAddressesUsed(queryString);
 
  684                 String queryString = ADDRESS_USAGE_QUERY
 
  685                                 + 
" WHERE usage.data_source_obj_id = " + dataSource.getId();
 
  687                 return getHostAddressesUsed(queryString);
 
  699         private List<HostAddress> getHostAddressesUsed(String addressesUsedSQL) 
throws TskCoreException {
 
  701                 List<HostAddress> addressesUsed = 
new ArrayList<>();
 
  704                 try (CaseDbConnection connection = this.db.getConnection();
 
  705                                 Statement s = connection.createStatement();
 
  706                                 ResultSet rs = connection.executeQuery(s, addressesUsedSQL)) {
 
  711                         return addressesUsed;
 
  712                 } 
catch (SQLException ex) {
 
  713                         throw new TskCoreException(String.format(
"Error getting host addresses used with query string = %s", addressesUsedSQL), ex);
 
  726         private HostAddress.HostAddressType getDNSType(String address) {
 
  727                 if (isIPv4(address)) {
 
  728                         return HostAddress.HostAddressType.IPV4;
 
  729                 } 
else if (isIPv6(address)) {
 
  730                         return HostAddress.HostAddressType.IPV6;
 
  732                         return HostAddress.HostAddressType.HOSTNAME;
 
  736         private static final Pattern IPV4_PATTERN =
 
  737             Pattern.compile(
"^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\\.(?!$)|$)){4}$");
 
  745         private static boolean isIPv4(String ipAddress) {
 
  746                 if (ipAddress != null) {
 
  747                         return IPV4_PATTERN.matcher(ipAddress).matches();
 
  758         private static final Pattern IPV6_STD_PATTERN = 
 
  759             Pattern.compile(
"^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}(%.+)?$");
 
  760     private static final Pattern IPV6_HEX_COMPRESSED_PATTERN = 
 
  761             Pattern.compile(
"^((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)::((?:[0-9A-Fa-f]{1,4}(?::[0-9A-Fa-f]{1,4})*)?)(%.+)?$");
 
  764     private static boolean isIPv6StdAddress(
final String input) {
 
  765         return IPV6_STD_PATTERN.matcher(input).matches();
 
  767     private static boolean isIPv6HexCompressedAddress(
final String input) {
 
  768         return IPV6_HEX_COMPRESSED_PATTERN.matcher(input).matches();
 
  778         private static boolean isIPv6(String ipAddress) {
 
  780                 if (ipAddress != null) {
 
  781                          return isIPv6StdAddress(ipAddress) || isIPv6HexCompressedAddress(ipAddress);
 
  797         private static String getNormalizedAddress(String address) {
 
  799                 String normalizedAddress = address;
 
  801                 if (isIPv6(address)) {
 
  802                         normalizedAddress = getNormalizedIPV6Address(address);
 
  805                 return normalizedAddress;
 
  819         private static String getNormalizedIPV6Address(String address) {
 
  821                 String normalizedAddress = address;
 
  822                 if ( normalizedAddress.contains(
"%") ) {
 
  823                         normalizedAddress = normalizedAddress.substring(0, normalizedAddress.indexOf(
"%"));
 
  826                 return normalizedAddress;
 
List< HostAddress > getHostAddressesUsedOnDataSource(Content dataSource)
 
Optional< HostAddress > getHostAddress(HostAddress.HostAddressType type, String address)
 
List< HostAddress > getHostAddressesUsedByContent(Content content)
 
void addHostNameAndIpMapping(HostAddress dnsNameAddress, HostAddress ipAddress, Long time, Content source, final SleuthkitCase.CaseDbTransaction caseDbTransaction)
 
void addUsage(Content content, HostAddress hostAddress)
 
static HostAddressType fromID(int typeId)
 
Optional< Long > hostAddressExists(HostAddress.HostAddressType type, String address)
 
void releaseSingleUserCaseReadLock()
 
HostAddress newHostAddress(HostAddress.HostAddressType type, String address)
 
void acquireSingleUserCaseWriteLock()
 
void releaseSingleUserCaseWriteLock()
 
List< HostAddress > getIpAddress(String hostname)
 
void assignHostToAddress(Host host, HostAddress hostAddress, Long time, Content source)
 
HostAddress getHostAddress(long id)
 
boolean hostNameAndIpMappingExists(long addressObjectId)
 
void acquireSingleUserCaseReadLock()
 
void addHostNameAndIpMapping(HostAddress dnsNameAddress, HostAddress ipAddress, Long time, Content source)