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