Autopsy  4.13.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 = null;
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 
101  LOG.log(Level.INFO, "No cookies found in {0}", cookieFile.getName()); //NON-NLS
102  }
103 
104  reader = new BinaryCookieReader(cookieFile, sizeArray);
105  }
106 
107  return reader;
108  }
109 
115  @Override
116  public Iterator<Cookie> iterator() {
117  return new CookiePageIterator();
118  }
119 
123  private class CookiePageIterator implements Iterator<Cookie> {
124 
125  int pageIndex = 0;
126  CookiePage currentPage = null;
127  Iterator<Cookie> currentIterator = null;
128  DataInputStream dataStream = null;
129 
134  if (pageSizeArray == null || pageSizeArray.length == 0) {
135  return;
136  }
137 
138  try {
139  dataStream = new DataInputStream(new FileInputStream(cookieFile));
140  // skip to the first page
141  dataStream.skipBytes((2 * SIZEOF_INT_BYTES) + (pageSizeArray.length * SIZEOF_INT_BYTES));
142  } catch (IOException ex) {
143 
144  String errorMessage = String.format("An error occurred creating an input stream for %s", cookieFile.getName());
145  LOG.log(Level.WARNING, errorMessage, ex); //NON-NLS
146  closeStream(); // Just incase the error was from skip
147  }
148  }
149 
155  @Override
156  public boolean hasNext() {
157 
158  if (dataStream == null) {
159  return false;
160  }
161 
162  if (currentIterator == null || !currentIterator.hasNext()) {
163  try {
164 
165  if (pageIndex < pageSizeArray.length) {
166  byte[] nextPage = new byte[pageSizeArray[pageIndex]];
167  dataStream.read(nextPage);
168 
169  currentPage = new CookiePage(nextPage);
170  currentIterator = currentPage.iterator();
171  } else {
172  closeStream();
173  return false;
174  }
175 
176  pageIndex++;
177  } catch (IOException ex) {
178  closeStream();
179  String errorMessage = String.format("A read error occured for file %s (pageIndex = %d)", cookieFile.getName(), pageIndex);
180  LOG.log(Level.WARNING, errorMessage, ex); //NON-NLS
181  return false;
182  }
183  }
184 
185  return currentIterator.hasNext();
186  }
187 
193  @Override
194  public Cookie next() {
195  // Just in case someone uses next without hasNext, this check will
196  // make sure there are more elements and that we iterate properly
197  // through the pages.
198  if (!hasNext()) {
199  throw new NoSuchElementException();
200  }
201  return currentIterator.next();
202  }
203 
207  private void closeStream() {
208  if (dataStream != null) {
209  try {
210  dataStream.close();
211  dataStream = null;
212  } catch (IOException ex) {
213  String errorMessage = String.format("An error occurred trying to close stream for file %s", cookieFile.getName());
214  LOG.log(Level.WARNING, errorMessage, ex); //NON-NLS
215  }
216  }
217  }
218  }
219 
223  private class CookiePage implements Iterable<Cookie> {
224 
225  int[] cookieOffSets;
226  ByteBuffer pageBuffer;
227 
236  CookiePage(byte[] page) throws IOException {
237  if (page == null || page.length == 0) {
238  throw new IllegalArgumentException("Invalid value for page passed to CookiePage constructor"); //NON-NLS
239  }
240 
241  pageBuffer = ByteBuffer.wrap(page);
242 
243  if (pageBuffer.getInt() != PAGE_HEADER_VALUE) {
244  pageBuffer = null;
245  throw new IOException("Invalid file format, bad page head value found"); //NON-NLS
246  }
247 
248  pageBuffer.order(ByteOrder.LITTLE_ENDIAN);
249  int count = pageBuffer.getInt();
250  cookieOffSets = new int[count];
251 
252  for (int cnt = 0; cnt < count; cnt++) {
253  cookieOffSets[cnt] = pageBuffer.getInt();
254  }
255 
256  pageBuffer.getInt(); // All 0, not needed
257  }
258 
264  @Override
265  public Iterator<Cookie> iterator() {
266  return new CookieIterator();
267  }
268 
272  private class CookieIterator implements Iterator<Cookie> {
273 
274  int index = 0;
275 
281  @Override
282  public boolean hasNext() {
283  if (pageBuffer == null) {
284  return false;
285  }
286 
287  return index < cookieOffSets.length;
288  }
289 
295  @Override
296  public Cookie next() {
297  if (!hasNext()) {
298  throw new NoSuchElementException();
299  }
300 
301  int offset = cookieOffSets[index];
302  int size = pageBuffer.getInt(offset);
303  byte[] cookieBytes = new byte[size];
304  pageBuffer.get(cookieBytes, 0, size);
305  index++;
306 
307  return new Cookie(cookieBytes);
308  }
309  }
310  }
311 
315  public class Cookie {
316 
317  private final static int COOKIE_HEAD_SKIP = 16;
318 
319  private final double expirationDate;
320  private final double creationDate;
321 
322  private final String name;
323  private final String url;
324  private final String path;
325  private final String value;
326 
332  protected Cookie(byte[] cookieBytes) {
333  if (cookieBytes == null || cookieBytes.length == 0) {
334  throw new IllegalArgumentException("Invalid value for cookieBytes passed to Cookie constructor"); //NON-NLS
335  }
336 
337  ByteBuffer byteBuffer = ByteBuffer.wrap(cookieBytes);
338  byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
339 
340  // Skip past the four int values that we are not interested in
341  byteBuffer.position(byteBuffer.position() + COOKIE_HEAD_SKIP);
342 
343  int urlOffset = byteBuffer.getInt();
344  int nameOffset = byteBuffer.getInt();
345  int pathOffset = byteBuffer.getInt();
346  int valueOffset = byteBuffer.getInt();
347  byteBuffer.getLong(); // 8 bytes of not needed
348 
349  expirationDate = byteBuffer.getDouble();
350  creationDate = byteBuffer.getDouble();
351 
352  url = decodeString(cookieBytes, urlOffset);
353  name = decodeString(cookieBytes, nameOffset);
354  path = decodeString(cookieBytes, pathOffset);
355  value = decodeString(cookieBytes, valueOffset);
356  }
357 
364  public final Long getExpirationDate() {
365  return ((long) expirationDate) + MAC_EPOC_FIX;
366  }
367 
374  public final Long getCreationDate() {
375  return ((long) creationDate) + MAC_EPOC_FIX;
376  }
377 
383  public final String getURL() {
384  return url;
385  }
386 
392  public final String getName() {
393  return name;
394  }
395 
401  public final String getPath() {
402  return path;
403  }
404 
410  public final String getValue() {
411  return value;
412  }
413 
423  private String decodeString(byte[] byteArray, int offset) {
424  byte[] stringBytes = new byte[byteArray.length - offset];
425  for (int index = 0; index < stringBytes.length; index++) {
426  byte nibble = byteArray[offset + index];
427  if (nibble != '\0') { //NON-NLS
428  stringBytes[index] = nibble;
429  } else {
430  break;
431  }
432  }
433 
434  return new String(stringBytes);
435  }
436  }
437 }
synchronized static Logger getLogger(String name)
Definition: Logger.java:124

Copyright © 2012-2019 Basis Technology. Generated on: Tue Jan 7 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.