21 Commits

Author SHA1 Message Date
6c7564062f make things a bit less ugly 2023-02-07 19:17:30 +01:00
c6ffa0e628 make linter happy 2022-12-29 23:04:13 +01:00
73414736f9 add lint workflow 2022-12-29 19:52:31 +01:00
5c8a39ad75 add user and group to systemd service example 2022-08-23 03:31:56 +02:00
deab05167d docker: trim binary, add standalone traceroute, run as own user 2022-08-23 03:20:01 +02:00
2866c9915d still use dockerhub 2022-05-29 15:53:09 +02:00
d87f9b568b update docker files 2022-05-29 15:32:28 +02:00
188dc88d12 update readme 2022-05-29 15:32:18 +02:00
3b4e96509b explicitly dont handle returns in certain places 2022-05-29 15:09:58 +02:00
a0e42d369a fix command order if host is invalid but port proper 2022-05-29 15:07:01 +02:00
871fe76df5 return proper http errors 2022-05-29 15:05:13 +02:00
2b8510c5ee cleanup nping commands 2022-05-29 14:26:09 +02:00
955f1253af add nping handler, port support and fmt code 2022-05-29 14:20:06 +02:00
a94503ba7a add caddy config example 2022-01-07 10:05:29 +01:00
ce95696ef0 add environment values 2022-01-06 23:37:03 +01:00
62564fab41 add reverse proxy note to readme 2022-01-02 00:54:39 +01:00
d3df87b266 add altnames for options 2022-01-02 00:52:03 +01:00
cd12cab2d0 add proper readme 2022-01-02 00:10:12 +01:00
9c2e013720 add iputils to dockerfile 2022-01-01 23:15:39 +01:00
bc827f5a8d disable private range lookups by default 2022-01-01 22:24:27 +01:00
c328205a12 change x-forwarding flag 2022-01-01 21:52:11 +01:00
10 changed files with 458 additions and 140 deletions

7
.github/workflows/lint.yml vendored Normal file
View File

@ -0,0 +1,7 @@
name: ci
on: [push]
jobs:
test:
uses: byReqz/workflows/.github/workflows/golint_with_codeql.yml@main

197
README.md
View File

