/* $Id$ */
/* Copyright (C) 2005 Nicholas Harbour
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* This file is part of
Tcpxtract, a sniffer that extracts files based on headers
by Nick Harbour
*/
#include <assert.h>
#include <sys/types.h>
#include <inttypes.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include "extract.h"
#include "search.h"
#include "util.h"
#include "sessionlist.h"
int filenum;
char *output_prefix;
static void add_extract(extract_list_t **, fileid_t *, slist_t *, int, int);
static void set_segment_marks(extract_list_t *, size_t);
static void mark_footer(extract_list_t *, srch_results_t *);
static void extract_segment(extract_list_t *, const uint8_t *);
static void sweep_extract_list(extract_list_t **);
static int open_extract(char *);
/* called once for each packet, this funciton starts, updates, and closes
* file extractions. this is the one-stop-shop for all your file extraction needs */
void extract(extract_list_t **elist, srch_results_t *results, slist_t *session, const uint8_t *data, size_t size)
{
srch_results_t *rptr;
extract_list_t *eptr;
assert(elist != NULL);
/* set all existing segment values to what they would be with no search results */
for (eptr = *elist; eptr != NULL; eptr = eptr->next)
set_segment_marks(eptr, size);
/* look for new headers in the results set */
for (rptr = results; rptr != NULL; rptr = rptr->next)
if (rptr->spectype == HEADER)
add_extract(elist, rptr->fileid, session, rptr->offset.start, size);
/* flip through any footers we found and close out those extracts */
for (rptr = results; rptr != NULL; rptr = rptr->next)
if (rptr->spectype == FOOTER)
mark_footer(*elist, rptr);
/* now lets do all the file writing and whatnot */
for (eptr = *elist; eptr != NULL; eptr = eptr->next)
extract_segment(eptr, data);
/* remove any finished extractions from the list */
sweep_extract_list(elist);
}
/* Add a new header match to the list of files being extracted */
static void add_extract(extract_list_t **elist, fileid_t *fileid, slist_t *session, int offset, int size)
{
extract_list_t *eptr;
assert(elist != NULL);
assert(fileid != NULL);
/* add a new entry to the list */
eptr = ecalloc(1, sizeof *eptr);
eptr->next = *elist;
eptr->fileid = fileid;
if (eptr->next != NULL)
eptr->next->prev = eptr;
report("Found file of type \"%s\" in session [", fileid->ext);
printip(session->connection.ip_src);
report(":%d -> ", session->connection.port_src);
printip(session->connection.ip_dst);
report(":%d], exporting to ", session->connection.port_dst);
eptr->fd = open_extract(fileid->ext);
eptr->segment.start = offset;
if (fileid->maxlen <= size - offset)
eptr->segment.end = offset + fileid->maxlen;
else
eptr->segment.end = size;
*elist = eptr;
}
/* open the next availible filename for writing */
static int open_extract(char *ext)
{
int retval;
char fname[FILENAME_BUFFER_SIZE] = {'\0'}; /* buffer to snprintf our filename to */
do
snprintf(fname, FILENAME_BUFFER_SIZE, "%s%08d.%s", output_prefix == NULL ? "" : output_prefix, filenum++, ext);
while ((retval = open(fname, O_WRONLY|O_CREAT|O_EXCL, S_IRWXU)) == -1);
report("%s\n", fname);
return retval;
}
/* set segment start and end values to the contraints of the data buffer or maxlen */
static void set_segment_marks(extract_list_t *elist, size_t size)
{
extract_list_t *eptr;
for (eptr = elist; eptr != NULL; eptr = eptr->next) {
eptr->segment.start = 0;
if (eptr->fileid->maxlen - eptr->nwritten < size) {
eptr->segment.end = eptr->fileid->maxlen - eptr->nwritten;
eptr->finish++;
} else
eptr->segment.end = size;
}
}
/* adjust segment end values depending on footers found */
static void mark_footer(extract_list_t *elist, srch_results_t *footer)
{
extract_list_t *eptr;
/* this associates the first footer found with the last header found of a given type
* this is to accommodate embedded document types. Somebody may have differing needs
* so this may want to be reworked later */
for (eptr = elist; eptr != NULL; eptr = eptr->next) {
if (footer->fileid->id == eptr->fileid->id && eptr->segment.start < footer->offset.start) {
eptr->segment.end = footer->offset.end; /* this could extend beyond maxlen */
eptr->finish++;
break;
}
}
}
/* write data to a specified extract file */
static void extract_segment(extract_list_t *elist, const uint8_t *data)
{
size_t nbytes = elist->segment.end - elist->segment.start;
if (nbytes != write(elist->fd, data + elist->segment.start, nbytes)) {
perror("Error Writing File");
error("Quiting.");
}
elist->nwritten += nbytes;
sync();
}
/* remove all finished extracts from the list */
static void sweep_extract_list(extract_list_t **elist)
{
extract_list_t *eptr, *nxt;
assert(elist != NULL);
for (eptr = *elist; eptr != NULL; eptr = nxt) {
nxt = eptr->next;
if (eptr->finish) {
if (eptr->prev != NULL)
eptr->prev->next = eptr->next;
if (eptr->next != NULL)
eptr->next->prev = eptr->prev;
if (*elist == eptr)
*elist = eptr->next;
close(eptr->fd);
free(eptr);
}
}
}
syntax highlighted by Code2HTML, v. 0.9.1