1 /* time_shift.c
2 * Routines for "Time Shift" window
3 * Submitted by Edwin Groothuis <wireshark@mavetju.org>
4 *
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
12 #include "config.h"
13
14 #include <stdio.h>
15 #include <string.h>
16 #include <math.h>
17
18 #include "time_shift.h"
19
20 #include "ui/ws_ui_util.h"
21
22 #define SHIFT_POS 0
23 #define SHIFT_NEG 1
24 #define SHIFT_SETTOZERO 1
25 #define SHIFT_KEEPOFFSET 0
26
27 #define CHECK_YEARS(Y) \
28 if (*Y < 1970) { \
29 return "Years must be larger than 1970"; \
30 }
31 #define CHECK_MONTHS(M) \
32 if (*M < 1 || *M > 12) { \
33 return "Months must be between [1..12]"; \
34 }
35 #define CHECK_DAYS(D) \
36 if (*D < 1 || *D > 31) { \
37 return "Days must be between [1..31]"; \
38 }
39 #define CHECK_HOURS(h) \
40 if (*h < 0 || *h > 23) { \
41 return "Hours must be between [0..23]"; \
42 }
43 #define CHECK_HOUR(h) \
44 if (*h < 0) { \
45 return "Negative hours. Have you specified more than " \
46 "one minus character?"; \
47 }
48 #define CHECK_MINUTE(m) \
49 if (*m < 0 || *m > 59) { \
50 return "Minutes must be between [0..59]"; \
51 }
52 #define CHECK_SECOND(s) \
53 if (*s < 0 || *s > 59) { \
54 return "Seconds must be between [0..59]"; \
55 }
56
57 static void
modify_time_perform(frame_data * fd,int neg,nstime_t * offset,int settozero)58 modify_time_perform(frame_data *fd, int neg, nstime_t *offset, int settozero)
59 {
60 /* The actual shift */
61 if (settozero == SHIFT_SETTOZERO) {
62 nstime_subtract(&(fd->abs_ts), &(fd->shift_offset));
63 nstime_set_zero(&(fd->shift_offset));
64 }
65
66 if (neg == SHIFT_POS) {
67 nstime_add(&(fd->abs_ts), offset);
68 nstime_add(&(fd->shift_offset), offset);
69 } else if (neg == SHIFT_NEG) {
70 nstime_subtract(&(fd->abs_ts), offset);
71 nstime_subtract(&(fd->shift_offset), offset);
72 } else {
73 fprintf(stderr, "Modify_time_perform: neg = %d?\n", neg);
74 }
75 }
76
77 /*
78 * If the line between (OT1, NT1) and (OT2, NT2) is a straight line
79 * and (OT3, NT3) is on that line,
80 * then (NT2 - NT1) / (OT2 - OT2) = (NT3 - NT1) / (OT3 - OT1) and
81 * then (OT3 - OT1) * (NT2 - NT1) / (OT2 - OT2) = (NT3 - NT1) and
82 * then NT1 + (OT3 - OT1) * (NT2 - NT1) / (OT2 - OT2) = NT3 and
83 * then NT3 = NT1 + (OT3 - OT1) * (NT2 - NT1) / (OT2 - OT2) and
84 * thus NT3 = NT1 + (OT3 - OT1) * (NT2 - NT1) / (OT2 - OT1)
85 * or NT3 = NT1 + (OT3 - OT1) * ( deltaNT12 / deltaOT12)
86 *
87 * All the things you come up when waiting for the train to come...
88 */
89 static void
calcNT3(nstime_t * OT1,nstime_t * OT3,nstime_t * NT1,nstime_t * NT3,nstime_t * deltaOT,nstime_t * deltaNT)90 calcNT3(nstime_t *OT1, nstime_t *OT3, nstime_t *NT1, nstime_t *NT3,
91 nstime_t *deltaOT, nstime_t *deltaNT)
92 {
93 long double fnt, fot, f, secs, nsecs;
94
95 fnt = (long double)deltaNT->secs + (deltaNT->nsecs / 1000000000.0L);
96 fot = (long double)deltaOT->secs + (deltaOT->nsecs / 1000000000.0L);
97 f = fnt / fot;
98
99 nstime_copy(NT3, OT3);
100 nstime_subtract(NT3, OT1);
101
102 secs = f * (long double)NT3->secs;
103 nsecs = f * (long double)NT3->nsecs;
104 nsecs += (secs - floorl(secs)) * 1000000000.0L;
105 while (nsecs > 1000000000L) {
106 secs += 1;
107 nsecs -= 1000000000L;
108 }
109 while (nsecs < 0) {
110 secs -= 1;
111 nsecs += 1000000000L;
112 }
113 NT3->secs = (time_t)secs;
114 NT3->nsecs = (int)nsecs;
115 nstime_add(NT3, NT1);
116 }
117
118 const gchar *
time_string_parse(const gchar * time_text,int * year,int * month,int * day,gboolean * negative,int * hour,int * minute,long double * second)119 time_string_parse(const gchar *time_text, int *year, int *month, int *day, gboolean *negative, int *hour, int *minute, long double *second) {
120 const gchar *pts = time_text;
121
122 if (!time_text || !hour || !minute || !second)
123 return "Unable to convert time.";
124
125 /* strip whitespace */
126 while (g_ascii_isspace(pts[0]))
127 ++pts;
128
129 if (year && month && day) {
130 /*
131 * The following time format is allowed:
132 * [YYYY-MM-DD] hh:mm:ss(.decimals)?
133 *
134 * Since Wireshark doesn't support regular expressions (please prove me
135 * wrong :-) we will have to figure it out ourselves in the
136 * following order:
137 *
138 * 1. YYYY-MM-DD hh:mm:ss.decimals
139 * 2. hh:mm:ss.decimals
140 *
141 */
142
143 /* check for empty string */
144 if (pts[0] == '\0')
145 return "Time is empty.";
146
147 if (sscanf(pts, "%d-%d-%d %d:%d:%Lf", year, month, day, hour, minute, second) == 6) {
148 /* printf("%%d-%%d-%%d %%d:%%d:%%f\n"); */
149 CHECK_YEARS(year);
150 CHECK_MONTHS(month);
151 CHECK_DAYS(day);
152 CHECK_HOURS(hour);
153 CHECK_MINUTE(minute);
154 CHECK_SECOND(second);
155 } else if (sscanf(pts, "%d:%d:%Lf", hour, minute, second) == 3) {
156 /* printf("%%d:%%d:%%f\n"); */
157 *year = *month = *day = 0;
158 CHECK_HOUR(hour);
159 CHECK_MINUTE(minute);
160 CHECK_SECOND(second);
161 } else {
162 return "Could not parse the time. Expected [YYYY-MM-DD] "
163 "hh:mm:ss[.dec].";
164 }
165 } else {
166 if (!negative)
167 return "Unable to convert time.";
168
169 /*
170 * The following offset types are allowed:
171 * -?((hh:)mm:)ss(.decimals)?
172 *
173 * Since Wireshark doesn't support regular expressions (please prove me
174 * wrong :-) we will have to figure it out ourselves in the
175 * following order:
176 *
177 * 1. hh:mm:ss.decimals
178 * 2. mm:ss.decimals
179 * 3. ss.decimals
180 *
181 */
182
183 /* check for minus sign */
184 *negative = FALSE;
185 if (pts[0] == '-') {
186 *negative = TRUE;
187 pts++;
188 }
189
190 /* check for empty string */
191 if (pts[0] == '\0')
192 return "Time is empty.";
193
194 if (sscanf(pts, "%d:%d:%Lf", hour, minute, second) == 3) {
195 /* printf("%%d:%%d:%%d.%%d\n"); */
196 CHECK_HOUR(hour);
197 CHECK_MINUTE(minute);
198 CHECK_SECOND(second);
199 } else if (sscanf(pts, "%d:%Lf", minute, second) == 2) {
200 /* printf("%%d:%%d.%%d\n"); */
201 CHECK_MINUTE(minute);
202 CHECK_SECOND(second);
203 *hour = 0;
204 } else if (sscanf(pts, "%Lf", second) == 1) {
205 /* printf("%%d.%%d\n"); */
206 CHECK_SECOND(second);
207 *hour = *minute = 0;
208 } else {
209 return "Could not parse the time: Expected [[hh:]mm:]ss.[dec].";
210 }
211 }
212
213 return NULL;
214 }
215
216 static const gchar *
time_string_to_nstime(const gchar * time_text,nstime_t * packettime,nstime_t * nstime)217 time_string_to_nstime(const gchar *time_text, nstime_t *packettime, nstime_t *nstime)
218 {
219 int h, m, Y, M, D;
220 long double f;
221 struct tm tm, *tmptm;
222 time_t tt;
223 const gchar *err_str;
224
225 if ((err_str = time_string_parse(time_text, &Y, &M, &D, NULL, &h, &m, &f)) != NULL)
226 return err_str;
227
228 /* Convert the time entered in an epoch offset */
229 tmptm = localtime(&(packettime->secs));
230 if (tmptm) {
231 tm = *tmptm;
232 } else {
233 memset (&tm, 0, sizeof (tm));
234 }
235 if (Y != 0) {
236 tm.tm_year = Y - 1900;
237 tm.tm_mon = M - 1;
238 tm.tm_mday = D;
239 }
240 tm.tm_hour = h;
241 tm.tm_min = m;
242 tm.tm_sec = (int)floorl(f);
243 tm.tm_isdst = -1;
244 tt = mktime(&tm);
245 if (tt == -1) {
246 return "Mktime went wrong. Is the time valid?";
247 }
248
249 nstime->secs = tt;
250 f -= tm.tm_sec;
251 nstime->nsecs = (int)(f * 1000000000);
252
253 return NULL;
254 }
255
256 const gchar *
time_shift_all(capture_file * cf,const gchar * offset_text)257 time_shift_all(capture_file *cf, const gchar *offset_text)
258 {
259 nstime_t offset;
260 long double offset_float = 0;
261 guint32 i;
262 frame_data *fd;
263 gboolean neg;
264 int h, m;
265 long double f;
266 const gchar *err_str;
267
268 if (!cf || !offset_text)
269 return "Nothing to work with.";
270
271 if ((err_str = time_string_parse(offset_text, NULL, NULL, NULL, &neg, &h, &m, &f)) != NULL)
272 return err_str;
273
274 offset_float = h * 3600 + m * 60 + f;
275
276 if (offset_float == 0)
277 return "Offset is zero.";
278
279 nstime_set_zero(&offset);
280 offset.secs = (time_t)floorl(offset_float);
281 offset_float -= offset.secs;
282 offset.nsecs = (int)(offset_float * 1000000000);
283
284 if (!frame_data_sequence_find(cf->provider.frames, 1))
285 return "No frames found."; /* Shouldn't happen */
286
287 for (i = 1; i <= cf->count; i++) {
288 if ((fd = frame_data_sequence_find(cf->provider.frames, i)) == NULL)
289 continue; /* Shouldn't happen */
290 modify_time_perform(fd, neg ? SHIFT_NEG : SHIFT_POS, &offset, SHIFT_KEEPOFFSET);
291 }
292 cf->unsaved_changes = TRUE;
293 packet_list_queue_draw();
294
295 return NULL;
296 }
297
298 const gchar *
time_shift_settime(capture_file * cf,guint packet_num,const gchar * time_text)299 time_shift_settime(capture_file *cf, guint packet_num, const gchar *time_text)
300 {
301 nstime_t set_time, diff_time, packet_time;
302 frame_data *fd, *packetfd;
303 guint32 i;
304 const gchar *err_str;
305
306 if (!cf || !time_text)
307 return "Nothing to work with.";
308
309 if (packet_num < 1 || packet_num > cf->count)
310 return "Packet out of range.";
311
312 /*
313 * Get a copy of the real time (abs_ts - shift_offset) do we can find out the
314 * difference between the specified time and the original packet
315 */
316 if ((packetfd = frame_data_sequence_find(cf->provider.frames, packet_num)) == NULL)
317 return "No packets found.";
318 nstime_delta(&packet_time, &(packetfd->abs_ts), &(packetfd->shift_offset));
319
320 if ((err_str = time_string_to_nstime(time_text, &packet_time, &set_time)) != NULL)
321 return err_str;
322
323 /* Calculate difference between packet time and requested time */
324 nstime_delta(&diff_time, &set_time, &packet_time);
325
326 /* Up to here nothing is changed */
327
328 if (!frame_data_sequence_find(cf->provider.frames, 1))
329 return "No frames found."; /* Shouldn't happen */
330
331 /* Set everything back to the original time */
332 for (i = 1; i <= cf->count; i++) {
333 if ((fd = frame_data_sequence_find(cf->provider.frames, i)) == NULL)
334 continue; /* Shouldn't happen */
335 modify_time_perform(fd, SHIFT_POS, &diff_time, SHIFT_SETTOZERO);
336 }
337
338 cf->unsaved_changes = TRUE;
339 packet_list_queue_draw();
340 return NULL;
341 }
342
343 const gchar *
time_shift_adjtime(capture_file * cf,guint packet1_num,const gchar * time1_text,guint packet2_num,const gchar * time2_text)344 time_shift_adjtime(capture_file *cf, guint packet1_num, const gchar *time1_text, guint packet2_num, const gchar *time2_text)
345 {
346 nstime_t nt1, nt2, ot1, ot2, nt3;
347 nstime_t dnt, dot, d3t;
348 frame_data *fd, *packet1fd, *packet2fd;
349 guint32 i;
350 const gchar *err_str;
351
352 if (!cf || !time1_text || !time2_text)
353 return "Nothing to work with.";
354
355 if (packet1_num < 1 || packet1_num > cf->count || packet2_num < 1 || packet2_num > cf->count)
356 return "Packet out of range.";
357
358 /*
359 * The following time format is allowed:
360 * [YYYY-MM-DD] hh:mm:ss(.decimals)?
361 *
362 * Since Wireshark doesn't support regular expressions (please prove me
363 * wrong :-) we will have to figure it out ourselves in the
364 * following order:
365 *
366 * 1. YYYY-MM-DD hh:mm:ss.decimals
367 * 2. hh:mm:ss.decimals
368 *
369 */
370
371 /*
372 * Get a copy of the real time (abs_ts - shift_offset) do we can find out the
373 * difference between the specified time and the original packet
374 */
375 if ((packet1fd = frame_data_sequence_find(cf->provider.frames, packet1_num)) == NULL)
376 return "No frames found.";
377 nstime_copy(&ot1, &(packet1fd->abs_ts));
378 nstime_subtract(&ot1, &(packet1fd->shift_offset));
379
380 if ((err_str = time_string_to_nstime(time1_text, &ot1, &nt1)) != NULL)
381 return err_str;
382
383 /*
384 * Get a copy of the real time (abs_ts - shift_offset) do we can find out the
385 * difference between the specified time and the original packet
386 */
387 if ((packet2fd = frame_data_sequence_find(cf->provider.frames, packet2_num)) == NULL)
388 return "No frames found.";
389 nstime_copy(&ot2, &(packet2fd->abs_ts));
390 nstime_subtract(&ot2, &(packet2fd->shift_offset));
391
392 if ((err_str = time_string_to_nstime(time2_text, &ot2, &nt2)) != NULL)
393 return err_str;
394
395 nstime_copy(&dot, &ot2);
396 nstime_subtract(&dot, &ot1);
397
398 nstime_copy(&dnt, &nt2);
399 nstime_subtract(&dnt, &nt1);
400
401 /* Up to here nothing is changed */
402 if (!frame_data_sequence_find(cf->provider.frames, 1))
403 return "No frames found."; /* Shouldn't happen */
404
405 for (i = 1; i <= cf->count; i++) {
406 if ((fd = frame_data_sequence_find(cf->provider.frames, i)) == NULL)
407 continue; /* Shouldn't happen */
408
409 /* Set everything back to the original time */
410 nstime_subtract(&(fd->abs_ts), &(fd->shift_offset));
411 nstime_set_zero(&(fd->shift_offset));
412
413 /* Add the difference to each packet */
414 calcNT3(&ot1, &(fd->abs_ts), &nt1, &nt3, &dot, &dnt);
415
416 nstime_copy(&d3t, &nt3);
417 nstime_subtract(&d3t, &(fd->abs_ts));
418
419 modify_time_perform(fd, SHIFT_POS, &d3t, SHIFT_SETTOZERO);
420 }
421
422 cf->unsaved_changes = TRUE;
423 packet_list_queue_draw();
424 return NULL;
425 }
426
427 const gchar *
time_shift_undo(capture_file * cf)428 time_shift_undo(capture_file *cf)
429 {
430 guint32 i;
431 frame_data *fd;
432 nstime_t nulltime;
433
434 if (!cf)
435 return "Nothing to work with.";
436
437 nulltime.secs = nulltime.nsecs = 0;
438
439 if (!frame_data_sequence_find(cf->provider.frames, 1))
440 return "No frames found."; /* Shouldn't happen */
441
442 for (i = 1; i <= cf->count; i++) {
443 if ((fd = frame_data_sequence_find(cf->provider.frames, i)) == NULL)
444 continue; /* Shouldn't happen */
445 modify_time_perform(fd, SHIFT_NEG, &nulltime, SHIFT_SETTOZERO);
446 }
447 packet_list_queue_draw();
448 return NULL;
449 }
450