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 0000000..54f6f29 Binary files /dev/null and b/inst/pcap/http.pcap differ diff --git a/man/find_tshark.Rd b/man/find_tshark.Rd new file mode 100644 index 0000000..03b8122 --- /dev/null +++ b/man/find_tshark.Rd @@ -0,0 +1,24 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/find-tshark.R +\name{find_tshark} +\alias{find_tshark} +\title{Find the \code{tshark} binary} +\usage{ +find_tshark(path = Sys.getenv("TSHARK_PATH", "")) +} +\arguments{ +\item{path}{hint to where to look for the \code{tshark} binary} +} +\value{ +length 1 character vector of the path to the \code{tshark} binary or \code{""} +} +\description{ +Use the environment variable \code{TSHARK_PATH} or specify the directory in +the call to this function. +} +\examples{ +loc <- tryCatch( + find_tshark(), + error = function(e) message("No tshark") +) +} diff --git a/man/get_tshark.Rd b/man/get_tshark.Rd new file mode 100644 index 0000000..92339bf --- /dev/null +++ b/man/get_tshark.Rd @@ -0,0 +1,14 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/get-tshark.R +\name{get_tshark} +\alias{get_tshark} +\title{Get tshark} +\usage{ +get_tshark() +} +\description{ +Opens the default browser to the place where you can get tshark +} +\examples{ +if (interactive()) get_tshark() +} diff --git a/man/packet_summary.Rd b/man/packet_summary.Rd new file mode 100644 index 0000000..414a61f --- /dev/null +++ b/man/packet_summary.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/packet-sum.R +\name{packet_summary} +\alias{packet_summary} +\title{Extract packet summary table (if any) from a PCAP} +\usage{ +packet_summary(pcap) +} +\arguments{ +\item{pcap}{path to PCAP file (\code{\link[=path.expand]{path.expand()}} will be called on this value)} +} +\value{ +data frame +} +\description{ +Extract packet summary table (if any) from a PCAP +} +\examples{ +packet_summary(system.file("pcap", "http.pcap", package = "tsharrk")) +} diff --git a/man/tshark_exec.Rd b/man/tshark_exec.Rd new file mode 100644 index 0000000..af9c71f --- /dev/null +++ b/man/tshark_exec.Rd @@ -0,0 +1,22 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/tshark.R +\name{tshark_exec} +\alias{tshark_exec} +\title{Call the tshark binary with optional custom environment variables and options} +\usage{ +tshark_exec(tshark_bin = find_tshark(), args = c(), env = c()) +} +\arguments{ +\item{tshark_bin}{specify a complete path or let \code{\link[=find_tshark]{find_tshark()}} do the dirty work.} + +\item{args}{same as \code{\link[=system2]{system2()}} \code{args}} + +\item{env}{same as \code{\link[=system2]{system2()}} \code{env}} +} +\value{ +\code{list} with \code{stderr}, \code{stdout}, and \code{status} (invisibly) +} +\description{ +This is just a convenience wrapper around \code{\link[=system2]{system2()}}. See \code{\link[=find_tshark]{find_tshark()}} for +information on helping this package find the tshark binary. +} diff --git a/man/tshark_hosts.Rd b/man/tshark_hosts.Rd new file mode 100644 index 0000000..25a1788 --- /dev/null +++ b/man/tshark_hosts.Rd @@ -0,0 +1,20 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/tshark-hosts.R +\name{tshark_hosts} +\alias{tshark_hosts} +\title{Extract hostname/IP table (if any) from a PCAP} +\usage{ +tshark_hosts(pcap) +} +\arguments{ +\item{pcap}{path to PCAP file (\code{\link[=path.expand]{path.expand()}} will be called on this value)} +} +\value{ +data frame +} +\description{ +Extract hostname/IP table (if any) from a PCAP +} +\examples{ +tshark_hosts(system.file("pcap", "http.pcap", package = "tsharrk")) +} diff --git a/man/tsharrk.Rd b/man/tsharrk.Rd index eb47b10..cee494b 100644 --- a/man/tsharrk.Rd +++ b/man/tsharrk.Rd @@ -4,9 +4,12 @@ \name{tsharrk} \alias{tsharrk} \alias{tsharrk-package} -\title{...} +\title{Tools to Make Analyses Using 'tshark' Easier} \description{ -A good description goes here otherwise CRAN checks fail. +The 'tshark' (\url{https://www.wireshark.org/docs/man-pages/tshark.html}) +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. } \seealso{ Useful links: