/* time_shift.c * Routines for "Time Shift" window * Submitted by Edwin Groothuis * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #include #include #include #include "time_shift.h" #include "ui/packet_list_utils.h" #define SHIFT_POS 0 #define SHIFT_NEG 1 #define SHIFT_SETTOZERO 1 #define SHIFT_KEEPOFFSET 0 #define CHECK_YEARS(Y) \ if (*Y < 1970) { \ return "Years must be larger than 1970"; \ } #define CHECK_MONTHS(M) \ if (*M < 1 || *M > 12) { \ return "Months must be between [1..12]"; \ } #define CHECK_DAYS(D) \ if (*D < 1 || *D > 31) { \ return "Days must be between [1..31]"; \ } #define CHECK_HOURS(h) \ if (*h < 0 || *h > 23) { \ return "Hours must be between [0..23]"; \ } #define CHECK_HOUR(h) \ if (*h < 0) { \ return "Negative hours. Have you specified more than " \ "one minus character?"; \ } #define CHECK_MINUTE(m) \ if (*m < 0 || *m > 59) { \ return "Minutes must be between [0..59]"; \ } #define CHECK_SECOND(s) \ if (*s < 0 || *s > 59) { \ return "Seconds must be between [0..59]"; \ } static void modify_time_perform(frame_data *fd, int neg, nstime_t *offset, int settozero) { /* The actual shift */ if (settozero == SHIFT_SETTOZERO) { nstime_subtract(&(fd->abs_ts), &(fd->shift_offset)); nstime_set_zero(&(fd->shift_offset)); } if (neg == SHIFT_POS) { nstime_add(&(fd->abs_ts), offset); nstime_add(&(fd->shift_offset), offset); } else if (neg == SHIFT_NEG) { nstime_subtract(&(fd->abs_ts), offset); nstime_subtract(&(fd->shift_offset), offset); } else { fprintf(stderr, "Modify_time_perform: neg = %d?\n", neg); } } /* * If the line between (OT1, NT1) and (OT2, NT2) is a straight line * and (OT3, NT3) is on that line, * then (NT2 - NT1) / (OT2 - OT2) = (NT3 - NT1) / (OT3 - OT1) and * then (OT3 - OT1) * (NT2 - NT1) / (OT2 - OT2) = (NT3 - NT1) and * then NT1 + (OT3 - OT1) * (NT2 - NT1) / (OT2 - OT2) = NT3 and * then NT3 = NT1 + (OT3 - OT1) * (NT2 - NT1) / (OT2 - OT2) and * thus NT3 = NT1 + (OT3 - OT1) * (NT2 - NT1) / (OT2 - OT1) * or NT3 = NT1 + (OT3 - OT1) * ( deltaNT12 / deltaOT12) * * All the things you come up when waiting for the train to come... */ static void calcNT3(nstime_t *OT1, nstime_t *OT3, nstime_t *NT1, nstime_t *NT3, nstime_t *deltaOT, nstime_t *deltaNT) { long double fnt, fot, f, secs, nsecs; fnt = (long double)deltaNT->secs + (deltaNT->nsecs / 1000000000.0L); fot = (long double)deltaOT->secs + (deltaOT->nsecs / 1000000000.0L); f = fnt / fot; nstime_copy(NT3, OT3); nstime_subtract(NT3, OT1); secs = f * (long double)NT3->secs; nsecs = f * (long double)NT3->nsecs; nsecs += (secs - floorl(secs)) * 1000000000.0L; while (nsecs > 1000000000L) { secs += 1; nsecs -= 1000000000L; } while (nsecs < 0) { secs -= 1; nsecs += 1000000000L; } NT3->secs = (time_t)secs; NT3->nsecs = (int)nsecs; nstime_add(NT3, NT1); } const char * time_string_parse(const char *time_text, int *year, int *month, int *day, bool *negative, int *hour, int *minute, long double *second) { const char *pts = time_text; if (!time_text || !hour || !minute || !second) return "Unable to convert time."; /* strip whitespace */ while (g_ascii_isspace(pts[0])) ++pts; if (year && month && day) { /* * The following time format is allowed: * [YYYY-MM-DD] hh:mm:ss(.decimals)? * * Since Wireshark doesn't support regular expressions (please prove me * wrong :-) we will have to figure it out ourselves in the * following order: * * 1. YYYY-MM-DD hh:mm:ss.decimals * 2. hh:mm:ss.decimals * */ /* check for empty string */ if (pts[0] == '\0') return "Time is empty."; if (sscanf(pts, "%d-%d-%d %d:%d:%Lf", year, month, day, hour, minute, second) == 6) { /* printf("%%d-%%d-%%d %%d:%%d:%%f\n"); */ CHECK_YEARS(year); CHECK_MONTHS(month); CHECK_DAYS(day); CHECK_HOURS(hour); CHECK_MINUTE(minute); CHECK_SECOND(second); } else if (sscanf(pts, "%d:%d:%Lf", hour, minute, second) == 3) { /* printf("%%d:%%d:%%f\n"); */ *year = *month = *day = 0; CHECK_HOUR(hour); CHECK_MINUTE(minute); CHECK_SECOND(second); } else { return "Could not parse the time. Expected [YYYY-MM-DD] " "hh:mm:ss[.dec]."; } } else { if (!negative) return "Unable to convert time."; /* * The following offset types are allowed: * -?((hh:)mm:)ss(.decimals)? * * Since Wireshark doesn't support regular expressions (please prove me * wrong :-) we will have to figure it out ourselves in the * following order: * * 1. hh:mm:ss.decimals * 2. mm:ss.decimals * 3. ss.decimals * */ /* check for minus sign */ *negative = false; if (pts[0] == '-') { *negative = true; pts++; } /* check for empty string */ if (pts[0] == '\0') return "Time is empty."; if (sscanf(pts, "%d:%d:%Lf", hour, minute, second) == 3) { /* printf("%%d:%%d:%%d.%%d\n"); */ CHECK_HOUR(hour); CHECK_MINUTE(minute); CHECK_SECOND(second); } else if (sscanf(pts, "%d:%Lf", minute, second) == 2) { /* printf("%%d:%%d.%%d\n"); */ CHECK_MINUTE(minute); CHECK_SECOND(second); *hour = 0; } else if (sscanf(pts, "%Lf", second) == 1) { /* printf("%%d.%%d\n"); */ CHECK_SECOND(second); *hour = *minute = 0; } else { return "Could not parse the time: Expected [[hh:]mm:]ss.[dec]."; } } return NULL; } static const char * time_string_to_nstime(const char *time_text, nstime_t *packettime, nstime_t *nstime) { int h, m, Y, M, D; long double f; struct tm tm, *tmptm; time_t tt; const char *err_str; if ((err_str = time_string_parse(time_text, &Y, &M, &D, NULL, &h, &m, &f)) != NULL) return err_str; /* Convert the time entered in an epoch offset */ tmptm = localtime(&(packettime->secs)); if (tmptm) { tm = *tmptm; } else { memset (&tm, 0, sizeof (tm)); } if (Y != 0) { tm.tm_year = Y - 1900; tm.tm_mon = M - 1; tm.tm_mday = D; } tm.tm_hour = h; tm.tm_min = m; tm.tm_sec = (int)floorl(f); tm.tm_isdst = -1; tt = mktime(&tm); if (tt == -1) { return "Mktime went wrong. Is the time valid?"; } nstime->secs = tt; f -= tm.tm_sec; nstime->nsecs = (int)(f * 1000000000); return NULL; } const char * time_shift_all(capture_file *cf, const char *offset_text) { nstime_t offset; long double offset_float = 0; uint32_t i; frame_data *fd; bool neg; int h, m; long double f; const char *err_str; if (!cf || !offset_text) return "Nothing to work with."; if ((err_str = time_string_parse(offset_text, NULL, NULL, NULL, &neg, &h, &m, &f)) != NULL) return err_str; offset_float = h * 3600 + m * 60 + f; if (offset_float == 0) return "Offset is zero."; nstime_set_zero(&offset); offset.secs = (time_t)floorl(offset_float); offset_float -= offset.secs; offset.nsecs = (int)(offset_float * 1000000000); if (!frame_data_sequence_find(cf->provider.frames, 1)) return "No frames found."; /* Shouldn't happen */ for (i = 1; i <= cf->count; i++) { if ((fd = frame_data_sequence_find(cf->provider.frames, i)) == NULL) continue; /* Shouldn't happen */ modify_time_perform(fd, neg ? SHIFT_NEG : SHIFT_POS, &offset, SHIFT_KEEPOFFSET); } cf->unsaved_changes = true; packet_list_queue_draw(); return NULL; } const char * time_shift_settime(capture_file *cf, unsigned packet_num, const char *time_text) { nstime_t set_time, diff_time, packet_time; frame_data *fd, *packetfd; uint32_t i; const char *err_str; if (!cf || !time_text) return "Nothing to work with."; if (packet_num < 1 || packet_num > cf->count) return "Packet out of range."; /* * Get a copy of the real time (abs_ts - shift_offset) do we can find out the * difference between the specified time and the original packet */ if ((packetfd = frame_data_sequence_find(cf->provider.frames, packet_num)) == NULL) return "No packets found."; nstime_delta(&packet_time, &(packetfd->abs_ts), &(packetfd->shift_offset)); if ((err_str = time_string_to_nstime(time_text, &packet_time, &set_time)) != NULL) return err_str; /* Calculate difference between packet time and requested time */ nstime_delta(&diff_time, &set_time, &packet_time); /* Up to here nothing is changed */ if (!frame_data_sequence_find(cf->provider.frames, 1)) return "No frames found."; /* Shouldn't happen */ /* Set everything back to the original time */ for (i = 1; i <= cf->count; i++) { if ((fd = frame_data_sequence_find(cf->provider.frames, i)) == NULL) continue; /* Shouldn't happen */ modify_time_perform(fd, SHIFT_POS, &diff_time, SHIFT_SETTOZERO); } cf->unsaved_changes = true; packet_list_queue_draw(); return NULL; } const char * time_shift_adjtime(capture_file *cf, unsigned packet1_num, const char *time1_text, unsigned packet2_num, const char *time2_text) { nstime_t nt1, nt2, ot1, ot2, nt3; nstime_t dnt, dot, d3t; frame_data *fd, *packet1fd, *packet2fd; uint32_t i; const char *err_str; if (!cf || !time1_text || !time2_text) return "Nothing to work with."; if (packet1_num < 1 || packet1_num > cf->count || packet2_num < 1 || packet2_num > cf->count) return "Packet out of range."; /* * The following time format is allowed: * [YYYY-MM-DD] hh:mm:ss(.decimals)? * * Since Wireshark doesn't support regular expressions (please prove me * wrong :-) we will have to figure it out ourselves in the * following order: * * 1. YYYY-MM-DD hh:mm:ss.decimals * 2. hh:mm:ss.decimals * */ /* * Get a copy of the real time (abs_ts - shift_offset) do we can find out the * difference between the specified time and the original packet */ if ((packet1fd = frame_data_sequence_find(cf->provider.frames, packet1_num)) == NULL) return "No frames found."; nstime_copy(&ot1, &(packet1fd->abs_ts)); nstime_subtract(&ot1, &(packet1fd->shift_offset)); if ((err_str = time_string_to_nstime(time1_text, &ot1, &nt1)) != NULL) return err_str; /* * Get a copy of the real time (abs_ts - shift_offset) do we can find out the * difference between the specified time and the original packet */ if ((packet2fd = frame_data_sequence_find(cf->provider.frames, packet2_num)) == NULL) return "No frames found."; nstime_copy(&ot2, &(packet2fd->abs_ts)); nstime_subtract(&ot2, &(packet2fd->shift_offset)); if ((err_str = time_string_to_nstime(time2_text, &ot2, &nt2)) != NULL) return err_str; nstime_copy(&dot, &ot2); nstime_subtract(&dot, &ot1); nstime_copy(&dnt, &nt2); nstime_subtract(&dnt, &nt1); /* Up to here nothing is changed */ if (!frame_data_sequence_find(cf->provider.frames, 1)) return "No frames found."; /* Shouldn't happen */ for (i = 1; i <= cf->count; i++) { if ((fd = frame_data_sequence_find(cf->provider.frames, i)) == NULL) continue; /* Shouldn't happen */ /* Set everything back to the original time */ nstime_subtract(&(fd->abs_ts), &(fd->shift_offset)); nstime_set_zero(&(fd->shift_offset)); /* Add the difference to each packet */ calcNT3(&ot1, &(fd->abs_ts), &nt1, &nt3, &dot, &dnt); nstime_copy(&d3t, &nt3); nstime_subtract(&d3t, &(fd->abs_ts)); modify_time_perform(fd, SHIFT_POS, &d3t, SHIFT_SETTOZERO); } cf->unsaved_changes = true; packet_list_queue_draw(); return NULL; } const char * time_shift_undo(capture_file *cf) { uint32_t i; frame_data *fd; nstime_t nulltime; if (!cf) return "Nothing to work with."; nulltime.secs = nulltime.nsecs = 0; if (!frame_data_sequence_find(cf->provider.frames, 1)) return "No frames found."; /* Shouldn't happen */ for (i = 1; i <= cf->count; i++) { if ((fd = frame_data_sequence_find(cf->provider.frames, i)) == NULL) continue; /* Shouldn't happen */ modify_time_perform(fd, SHIFT_NEG, &nulltime, SHIFT_SETTOZERO); } packet_list_queue_draw(); return NULL; }