mirror of
https://github.com/Rucknium/xmrpeers.git
synced 2025-03-12 09:29:25 +00:00
Add peers.ip.collect()
This commit is contained in:
parent
65488a4bac
commit
6088f1828e
4 changed files with 225 additions and 3 deletions
|
@ -35,5 +35,5 @@ Suggests:
|
|||
testthat (>= 3.0.0),
|
||||
withr
|
||||
Config/testthat/edition: 3
|
||||
RoxygenNote: 7.3.1
|
||||
RoxygenNote: 7.3.2
|
||||
Roxygen: list(markdown = TRUE)
|
||||
|
|
|
@ -6,5 +6,6 @@ export("newborn.nodes")
|
|||
export("txpool.init")
|
||||
export("txpool.collect")
|
||||
export("txpool.export")
|
||||
importFrom("utils", "read.csv", "write.csv", "untar", "installed.packages")
|
||||
importFrom("stats", "complete.cases", "quantile")
|
||||
export("peers.ip.collect")
|
||||
importFrom("utils", "read.csv", "write.csv", "write.table", "untar", "installed.packages")
|
||||
importFrom("stats", "complete.cases", "quantile", "na.omit")
|
||||
|
|
167
R/ip.R
Normal file
167
R/ip.R
Normal file
|
@ -0,0 +1,167 @@
|
|||
|
||||
|
||||
convert.to.subnet <- function(x, mask) {
|
||||
result <- intToBits(IP::ipv4(x)@ipv4)
|
||||
result <- matrix(result, nrow = 32)
|
||||
result[seq_len(32 - mask), ] <- raw(1)
|
||||
result <- packBits(c(result), "integer")
|
||||
result <- suppressWarnings(as.character(IP::ipv4(result)))
|
||||
# Gets a warning about negative values because the actual value is an unsigned integer
|
||||
result
|
||||
}
|
||||
|
||||
|
||||
in.malicious.ips <- function(x, malicious.ips) {
|
||||
# x can have duplicated elements
|
||||
malicious.ips.singletons <- malicious.ips[ ! grepl("/", malicious.ips)]
|
||||
malicious.ips.ranges <- malicious.ips[grepl("/", malicious.ips)]
|
||||
result <- x %in% malicious.ips.singletons
|
||||
result <- result | (! is.na(IP::ip.match(IP::ipv4(x), IP::ipv4r(malicious.ips.ranges))))
|
||||
result
|
||||
}
|
||||
|
||||
|
||||
#' Collect connected peers' IP addresses
|
||||
#'
|
||||
#' @description Collects IP addreses of peers that the local node has
|
||||
#' established outbound connections to. The time and set of IP addreses are
|
||||
#' saved to a CSV file. These IP addreses are checked against an optional
|
||||
#' set of suspected malicious IP addresses. Information about the share of
|
||||
#' outbound connections to suspected maclicious IP addreses is printed.
|
||||
#' IP addresses are grouped by subnet and information is printed to check for
|
||||
#' possible "subnet saturation" by malicious entities. This function is an
|
||||
#' infinite loop. `ctrl + c` to interrupt the function.
|
||||
#'
|
||||
#' @param csv.file The name of the CSV file to write to and read from. If it
|
||||
#' already exists, data will be appended to it and the whole file will be
|
||||
#' used to compute top subnet information.
|
||||
#' @param unrestricted.rpc.url URL and port of the `monerod` unrestricted RPC.
|
||||
#' Default is `http://127.0.0.1:18081`
|
||||
#' @param malicious.ips A character vector of IP addreses that are suspected
|
||||
#' to be malicious.
|
||||
#' @param top.subnet.mask Numeric value. The IP address subnet mask to print
|
||||
#' summary information about.
|
||||
#' @param n.top.subnets Number of subnets to print summary information about.
|
||||
#' @param poll.time How often, in seconds, to collect data from the local
|
||||
#' monero node. Default is 30 seconds.
|
||||
#'
|
||||
#' @return
|
||||
#' NULL (invisible)
|
||||
#' @export
|
||||
#'
|
||||
#' @examples
|
||||
#' \dontrun{
|
||||
#' suspected.malicious.ips <-readLines(
|
||||
#' "https://raw.githubusercontent.com/Boog900/monero-ban-list/refs/heads/main/ban_list.txt")
|
||||
#' peers.ip.collect(malicious.ips = suspected.malicious.ips)
|
||||
#' }
|
||||
|
||||
peers.ip.collect <- function(csv.file = "xmr-peers-ip.csv",
|
||||
unrestricted.rpc.url = "http://127.0.0.1:18081", malicious.ips = NULL,
|
||||
top.subnet.mask = 24, n.top.subnets = 10, poll.time = 30) {
|
||||
|
||||
if ( ! (is.null(malicious.ips) | is.vector(malicious.ips)) ) {
|
||||
stop("malicious.ips must be an atomic vector, i.e. not a data.frame or matrix.")
|
||||
}
|
||||
|
||||
if (file.exists(csv.file)) {
|
||||
peer.ip.data <- as.matrix(read.csv(csv.file, header = FALSE))
|
||||
peer.ip.data <- peer.ip.data[, -1] # Remove the time column
|
||||
} else {
|
||||
file.create(csv.file)
|
||||
peer.ip.data <- matrix(NA_character_, ncol = 24, nrow = 0)
|
||||
}
|
||||
|
||||
json.post <- RJSONIO::toJSON(
|
||||
list(
|
||||
jsonrpc = "2.0",
|
||||
id = "0",
|
||||
method = "get_connections",
|
||||
params = ""
|
||||
)
|
||||
)
|
||||
|
||||
session.start.time <- Sys.time()
|
||||
|
||||
cat(paste0(format(session.start.time, "%Y-%m-%d %T"),
|
||||
ifelse(length(malicious.ips) > 0, " Start collecting peer data...\n",
|
||||
" Start collecting peer data (no malicious peer list)...\n")))
|
||||
|
||||
# peer.malicious.ips.data <- matrix(NA_real_, ncol = 24, nrow = 0)
|
||||
|
||||
while (TRUE) {
|
||||
|
||||
RPC.time <- as.numeric(Sys.time())
|
||||
|
||||
peers <- RJSONIO::fromJSON(
|
||||
RCurl::postForm(paste0(unrestricted.rpc.url, "/json_rpc"),
|
||||
.opts = list(
|
||||
userpwd = "",
|
||||
postfields = json.post,
|
||||
httpheader = c('Content-Type' = 'application/json', Accept = 'application/json')
|
||||
)
|
||||
), asText = TRUE
|
||||
)
|
||||
|
||||
if(length(peers$result$connections) == 0 ) {
|
||||
cat("Unexpected results from node's RPC response. Check that monerod's unrestricted RPC port is at ",
|
||||
unrestricted.rpc.url, "\n")
|
||||
Sys.sleep(poll.time)
|
||||
next
|
||||
}
|
||||
|
||||
peers <- peers$result$connections
|
||||
|
||||
peer.address <- sapply(peers, FUN = function(x) {gsub("[:][0-9]*", "", x$address)})
|
||||
peer.incoming <- sapply(peers, FUN = function(x) {x$incoming})
|
||||
|
||||
peer.address <- peer.address[ ! peer.incoming]
|
||||
|
||||
peer.address.ipv4 <- IP::ipv4(peer.address)
|
||||
|
||||
peer.address <- peer.address[order(peer.address.ipv4)]
|
||||
|
||||
peer.ip.data <- rbind(peer.ip.data,
|
||||
matrix(c(peer.address, rep(NA_character_, 24 - length(peer.address))),
|
||||
ncol = 24, nrow = 1))
|
||||
|
||||
write.table(cbind(RPC.time, peer.ip.data[nrow(peer.ip.data), , drop = FALSE]), file = csv.file,
|
||||
sep = ",", append = TRUE, col.names = FALSE, row.names = FALSE)
|
||||
|
||||
if (length(malicious.ips) > 0) {
|
||||
|
||||
peer.address.malicious.ips <- as.numeric(in.malicious.ips(peer.address, malicious.ips))
|
||||
|
||||
# peer.malicious.ips.data <- rbind(peer.malicious.ips.data,
|
||||
# matrix(c(peer.address.malicious.ips, rep(NA_real_, 24 - length(peer.address.malicious.ips))),
|
||||
# ncol = 24, nrow = 1))
|
||||
|
||||
cat(format(Sys.time(), "%Y-%m-%d %T"), " Outbound peers on malicious IPs list: ",
|
||||
sum(peer.address.malicious.ips), "/", length(peer.address.malicious.ips),
|
||||
" (", round(100 * mean(peer.address.malicious.ips)), "%)", "\n", sep = "")
|
||||
|
||||
}
|
||||
|
||||
peer.ip.data.unique <- na.omit(unique(c(peer.ip.data)))
|
||||
|
||||
peer.ip.data.unique.subnet <- convert.to.subnet(peer.ip.data.unique, top.subnet.mask)
|
||||
|
||||
if (length(peer.ip.data.unique.subnet) < n.top.subnets) { Sys.sleep(poll.time); next }
|
||||
|
||||
subnets.message <- sort(table(peer.ip.data.unique.subnet), decreasing = TRUE)[1:n.top.subnets]
|
||||
|
||||
subnets.message <- paste0(names(subnets.message), "/", top.subnet.mask,
|
||||
": ", subnets.message, collapse = ", ")
|
||||
|
||||
cat("Top subnets (", 2^(32 - top.subnet.mask) - 2,
|
||||
" possible /", top.subnet.mask, " IPs each): ", subnets.message,
|
||||
"\n", sep = "")
|
||||
|
||||
Sys.sleep(poll.time)
|
||||
|
||||
}
|
||||
|
||||
return(invisible(NULL))
|
||||
|
||||
}
|
||||
|
54
man/peers.ip.collect.Rd
Normal file
54
man/peers.ip.collect.Rd
Normal file
|
@ -0,0 +1,54 @@
|
|||
% Generated by roxygen2: do not edit by hand
|
||||
% Please edit documentation in R/ip.R
|
||||
\name{peers.ip.collect}
|
||||
\alias{peers.ip.collect}
|
||||
\title{Collect connected peers' IP addresses}
|
||||
\usage{
|
||||
peers.ip.collect(
|
||||
csv.file = "xmr-peers-ip.csv",
|
||||
unrestricted.rpc.url = "http://127.0.0.1:18081",
|
||||
malicious.ips = NULL,
|
||||
top.subnet.mask = 24,
|
||||
n.top.subnets = 10,
|
||||
poll.time = 30
|
||||
)
|
||||
}
|
||||
\arguments{
|
||||
\item{csv.file}{The name of the CSV file to write to and read from. If it
|
||||
already exists, data will be appended to it and the whole file will be
|
||||
used to compute top subnet information.}
|
||||
|
||||
\item{unrestricted.rpc.url}{URL and port of the \code{monerod} unrestricted RPC.
|
||||
Default is \verb{http://127.0.0.1:18081}}
|
||||
|
||||
\item{malicious.ips}{A character vector of IP addreses that are suspected
|
||||
to be malicious.}
|
||||
|
||||
\item{top.subnet.mask}{Numeric value. The IP address subnet mask to print
|
||||
summary information about.}
|
||||
|
||||
\item{n.top.subnets}{Number of subnets to print summary information about.}
|
||||
|
||||
\item{poll.time}{How often, in seconds, to collect data from the local
|
||||
monero node. Default is 30 seconds.}
|
||||
}
|
||||
\value{
|
||||
NULL (invisible)
|
||||
}
|
||||
\description{
|
||||
Collects IP addreses of peers that the local node has
|
||||
established outbound connections to. The time and set of IP addreses are
|
||||
saved to a CSV file. These IP addreses are checked against an optional
|
||||
set of suspected malicious IP addresses. Information about the share of
|
||||
outbound connections to suspected maclicious IP addreses is printed.
|
||||
IP addresses are grouped by subnet and information is printed to check for
|
||||
possible "subnet saturation" by malicious entities. This function is an
|
||||
infinite loop. \code{ctrl + c} to interrupt the function.
|
||||
}
|
||||
\examples{
|
||||
\dontrun{
|
||||
suspected.malicious.ips <-readLines(
|
||||
"https://raw.githubusercontent.com/Boog900/monero-ban-list/refs/heads/main/ban_list.txt")
|
||||
peers.ip.collect(malicious.ips = suspected.malicious.ips)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue