Autopsy  4.20.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
BinaryCookieReader.java
Go to the documentation of this file.
1 /*
2  *
3  * Autopsy Forensic Browser
4  *
5  * Copyright 2019 Basis Technology Corp.
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.autopsy.recentactivity;
20 
21 import java.io.DataInputStream;
22 import java.io.File;
23 import java.io.FileInputStream;
24 import java.io.FileNotFoundException;
25 import java.io.IOException;
26 import java.nio.ByteBuffer;
27 import java.nio.ByteOrder;
28 import java.util.Iterator;
29 import java.util.NoSuchElementException;
30 import java.util.logging.Level;
33 
43 final class BinaryCookieReader implements Iterable<Cookie> {
44 
45  private static final Logger LOG = Logger.getLogger(BinaryCookieReader.class.getName());
46  private static final int MAGIC_SIZE = 4;
47  private static final int SIZEOF_INT_BYTES = 4;
48  private static final int PAGE_HEADER_VALUE = 256;
49 
50  private static final String COOKIE_MAGIC = "cook"; //NON-NLS
51 
52  private static final int MAC_EPOC_FIX = 978307200;
53 
54  private final int[] pageSizeArray;
55  private final File cookieFile;
56 
62  private BinaryCookieReader(File cookieFile, int[] pageSizeArray) {
63  this.cookieFile = cookieFile;
64  this.pageSizeArray = pageSizeArray.clone();
65  }
66 
79  public static BinaryCookieReader initalizeReader(File cookieFile) throws FileNotFoundException, IOException {
80  BinaryCookieReader reader = null;
81  try (DataInputStream dataStream = new DataInputStream(new FileInputStream(cookieFile))) {
82 
83  byte[] magic = new byte[MAGIC_SIZE];
84  if (dataStream.read(magic) != MAGIC_SIZE) {
85  throw new IOException("Failed to read header, invalid file size (" + cookieFile.getName() + ")"); //NON-NLS
86  }
87 
88  if (!(new String(magic)).equals(COOKIE_MAGIC)) {
89  throw new IOException(cookieFile.getName() + " is not a cookie file"); //NON-NLS
90  }
91 
92  int[] sizeArray;
93  int pageCount = dataStream.readInt();
94  if (pageCount != 0) {
95  sizeArray = new int[pageCount];
96 
97  for (int cnt = 0; cnt < pageCount; cnt++) {
98  sizeArray[cnt] = dataStream.readInt();
99  }
100  } else {
101  LOG.log(Level.INFO, "No cookies found in {0}", cookieFile.getName()); //NON-NLS
102  sizeArray = new int[0];
103  }
104 
105  reader = new BinaryCookieReader(cookieFile, sizeArray);
106  }
107 
108  return reader;
109  }
110 
116  @Override
117  public Iterator<Cookie> iterator() {
118  return new CookiePageIterator();
119  }
120 
124  private class CookiePageIterator implements Iterator<Cookie> {
125 
126  int pageIndex = 0;
127  CookiePage currentPage = null;
128  Iterator<Cookie> currentIterator = null;
129  DataInputStream dataStream = null;
130 
135  if (pageSizeArray == null || pageSizeArray.length == 0) {
136  return;
137  }
138 
139  try {
140  dataStream = new DataInputStream(new FileInputStream(cookieFile));
141  // skip to the first page
142  dataStream.skipBytes((2 * SIZEOF_INT_BYTES) + (pageSizeArray.length * SIZEOF_INT_BYTES));
143  } catch (IOException ex) {
144 
145  String errorMessage = String.format("An error occurred creating an input stream for %s", cookieFile.getName());
146  LOG.log(Level.WARNING, errorMessage, ex); //NON-NLS
147  closeStream(); // Just incase the error was from skip
148  }
149  }
150 
156  @Override
157  public boolean hasNext() {
158 
159  if (dataStream == null) {
160  return false;
161  }
162 
163  if (currentIterator == null || !currentIterator.hasNext()) {
164  try {
165 
166  if (pageIndex < pageSizeArray.length) {
167  byte[] nextPage = new byte[pageSizeArray[pageIndex]];
168  dataStream.read(nextPage);
169 
170  currentPage = new CookiePage(nextPage);
171  currentIterator = currentPage.iterator();
172  } else {
173  closeStream();
174  return false;
175  }
176 
177  pageIndex++;
178  } catch (IOException ex) {
179  closeStream();
180  String errorMessage = String.format("A read error occured for file %s (pageIndex = %d)", cookieFile.getName(), pageIndex);
181  LOG.log(Level.WARNING, errorMessage, ex); //NON-NLS
182  return false;
183  }
184  }
185 
186  return currentIterator.hasNext();
187  }
188 
194  @Override
195  public Cookie next() {
196  // Just in case someone uses next without hasNext, this check will
197  // make sure there are more elements and that we iterate properly
198  // through the pages.
199  if (!hasNext()) {
200  throw new NoSuchElementException();
201  }
202  return currentIterator.next();
203  }
204 
208  private void closeStream() {
209  if (dataStream != null) {
210  try {
211  dataStream.close();
212  dataStream = null;
213  } catch (IOException ex) {
214  String errorMessage = String.format("An error occurred trying to close stream for file %s", cookieFile.getName());
215  LOG.log(Level.WARNING, errorMessage, ex); //NON-NLS
216  }
217  }
218  }
219  }
220 
224  private class CookiePage implements Iterable<Cookie> {
225 
226  int[] cookieOffSets;
227  ByteBuffer pageBuffer;
228 
237  CookiePage(byte[] page) throws IOException {
238  if (page == null || page.length == 0) {
239  throw new IllegalArgumentException("Invalid value for page passed to CookiePage constructor"); //NON-NLS
240  }
241 
242  pageBuffer = ByteBuffer.wrap(page);
243 
244  if (pageBuffer.getInt() != PAGE_HEADER_VALUE) {
245  pageBuffer = null;
246  throw new IOException("Invalid file format, bad page head value found"); //NON-NLS
247  }
248 
249  pageBuffer.order(ByteOrder.LITTLE_ENDIAN);
250  int count = pageBuffer.getInt();
251  cookieOffSets = new int[count];
252 
253  for (int cnt = 0; cnt < count; cnt++) {
254  cookieOffSets[cnt] = pageBuffer.getInt();
255  }
256 
257  pageBuffer.getInt(); // All 0, not needed
258  }
259 
265  @Override
266  public Iterator<Cookie> iterator() {
267  return new CookieIterator();
268  }
269 
273  private class CookieIterator implements Iterator<Cookie> {
274 
275  int index = 0;
276 
282  @Override
283  public boolean hasNext() {
284  if (pageBuffer == null) {
285  return false;
286  }
287 
288  return index < cookieOffSets.length;
289  }
290 
296  @Override
297  public Cookie next() {
298  if (!hasNext()) {
299  throw new NoSuchElementException();
300  }
301 
302  int offset = cookieOffSets[index];
303  int size = pageBuffer.getInt(offset);
304  byte[] cookieBytes = new byte[size];
305  pageBuffer.get(cookieBytes, 0, size);
306  index++;
307 
308  return new Cookie(cookieBytes);
309  }
310  }
311  }
312 
316  public class Cookie {
317 
318  private final static int COOKIE_HEAD_SKIP = 16;
319 
320  private final double expirationDate;
321  private final double creationDate;
322 
323  private final String name;
324  private final String url;
325  private final String path;
326  private final String value;
327 
333  protected Cookie(byte[] cookieBytes) {
334  if (cookieBytes == null || cookieBytes.length == 0) {
335  throw new IllegalArgumentException("Invalid value for cookieBytes passed to Cookie constructor"); //NON-NLS
336  }
337 
338  ByteBuffer byteBuffer = ByteBuffer.wrap(cookieBytes);
339  byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
340 
341  // Skip past the four int values that we are not interested in
342  byteBuffer.position(byteBuffer.position() + COOKIE_HEAD_SKIP);
343 
344  int urlOffset = byteBuffer.getInt();
345  int nameOffset = byteBuffer.getInt();
346  int pathOffset = byteBuffer.getInt();
347  int valueOffset = byteBuffer.getInt();
348  byteBuffer.getLong(); // 8 bytes of not needed
349 
350  expirationDate = byteBuffer.getDouble();
351  creationDate = byteBuffer.getDouble();
352 
353  url = decodeString(cookieBytes, urlOffset);
354  name = decodeString(cookieBytes, nameOffset);
355  path = decodeString(cookieBytes, pathOffset);
356  value = decodeString(cookieBytes, valueOffset);
357  }
358 
365  public final Long getExpirationDate() {
366  return ((long) expirationDate) + MAC_EPOC_FIX;
367  }
368 
375  public final Long getCreationDate() {
376  return ((long) creationDate) + MAC_EPOC_FIX;
377  }
378 
384  public final String getURL() {
385  return url;
386  }
387 
393  public final String getName() {
394  return name;
395  }
396 
402  public final String getPath() {
403  return path;
404  }
405 
411  public final String getValue() {
412  return value;
413  }
414 
424  private String decodeString(byte[] byteArray, int offset) {
425  byte[] stringBytes = new byte[byteArray.length - offset];
426  for (int index = 0; index < stringBytes.length; index++) {
427  byte nibble = byteArray[offset + index];
428  if (nibble != '\0') { //NON-NLS
429  stringBytes[index] = nibble;
430  } else {
431  break;
432  }
433  }
434 
435  return new String(stringBytes);
436  }
437  }
438 }
synchronized static Logger getLogger(String name)
Definition: Logger.java:124

Copyright © 2012-2022 Basis Technology. Generated on: Tue Aug 1 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.