FreeRDP
pcap.c
1 
20 #include <freerdp/config.h>
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #include <winpr/wtypes.h>
27 #include <winpr/assert.h>
28 #include <winpr/file.h>
29 #include <winpr/crt.h>
30 #include <winpr/sysinfo.h>
31 
32 #include <freerdp/types.h>
33 #include <freerdp/utils/pcap.h>
34 
35 #define PCAP_MAGIC 0xA1B2C3D4
36 
37 struct rdp_pcap
38 {
39  FILE* fp;
40  char* name;
41  BOOL write;
42  INT64 file_size;
43  size_t record_count;
44  pcap_header header;
45  pcap_record* head;
46  pcap_record* tail;
47  pcap_record* record;
48 };
49 
50 static BOOL pcap_read_header(rdpPcap* pcap, pcap_header* header)
51 {
52  WINPR_ASSERT(pcap);
53  WINPR_ASSERT(header);
54 
55  return fread(header, sizeof(pcap_header), 1, pcap->fp) == 1;
56 }
57 
58 static BOOL pcap_write_header(rdpPcap* pcap, const pcap_header* header)
59 {
60  WINPR_ASSERT(pcap);
61  WINPR_ASSERT(header);
62 
63  return fwrite(header, sizeof(pcap_header), 1, pcap->fp) == 1;
64 }
65 
66 static BOOL pcap_read_record_header(rdpPcap* pcap, pcap_record_header* record)
67 {
68  WINPR_ASSERT(pcap);
69  WINPR_ASSERT(record);
70 
71  return fread(record, sizeof(pcap_record_header), 1, pcap->fp) == 1;
72 }
73 
74 static BOOL pcap_write_record_header(rdpPcap* pcap, const pcap_record_header* record)
75 {
76  WINPR_ASSERT(pcap);
77  WINPR_ASSERT(record);
78 
79  return fwrite(record, sizeof(pcap_record_header), 1, pcap->fp) == 1;
80 }
81 
82 static BOOL pcap_read_record(rdpPcap* pcap, pcap_record* record)
83 {
84  WINPR_ASSERT(pcap);
85  WINPR_ASSERT(record);
86 
87  if (!pcap_read_record_header(pcap, &record->header))
88  return FALSE;
89 
90  record->length = record->header.incl_len;
91  record->data = malloc(record->length);
92  if (!record->data)
93  return FALSE;
94 
95  if (fread(record->data, record->length, 1, pcap->fp) != 1)
96  {
97  free(record->data);
98  record->data = NULL;
99  return FALSE;
100  }
101  return TRUE;
102 }
103 
104 static BOOL pcap_write_record(rdpPcap* pcap, const pcap_record* record)
105 {
106  WINPR_ASSERT(pcap);
107  WINPR_ASSERT(record);
108 
109  return pcap_write_record_header(pcap, &record->header) &&
110  (fwrite(record->cdata, record->length, 1, pcap->fp) == 1);
111 }
112 
113 BOOL pcap_add_record(rdpPcap* pcap, const void* data, size_t length)
114 {
115  WINPR_ASSERT(pcap);
116  WINPR_ASSERT(data || (length == 0));
117  WINPR_ASSERT(length <= UINT32_MAX);
118 
119  pcap_record* record = (pcap_record*)calloc(1, sizeof(pcap_record));
120  if (!record)
121  return FALSE;
122 
123  record->cdata = data;
124  record->length = (UINT32)length;
125  record->header.incl_len = (UINT32)length;
126  record->header.orig_len = (UINT32)length;
127 
128  const UINT64 ns = winpr_GetUnixTimeNS();
129 
130  record->header.ts_sec = (UINT32)WINPR_TIME_NS_TO_S(ns);
131  record->header.ts_usec = (UINT32)WINPR_TIME_NS_REM_US(ns);
132 
133  if (pcap->tail == NULL)
134  {
135  pcap->tail = record;
136  if (!pcap->tail)
137  return FALSE;
138 
139  pcap->head = pcap->tail;
140  }
141  else
142  {
143  record->next = pcap->tail;
144  pcap->tail = record;
145  }
146 
147  if (pcap->record == NULL)
148  pcap->record = record;
149 
150  return TRUE;
151 }
152 
153 BOOL pcap_has_next_record(const rdpPcap* pcap)
154 {
155  WINPR_ASSERT(pcap);
156 
157  if (pcap->file_size - (_ftelli64(pcap->fp)) <= 16)
158  return FALSE;
159 
160  return TRUE;
161 }
162 
163 BOOL pcap_get_next_record_header(rdpPcap* pcap, pcap_record* record)
164 {
165  WINPR_ASSERT(pcap);
166  WINPR_ASSERT(record);
167 
168  if (pcap_has_next_record(pcap) != TRUE)
169  return FALSE;
170 
171  pcap_read_record_header(pcap, &record->header);
172  record->length = record->header.incl_len;
173 
174  return TRUE;
175 }
176 
177 BOOL pcap_get_next_record_content(rdpPcap* pcap, pcap_record* record)
178 {
179  WINPR_ASSERT(pcap);
180  WINPR_ASSERT(record);
181 
182  return fread(record->data, record->length, 1, pcap->fp) == 1;
183 }
184 
185 BOOL pcap_get_next_record(rdpPcap* pcap, pcap_record* record)
186 {
187  WINPR_ASSERT(pcap);
188  WINPR_ASSERT(record);
189 
190  return pcap_has_next_record(pcap) && pcap_read_record(pcap, record);
191 }
192 
193 rdpPcap* pcap_open(const char* name, BOOL write)
194 {
195  rdpPcap* pcap = NULL;
196 
197  WINPR_ASSERT(name);
198 
199  pcap = (rdpPcap*)calloc(1, sizeof(rdpPcap));
200  if (!pcap)
201  goto fail;
202 
203  pcap->name = _strdup(name);
204  pcap->write = write;
205  pcap->record_count = 0;
206  pcap->fp = winpr_fopen(name, write ? "w+b" : "rb");
207 
208  if (pcap->fp == NULL)
209  goto fail;
210 
211  if (write)
212  {
213  pcap->header.magic_number = PCAP_MAGIC;
214  pcap->header.version_major = 2;
215  pcap->header.version_minor = 4;
216  pcap->header.thiszone = 0;
217  pcap->header.sigfigs = 0;
218  pcap->header.snaplen = UINT32_MAX;
219  pcap->header.network = 0;
220  if (!pcap_write_header(pcap, &pcap->header))
221  goto fail;
222  }
223  else
224  {
225  (void)_fseeki64(pcap->fp, 0, SEEK_END);
226  pcap->file_size = _ftelli64(pcap->fp);
227  (void)_fseeki64(pcap->fp, 0, SEEK_SET);
228  if (!pcap_read_header(pcap, &pcap->header))
229  goto fail;
230  }
231 
232  return pcap;
233 
234 fail:
235  pcap_close(pcap);
236  return NULL;
237 }
238 
239 void pcap_flush(rdpPcap* pcap)
240 {
241  WINPR_ASSERT(pcap);
242 
243  while (pcap->record != NULL)
244  {
245  pcap_write_record(pcap, pcap->record);
246  pcap->record = pcap->record->next;
247  }
248 
249  if (pcap->fp != NULL)
250  (void)fflush(pcap->fp);
251 }
252 
253 void pcap_close(rdpPcap* pcap)
254 {
255  if (!pcap)
256  return;
257 
258  pcap_flush(pcap);
259 
260  if (pcap->fp != NULL)
261  (void)fclose(pcap->fp);
262 
263  free(pcap->name);
264  free(pcap);
265 }