@ -1,11 +1,194 @@
# probehost2
an http endpoint to query network diagnosis tools from remote hosts
a http endpoint to query network diagnosis tools from remote hosts
# disclaimer
Dont expect good or even mediocre code here. This is my first take at go and is mostly for myself to learn. Suggestions and improvements are welcome.
- <a href="#probehost2">Overview</a>
- <a href="#disclaimer">Disclaimer</a>
- <a href="#installation">Installation</a>
- <a href="#building">Building</a>
- <a href="#systemd">Systemd</a>
- <a href="#docker">Docker</a>
- <a href="#proxy">Proxy</a>
- <a href="#usage">Usage</a>
- <a href="#server">Server</a>
- <a href="#client">Client</a>
- <a href="#general">General</a>
- <a href="#ping">Ping</a>
- <a href="#mtr">MTR</a>
- <a href="#traceroute">Traceroute</a>
- <a href="#nping">Nping</a>
# installation
wip
# Disclaimer
Don't expect good or even mediocre code here. This is my first take at go and is mostly for myself to learn. Suggestions and improvements are welcome.
# doc
wip
Please note that this project does not include any kind of rate limiting or other protection. It is therefore heavily advised to only make it publicly reachable if a reverse proxy is in place. A sample config for <a href="caddyserver.com/">Caddy</a> can be found in the `caddy` subfolder.
# Installation
The runtime dependencies are currently `iputils`, `traceroute`, `nping` (usually provided by nmap) and `mtr` (sometimes called `mtr-tiny`). `iputils` and `traceroute` can be substituted by `busybox`.
## Building
The app can be built with the latest Go toolchain.
First get the external module dependencies:
```sh
go get -u
```
Then build the app, this already strips debug symbols.
```sh
go build -ldflags "-s -w" -o "probehost2" main.go
```
(if this is unwanted, just leave out the ldflags argument)
## Systemd
Example files for a systemd service can be found in the `systemd` subfolder.
## Docker
A docker container based on <a href="https://alpinelinux.org">Alpine</a> can be built by using the included dockerfile (`docker/Dockerfile`).
```sh
docker build -f docker/Dockerfile . -t byreqz/probehost2:latest
```
A compose file can also be found in `docker/docker-compose.yml`.
## Proxy
It's recommended to only run this app together with a rate-limiting reverse-proxy. An example configuration for <a href="caddyserver.com/">Caddy</a> can be found in the `caddy` subfolder.
# Usage
## Server
The app currently has 4 runtime flags:
- `-p / --listenport` -- sets the port to listen on
- `-o / --logfilepath` -- sets the log output file
- `-x / --disable-x-forwarded-for` -- disables checking for the X-Forwarded-For header
- `-l / --allow-private` -- allows lookups of private IP ranges
All the Flags also have an accompanying environment value: `PROBEHOST_LOGPATH`, `PROBEHOST_ALLOW_PRIVATE`, `PROBEHOST_LISTEN_PORT` and `PROBEHOST_DISABLE_X_FORWARDED_FOR` but the options given via commandline have priority.
The app will log every request including the IP that's querying and show failed requests on stdout.
Requests that contain an X-Forwarded-For header (implying the app is behind a reverse proxy) will automatically log that address instead of the requesting IP (the proxy itself), this can be turned off with -x.
## Client
### General
The app can be queried via HTTP/HTTPS with the following scheme:
```
https://[address]/[command]/[host](_[port]),[host].../[options]
```
- [address] = the IP or domain serving the site
- [command] = the command to return, currently available:
- ping
- mtr
- traceroute
- nping
- [host] = can be one or more hosts query, seperated by a comma
- [port] = port to be queried, optional
- [options] = options to run the command with, seperated by a comma
All inputs are validated and invalid input is discarded. If the request contains no valid data, the server will return HTTP 500.
Local IP ranges are by default excluded from lookups, this currently only includes IPs and not hostnames and can be disabled on the server by passing the -l flag.
Command options are based on the originally given cli flags but also have a more understandable altname.
### Ping
The default options are:
- `-c 10`: send 10 pings
Available options are:
- `4` / `force4`: force IPv4
- `6` / `force6`: force IPv6
- `d` / `timestamps`: print timestamps
- `n` / `nodns`: no dns name resolution
- `v` / `verbose`: verbose output
- `c1` / `count1`: send 1 ping
- `c5` / `count5`: send 5 pings
- `c10` / `count10`: send 10 pings
Example query:
```sh
$ curl http://localhost:8000/ping/localhost/c1
PING localhost(localhost (::1)) 56 data bytes
64 bytes from localhost (::1): icmp_seq=1 ttl=64 time=0.189 ms
--- localhost ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.189/0.189/0.189/0.000 ms
```
### MTR
The default options are:
- `-r`: output using report mode
- `-w`: output wide report
- `-c10`: send 10 pings
Available options are:
- `4` / `force4`: force IPv4
- `6` / `force6`: force IPv6
- `u` / `udp`: use UDP instead of ICMP echo
- `t` / `tcp`: use TCP instead of ICMP echo
- `e` / `ext`: display information from ICMP extensions
- `x` / `xml`: output xml
- `n` / `nodns`: do not resolve host names
- `b` / `cmb`: show IP numbers and host names
- `z` / `asn`: display AS number
- `c1` / `count1`: send 1 ping
- `c5` / `count5`: send 5 pings
- `c10` / `count10`: send 10 pings
Example query:
```sh
$ curl http://localhost:8000/mtr/localhost/c1,z
Start: 2022-01-02T00:06:56+0100
HOST: xxx Loss% Snt Last Avg Best Wrst StDev
1. AS??? localhost 0.0% 1 0.6 0.6 0.6 0.6 0.0
```
### Traceroute
The default options are:
- none
Available options are:
- `4` / `force4`: force IPv4
- `6` / `force6`: force IPv6
- `f` / `dnf`: do not fragment packets
- `i` / `ìcmp`: use ICMP ECHO for tracerouting
- `t` / `tcp`: use TCP SYN for tracerouting (default port is 80)
- `n` / `nodns`: do not resolve IP addresses to their domain names
- `u` / `udp`: use UDP to particular port for tracerouting (default port is 53)
- `ul` / `udplite`: Use UDPLITE for tracerouting (default port is 53)
- `d` / `dccp`: Use DCCP Request for tracerouting (default port is 33434)
- `b` / `back`: Guess the number of hops in the backward path and print if it differs
Example query:
```sh
$ curl http://localhost:8000/tracert/localhost/i
traceroute to localhost (127.0.0.1), 30 hops max, 60 byte packets
1 localhost (127.0.0.1) 0.063 ms 0.008 ms 0.006 ms
```
### Nping
The default options are:
- `c3`: send 3 pings
Available options are:
- `4` / `force4`: force IPv4
- `6` / `force6`: force IPv6
- `u` / `udp`: use UDP
- `t` / `tcp`: use TCP
- `v` / `verbose`: be verbose
- `c1` / `count1`: send 1 ping
- `c3` / `count3`: send 3 pings
- `c5` / `count5`: send 5 pings
Example query:
```sh
$ curl localhost:8000/nping/localhost_22
Starting Nping 0.7.92 ( https://nmap.org/nping ) at 2022-05-29 15:28 CEST
SENT (0.0022s) Starting TCP Handshake > localhost:22 (127.0.0.1:22)
RCVD (0.0133s) Handshake with localhost:22 (127.0.0.1:22) completed
SENT (1.0041s) Starting TCP Handshake > localhost:22 (127.0.0.1:22)
RCVD (1.0089s) Handshake with localhost:22 (127.0.0.1:22) completed
SENT (2.0071s) Starting TCP Handshake > localhost:22 (127.0.0.1:22)
RCVD (2.0090s) Handshake with localhost:22 (127.0.0.1:22) completed
Max rtt: 11.130ms | Min rtt: 1.945ms | Avg rtt: 5.965ms
TCP connection attempts: 3 | Successful connections: 3 | Failed: 0 (0.00%)
Nping done: 1 IP address pinged in 2.01 seconds
```

29
caddy/Caddyfile Normal file
View File

@ -0,0 +1,29 @@
{
email [your email]
order rate_limit before basicauth
}
:80, :443 {
redir * https://[your domain]
}
[your domain] {
reverse_proxy [host]:[port]
log {
output file [path] {
roll_size 10GiB
roll_keep 10
}
level INFO
}
handle_errors {
respond "{http.error.status_code} {http.error.status_text}"
}
rate_limit {
zone dynamic {
key {remote_host}
events 2
window 5s
}
}
}

8
caddy/Dockerfile Normal file
View File

@ -0,0 +1,8 @@
FROM caddy:builder AS builder
RUN xcaddy build \
--with github.com/mholt/caddy-ratelimit \
--with github.com/caddy-dns/cloudflare
FROM caddy:latest
COPY --from=builder /usr/bin/caddy /usr/bin/caddy

5
caddy/README.md Normal file
View File

@ -0,0 +1,5 @@
# caddy
This folder contains a sample configuration for caddy with ratelimiting enabled. this will allow 2 requests per IP every 5 seconds. For more on that, check here: https://github.com/mholt/caddy-ratelimit
I have also included a dockerfile to build caddy with the ratelimiting module.

1
caddy/build.sh Executable file
View File

@ -0,0 +1 @@
xcaddy build --with github.com/mholt/caddy-ratelimit

View File

@ -2,11 +2,14 @@ FROM golang:latest as builder
WORKDIR /build
COPY . .
RUN go get -u
RUN CGO_ENABLED=0 go build -o probehost2
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o probehost2
FROM alpine:latest
RUN apk update
RUN apk add mtr
RUN apk add mtr iputils nmap-nping traceroute
RUN adduser -D probehost2
COPY --from=builder /build/probehost2 /
RUN touch /probehost2.log
RUN chown probehost2:users /probehost2.log
USER probehost2
CMD ["/probehost2"]

View File

@ -5,6 +5,11 @@ services:
container_name: probehost2
image: byreqz/probehost2:latest
restart: unless-stopped
environment:
- PROBEHOST_LOGPATH=/probehost2.log
- PROBEHOST_ALLOW_PRIVATE=false
- PROBEHOST_DISABLE_X_FORWARDED_FOR=false
- PROBEHOST_LISTEN_PORT=8000
ports:
- 1234:8000
volumes:

335
main.go
View File

@ -1,161 +1,236 @@
package main
import (
"fmt"
"os"
"os/exec"
"strings"
"net/http"
"net"
log "github.com/sirupsen/logrus"
flag "github.com/spf13/pflag"
"fmt"
"net"
"net/http"
"os"
"os/exec"
"strconv"
"strings"
log "github.com/sirupsen/logrus"
flag "github.com/spf13/pflag"
)
var logstdout = log.New()
var logfile = log.New()
var logStdout = log.New()
var logFile = log.New()
var listenport int
var usexforwardedfor bool
var listenPort = 8080 // port to listen on
var disableXForwardedFor bool // whether to disable parsing the X-Forwarded-For header or not
var allowPrivate bool // whether to allow private IP ranges or not
func init() {
var logfilepath string
flag.StringVarP(&logfilepath, "logfilepath", "o","probehost2.log", "sets the output file for the log")
flag.IntVarP(&listenport, "port", "p", 8000, "sets the port to listen on")
flag.BoolVarP(&usexforwardedfor, "use-x-forwarded-for", "x", true, "specifies whether to show x-forwarded-for or the requesting IP")
flag.Parse()
logStdout.SetFormatter(&log.TextFormatter{
FullTimestamp: true})
logStdout.SetOutput(os.Stdout)
logStdout.SetLevel(log.InfoLevel)
logstdout.SetFormatter(&log.TextFormatter{
FullTimestamp: true})
logstdout.SetOutput(os.Stdout)
logstdout.SetLevel(log.InfoLevel)
logFilePath := "probehost2.log"
if val, exists := os.LookupEnv("PROBEHOST_LOGPATH"); exists {
logFilePath = val
}
logpath, err := os.OpenFile(logfilepath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0660)
if err != nil {
logstdout.Fatal("Failed to initialize the logfile: ", err.Error())
}
logfile.SetLevel(log.InfoLevel)
logfile.SetOutput(logpath)
logfile.Info("probehost2 initialized")
_, allowPrivate = os.LookupEnv("PROBEHOST_ALLOW_PRIVATE")
_, disableXForwardedFor = os.LookupEnv("PROBEHOST_DISABLE_X_FORWARDED_FOR")
if val, exists := os.LookupEnv("PROBEHOST_LISTEN_PORT"); exists {
var err error
listenPort, err = strconv.Atoi(val)
if err != nil {
logStdout.Fatal("Failed to read PROBEHOST_LISTEN_PORT: ", err.Error())
}
}
flag.StringVarP(&logFilePath, "logFilePath", "o", logFilePath, "sets the output file for the log")
flag.IntVarP(&listenPort, "port", "p", listenPort, "sets the port to listen on")
flag.BoolVarP(&disableXForwardedFor, "disable-x-forwarded-for", "x", disableXForwardedFor, "whether to show x-forwarded-for or the requesting IP")
flag.BoolVarP(&allowPrivate, "allow-private", "l", allowPrivate, "whether to show lookups of private IP ranges")
flag.Parse()
logpath, err := os.OpenFile(logFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0660)
if err != nil {
logStdout.Fatal("Failed to initialize the logFile: ", err.Error())
}
logFile.SetLevel(log.InfoLevel)
logFile.SetOutput(logpath)
logFile.Info("probehost2 initialized")
}
func runner(remoteip string, command string, args... string) string{
logfile.WithFields(log.Fields{
"remote_ip": remoteip,
"command": fmt.Sprint(command, args),
}).Info("request initiated:")
cmd, err := exec.Command(command, args...).Output()
if err != nil {
logstdout.WithFields(log.Fields{
"remote_ip": remoteip,
"command": fmt.Sprint(command, args),
"error": err.Error(),
}).Warn("request failed:")
logfile.WithFields(log.Fields{
"remote_ip": remoteip,
"command": fmt.Sprint(command, args),
"error": err.Error(),
}).Warn("request failed:")
} else {
logfile.WithFields(log.Fields{
"remote_ip": remoteip,
"command": fmt.Sprint(command, args),
}).Info("request succeeded:")
}
return string(cmd)
// runner runs the given command with the given args and returns stdout as string. Also logs all executed commands and their exit state.
func runner(remoteip string, command string, args ...string) string {
logFile.WithFields(log.Fields{
"remote_ip": remoteip,
"command": fmt.Sprint(command, args),
}).Info("request initiated:")
cmd, err := exec.Command(command, args...).Output()
if err != nil {
logStdout.WithFields(log.Fields{
"remote_ip": remoteip,
"command": fmt.Sprint(command, args),
"error": err.Error(),
}).Warn("request failed:")
logFile.WithFields(log.Fields{
"remote_ip": remoteip,
"command": fmt.Sprint(command, args),
"error": err.Error(),
}).Warn("request failed:")
} else {
logFile.WithFields(log.Fields{
"remote_ip": remoteip,
"command": fmt.Sprint(command, args),
}).Info("request succeeded:")
}
return string(cmd)
}
func validatehosts(hosts []string) []string{
var valid []string
for _, host := range hosts {
if net.ParseIP(host) != nil {
valid = append(valid, host)
} else if _, err := net.LookupIP(host); err == nil {
valid = append(valid, host)
}
}
return valid
// validatehosts checks the given host+port combinations for validity and returns valid hosts + valid ports seperately.
func validatehosts(hosts []string) ([]string, []string) {
var validHosts []string
var validPorts []string
for _, host := range hosts {
split := strings.Split(host, "_")
host = split[0]
if hostparse := net.ParseIP(host); hostparse != nil {
if (net.IP.IsPrivate(hostparse) || net.IP.IsLoopback(hostparse)) && allowPrivate {
validHosts = append(validHosts, host)
} else if !(net.IP.IsPrivate(hostparse) || net.IP.IsLoopback(hostparse)) {
validHosts = append(validHosts, host)
}
} else if _, err := net.LookupIP(host); err == nil {
validHosts = append(validHosts, host)
} else {
continue
}
var port string
if len(split) > 1 {
port = split[1]
_, err := strconv.Atoi(port) // validate if port is just an int
if err == nil {
validPorts = append(validPorts, port)
} else {
validPorts = append(validPorts, "0")
}
} else {
validPorts = append(validPorts, "0")
}
}
return validHosts, validPorts
}
func parseopts(options []string, cmdopts map[string]string) []string{
var opts []string
for _, opt := range options {
opts = append(opts, cmdopts[opt])
}
return opts
// parseopts matches the given user options to the valid optionmap.
func parseopts(options []string, cmdopts map[string]string) []string {
var opts []string
for _, opt := range options {
opts = append(opts, cmdopts[opt])
}
return opts
}
func prerunner(req *http.Request, cmd string, cmdopts map[string]string, defaultopts []string) string{
geturl := strings.Split(req.URL.String(), "/")
targets := strings.Split(geturl[2], ",")
hosts := validatehosts(targets)
var opts []string
opts = append(opts, defaultopts...)
if len(geturl) > 3 && len(geturl[3]) > 0 {
options := strings.Split(geturl[3], ",")
opts = append(opts, parseopts(options, cmdopts)...)
}
var res string
var args []string
var remoteaddr string
if req.Header.Get("X-Forwarded-For") != "" && usexforwardedfor != false {
remoteaddr = req.Header.Get("X-Forwarded-For")
} else {
remoteaddr = req.RemoteAddr
}
for _, host := range hosts {
args = append(args, opts...)
args = append(args, host)
res = fmt.Sprint(res, runner(remoteaddr, cmd, args...), "\n")
}
return res
// prerunner processes the incoming request to send it to runner.
func prerunner(req *http.Request, cmd string, cmdopts map[string]string, defaultopts []string) string {
geturl := strings.Split(req.URL.String(), "/")
targets := strings.Split(geturl[2], ",")
hosts, ports := validatehosts(targets)
var opts []string
opts = append(opts, defaultopts...)
if len(geturl) > 3 && len(geturl[3]) > 0 {
options := strings.Split(geturl[3], ",")
opts = append(opts, parseopts(options, cmdopts)...)
}
var res string
var args []string
remoteaddr := req.RemoteAddr
if req.Header.Get("X-Forwarded-For") != "" && !disableXForwardedFor {
remoteaddr = req.Header.Get("X-Forwarded-For")
}
for i, host := range hosts {
runargs := append(args, opts...)
if ports[i] != "0" && cmd == "nping" {
runargs = append(runargs, "-p"+ports[i])
}
runargs = append(runargs, host)
res = fmt.Sprint(res, runner(remoteaddr, cmd, runargs...), "\n")
}
return res
}
// ping is the response handler for the ping command. It defines the allowed options.
func ping(w http.ResponseWriter, req *http.Request) {
cmd := "ping"
cmdopts := map[string]string{"4": "-4", "6": "-6", "d": "-D", "n": "-n", "v": "-v", "c1": "-c1", "c5": "-c5", "c10": "-c10"}
var defaultopts []string
defaultopts = append(defaultopts, "-c10")
res := prerunner(req, cmd, cmdopts, defaultopts)
if strings.TrimSpace(res) == "" {
fmt.Fprintln(w, http.StatusInternalServerError)
} else {
fmt.Fprint(w, strings.TrimSpace(res), "\n")
}
cmd := "ping"
cmdopts := map[string]string{
"4": "-4", "6": "-6", "d": "-D", "n": "-n", "v": "-v", "c1": "-c1", "c5": "-c5", "c10": "-c10",
"force4": "-4", "force6": "-6", "timestamps": "-D", "nodns": "-n", "verbose": "-v", "count1": "-c1", "count5": "-c5", "count10": "-c10",
}
var defaultopts []string
defaultopts = append(defaultopts, "-c10")
res := prerunner(req, cmd, cmdopts, defaultopts)
if strings.TrimSpace(res) == "" {
http.Error(w, "500: Internal Server Error", http.StatusInternalServerError)
} else {
_, _ = fmt.Fprint(w, strings.TrimSpace(res), "\n")
}
}
// mtr is the response handler for the mtr command. It defines the allowed options.
func mtr(w http.ResponseWriter, req *http.Request) {
cmd := "mtr"
cmdopts := map[string]string{"4": "-4", "6": "-6", "u": "-u", "t": "-T", "e": "-e", "x": "-x", "n": "-n", "b": "-b", "z": "-z", "c1": "-c1", "c5": "-c5", "c10": "-c10"}
var defaultopts []string
defaultopts = append(defaultopts, "-r", "-w", "-c10")
res := prerunner(req, cmd, cmdopts, defaultopts)
if strings.TrimSpace(res) == "" {
fmt.Fprintln(w, http.StatusInternalServerError)
} else {
fmt.Fprint(w, strings.TrimSpace(res), "\n")
}
cmd := "mtr"
cmdopts := map[string]string{
"4": "-4", "6": "-6", "u": "-u", "t": "-T", "e": "-e", "x": "-x", "n": "-n", "b": "-b", "z": "-z", "c1": "-c1", "c5": "-c5", "c10": "-c10",
"force4": "-4", "force6": "-6", "udp": "-u", "tcp": "-T", "ext": "-e", "xml": "-x", "nodns": "-n", "cmb": "-b", "asn": "-z", "count1": "-c1", "count5": "-c5", "count10": "-c10",
}
var defaultopts []string
defaultopts = append(defaultopts, "-r", "-w", "-c10")
res := prerunner(req, cmd, cmdopts, defaultopts)
if strings.TrimSpace(res) == "" {
http.Error(w, "500: Internal Server Error", http.StatusInternalServerError)
} else {
_, _ = fmt.Fprint(w, strings.TrimSpace(res), "\n")
}
}
// traceroute is the response handler for the traceroute command. It defines the allowed options.
func traceroute(w http.ResponseWriter, req *http.Request) {
cmd := "traceroute"
cmdopts := map[string]string{"4": "-4", "6": "-6", "dnf": "-F", "i": "-I", "t": "-T", "n": "-n", "u": "-U", "ul": "-UL", "d": "-D", "b": "--back"}
var defaultopts []string
//defaultopts = append(defaultopts) // no default options for traceroute
res := prerunner(req, cmd, cmdopts, defaultopts)
if strings.TrimSpace(res) == "" {
fmt.Fprintln(w, http.StatusInternalServerError)
} else {
fmt.Fprint(w, strings.TrimSpace(res), "\n")
}
cmd := "traceroute"
cmdopts := map[string]string{
"4": "-4", "6": "-6", "f": "-F", "i": "-I", "t": "-T", "n": "-n", "u": "-U", "ul": "-UL", "d": "-D", "b": "--back",
"force4": "-4", "force6": "-6", "dnf": "-F", "icmp": "-I", "tcp": "-T", "nodns": "-n", "udp": "-U", "udplite": "-UL", "dccp": "-D", "back": "--back",
}
var defaultopts []string
//defaultopts = append(defaultopts) // no default options for traceroute
res := prerunner(req, cmd, cmdopts, defaultopts)
if strings.TrimSpace(res) == "" {
http.Error(w, "500: Internal Server Error", http.StatusInternalServerError)
} else {
_, _ = fmt.Fprint(w, strings.TrimSpace(res), "\n")
}
}
// nping is the response handler for the nping command. It defines the allowed options.
func nping(w http.ResponseWriter, req *http.Request) {
cmd := "nping"
cmdopts := map[string]string{
"4": "-4", "6": "-6", "u": "--udp", "t": "--tcp-connect", "v": "-v", "c1": "-c1", "c3": "-c3", "c5": "-c5",
"force4": "-4", "force6": "-6", "udp": "--udp", "tcp": "--tcp-connect", "verbose": "-v", "count1": "-c1", "count3": "-c3", "count5": "-c5",
}
var defaultopts []string
defaultopts = append(defaultopts, "-c3")
res := prerunner(req, cmd, cmdopts, defaultopts)
if strings.TrimSpace(res) == "" {
http.Error(w, "500: Internal Server Error", http.StatusInternalServerError)
} else {
_, _ = fmt.Fprint(w, strings.TrimSpace(res), "\n")
}
}
func main() {
http.HandleFunc("/ping/", ping)
http.HandleFunc("/mtr/", mtr)
http.HandleFunc("/tracert/", traceroute)
http.HandleFunc("/traceroute/", traceroute)
logstdout.Info("Serving on :", listenport)
logfile.Info("Serving on :", listenport)
http.ListenAndServe(fmt.Sprint(":", listenport), nil)
http.HandleFunc("/ping/", ping)
http.HandleFunc("/mtr/", mtr)
http.HandleFunc("/tracert/", traceroute)
http.HandleFunc("/traceroute/", traceroute)
http.HandleFunc("/nping/", nping)
logStdout.Info("Serving on :", listenPort)
logFile.Info("Serving on :", listenPort)
_ = http.ListenAndServe(fmt.Sprint(":", listenPort), nil)
}

View File

@ -8,6 +8,8 @@ StartLimitBurst=5
StartLimitIntervalSec=20
[Service]
User=1000
Group=1000
Restart=always
RestartSec=1
ExecStart=/bin/probehost2 --logfilepath "/var/log/probehost2.log" --port 8000