Sleuth Kit Java Bindings (JNI)  4.10.2
Java bindings for using The Sleuth Kit
HostAddressManager.java
Go to the documentation of this file.
1 /*
2  * Sleuth Kit Data Model
3  *
4  * Copyright 2021 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.datamodel;
20 
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;
36 
40 public class HostAddressManager {
41 
42  private static final Logger LOGGER = Logger.getLogger(HostAddressManager.class.getName());
43 
44  private final SleuthkitCase db;
45 
51  private final Cache<Long, Byte> recentHostNameAndIpMappingCache = CacheBuilder.newBuilder().maximumSize(200000).build();
52 
59  private final Cache<String, HostAddress> recentHostAddressCache = CacheBuilder.newBuilder().maximumSize(200000).build();
60 
67  private final Cache<String, Boolean> hostAddressUsageCache = CacheBuilder.newBuilder().maximumSize(200000).build();
68 
76  this.db = skCase;
77  }
78 
89  public Optional<HostAddress> getHostAddress(HostAddress.HostAddressType type, String address) throws TskCoreException {
90 
92  try (CaseDbConnection connection = this.db.getConnection()) {
93  return HostAddressManager.this.getHostAddress(type, address, connection);
94  } finally {
96  }
97  }
98 
110  private Optional<HostAddress> getHostAddress(HostAddress.HostAddressType type, String address, CaseDbConnection connection) throws TskCoreException {
111 
112  HostAddress hostAddress = recentHostAddressCache.getIfPresent(createRecentHostAddressKey(type, address));
113  if (Objects.nonNull(hostAddress)) {
114  return Optional.of(hostAddress);
115  }
116  HostAddress.HostAddressType addressType = type;
117  if (type.equals(HostAddress.HostAddressType.DNS_AUTO)) {
118  addressType = getDNSType(address);
119  }
120  String queryString = "SELECT * FROM tsk_host_addresses"
121  + " WHERE address = ? AND address_type = ?";
122  try {
123  PreparedStatement query = connection.getPreparedStatement(queryString, Statement.NO_GENERATED_KEYS);
124  query.clearParameters();
125  query.setString(1, address.toLowerCase());
126  query.setInt(2, addressType.getId());
127  try (ResultSet rs = query.executeQuery()) {
128  if (!rs.next()) {
129  return Optional.empty(); // no match found
130  } else {
131  HostAddress newHostAddress = new HostAddress(db, rs.getLong("id"), HostAddressType.fromID(rs.getInt("address_type")), address);
132  recentHostAddressCache.put(createRecentHostAddressKey(newHostAddress.getAddressType(), address), newHostAddress);
133  return Optional.of(newHostAddress);
134  }
135  }
136  } catch (SQLException ex) {
137  throw new TskCoreException(String.format("Error getting host address with type = %s and address = %s", type.getName(), address), ex);
138  }
139  }
140 
149  private String createRecentHostAddressKey(HostAddressType type, String address) {
150  return createRecentHostAddressKey(type.getId(), address);
151  }
152 
161  private String createRecentHostAddressKey(int typeId, String address) {
162  return typeId + "#" + address.toLowerCase();
163  }
164 
178  CaseDbConnection connection = this.db.getConnection();
179  try {
180  return HostAddressManager.this.newHostAddress(type, address, connection);
181  } catch (TskCoreException ex) {
182  // The insert may have failed because the HostAddress already exists, so
183  // try loading it from the database.
184  Optional<HostAddress> hostAddress = HostAddressManager.this.getHostAddress(type, address, connection);
185  if (hostAddress.isPresent()) {
186  return hostAddress.get();
187  }
188  throw ex;
189  } finally {
190  connection.close();
192  }
193  }
194 
206  private HostAddress newHostAddress(HostAddress.HostAddressType type, String address, CaseDbConnection connection) throws TskCoreException {
207  HostAddress.HostAddressType addressType = type;
208  if (type.equals(HostAddress.HostAddressType.DNS_AUTO)) {
209  addressType = getDNSType(address);
210  }
211 
212  try {
213 
214  // TODO: need to get the correct parent obj id.
215  long parentObjId = 0;
216  int objTypeId = TskData.ObjectType.HOST_ADDRESS.getObjectType();
217 
218  long objId = db.addObject(parentObjId, objTypeId, connection);
219 
220  String hostAddressInsertSQL = "INSERT INTO tsk_host_addresses(id, address_type, address) VALUES (?, ?, ?)"; // NON-NLS
221  PreparedStatement preparedStatement = connection.getPreparedStatement(hostAddressInsertSQL, Statement.RETURN_GENERATED_KEYS);
222 
223  preparedStatement.clearParameters();
224  preparedStatement.setLong(1, objId);
225  preparedStatement.setInt(2, addressType.getId());
226  preparedStatement.setString(3, address.toLowerCase());
227 
228  connection.executeUpdate(preparedStatement);
229  HostAddress hostAddress = new HostAddress(db, objId, addressType, address);
230  recentHostAddressCache.put(createRecentHostAddressKey(addressType, address), hostAddress);
231  return hostAddress;
232  } catch (SQLException ex) {
233  throw new TskCoreException(String.format("Error adding host address of type = %s, with address = %s", type.getName(), address), ex);
234  }
235  }
236 
247  public void assignHostToAddress(Host host, HostAddress hostAddress, Long time, Content source) throws TskCoreException {
248 
249  String insertSQL = db.getInsertOrIgnoreSQL(" INTO tsk_host_address_map(host_id, addr_obj_id, source_obj_id, time) "
250  + " VALUES(?, ?, ?, ?) ");
251 
253  try (CaseDbConnection connection = this.db.getConnection()) {
254 
255  PreparedStatement preparedStatement = connection.getPreparedStatement(insertSQL, Statement.NO_GENERATED_KEYS);
256 
257  preparedStatement.clearParameters();
258  preparedStatement.setLong(1, host.getHostId());
259  preparedStatement.setLong(2, hostAddress.getId());
260  preparedStatement.setLong(3, source.getId());
261  if (time != null) {
262  preparedStatement.setLong(4, time);
263  } else {
264  preparedStatement.setNull(4, java.sql.Types.BIGINT);
265  }
266 
267  connection.executeUpdate(preparedStatement);
268  } catch (SQLException ex) {
269  LOGGER.log(Level.SEVERE, null, ex);
270  throw new TskCoreException(String.format("Error adding host address mapping for host name = %s, with address = %s", host.getName(), hostAddress.getAddress()), ex);
271  } finally {
273  }
274  }
275 
283  List<HostAddress> getHostAddressesAssignedTo(Host host) throws TskCoreException {
284 
285  String queryString = "SELECT addr_obj_id FROM tsk_host_address_map "
286  + " WHERE host_id = " + host.getHostId();
287 
288  List<HostAddress> addresses = new ArrayList<>();
289 
291  try (CaseDbConnection connection = this.db.getConnection();
292  Statement s = connection.createStatement();
293  ResultSet rs = connection.executeQuery(s, queryString)) {
294 
295  while (rs.next()) {
296  addresses.add(HostAddressManager.this.getHostAddress(rs.getLong("addr_obj_id"), connection));
297  }
298 
299  return addresses;
300  } catch (SQLException ex) {
301  throw new TskCoreException(String.format("Error getting host addresses for host " + host.getName()), ex);
302  } finally {
304  }
305  }
306 
316  public HostAddress getHostAddress(long id) throws TskCoreException {
318  try (CaseDbConnection connection = this.db.getConnection()) {
319  return HostAddressManager.this.getHostAddress(id, connection);
320  } finally {
322  }
323  }
324 
335  private HostAddress getHostAddress(long id, CaseDbConnection connection) throws TskCoreException {
336  String queryString = "SELECT * FROM tsk_host_addresses"
337  + " WHERE id = " + id;
338 
339  try (Statement s = connection.createStatement();
340  ResultSet rs = connection.executeQuery(s, queryString)) {
341 
342  if (!rs.next()) {
343  throw new TskCoreException(String.format("No address found with id = %d", id));
344  } else {
345  long objId = rs.getLong("id");
346  int type = rs.getInt("address_type");
347  String address = rs.getString("address");
348  HostAddress hostAddress = new HostAddress(db, objId, HostAddress.HostAddressType.fromID(type), address);
349  recentHostAddressCache.put(createRecentHostAddressKey(type, address), hostAddress);
350  return hostAddress;
351  }
352  } catch (SQLException ex) {
353  throw new TskCoreException(String.format("Error getting host address with id = %d", id), ex);
354  }
355  }
356 
367  public void addHostNameAndIpMapping(HostAddress dnsNameAddress, HostAddress ipAddress, Long time, Content source) throws TskCoreException {
368 
370  try (CaseDbConnection connection = this.db.getConnection()) {
371  addHostNameAndIpMapping(dnsNameAddress, ipAddress, time, source, connection);
372  } catch (SQLException ex) {
373  throw new TskCoreException(String.format("Error adding host DNS address mapping for DNS name = %s, and IP address = %s", dnsNameAddress.getAddress(), ipAddress.getAddress()), ex);
374  } finally {
376  }
377  }
378 
392  public void addHostNameAndIpMapping(HostAddress dnsNameAddress, HostAddress ipAddress, Long time, Content source, final SleuthkitCase.CaseDbTransaction caseDbTransaction) throws TskCoreException {
393 
394  if (Objects.isNull(caseDbTransaction)) {
395  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()));
396  }
397  try {
398  addHostNameAndIpMapping(dnsNameAddress, ipAddress, time, source, caseDbTransaction.getConnection());
399  } catch (SQLException ex) {
400  throw new TskCoreException(String.format("Error adding host DNS address mapping for DNS name = %s, and IP address = %s", dnsNameAddress.getAddress(), ipAddress.getAddress()), ex);
401  }
402  }
403 
415  private void addHostNameAndIpMapping(HostAddress dnsNameAddress, HostAddress ipAddress, Long time, Content source, final CaseDbConnection connection) throws SQLException, TskCoreException {
416 
417  if (dnsNameAddress.getAddressType() != HostAddress.HostAddressType.HOSTNAME) {
418  throw new TskCoreException("IllegalArguments passed to addHostNameAndIpMapping: A host name address is expected.");
419  }
420  if ((ipAddress.getAddressType() != HostAddress.HostAddressType.IPV4) && (ipAddress.getAddressType() != HostAddress.HostAddressType.IPV6)) {
421  throw new TskCoreException("IllegalArguments passed to addHostNameAndIpMapping:An IPv4/IPv6 address is expected.");
422  }
423  if (Objects.isNull(connection)) {
424  throw new TskCoreException("IllegalArguments passed to addHostNameAndIpMapping: null connection passed to addHostNameAndIpMapping");
425  }
426 
427  String insertSQL = db.getInsertOrIgnoreSQL(" INTO tsk_host_address_dns_ip_map(dns_address_id, ip_address_id, source_obj_id, time) "
428  + " VALUES(?, ?, ?, ?) ");
429 
430  PreparedStatement preparedStatement = connection.getPreparedStatement(insertSQL, Statement.NO_GENERATED_KEYS);
431 
432  preparedStatement.clearParameters();
433  preparedStatement.setLong(1, dnsNameAddress.getId());
434  preparedStatement.setLong(2, ipAddress.getId());
435  preparedStatement.setLong(3, source.getId());
436  if (time != null) {
437  preparedStatement.setLong(4, time);
438  } else {
439  preparedStatement.setNull(4, java.sql.Types.BIGINT);
440  }
441  connection.executeUpdate(preparedStatement);
442  recentHostNameAndIpMappingCache.put(ipAddress.getId(), new Byte((byte) 1));
443  recentHostNameAndIpMappingCache.put(dnsNameAddress.getId(), new Byte((byte) 1));
444  }
445 
458  public boolean hostNameAndIpMappingExists(long addressObjectId) throws TskCoreException {
459 
460  Byte isPresent = recentHostNameAndIpMappingCache.getIfPresent(addressObjectId);
461 
462  if (Objects.nonNull(isPresent)) {
463  return true;
464  }
465 
466  String queryString = "SELECT count(*) as mappingCount FROM tsk_host_address_dns_ip_map WHERE ip_address_id = ? OR dns_address_id = ? ";
467 
469  try (CaseDbConnection connection = this.db.getConnection();
470  PreparedStatement ps = connection.getPreparedStatement(queryString, Statement.NO_GENERATED_KEYS);) {
471  ps.clearParameters();
472  ps.setLong(1, addressObjectId);
473  ps.setLong(2, addressObjectId);
474  try (ResultSet rs = ps.executeQuery()) {
475  if (!rs.next()) {
476  return false;
477  } else {
478  boolean status = rs.getLong("mappingCount") > 0;
479  if (status) {
480  recentHostNameAndIpMappingCache.put(addressObjectId, new Byte((byte) 1));
481  }
482  return status;
483  }
484  }
485  } catch (SQLException ex) {
486  throw new TskCoreException("Error looking up host address / Ip mapping for address = " + addressObjectId, ex);
487  } finally {
489  }
490  }
491 
505  public Optional<Long> hostAddressExists(HostAddress.HostAddressType type, String address) throws TskCoreException {
506 
507  HostAddress hostAddress = recentHostAddressCache.getIfPresent(createRecentHostAddressKey(type, address));
508  if (Objects.nonNull(hostAddress)) {
509  return Optional.of(hostAddress.getId());
510  }
511 
512  HostAddress.HostAddressType addressType = type;
513  if (type.equals(HostAddress.HostAddressType.DNS_AUTO)) {
514  addressType = getDNSType(address);
515  }
516 
517  String queryString = "SELECT id, address_type, address FROM tsk_host_addresses"
518  + " WHERE address = ? AND address_type = ?";
519 
521  try (CaseDbConnection connection = this.db.getConnection();
522  PreparedStatement query = connection.getPreparedStatement(queryString, Statement.NO_GENERATED_KEYS);) {
523  query.clearParameters();
524  query.setString(1, address.toLowerCase());
525  query.setInt(2, addressType.getId());
526  try (ResultSet rs = query.executeQuery()) {
527  if (!rs.next()) {
528  return Optional.empty(); // no match found
529  } else {
530  long objId = rs.getLong("id");
531  int addrType = rs.getInt("address_type");
532  String addr = rs.getString("address");
533  HostAddress hostAddr = new HostAddress(db, objId, HostAddress.HostAddressType.fromID(addrType), addr);
534  recentHostAddressCache.put(createRecentHostAddressKey(addrType, address), hostAddr);
535  return Optional.of(objId);
536  }
537  }
538  } catch (SQLException ex) {
539  throw new TskCoreException(String.format("Error getting host address with type = %s and address = %s", type.getName(), address), ex);
540  } finally {
542  }
543  }
544 
554  public List<HostAddress> getIpAddress(String hostname) throws TskCoreException {
555  String queryString = "SELECT ip_address_id FROM tsk_host_address_dns_ip_map as map "
556  + " JOIN tsk_host_addresses as addresses "
557  + " ON map.dns_address_id = addresses.id "
558  + " WHERE addresses.address_type = " + HostAddress.HostAddressType.HOSTNAME.getId()
559  + " AND addresses.address = ?";
560 
562  try (CaseDbConnection connection = this.db.getConnection()) {
563  List<HostAddress> IpAddresses = new ArrayList<>();
564  PreparedStatement query = connection.getPreparedStatement(queryString, Statement.NO_GENERATED_KEYS);
565  query.clearParameters();
566  query.setString(1, hostname.toLowerCase());
567  try (ResultSet rs = query.executeQuery()) {
568  while (rs.next()) {
569  long ipAddressObjId = rs.getLong("ip_address_id");
570  IpAddresses.add(HostAddressManager.this.getHostAddress(ipAddressObjId, connection));
571  recentHostNameAndIpMappingCache.put(ipAddressObjId, new Byte((byte) 1));
572  }
573  return IpAddresses;
574  }
575  } catch (SQLException ex) {
576  throw new TskCoreException(String.format("Error getting host addresses for host name: " + hostname), ex);
577  } finally {
579  }
580  }
581 
591  List<HostAddress> getHostNameByIp(String ipAddress) throws TskCoreException {
592  String queryString = "SELECT dns_address_id FROM tsk_host_address_dns_ip_map as map "
593  + " JOIN tsk_host_addresses as addresses "
594  + " ON map.ip_address_id = addresses.id "
595  + " WHERE ( addresses.address_type = " + HostAddress.HostAddressType.IPV4.getId()
596  + " OR addresses.address_type = " + HostAddress.HostAddressType.IPV6.getId() + ")"
597  + " AND addresses.address = ?";
598 
600  try (CaseDbConnection connection = this.db.getConnection()) {
601  List<HostAddress> dnsNames = new ArrayList<>();
602  PreparedStatement query = connection.getPreparedStatement(queryString, Statement.NO_GENERATED_KEYS);
603  query.clearParameters();
604  query.setString(1, ipAddress.toLowerCase());
605  try (ResultSet rs = query.executeQuery()) {
606  while (rs.next()) {
607  long dnsAddressId = rs.getLong("dns_address_id");
608  dnsNames.add(HostAddressManager.this.getHostAddress(dnsAddressId, connection));
609  recentHostNameAndIpMappingCache.put(dnsAddressId, new Byte((byte) 1));
610  }
611  return dnsNames;
612  }
613  } catch (SQLException ex) {
614  throw new TskCoreException(String.format("Error getting host addresses for IP address: " + ipAddress), ex);
615  } finally {
617  }
618  }
619 
626  public void addUsage(Content content, HostAddress hostAddress) throws TskCoreException {
627 
628  String key = content.getDataSource().getId() + "#" + hostAddress.getId() + "#" + content.getId();
629  Boolean cachedValue = hostAddressUsageCache.getIfPresent(key);
630  if (null != cachedValue) {
631  return;
632  }
633 
634  final String insertSQL = db.getInsertOrIgnoreSQL(" INTO tsk_host_address_usage(addr_obj_id, obj_id, data_source_obj_id) "
635  + " VALUES(" + hostAddress.getId() + ", " + content.getId() + ", " + content.getDataSource().getId() + ") ");
636 
638  try (CaseDbConnection connection = this.db.getConnection();
639  Statement s = connection.createStatement()) {
640  connection.executeUpdate(s, insertSQL);
641  hostAddressUsageCache.put(key, true);
642  } catch (SQLException ex) {
643  throw new TskCoreException(String.format("Error associating host address %s with artifact with id %d", hostAddress.getAddress(), content.getId()), ex);
644  } finally {
646  }
647  }
648 
649  private final String ADDRESS_USAGE_QUERY = "SELECT addresses.id as id, addresses.address_type as address_type, addresses.address as address "
650  + " FROM tsk_host_address_usage as usage "
651  + " JOIN tsk_host_addresses as addresses "
652  + " ON usage.addr_obj_id = addresses.id ";
653 
663  public List<HostAddress> getHostAddressesUsedByContent(Content content) throws TskCoreException {
664  String queryString = ADDRESS_USAGE_QUERY
665  + " WHERE usage.obj_id = " + content.getId();
666 
667  return getHostAddressesUsed(queryString);
668  }
669 
679  public List<HostAddress> getHostAddressesUsedOnDataSource(Content dataSource) throws TskCoreException {
680  String queryString = ADDRESS_USAGE_QUERY
681  + " WHERE usage.data_source_obj_id = " + dataSource.getId();
682 
683  return getHostAddressesUsed(queryString);
684  }
685 
695  private List<HostAddress> getHostAddressesUsed(String addressesUsedSQL) throws TskCoreException {
696 
697  List<HostAddress> addressesUsed = new ArrayList<>();
698 
700  try (CaseDbConnection connection = this.db.getConnection();
701  Statement s = connection.createStatement();
702  ResultSet rs = connection.executeQuery(s, addressesUsedSQL)) {
703 
704  while (rs.next()) {
705  addressesUsed.add(new HostAddress(db, rs.getLong("id"), HostAddress.HostAddressType.fromID(rs.getInt("address_type")), rs.getString("address")));
706  }
707  return addressesUsed;
708  } catch (SQLException ex) {
709  throw new TskCoreException(String.format("Error getting host addresses used with query string = %s", addressesUsedSQL), ex);
710  } finally {
712  }
713  }
714 
722  private HostAddress.HostAddressType getDNSType(String address) {
723  if (isIPv4(address)) {
724  return HostAddress.HostAddressType.IPV4;
725  } else if (isIPv6(address)) {
726  return HostAddress.HostAddressType.IPV6;
727  } else {
728  return HostAddress.HostAddressType.HOSTNAME;
729  }
730  }
731 
732  private static final Pattern IPV4_PATTERN =
733  Pattern.compile("^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\\.(?!$)|$)){4}$");
741  private static boolean isIPv4(String ipAddress) {
742  if (ipAddress != null) {
743  return IPV4_PATTERN.matcher(ipAddress).matches();
744  }
745  return false;
746  }
747 
748 
749  private static final Pattern IPV6_STD_PATTERN =
750  Pattern.compile("^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$");
751  private static final Pattern IPV6_HEX_COMPRESSED_PATTERN =
752  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})*)?)$");
753 
754 
755  private static boolean isIPv6StdAddress(final String input) {
756  return IPV6_STD_PATTERN.matcher(input).matches();
757  }
758  private static boolean isIPv6HexCompressedAddress(final String input) {
759  return IPV6_HEX_COMPRESSED_PATTERN.matcher(input).matches();
760  }
761 
769  private static boolean isIPv6(String ipAddress) {
770 
771  if (ipAddress != null) {
772  return isIPv6StdAddress(ipAddress) || isIPv6HexCompressedAddress(ipAddress);
773  }
774 
775  return false;
776  }
777 }
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)
HostAddress newHostAddress(HostAddress.HostAddressType type, String address)
List< HostAddress > getIpAddress(String hostname)
void assignHostToAddress(Host host, HostAddress hostAddress, Long time, Content source)
boolean hostNameAndIpMappingExists(long addressObjectId)
void addHostNameAndIpMapping(HostAddress dnsNameAddress, HostAddress ipAddress, Long time, Content source)

Copyright © 2011-2021 Brian Carrier. (carrier -at- sleuthkit -dot- org)
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.