From 531c644f20164131d2b59f9894fd81adef8e42e9 Mon Sep 17 00:00:00 2001 From: hrbrmstr Date: Tue, 27 Jul 2021 06:23:49 -0400 Subject: [PATCH] initial commit --- DESCRIPTION | 13 +++++++---- NAMESPACE | 13 +++++++++-- R/find-tshark.R | 35 ++++++++++++++++++++++++++++++ R/get-tshark.R | 10 +++++++++ R/packet-sum.R | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++ R/tshark-hosts.R | 42 +++++++++++++++++++++++++++++++++++ R/tshark.R | 33 ++++++++++++++++++++++++++++ R/tsharrk-package.R | 15 +++++++++---- inst/pcap/http.pcap | Bin 0 -> 25803 bytes man/find_tshark.Rd | 24 ++++++++++++++++++++ man/get_tshark.Rd | 14 ++++++++++++ man/packet_summary.Rd | 20 +++++++++++++++++ man/tshark_exec.Rd | 22 +++++++++++++++++++ man/tshark_hosts.Rd | 20 +++++++++++++++++ man/tsharrk.Rd | 7 ++++-- 15 files changed, 315 insertions(+), 12 deletions(-) create mode 100644 R/find-tshark.R create mode 100644 R/get-tshark.R create mode 100644 R/packet-sum.R create mode 100644 R/tshark-hosts.R create mode 100644 R/tshark.R create mode 100644 inst/pcap/http.pcap create mode 100644 man/find_tshark.Rd create mode 100644 man/get_tshark.Rd create mode 100644 man/packet_summary.Rd create mode 100644 man/tshark_exec.Rd create mode 100644 man/tshark_hosts.Rd diff --git a/DESCRIPTION b/DESCRIPTION index 41bfe62..32a2ab8 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: tsharrk Type: Package -Title: tsharrk Title Goes Here Otherwise CRAN Checks Fail +Title: Tools to Make Analyses Using 'tshark' Easier Version: 0.1.0 Date: 2021-07-26 Authors@R: c( @@ -8,7 +8,10 @@ Authors@R: c( comment = c(ORCID = "0000-0001-5670-2640")) ) Maintainer: Bob Rudis -Description: A good description goes here otherwise CRAN checks fail. +Description: The 'tshark' () + command line utility comes with Wireshark and is a is useful when performing + analyses on packet captures (PCAPs). Tools are provided to make it a bit easier + to work with 'tshark' to perform analyses with R. URL: https://git.rud.is/hrbrmstr/tsharrk BugReports: https://git.rud.is/hrbrmstr/tsharrk/issues Encoding: UTF-8 @@ -18,7 +21,9 @@ Suggests: Depends: R (>= 3.6.0) Imports: - httr, - jsonlite + utils, + arrow, + ndjson, + tools Roxygen: list(markdown = TRUE) RoxygenNote: 7.1.1 diff --git a/NAMESPACE b/NAMESPACE index 5b4b9ae..ff020c8 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,4 +1,13 @@ # Generated by roxygen2: do not edit by hand -import(httr) -importFrom(jsonlite,fromJSON) +export(find_tshark) +export(get_tshark) +export(packet_summary) +export(tshark_exec) +export(tshark_hosts) +import(arrow) +import(ndjson) +importFrom(tools,file_ext) +importFrom(tools,file_path_sans_ext) +importFrom(utils,browseURL) +importFrom(utils,help) diff --git a/R/find-tshark.R b/R/find-tshark.R new file mode 100644 index 0000000..90d361d --- /dev/null +++ b/R/find-tshark.R @@ -0,0 +1,35 @@ +#' Find the `tshark` binary +#' +#' Use the environment variable `TSHARK_PATH` or specify the directory in +#' the call to this function. +#' +#' @param path hint to where to look for the `tshark` binary +#' @export +#' @return length 1 character vector of the path to the `tshark` binary or `""` +#' @examples +#' loc <- tryCatch( +#' find_tshark(), +#' error = function(e) message("No tshark") +#' ) +find_tshark <- function(path = Sys.getenv("TSHARK_PATH", "")) { + + if (path != "") { + Sys.setenv( + PATH = paste0(c(path, Sys.getenv("PATH")), collapse = .Platform$path.sep) + ) + } + + res <- Sys.which("tshark") + + if (res == "") { + stop("Cannot locate tshark binary.", call.=FALSE) + } + + unname(res) + +} + +set_names <- function (object = nm, nm) { + names(object) <- nm + object +} \ No newline at end of file diff --git a/R/get-tshark.R b/R/get-tshark.R new file mode 100644 index 0000000..01c7bb2 --- /dev/null +++ b/R/get-tshark.R @@ -0,0 +1,10 @@ +#' Get tshark +#' +#' Opens the default browser to the place where you can get tshark +#' +#' @export +#' @examples +#' if (interactive()) get_tshark() +get_tshark <- function() { + utils::browseURL("https://tshark.dev/setup/install/") +} \ No newline at end of file diff --git a/R/packet-sum.R b/R/packet-sum.R new file mode 100644 index 0000000..2997b7f --- /dev/null +++ b/R/packet-sum.R @@ -0,0 +1,59 @@ +#' Extract packet summary table (if any) from a PCAP +#' +#' @param pcap path to PCAP file ([path.expand()] will be called on this value) +#' @return data frame +#' @export +#' @examples +#' packet_summary(system.file("pcap", "http.pcap", package = "tsharrk")) +packet_summary <- function(pcap) { + + pcap <- path.expand(pcap[1]) + + if (!file.exists(pcap)) { + stop(sprintf("Cannont locate %s", pcap), call.=FALSE) + } + + errf <- tempfile() + on.exit(unlink(errf)) + + outf <- tempfile() + on.exit(unlink(outf)) + + system2( + command = find_tshark(), + args = c("-T", "tabs", "-r", pcap), + stderr = errf, + stdout = outf + ) -> res + + if (res != 0) { + stop("Error retrieving packet summary from PCAP.", call.=FALSE) + } + + if (file.size(outf) == 0) { + data.frame( + packet_num = double(0), + ts = double(0), + src = character(0), + dst = character(0), + proto = character(0), + length = double(0), + info = character(0) + ) + } else { + + read.csv( + file = outf, + sep = "\t", + header = FALSE, + col.names = c("packet_num", "ts", "src", "junk", "dst", "proto", "length", "info"), + colClasses = c("double", "double", "character", "character", "character", "character", "double", "character") + ) -> out + + out$junk <- NULL + + out + + } + +} \ No newline at end of file diff --git a/R/tshark-hosts.R b/R/tshark-hosts.R new file mode 100644 index 0000000..ad17671 --- /dev/null +++ b/R/tshark-hosts.R @@ -0,0 +1,42 @@ +#' Extract hostname/IP table (if any) from a PCAP +#' +#' @param pcap path to PCAP file ([path.expand()] will be called on this value) +#' @return data frame +#' @export +#' @examples +#' tshark_hosts(system.file("pcap", "http.pcap", package = "tsharrk")) +tshark_hosts <- function(pcap) { + + pcap <- path.expand(pcap[1]) + + if (!file.exists(pcap)) { + stop(sprintf("Cannont locate %s", pcap), call.=FALSE) + } + + tshark_exec( + args = c("-q", "-z", "hosts", "-r", pcap) + ) -> res + + if (res$status != 0) { + stop("Error retrieving hosts from PCAP.", call.=FALSE) + } + + host_table_raw <- tail(res$stdout, -4) + + if (length(host_table_raw) == 0) { + data.frame( + ip = character(0), + host = character(0) + ) + } else { + read.csv( + text = paste0(host_table_raw, collapse = "\n"), + sep = "\t", + header = FALSE, + col.names = c("ip", "host"), + colClasses = c("character", "character") + ) + } + + +} \ No newline at end of file diff --git a/R/tshark.R b/R/tshark.R new file mode 100644 index 0000000..366c992 --- /dev/null +++ b/R/tshark.R @@ -0,0 +1,33 @@ +#' Call the tshark binary with optional custom environment variables and options +#' +#' This is just a convenience wrapper around [system2()]. See [find_tshark()] for +#' information on helping this package find the tshark binary. +#' +#' @param tshark_bin specify a complete path or let [find_tshark()] do the dirty work. +#' @param args same as [system2()] `args` +#' @param env same as [system2()] `env` +#' @return `list` with `stderr`, `stdout`, and `status` (invisibly) +#' @export +tshark_exec <- function(tshark_bin = find_tshark(), args = c(), env = c()) { + + errf <- tempfile() + on.exit(unlink(errf)) + + outf <- tempfile() + on.exit(unlink(outf)) + + system2( + command = tshark_bin, + args = args, + env = env, + stderr = errf, + stdout = outf + ) -> res + + invisible(list( + stderr = readLines(errf, warn = FALSE), + stdout = readLines(outf, warn = FALSE), + status = res + )) + +} \ No newline at end of file diff --git a/R/tsharrk-package.R b/R/tsharrk-package.R index 19dc5fa..c97dc26 100644 --- a/R/tsharrk-package.R +++ b/R/tsharrk-package.R @@ -1,9 +1,16 @@ -#' ... -#' +#' Tools to Make Analyses Using 'tshark' Easier +#' +#' The 'tshark' () +#' command line utility comes with Wireshark and is a is useful when performing +#' analyses on packet captures (PCAPs). Tools are provided to make it a bit easier +#' to work with 'tshark' to perform analyses with R. +#' #' @md #' @name tsharrk #' @keywords internal #' @author Bob Rudis (bob@@rud.is) -#' @import httr -#' @importFrom jsonlite fromJSON +#' @import arrow +#' @import ndjson +#' @importFrom utils browseURL help +#' @importFrom tools file_path_sans_ext file_ext "_PACKAGE" diff --git a/inst/pcap/http.pcap b/inst/pcap/http.pcap new file mode 100644 index 0000000000000000000000000000000000000000..54f6f2953d5553eddc5af6bc29e29b49ceb864ba GIT binary patch literal 25803 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 z