1 /* $Id$ */
2 /* Copyright (C) 2005 Nicholas Harbour
3    This program is free software; you can redistribute it and/or modify
4    it under the terms of the GNU General Public License as published by
5    the Free Software Foundation; either version 2, or (at your option)
6    any later version.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software Foundation,
15    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 */
17 
18 /* This file is part of
19    Tcpxtract, a sniffer that extracts files based on headers
20    by Nick Harbour
21 */
22 
23 #include <assert.h>
24 #include <sys/types.h>
25 #include <inttypes.h>
26 #include <errno.h>
27 #include <stdlib.h>
28 #include <sys/stat.h>
29 #include <fcntl.h>
30 #include <unistd.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include "extract.h"
34 #include "search.h"
35 #include "util.h"
36 #include "sessionlist.h"
37 
38 int filenum;
39 char *output_prefix;
40 
41 static void add_extract(extract_list_t **, fileid_t *, slist_t *, int, int);
42 static void set_segment_marks(extract_list_t *, size_t);
43 static void mark_footer(extract_list_t *, srch_results_t *);
44 static void extract_segment(extract_list_t *, const uint8_t *);
45 static void sweep_extract_list(extract_list_t **);
46 static int open_extract(char *);
47 
48 /* called once for each packet, this funciton starts, updates, and closes
49  * file extractions.  this is the one-stop-shop for all your file extraction needs */
extract(extract_list_t ** elist,srch_results_t * results,slist_t * session,const uint8_t * data,size_t size)50 void extract(extract_list_t **elist, srch_results_t *results, slist_t *session, const uint8_t *data, size_t size)
51 {
52     srch_results_t *rptr;
53     extract_list_t *eptr;
54 
55     assert(elist != NULL);
56 
57     /* set all existing segment values to what they would be with no search results */
58     for (eptr = *elist; eptr != NULL; eptr = eptr->next)
59         set_segment_marks(eptr, size);
60 
61     /* look for new headers in the results set */
62     for (rptr = results; rptr != NULL; rptr = rptr->next)
63         if (rptr->spectype == HEADER)
64             add_extract(elist, rptr->fileid, session, rptr->offset.start, size);
65 
66     /* flip through any footers we found and close out those extracts */
67     for (rptr = results; rptr != NULL; rptr = rptr->next)
68         if (rptr->spectype == FOOTER)
69             mark_footer(*elist, rptr);
70 
71     /* now lets do all the file writing and whatnot */
72     for (eptr = *elist; eptr != NULL; eptr = eptr->next)
73         extract_segment(eptr, data);
74 
75     /* remove any finished extractions from the list */
76     sweep_extract_list(elist);
77 }
78 
79 /* Add a new header match to the list of files being extracted */
add_extract(extract_list_t ** elist,fileid_t * fileid,slist_t * session,int offset,int size)80 static void add_extract(extract_list_t **elist, fileid_t *fileid, slist_t *session, int offset, int size)
81 {
82     extract_list_t *eptr;
83 
84     assert(elist != NULL);
85     assert(fileid != NULL);
86 
87     /* add a new entry to the list */
88     eptr = ecalloc(1, sizeof *eptr);
89     eptr->next = *elist;
90     eptr->fileid = fileid;
91     if (eptr->next != NULL)
92         eptr->next->prev = eptr;
93 
94     report("Found file of type \"%s\" in session [", fileid->ext);
95     printip(session->connection.ip_src);
96     report(":%d -> ", session->connection.port_src);
97     printip(session->connection.ip_dst);
98     report(":%d], exporting to ", session->connection.port_dst);
99     eptr->fd = open_extract(fileid->ext);
100     eptr->segment.start = offset;
101     if (fileid->maxlen <= size - offset)
102         eptr->segment.end = offset + fileid->maxlen;
103     else
104         eptr->segment.end = size;
105 
106     *elist = eptr;
107 }
108 
109 /* open the next availible filename for writing */
open_extract(char * ext)110 static int open_extract(char *ext)
111 {
112     int retval;
113     char fname[FILENAME_BUFFER_SIZE] = {'\0'};      /* buffer to snprintf our filename to */
114 
115     do
116         snprintf(fname, FILENAME_BUFFER_SIZE, "%s%08d.%s", output_prefix == NULL ? "" : output_prefix, filenum++, ext);
117     while ((retval = open(fname, O_WRONLY|O_CREAT|O_EXCL, S_IRWXU)) == -1);
118 
119     report("%s\n", fname);
120 
121     return retval;
122 }
123 
124 /* set segment start and end values to the contraints of the data buffer or maxlen */
set_segment_marks(extract_list_t * elist,size_t size)125 static void set_segment_marks(extract_list_t *elist, size_t size)
126 {
127     extract_list_t *eptr;
128 
129     for (eptr = elist; eptr != NULL; eptr = eptr->next) {
130         eptr->segment.start = 0;
131         if (eptr->fileid->maxlen - eptr->nwritten < size) {
132             eptr->segment.end = eptr->fileid->maxlen - eptr->nwritten;
133             eptr->finish++;
134         } else
135             eptr->segment.end = size;
136     }
137 }
138 
139 /* adjust segment end values depending on footers found */
mark_footer(extract_list_t * elist,srch_results_t * footer)140 static void mark_footer(extract_list_t *elist, srch_results_t *footer)
141 {
142     extract_list_t *eptr;
143 
144     /* this associates the first footer found with the last header found of a given type
145      * this is to accommodate embedded document types.  Somebody may have differing needs
146      * so this may want to be reworked later */
147     for (eptr = elist; eptr != NULL; eptr = eptr->next) {
148         if (footer->fileid->id == eptr->fileid->id && eptr->segment.start < footer->offset.start) {
149             eptr->segment.end = footer->offset.end;  /* this could extend beyond maxlen */
150             eptr->finish++;
151             break;
152         }
153     }
154 }
155 
156 /* write data to a specified extract file */
extract_segment(extract_list_t * elist,const uint8_t * data)157 static void extract_segment(extract_list_t *elist, const uint8_t *data)
158 {
159     size_t nbytes = elist->segment.end - elist->segment.start;
160 
161     if (nbytes != write(elist->fd, data + elist->segment.start, nbytes)) {
162         perror("Error Writing File");
163         error("Quiting.");
164     }
165     elist->nwritten += nbytes;
166     sync();
167 }
168 
169 /* remove all finished extracts from the list */
sweep_extract_list(extract_list_t ** elist)170 static void sweep_extract_list(extract_list_t **elist)
171 {
172     extract_list_t *eptr, *nxt;
173 
174     assert(elist != NULL);
175 
176     for (eptr = *elist; eptr != NULL; eptr = nxt) {
177         nxt = eptr->next;
178         if (eptr->finish) {
179             if (eptr->prev != NULL)
180                 eptr->prev->next = eptr->next;
181             if (eptr->next != NULL)
182                 eptr->next->prev = eptr->prev;
183             if (*elist == eptr)
184                 *elist = eptr->next;
185             close(eptr->fd);
186             free(eptr);
187         }
188     }
189 }
190 
191