Autopsy  4.10.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 public final class BinaryCookieReader implements Iterable<Cookie> {
44 
45  private static final int MAGIC_SIZE = 4;
46  private static final int SIZEOF_INT_BYTES = 4;
47  private static final int PAGE_HEADER_VALUE = 256;
48 
49  private static final String COOKIE_MAGIC = "cook"; //NON-NLS
50 
51  private static final int MAC_EPOC_FIX = 978307200;
52 
53  private final int[] pageSizeArray;
54  private final File cookieFile;
55 
56  private static final Logger LOG = Logger.getLogger(BinaryCookieReader.class.getName());
57 
63  private BinaryCookieReader(File cookieFile, int[] pageSizeArray) {
64  this.cookieFile = cookieFile;
65  this.pageSizeArray = pageSizeArray;
66  }
67 
78  public static BinaryCookieReader initalizeReader(File cookieFile) throws FileNotFoundException, IOException {
79  BinaryCookieReader reader = null;
80  try (DataInputStream dataStream = new DataInputStream(new FileInputStream(cookieFile))) {
81 
82  byte[] magic = new byte[MAGIC_SIZE];
83  if (dataStream.read(magic) != MAGIC_SIZE) {
84  throw new IOException("Failed to read header, invalid file size (" + cookieFile.getName() + ")"); //NON-NLS
85  }
86 
87  if (!(new String(magic)).equals(COOKIE_MAGIC)) {
88  throw new IOException(cookieFile.getName() + " is not a cookie file"); //NON-NLS
89  }
90 
91  int[] sizeArray = null;
92  int pageCount = dataStream.readInt();
93  if (pageCount != 0) {
94  sizeArray = new int[pageCount];
95 
96  for (int cnt = 0; cnt < pageCount; cnt++) {
97  sizeArray[cnt] = dataStream.readInt();
98  }
99 
100  LOG.log(Level.INFO, "No cookies found in {0}", cookieFile.getName()); //NON-NLS
101  }
102 
103  reader = new BinaryCookieReader(cookieFile, sizeArray);
104  }
105 
106  return reader;
107  }
108 
114  @Override
115  public Iterator<Cookie> iterator() {
116  return new CookiePageIterator();
117  }
118 
122  private class CookiePageIterator implements Iterator<Cookie> {
123 
124  int pageIndex = 0;
125  CookiePage currentPage = null;
126  Iterator<Cookie> currentIterator = null;
127  DataInputStream dataStream = null;
128 
133  if(pageSizeArray == null || pageSizeArray.length == 0) {
134  return;
135  }
136 
137  try {
138  dataStream = new DataInputStream(new FileInputStream(cookieFile));
139  // skip to the first page
140  dataStream.skipBytes((2 * SIZEOF_INT_BYTES) + (pageSizeArray.length * SIZEOF_INT_BYTES));
141  } catch (IOException ex) {
142 
143  String errorMessage = String.format("An error occurred creating an input stream for %s", cookieFile.getName());
144  LOG.log(Level.WARNING, errorMessage, ex); //NON-NLS
145  closeStream(); // Just incase the error was from skip
146  }
147  }
148 
154  @Override
155  public boolean hasNext() {
156 
157  if (dataStream == null) {
158  return false;
159  }
160 
161  if (currentIterator == null || !currentIterator.hasNext()) {
162  try {
163 
164  if (pageIndex < pageSizeArray.length) {
165  byte[] nextPage = new byte[pageSizeArray[pageIndex]];
166  dataStream.read(nextPage);
167 
168  currentPage = new CookiePage(nextPage);
169  currentIterator = currentPage.iterator();
170  } else {
171  closeStream();
172  return false;
173  }
174 
175  pageIndex++;
176  } catch (IOException ex) {
177  closeStream();
178  String errorMessage = String.format("A read error occured for file %s (pageIndex = %d)", cookieFile.getName(), pageIndex);
179  LOG.log(Level.WARNING, errorMessage, ex); //NON-NLS
180  return false;
181  }
182  }
183 
184  return currentIterator.hasNext();
185  }
186 
192  @Override
193  public Cookie next() {
194  // Just in case someone uses next without hasNext, this check will
195  // make sure there are more elements and that we iterate properly
196  // through the pages.
197  if (!hasNext()) {
198  throw new NoSuchElementException();
199  }
200  return currentIterator.next();
201  }
202 
206  private void closeStream() {
207  if (dataStream != null) {
208  try {
209  dataStream.close();
210  dataStream = null;
211  } catch (IOException ex) {
212  String errorMessage = String.format("An error occurred trying to close stream for file %s", cookieFile.getName());
213  LOG.log(Level.WARNING, errorMessage, ex); //NON-NLS
214  }
215  }
216  }
217  }
218 
222  private class CookiePage implements Iterable<Cookie> {
223 
224  int[] cookieOffSets;
225  ByteBuffer pageBuffer;
226 
234  CookiePage(byte[] page) throws IOException {
235  if (page == null || page.length == 0) {
236  throw new IllegalArgumentException("Invalid value for page passed to CookiePage constructor"); //NON-NLS
237  }
238 
239  pageBuffer = ByteBuffer.wrap(page);
240 
241  if (pageBuffer.getInt() != PAGE_HEADER_VALUE) {
242  pageBuffer = null;
243  throw new IOException("Invalid file format, bad page head value found"); //NON-NLS
244  }
245 
246  pageBuffer.order(ByteOrder.LITTLE_ENDIAN);
247  int count = pageBuffer.getInt();
248  cookieOffSets = new int[count];
249 
250  for (int cnt = 0; cnt < count; cnt++) {
251  cookieOffSets[cnt] = pageBuffer.getInt();
252  }
253 
254  pageBuffer.getInt(); // All 0, not needed
255  }
256 
262  @Override
263  public Iterator<Cookie> iterator() {
264  return new CookieIterator();
265  }
266 
270  private class CookieIterator implements Iterator<Cookie> {
271 
272  int index = 0;
273 
279  @Override
280  public boolean hasNext() {
281  if (pageBuffer == null) {
282  return false;
283  }
284 
285  return index < cookieOffSets.length;
286  }
287 
293  @Override
294  public Cookie next() {
295  if (!hasNext()) {
296  throw new NoSuchElementException();
297  }
298 
299  int offset = cookieOffSets[index];
300  int size = pageBuffer.getInt(offset);
301  byte[] cookieBytes = new byte[size];
302  pageBuffer.get(cookieBytes, 0, size);
303  index++;
304 
305  return new Cookie(cookieBytes);
306  }
307  }
308  }
309 
313  public class Cookie {
314 
315  private final static int COOKIE_HEAD_SKIP = 16;
316 
317  private final double expirationDate;
318  private final double creationDate;
319 
320  private final String name;
321  private final String url;
322  private final String path;
323  private final String value;
324 
330  protected Cookie(byte[] cookieBytes) {
331  if (cookieBytes == null || cookieBytes.length == 0) {
332  throw new IllegalArgumentException("Invalid value for cookieBytes passed to Cookie constructor"); //NON-NLS
333  }
334 
335  ByteBuffer byteBuffer = ByteBuffer.wrap(cookieBytes);
336  byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
337 
338  // Skip past the four int values that we are not interested in
339  byteBuffer.position(byteBuffer.position() + COOKIE_HEAD_SKIP);
340 
341  int urlOffset = byteBuffer.getInt();
342  int nameOffset = byteBuffer.getInt();
343  int pathOffset = byteBuffer.getInt();
344  int valueOffset = byteBuffer.getInt();
345  byteBuffer.getLong(); // 8 bytes of not needed
346 
347  expirationDate = byteBuffer.getDouble();
348  creationDate = byteBuffer.getDouble();
349 
350  url = decodeString(cookieBytes, urlOffset);
351  name = decodeString(cookieBytes, nameOffset);
352  path = decodeString(cookieBytes, pathOffset);
353  value = decodeString(cookieBytes, valueOffset);
354  }
355 
362  public final Long getExpirationDate() {
363  return ((long)expirationDate) + MAC_EPOC_FIX;
364  }
365 
372  public final Long getCreationDate() {
373  return ((long)creationDate) + MAC_EPOC_FIX;
374  }
375 
381  public final String getURL() {
382  return url;
383  }
384 
390  public final String getName() {
391  return name;
392  }
393 
399  public final String getPath() {
400  return path;
401  }
402 
408  public final String getValue() {
409  return value;
410  }
411 
420  private String decodeString(byte[] byteArray, int offset) {
421  byte[] stringBytes = new byte[byteArray.length - offset];
422  for (int index = 0; index < stringBytes.length; index++) {
423  byte nibble = byteArray[offset + index];
424  if (nibble != '\0') { //NON-NLS
425  stringBytes[index] = nibble;
426  } else {
427  break;
428  }
429  }
430 
431  return new String(stringBytes);
432  }
433  }
434 }
synchronized static Logger getLogger(String name)
Definition: Logger.java:124

Copyright © 2012-2018 Basis Technology. Generated on: Fri Mar 22 2019
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.