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)
List< HostAddress > getHostAddressesAssignedTo(Host host)
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)