Skip to content

Instantly share code, notes, and snippets.

@Low-power
Last active June 16, 2020 13:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Low-power/fbf5e822e2f9e81da1e50e8672f68464 to your computer and use it in GitHub Desktop.
Save Low-power/fbf5e822e2f9e81da1e50e8672f68464 to your computer and use it in GitHub Desktop.
Flow replay tool for tcpflow(1), just like scriptreplay(1) for script(1).
/* Replay flow captured by tcpflow(1) to stdout with timing.
Copyright 2015-2020 Rivoreo
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <termios.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include <locale.h>
#ifndef MIN
#define MIN(A,B) ((A)<(B)?(A):(B))
#endif
static int ff_tty_fd = -1;
static struct termios orig_termios;
static void __attribute__((__noreturn__)) exit_handler(int sig) {
if(ff_tty_fd != -1) tcsetattr(ff_tty_fd, TCSANOW, &orig_termios);
if(sig < 0) exit(-sig);
if(sig > 0) putchar('\n');
exit(sig ? 128 + sig : 0);
// close(2) file descriptors and free(3) memory? who cares?
}
static void print_usage(const char *name) {
fprintf(stderr, "Usage: %s [-q] [ -F <ff-fd> | -t ] { [-I <index-file>] <file> | -I <index-file> -[<fd>] }\n", name);
}
static int read_index_line(FILE *f, double *t, size_t *count) {
static unsigned int nline = 1;
if(fscanf(f, "%*u|%lf|%zu\n", t, count) < 2) {
if(feof(f)) return 0;
fprintf(stderr, "Invalid syntax at line %u of index\n", nline);
return -1;
}
nline++;
return 1;
}
static int transfer(int wfd, int rfd, size_t count) {
size_t buffer_size = count > 4096 ? 4096 : count;
char buffer[buffer_size];
int ss = 0;
do {
int s = read(rfd, buffer, MIN(buffer_size, count));
if(s < 0) {
if(errno == EINTR) continue;
return -1;
}
if(!s) return ss;
ss += s;
do {
int ws = write(wfd, buffer, s);
if(ws < 0) {
if(errno == EINTR) continue;
return -1;
}
s -= ws;
} while(s > 0);
} while(ss < count);
return ss;
}
static void delay(double s, int ff_fd) {
struct timeval tv;
tv.tv_sec = s;
tv.tv_usec = (s - tv.tv_sec) * 1000000;
if(ff_fd == -1) {
select(1, NULL, NULL, NULL, &tv);
} else {
char b;
fd_set ff_fd_set;
FD_ZERO(&ff_fd_set);
FD_SET(ff_fd, &ff_fd_set);
switch(select(ff_fd + 1, &ff_fd_set, NULL, NULL, &tv)) {
case -1:
if(errno == EINTR) break;
exit_handler(-1);
break;
case 0:
break;
default:
read(ff_fd, &b, 1);
break;
}
}
}
int main(int argc, char **argv) {
setlocale(LC_ALL, "");
int quiet = 0;
int fast_forward_fd = -1;
const char *index_file_name = NULL;
const char *file_name = NULL;
int i = 1;
int end_of_options = 0;
while(i < argc) {
if(!end_of_options && argv[i][0] == '-' && argv[i][1] && !isdigit(argv[i][1])) {
const char *o = argv[i] + 1;
while(*o) switch(*o++) {
case '-':
if(o[1]) {
fprintf(stderr, "%s: Invalid option '%s'\n", argv[0], argv[i]);
return -1;
} else end_of_options = 1;
break;
case 'q':
quiet = 1;
break;
case 'F':
if(++i >= argc) {
fprintf(stderr, "%s: Option '-F' requires an argument\n", argv[0]);
return -1;
}
{
char *end_p;
fast_forward_fd = strtol(argv[i], &end_p, 10);
if(*end_p || fast_forward_fd == -1) {
fprintf(stderr, "%s: Invalid file description '%s'\n",
argv[0], argv[i]);
return -1;
}
}
break;
case 't':
fast_forward_fd = open("/dev/tty", O_RDONLY);
if(fast_forward_fd == -1) {
perror("/dev/tty");
return 1;
}
break;
case 'I':
if(++i >= argc) {
fprintf(stderr, "%s: Option '-I' requires an argument\n", argv[0]);
return -1;
}
index_file_name = argv[i];
break;
default:
fprintf(stderr, "%s: Invalid option '-%c'\n", argv[0], o[-1]);
return -1;
}
} else {
if(file_name) {
fprintf(stderr, "%s: Only one file can be specified at a time", argv[0]);
return -1;
}
file_name = argv[i];
}
i++;
}
if(!file_name) {
print_usage(argv[0]);
return -1;
}
int fd;
if(*file_name == '-') {
const char *fdn = file_name + 1;
if(isdigit(*fdn)) {
char *endptr;
fd = strtol(fdn, &endptr, 10);
if(*endptr) goto open_file_name;
} else if(!*fdn) fd = STDIN_FILENO;
else goto open_file_name;
if(!index_file_name) {
fprintf(stderr, "%s: Index file must be specified via option '-I' for use with input FD '%s'\n",
argv[0], file_name);
return -1;
}
} else {
open_file_name:
fd = open(file_name, O_RDONLY);
if(fd == -1) {
perror(file_name);
return 1;
}
}
FILE *index_f;
if(index_file_name) {
if(*index_file_name == '-') {
const char *fdn = index_file_name + 1;
if(isdigit(*fdn)) {
char *endptr;
int fd = strtol(fdn, &endptr, 10);
if(*endptr) goto open_index_file_name;
index_f = fdopen(fd, "r");
} else if(!*fdn) index_f = stdin;
else goto open_index_file_name;
} else {
open_index_file_name:
index_f = fopen(index_file_name, "r");
}
if(!index_f) {
perror(index_file_name);
return 1;
}
} else {
size_t len = strlen(file_name);
char index_file_name[len + 7];
memcpy(index_file_name, file_name, len);
memcpy(index_file_name + len, ".findx", 7);
index_f = fopen(index_file_name, "r");
if(!index_f) {
perror(index_file_name);
return 1;
}
}
if(fast_forward_fd != -1 && isatty(fast_forward_fd)) {
struct sigaction act = { .sa_handler = exit_handler };
if(sigaction(SIGINT, &act, NULL) < 0) {
perror("sigaction: SIGINT");
return 1;
}
tcgetattr(fast_forward_fd, &orig_termios);
struct termios new_termios = orig_termios;
new_termios.c_lflag &= ~(ICANON | ECHO);
tcsetattr(fast_forward_fd, TCSANOW, &new_termios);
ff_tty_fd = fast_forward_fd;
}
double pt;
size_t count;
switch(read_index_line(index_f, &pt, &count)) {
case 0:
fputs("Index EOF too early\n", stderr);
case -1:
exit_handler(-1);
}
if(!quiet) {
time_t t = pt;
struct tm *tm = localtime(&t);
char buffer[64];
if(strftime(buffer, sizeof buffer, "Flow started on %x %T %z\n", tm)) fputs(buffer, stderr);
}
if(transfer(STDOUT_FILENO, fd, count) < 0) exit_handler(-1);
while(1) {
double t;
switch(read_index_line(index_f, &t, &count)) {
case -1:
exit_handler(-1);
case 0:
exit_handler(0);
}
delay(t - pt, fast_forward_fd);
if(transfer(STDOUT_FILENO, fd, count) < 0) exit_handler(-1);
pt = t;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment