40public class HostAddressManager {
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();
92 db.acquireSingleUserCaseReadLock();
93 try (CaseDbConnection connection = this.db.getConnection()) {
94 return HostAddressManager.this.getHostAddress(type, address, connection);
96 db.releaseSingleUserCaseReadLock();
113 HostAddress hostAddress = recentHostAddressCache.getIfPresent(createRecentHostAddressKey(type, address));
114 if (Objects.nonNull(hostAddress)) {
115 return Optional.of(hostAddress);
117 HostAddress.HostAddressType addressType = type;
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"));
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();
179 db.acquireSingleUserCaseWriteLock();
180 CaseDbConnection connection = this.db.getConnection();
182 return HostAddressManager.this.newHostAddress(type, address, connection);
186 Optional<HostAddress> hostAddress = HostAddressManager.this.getHostAddress(type, address, connection);
187 if (hostAddress.isPresent()) {
188 return hostAddress.get();
193 db.releaseSingleUserCaseWriteLock();
211 addressType = getDNSType(address);
214 String normalizedAddress = getNormalizedAddress(address);
216 Optional<HostAddress> existingAddr =
getHostAddress(addressType, normalizedAddress, connection);
217 if (existingAddr.isPresent()) {
218 return existingAddr.get();
223 long parentObjId = 0;
224 int objTypeId = TskData.ObjectType.HOST_ADDRESS.getObjectType();
226 long objId = db.addObject(parentObjId, objTypeId, connection);
228 String hostAddressInsertSQL =
"INSERT INTO tsk_host_addresses(id, address_type, address) VALUES (?, ?, ?) ";
230 PreparedStatement preparedStatement = connection.getPreparedStatement(hostAddressInsertSQL, Statement.RETURN_GENERATED_KEYS);
232 preparedStatement.clearParameters();
233 preparedStatement.setLong(1, objId);
234 preparedStatement.setInt(2, addressType.getId());
235 preparedStatement.setString(3, normalizedAddress.toLowerCase());
237 connection.executeUpdate(preparedStatement);
238 HostAddress hostAddress =
new HostAddress(db, objId, addressType, normalizedAddress);
239 recentHostAddressCache.put(createRecentHostAddressKey(addressType, normalizedAddress), hostAddress);
241 }
catch (SQLException ex) {
242 throw new TskCoreException(String.format(
"Error adding host address of type = %s, with address = %s", type.getName(), address), ex);
258 String insertSQL = db.getInsertOrIgnoreSQL(
" INTO tsk_host_address_map(host_id, addr_obj_id, source_obj_id, time) "
259 +
" VALUES(?, ?, ?, ?) ");
261 db.acquireSingleUserCaseWriteLock();
262 try (CaseDbConnection connection = this.db.getConnection()) {
264 PreparedStatement preparedStatement = connection.getPreparedStatement(insertSQL, Statement.NO_GENERATED_KEYS);
266 preparedStatement.clearParameters();
267 preparedStatement.setLong(1, host.getHostId());
268 preparedStatement.setLong(2, hostAddress.getId());
269 preparedStatement.setLong(3, source.getId());
271 preparedStatement.setLong(4, time);
273 preparedStatement.setNull(4, java.sql.Types.BIGINT);
276 connection.executeUpdate(preparedStatement);
277 }
catch (SQLException ex) {
278 LOGGER.log(Level.SEVERE,
null, ex);
279 throw new TskCoreException(String.format(
"Error adding host address mapping for host name = %s, with address = %s", host.getName(), hostAddress.getAddress()), ex);
281 db.releaseSingleUserCaseWriteLock();
294 String queryString =
"SELECT addr_obj_id FROM tsk_host_address_map "
295 +
" WHERE host_id = " + host.getHostId();
297 List<HostAddress> addresses =
new ArrayList<>();
299 db.acquireSingleUserCaseReadLock();
300 try (CaseDbConnection connection = this.db.getConnection();
301 Statement s = connection.createStatement();
302 ResultSet rs = connection.executeQuery(s, queryString)) {
305 addresses.add(HostAddressManager.this.getHostAddress(rs.getLong(
"addr_obj_id"), connection));
309 }
catch (SQLException ex) {
310 throw new TskCoreException(String.format(
"Error getting host addresses for host " + host.getName()), ex);
312 db.releaseSingleUserCaseReadLock();
326 db.acquireSingleUserCaseReadLock();
327 try (CaseDbConnection connection = this.db.getConnection()) {
328 return HostAddressManager.this.getHostAddress(
id, connection);
330 db.releaseSingleUserCaseReadLock();
345 String queryString =
"SELECT * FROM tsk_host_addresses"
346 +
" WHERE id = " + id;
348 try (Statement s = connection.createStatement();
349 ResultSet rs = connection.executeQuery(s, queryString)) {
352 throw new TskCoreException(String.format(
"No address found with id = %d",
id));
354 long objId = rs.getLong(
"id");
355 int type = rs.getInt(
"address_type");
356 String address = rs.getString(
"address");
357 HostAddress hostAddress =
new HostAddress(db, objId, HostAddress.HostAddressType.fromID(type), address);
358 recentHostAddressCache.put(createRecentHostAddressKey(type, address), hostAddress);
361 }
catch (SQLException ex) {
362 throw new TskCoreException(String.format(
"Error getting host address with id = %d",
id), ex);
378 db.acquireSingleUserCaseWriteLock();
379 try (CaseDbConnection connection = this.db.getConnection()) {
381 }
catch (SQLException ex) {
382 throw new TskCoreException(String.format(
"Error adding host DNS address mapping for DNS name = %s, and IP address = %s", dnsNameAddress.getAddress(), ipAddress.getAddress()), ex);
384 db.releaseSingleUserCaseWriteLock();
403 if (Objects.isNull(caseDbTransaction)) {
404 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()));
408 }
catch (SQLException ex) {
409 throw new TskCoreException(String.format(
"Error adding host DNS address mapping for DNS name = %s, and IP address = %s", dnsNameAddress.getAddress(), ipAddress.getAddress()), ex);
427 throw new TskCoreException(
"IllegalArguments passed to addHostNameAndIpMapping: A host name address is expected.");
429 if ((ipAddress.getAddressType() != HostAddress.HostAddressType.IPV4) && (ipAddress.getAddressType() != HostAddress.HostAddressType.IPV6)) {
430 throw new TskCoreException(
"IllegalArguments passed to addHostNameAndIpMapping:An IPv4/IPv6 address is expected.");
432 if (Objects.isNull(connection)) {
433 throw new TskCoreException(
"IllegalArguments passed to addHostNameAndIpMapping: null connection passed to addHostNameAndIpMapping");
436 String insertSQL = db.getInsertOrIgnoreSQL(
" INTO tsk_host_address_dns_ip_map(dns_address_id, ip_address_id, source_obj_id, time) "
437 +
" VALUES(?, ?, ?, ?) ");
439 PreparedStatement preparedStatement = connection.getPreparedStatement(insertSQL, Statement.NO_GENERATED_KEYS);
441 preparedStatement.clearParameters();
442 preparedStatement.setLong(1, dnsNameAddress.getId());
443 preparedStatement.setLong(2, ipAddress.getId());
444 preparedStatement.setLong(3, source.getId());
446 preparedStatement.setLong(4, time);
448 preparedStatement.setNull(4, java.sql.Types.BIGINT);
450 connection.executeUpdate(preparedStatement);
451 recentHostNameAndIpMappingCache.put(ipAddress.getId(), DEFAULT_MAPPING_CACHE_VALUE);
452 recentHostNameAndIpMappingCache.put(dnsNameAddress.getId(), DEFAULT_MAPPING_CACHE_VALUE);
469 Byte isPresent = recentHostNameAndIpMappingCache.getIfPresent(addressObjectId);
471 if (Objects.nonNull(isPresent)) {
475 String queryString =
"SELECT count(*) as mappingCount FROM tsk_host_address_dns_ip_map WHERE ip_address_id = ? OR dns_address_id = ? ";
477 db.acquireSingleUserCaseReadLock();
478 try (CaseDbConnection connection = this.db.getConnection();
479 PreparedStatement ps = connection.getPreparedStatement(queryString, Statement.NO_GENERATED_KEYS);) {
480 ps.clearParameters();
481 ps.setLong(1, addressObjectId);
482 ps.setLong(2, addressObjectId);
483 try (ResultSet rs = ps.executeQuery()) {
487 boolean status = rs.getLong(
"mappingCount") > 0;
489 recentHostNameAndIpMappingCache.put(addressObjectId, DEFAULT_MAPPING_CACHE_VALUE);
494 }
catch (SQLException ex) {
495 throw new TskCoreException(
"Error looking up host address / Ip mapping for address = " + addressObjectId, ex);
497 db.releaseSingleUserCaseReadLock();
516 HostAddress hostAddress = recentHostAddressCache.getIfPresent(createRecentHostAddressKey(type, address));
517 if (Objects.nonNull(hostAddress)) {
518 return Optional.of(hostAddress.
getId());
523 addressType = getDNSType(address);
525 String normalizedAddress = getNormalizedAddress(address);
527 String queryString =
"SELECT id, address_type, address FROM tsk_host_addresses"
528 +
" WHERE address = ? AND address_type = ?";
530 db.acquireSingleUserCaseReadLock();
531 try (CaseDbConnection connection = this.db.getConnection();
532 PreparedStatement query = connection.getPreparedStatement(queryString, Statement.NO_GENERATED_KEYS);) {
533 query.clearParameters();
534 query.setString(1, normalizedAddress.toLowerCase());
535 query.setInt(2, addressType.getId());
536 try (ResultSet rs = query.executeQuery()) {
538 return Optional.empty();
540 long objId = rs.getLong(
"id");
541 int addrType = rs.getInt(
"address_type");
542 String addr = rs.getString(
"address");
544 recentHostAddressCache.put(createRecentHostAddressKey(addrType, normalizedAddress), hostAddr);
545 return Optional.of(objId);
548 }
catch (SQLException ex) {
549 throw new TskCoreException(String.format(
"Error getting host address with type = %s and address = %s", type.getName(), address), ex);
551 db.releaseSingleUserCaseReadLock();
565 String queryString =
"SELECT ip_address_id FROM tsk_host_address_dns_ip_map as map "
566 +
" JOIN tsk_host_addresses as addresses "
567 +
" ON map.dns_address_id = addresses.id "
569 +
" AND addresses.address = ?";
571 db.acquireSingleUserCaseReadLock();
572 try (CaseDbConnection connection = this.db.getConnection()) {
573 List<HostAddress> IpAddresses =
new ArrayList<>();
574 PreparedStatement query = connection.getPreparedStatement(queryString, Statement.NO_GENERATED_KEYS);
575 query.clearParameters();
576 query.setString(1, hostname.toLowerCase());
577 try (ResultSet rs = query.executeQuery()) {
579 long ipAddressObjId = rs.getLong(
"ip_address_id");
580 IpAddresses.add(HostAddressManager.this.getHostAddress(ipAddressObjId, connection));
581 recentHostNameAndIpMappingCache.put(ipAddressObjId, DEFAULT_MAPPING_CACHE_VALUE);
585 }
catch (SQLException ex) {
586 throw new TskCoreException(String.format(
"Error getting host addresses for host name: " + hostname), ex);
588 db.releaseSingleUserCaseReadLock();
601 List<HostAddress> getHostNameByIp(String ipAddress)
throws TskCoreException {
602 String queryString =
"SELECT dns_address_id FROM tsk_host_address_dns_ip_map as map "
603 +
" JOIN tsk_host_addresses as addresses "
604 +
" ON map.ip_address_id = addresses.id "
607 +
" AND addresses.address = ?";
610 try (CaseDbConnection connection = this.db.getConnection()) {
611 List<HostAddress> dnsNames =
new ArrayList<>();
612 PreparedStatement query = connection.getPreparedStatement(queryString, Statement.NO_GENERATED_KEYS);
613 query.clearParameters();
614 query.setString(1, ipAddress.toLowerCase());
615 try (ResultSet rs = query.executeQuery()) {
617 long dnsAddressId = rs.getLong(
"dns_address_id");
619 recentHostNameAndIpMappingCache.put(dnsAddressId, DEFAULT_MAPPING_CACHE_VALUE);
623 }
catch (SQLException ex) {
624 throw new TskCoreException(String.format(
"Error getting host addresses for IP address: " + ipAddress), ex);
626 db.releaseSingleUserCaseReadLock();
638 String key = content.getDataSource().getId() +
"#" + hostAddress.getId() +
"#" + content.getId();
639 Boolean cachedValue = hostAddressUsageCache.getIfPresent(key);
640 if (
null != cachedValue) {
644 final String insertSQL = db.getInsertOrIgnoreSQL(
" INTO tsk_host_address_usage(addr_obj_id, obj_id, data_source_obj_id) "
645 +
" VALUES(" + hostAddress.getId() +
", " + content.getId() +
", " + content.getDataSource().getId() +
") ");
647 db.acquireSingleUserCaseWriteLock();
648 try (CaseDbConnection connection = this.db.getConnection();
649 Statement s = connection.createStatement()) {
650 connection.executeUpdate(s, insertSQL);
651 hostAddressUsageCache.put(key,
true);
652 }
catch (SQLException ex) {
653 throw new TskCoreException(String.format(
"Error associating host address %s with artifact with id %d", hostAddress.getAddress(), content.getId()), ex);
655 db.releaseSingleUserCaseWriteLock();
659 private final String ADDRESS_USAGE_QUERY =
"SELECT addresses.id as id, addresses.address_type as address_type, addresses.address as address "
660 +
" FROM tsk_host_address_usage as usage "
661 +
" JOIN tsk_host_addresses as addresses "
662 +
" ON usage.addr_obj_id = addresses.id ";
674 String queryString = ADDRESS_USAGE_QUERY
675 +
" WHERE usage.obj_id = " + content.getId();
677 return getHostAddressesUsed(queryString);
690 String queryString = ADDRESS_USAGE_QUERY
691 +
" WHERE usage.data_source_obj_id = " + dataSource.getId();
693 return getHostAddressesUsed(queryString);
705 private List<HostAddress> getHostAddressesUsed(String addressesUsedSQL)
throws TskCoreException {
707 List<HostAddress> addressesUsed =
new ArrayList<>();
710 try (CaseDbConnection connection = this.db.getConnection();
711 Statement s = connection.createStatement();
712 ResultSet rs = connection.executeQuery(s, addressesUsedSQL)) {
717 return addressesUsed;
718 }
catch (SQLException ex) {
719 throw new TskCoreException(String.format(
"Error getting host addresses used with query string = %s", addressesUsedSQL), ex);
732 private HostAddress.HostAddressType getDNSType(String address) {
733 if (isIPv4(address)) {
734 return HostAddress.HostAddressType.IPV4;
735 }
else if (isIPv6(address)) {
736 return HostAddress.HostAddressType.IPV6;
738 return HostAddress.HostAddressType.HOSTNAME;
742 private static final Pattern IPV4_PATTERN =
743 Pattern.compile(
"^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\\.(?!$)|$)){4}$");
751 private static boolean isIPv4(String ipAddress) {
752 if (ipAddress !=
null) {
753 return IPV4_PATTERN.matcher(ipAddress).matches();
764 private static final Pattern IPV6_STD_PATTERN =
765 Pattern.compile(
"^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}(%.+)?$");
766 private static final Pattern IPV6_HEX_COMPRESSED_PATTERN =
767 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})*)?)(%.+)?$");
770 private static boolean isIPv6StdAddress(
final String input) {
771 return IPV6_STD_PATTERN.matcher(input).matches();
773 private static boolean isIPv6HexCompressedAddress(
final String input) {
774 return IPV6_HEX_COMPRESSED_PATTERN.matcher(input).matches();
784 private static boolean isIPv6(String ipAddress) {
786 if (ipAddress !=
null) {
787 return isIPv6StdAddress(ipAddress) || isIPv6HexCompressedAddress(ipAddress);
803 private static String getNormalizedAddress(String address) {
805 String normalizedAddress = address;
807 if (isIPv6(address)) {
808 normalizedAddress = getNormalizedIPV6Address(address);
811 return normalizedAddress;
825 private static String getNormalizedIPV6Address(String address) {
827 String normalizedAddress = address;
828 if ( normalizedAddress.contains(
"%") ) {
829 normalizedAddress = normalizedAddress.substring(0, normalizedAddress.indexOf(
"%"));
832 return normalizedAddress;