Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/libfreerdp/utils/pcap.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * pcap File Format Utils
4
 *
5
 * Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
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
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
0
#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
0
{
52
0
  WINPR_ASSERT(pcap);
53
0
  WINPR_ASSERT(header);
54
55
0
  return fread(header, sizeof(pcap_header), 1, pcap->fp) == 1;
56
0
}
57
58
static BOOL pcap_write_header(rdpPcap* pcap, const pcap_header* header)
59
0
{
60
0
  WINPR_ASSERT(pcap);
61
0
  WINPR_ASSERT(header);
62
63
0
  return fwrite(header, sizeof(pcap_header), 1, pcap->fp) == 1;
64
0
}
65
66
static BOOL pcap_read_record_header(rdpPcap* pcap, pcap_record_header* record)
67
0
{
68
0
  WINPR_ASSERT(pcap);
69
0
  WINPR_ASSERT(record);
70
71
0
  return fread(record, sizeof(pcap_record_header), 1, pcap->fp) == 1;
72
0
}
73
74
static BOOL pcap_write_record_header(rdpPcap* pcap, const pcap_record_header* record)
75
0
{
76
0
  WINPR_ASSERT(pcap);
77
0
  WINPR_ASSERT(record);
78
79
0
  return fwrite(record, sizeof(pcap_record_header), 1, pcap->fp) == 1;
80
0
}
81
82
static BOOL pcap_read_record(rdpPcap* pcap, pcap_record* record)
83
0
{
84
0
  WINPR_ASSERT(pcap);
85
0
  WINPR_ASSERT(record);
86
87
0
  if (!pcap_read_record_header(pcap, &record->header))
88
0
    return FALSE;
89
90
0
  record->length = record->header.incl_len;
91
0
  record->data = malloc(record->length);
92
0
  if (!record->data)
93
0
    return FALSE;
94
95
0
  if (fread(record->data, record->length, 1, pcap->fp) != 1)
96
0
  {
97
0
    free(record->data);
98
0
    record->data = NULL;
99
0
    return FALSE;
100
0
  }
101
0
  return TRUE;
102
0
}
103
104
static BOOL pcap_write_record(rdpPcap* pcap, const pcap_record* record)
105
0
{
106
0
  WINPR_ASSERT(pcap);
107
0
  WINPR_ASSERT(record);
108
109
0
  return pcap_write_record_header(pcap, &record->header) &&
110
0
         (fwrite(record->cdata, record->length, 1, pcap->fp) == 1);
111
0
}
112
113
BOOL pcap_add_record(rdpPcap* pcap, const void* data, size_t length)
114
0
{
115
0
  WINPR_ASSERT(pcap);
116
0
  WINPR_ASSERT(data || (length == 0));
117
0
  WINPR_ASSERT(length <= UINT32_MAX);
118
119
0
  pcap_record* record = (pcap_record*)calloc(1, sizeof(pcap_record));
120
0
  if (!record)
121
0
    return FALSE;
122
123
0
  record->cdata = data;
124
0
  record->length = (UINT32)length;
125
0
  record->header.incl_len = (UINT32)length;
126
0
  record->header.orig_len = (UINT32)length;
127
128
0
  const UINT64 ns = winpr_GetUnixTimeNS();
129
130
0
  record->header.ts_sec = (UINT32)WINPR_TIME_NS_TO_S(ns);
131
0
  record->header.ts_usec = (UINT32)WINPR_TIME_NS_REM_US(ns);
132
133
0
  if (pcap->tail == NULL)
134
0
  {
135
0
    pcap->tail = record;
136
0
    if (!pcap->tail)
137
0
      return FALSE;
138
139
0
    pcap->head = pcap->tail;
140
0
  }
141
0
  else
142
0
  {
143
0
    record->next = pcap->tail;
144
0
    pcap->tail = record;
145
0
  }
146
147
0
  if (pcap->record == NULL)
148
0
    pcap->record = record;
149
150
0
  return TRUE;
151
0
}
152
153
BOOL pcap_has_next_record(const rdpPcap* pcap)
154
0
{
155
0
  WINPR_ASSERT(pcap);
156
157
0
  if (pcap->file_size - (_ftelli64(pcap->fp)) <= 16)
158
0
    return FALSE;
159
160
0
  return TRUE;
161
0
}
162
163
BOOL pcap_get_next_record_header(rdpPcap* pcap, pcap_record* record)
164
0
{
165
0
  WINPR_ASSERT(pcap);
166
0
  WINPR_ASSERT(record);
167
168
0
  if (pcap_has_next_record(pcap) != TRUE)
169
0
    return FALSE;
170
171
0
  pcap_read_record_header(pcap, &record->header);
172
0
  record->length = record->header.incl_len;
173
174
0
  return TRUE;
175
0
}
176
177
BOOL pcap_get_next_record_content(rdpPcap* pcap, pcap_record* record)
178
0
{
179
0
  WINPR_ASSERT(pcap);
180
0
  WINPR_ASSERT(record);
181
182
0
  return fread(record->data, record->length, 1, pcap->fp) == 1;
183
0
}
184
185
BOOL pcap_get_next_record(rdpPcap* pcap, pcap_record* record)
186
0
{
187
0
  WINPR_ASSERT(pcap);
188
0
  WINPR_ASSERT(record);
189
190
0
  return pcap_has_next_record(pcap) && pcap_read_record(pcap, record);
191
0
}
192
193
rdpPcap* pcap_open(const char* name, BOOL write)
194
0
{
195
0
  WINPR_ASSERT(name);
196
197
0
  rdpPcap* pcap = (rdpPcap*)calloc(1, sizeof(rdpPcap));
198
0
  if (!pcap)
199
0
    goto fail;
200
201
0
  pcap->name = _strdup(name);
202
0
  pcap->write = write;
203
0
  pcap->record_count = 0;
204
0
  pcap->fp = winpr_fopen(name, write ? "w+b" : "rb");
205
206
0
  if (pcap->fp == NULL)
207
0
    goto fail;
208
209
0
  if (write)
210
0
  {
211
0
    pcap->header.magic_number = PCAP_MAGIC;
212
0
    pcap->header.version_major = 2;
213
0
    pcap->header.version_minor = 4;
214
0
    pcap->header.thiszone = 0;
215
0
    pcap->header.sigfigs = 0;
216
0
    pcap->header.snaplen = UINT32_MAX;
217
0
    pcap->header.network = 0;
218
0
    if (!pcap_write_header(pcap, &pcap->header))
219
0
      goto fail;
220
0
  }
221
0
  else
222
0
  {
223
0
    (void)_fseeki64(pcap->fp, 0, SEEK_END);
224
0
    pcap->file_size = _ftelli64(pcap->fp);
225
0
    (void)_fseeki64(pcap->fp, 0, SEEK_SET);
226
0
    if (!pcap_read_header(pcap, &pcap->header))
227
0
      goto fail;
228
0
  }
229
230
0
  return pcap;
231
232
0
fail:
233
0
  pcap_close(pcap);
234
0
  return NULL;
235
0
}
236
237
void pcap_flush(rdpPcap* pcap)
238
0
{
239
0
  WINPR_ASSERT(pcap);
240
241
0
  while (pcap->record != NULL)
242
0
  {
243
0
    (void)pcap_write_record(pcap, pcap->record);
244
0
    pcap->record = pcap->record->next;
245
0
  }
246
247
0
  if (pcap->fp != NULL)
248
0
    (void)fflush(pcap->fp);
249
0
}
250
251
void pcap_close(rdpPcap* pcap)
252
0
{
253
0
  if (!pcap)
254
0
    return;
255
256
0
  pcap_flush(pcap);
257
258
0
  if (pcap->fp != NULL)
259
0
    (void)fclose(pcap->fp);
260
261
0
  free(pcap->name);
262
0
  free(pcap);
263
0
}