code upload

This commit is contained in:
Tanner Sommers 2024-01-11 15:05:04 -05:00
commit 8291b8b6a8
4 changed files with 563 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
go.sum
traceroute_dynamicIngest.txt
traceroute_region.txt
vrcdn-nettest.exe

15
go.mod Normal file
View File

@ -0,0 +1,15 @@
module sticksdev/vrcdn-nettest
go 1.21.6
require (
github.com/buger/goterm v1.0.4 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/prometheus-community/pro-bing v0.3.0 // indirect
golang.org/x/net v0.20.0 // indirect
golang.org/x/sync v0.6.0 // indirect
golang.org/x/sys v0.16.0 // indirect
)

513
main.go Normal file
View File

@ -0,0 +1,513 @@
package main
import (
"fmt"
"os"
"os/exec"
"os/signal"
"runtime"
"time"
probing "github.com/prometheus-community/pro-bing"
)
// Setup Color Palette
const (
Black = "\033[1;30m"
Red = "\033[1;31m"
Green = "\033[1;32m"
Yellow = "\033[1;33m"
Blue = "\033[1;34m"
Magenta = "\033[1;35m"
Cyan = "\033[1;36m"
White = "\033[1;37m"
Reset = "\033[0m"
)
// Regions
var regions = []string{
"Europe",
"Americas",
"Asia",
"Australia",
}
// URLs for each region. Key is the region name, value is a array of objects with the following keys:
// - url: URL to test
// - url_friendlyname: Friendly name for the URL. Used in the output.
var urls = map[string][]map[string]string{
"Europe": {
{
"url": "uk.ingest.vrcdn.live",
"url_friendlyname": "UK Ingest (England)",
},
{
"url": "de.ingest.vrcdn.live",
"url_friendlyname": "DE Ingest (Germany)",
},
},
"Americas": {
{
"url": "use.ingest.vrcdn.live",
"url_friendlyname": "USE Ingest (United States East)",
},
{
"url": "usc.ingest.vrcdn.live",
"url_friendlyname": "USC Ingest (United States Central)",
},
{
"url": "usw.ingest.vrcdn.live",
"url_friendlyname": "USW Ingest (United States West)",
},
},
"Asia": {
{
"url": "jpe.ingest.vrcdn.live",
"url_friendlyname": "JPE Ingest (Japan East)",
},
{
"url": "jpw.ingest.vrcdn.live",
"url_friendlyname": "JPW Ingest (Japan West)",
},
},
"Australia": {
{
"url": "au.ingest.vrcdn.live",
"url_friendlyname": "AUS Ingest (Sydney)",
},
},
}
// Define our structs
type dynamicIngestResult struct {
packetsTransmitted int
packetsReceived int
packetLoss float64
minPing float64
maxPing float64
avgPing float64
}
type regionResult struct {
region string
areaName string
packetsTransmitted int
packetsReceived int
packetLoss float64
minPing float64
maxPing float64
avgPing float64
}
func testRegionPing(region string) []regionResult {
// Get URLs for the region.
regionUrls := urls[region]
// Make the results array for the regions, using the regionRequest struct.
var regionResults []regionResult = make([]regionResult, len(regionUrls))
// Print region name.
fmt.Printf("%s[ping_test] Testing region %s...%s\n", Yellow, region, Reset)
// Test each URL.
for _, url := range regionUrls {
fmt.Printf("[ping_test] Testing endpoint %s in region %s with sample rate 10\n", url["url_friendlyname"], region)
pinger, err := probing.NewPinger(url["url"])
if err != nil {
panic(err)
}
// Listen for Ctrl-C.
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
for range c {
pinger.Stop()
fmt.Printf("\n%s[ping_test] Stopped.%s\n", Red, Reset)
os.Exit(0)
}
}()
pinger.Count = 10
pinger.Interval = 100 * time.Millisecond
pinger.Timeout = 1 * time.Second
pinger.OnSend = func(pkt *probing.Packet) {
// Increment the packetsTransmitted counter.
regionResults[len(regionResults)-1].packetsTransmitted++
}
pinger.OnRecv = func(pkt *probing.Packet) {
// Increment the packetsReceived counter.
regionResults[len(regionResults)-1].packetsReceived++
}
pinger.OnFinish = func(stats *probing.Statistics) {
// Add our results to the regionResults array.
regionResults = append(regionResults, regionResult{
region: region,
areaName: url["url_friendlyname"],
packetsTransmitted: regionResults[len(regionResults)-1].packetsTransmitted,
packetsReceived: regionResults[len(regionResults)-1].packetsReceived,
packetLoss: stats.PacketLoss,
minPing: stats.MinRtt.Seconds() * 1000,
maxPing: stats.MaxRtt.Seconds() * 1000,
avgPing: stats.AvgRtt.Seconds() * 1000,
})
}
// If we are in windows, we need to call pinger.SetPrivileged(true) to use ICMP.
if runtime.GOOS == "windows" {
pinger.SetPrivileged(true)
}
err = pinger.Run()
if err != nil {
panic(err)
}
}
return regionResults
}
func testDynamicIngestPing() dynamicIngestResult {
// Make a new pinger for ingest.vrcdn.live.
pinger, err := probing.NewPinger("ingest.vrcdn.live")
if err != nil {
panic(err)
}
// Listen for Ctrl-C.
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
for range c {
pinger.Stop()
fmt.Printf("\n%s[ping_test:dynamicIngestTest] Stopped.%s\n", Red, Reset)
os.Exit(0)
}
}()
pinger.Count = 10
pinger.Interval = 100 * time.Millisecond
pinger.Timeout = 1 * time.Second
// Create a empty dynamicIngestResult struct.
var dynamicIngestResultData dynamicIngestResult
pinger.OnSend = func(pkt *probing.Packet) {
// Increment the packetsTransmitted counter.
dynamicIngestResultData.packetsTransmitted++
}
pinger.OnRecv = func(pkt *probing.Packet) {
// Increment the packetsReceived counter.
dynamicIngestResultData.packetsReceived++
}
pinger.OnFinish = func(stats *probing.Statistics) {
// Print results.
fmt.Printf("%s[ping_test:dynamicIngestTest] %d/%d packets received (%.2f%% loss), min/avg/max = %.2f/%.2f/%.2f ms.%s\n", Cyan, stats.PacketsRecv, stats.PacketsSent, stats.PacketLoss, stats.MinRtt.Seconds()*1000, stats.AvgRtt.Seconds()*1000, stats.MaxRtt.Seconds()*1000, Reset)
// Add our results to the regionResults array.
dynamicIngestResultData = dynamicIngestResult{
packetsTransmitted: dynamicIngestResultData.packetsTransmitted,
packetsReceived: dynamicIngestResultData.packetsReceived,
packetLoss: stats.PacketLoss,
minPing: stats.MinRtt.Seconds() * 1000,
maxPing: stats.MaxRtt.Seconds() * 1000,
avgPing: stats.AvgRtt.Seconds() * 1000,
}
}
// If we are in windows, we need to call pinger.SetPrivileged(true) to use ICMP.
if runtime.GOOS == "windows" {
pinger.SetPrivileged(true)
}
err = pinger.Run()
if err != nil {
panic(err)
}
return dynamicIngestResultData
}
func Traceroute(mode string, region string) []string {
// Figure out the binary we need to use based on the OS.
var binary string
if runtime.GOOS == "windows" {
binary = "tracert"
} else {
binary = "traceroute"
}
// Ensure that the binary exists.
if _, err := exec.LookPath(binary); err != nil {
fmt.Printf("%s[!] Test Failed: %s %s is not installed. Please install it and try again.\n", Red, Reset, binary)
os.Exit(1)
}
switch mode {
case "region":
// Get URLs for the region.
regionUrls := urls[region]
// Make the results array for the regions, using the regionRequest struct.
var regionResults []string = make([]string, len(regionUrls))
// Print region name.
fmt.Printf("%s[traceroute_test] Testing region %s...%s\n", Yellow, region, Reset)
// Test each URL.
for _, url := range regionUrls {
fmt.Printf("[traceroute_test] Testing endpoint %s in region %s\n", url["url_friendlyname"], region)
// Run the traceroute command.
out, err := exec.Command(binary, url["url"]).Output()
if err != nil {
fmt.Printf("%s[!] Test Failed: %s %s failed to run. Please install it and try again.\n", Red, Reset, binary)
os.Exit(1)
}
// Add our results to the regionResults array.
regionResults = append(regionResults, string(out))
}
return regionResults
case "dynamicIngest":
// Run the traceroute command.
out, err := exec.Command(binary, "ingest.vrcdn.live").Output()
if err != nil {
fmt.Printf("%s[!] Test Failed: %s %s failed to run. Please install it and try again.\n", Red, Reset, binary)
os.Exit(1)
}
return []string{string(out)}
}
return []string{}
}
func printResults(regionResults []regionResult, dynamicIngestResultData dynamicIngestResult) {
// Find the best area in our selected region.
var bestArea regionResult = regionResult{
avgPing: 9999999999,
}
for _, regionResult := range regionResults {
if regionResult.areaName == "" {
continue
}
// Check if the regionResult is better than the best area.
if regionResult.avgPing < bestArea.avgPing {
bestArea = regionResult
} else if regionResult.avgPing == bestArea.avgPing {
// If the ping is the same, check if the packet loss is better.
if regionResult.packetLoss < bestArea.packetLoss {
bestArea = regionResult
}
} else {
// If the ping is worse, skip it.
continue
}
}
// Check if dynamic is better than the best area.
if dynamicIngestResultData.avgPing < bestArea.avgPing {
bestArea = regionResult{ // Reconstruct the bestArea struct with the dynamicIngestResultData.
region: "dynamicIngest",
areaName: "Dynamic Ingest",
packetsTransmitted: dynamicIngestResultData.packetsTransmitted,
packetsReceived: dynamicIngestResultData.packetsReceived,
packetLoss: dynamicIngestResultData.packetLoss,
minPing: dynamicIngestResultData.minPing,
maxPing: dynamicIngestResultData.maxPing,
avgPing: dynamicIngestResultData.avgPing,
}
}
// Print the results.
// ping less then 100ms = green
// ping between 100ms and 200ms = yellow
// ping more then 200ms = red
bestAreaPingColor := Green
if bestArea.avgPing > 100 && bestArea.avgPing < 200 {
bestAreaPingColor = Yellow
} else if bestArea.avgPing > 200 {
bestAreaPingColor = Red
}
fmt.Printf("Your best area in your region is %s%s%s with a average ping of %.2fms.%s\n", Green, bestArea.areaName, bestAreaPingColor, bestArea.avgPing, Reset)
// Print the results for each area in our region.
for _, regionResult := range regionResults {
// ping less then 100ms = green
// ping between 100ms and 200ms = yellow
// ping more then 200ms = red
regionResultPingColor := Green
if regionResult.avgPing > 100 && regionResult.avgPing < 200 {
regionResultPingColor = Yellow
} else if regionResult.avgPing > 200 {
regionResultPingColor = Red
}
// If no area name, continue.
if regionResult.areaName == "" {
continue
}
fmt.Printf("%s%s%s: %s%d/%d%s packets received (%s%.2f%%%s loss), min/avg/max = %s%.2f%s/%s%.2f%s/%s%.2f%s ms.%s\n", Cyan, regionResult.areaName, Reset, Cyan, regionResult.packetsReceived, regionResult.packetsTransmitted, Reset, Cyan, regionResult.packetLoss, Reset, Cyan, regionResult.minPing, Reset, regionResultPingColor, regionResult.avgPing, Reset, Cyan, regionResult.maxPing, Reset, Reset)
}
// Print the results for dynamic ingest.
// ping less then 100ms = green
// ping between 100ms and 200ms = yellow
// ping more then 200ms = red
dynamicIngestResultPingColor := Green
if dynamicIngestResultData.avgPing > 100 && dynamicIngestResultData.avgPing < 200 {
dynamicIngestResultPingColor = Yellow
} else if dynamicIngestResultData.avgPing > 200 {
dynamicIngestResultPingColor = Red
}
fmt.Printf("%sDynamic Ingest%s: %s%d/%d%s packets received (%s%.2f%%%s loss), min/avg/max = %s%.2f%s/%s%.2f%s/%s%.2f%s ms.%s\n", Cyan, Reset, Cyan, dynamicIngestResultData.packetsReceived, dynamicIngestResultData.packetsTransmitted, Reset, Cyan, dynamicIngestResultData.packetLoss, Reset, Cyan, dynamicIngestResultData.minPing, Reset, dynamicIngestResultPingColor, dynamicIngestResultData.avgPing, Reset, Cyan, dynamicIngestResultData.maxPing, Reset, Reset)
// Print the traceroute results.
fmt.Printf("\nTraceroute results have been saved to traceroute_region.txt and traceroute_dynamicIngest.txt\n")
// Print the disclaimer.
fmt.Printf("\nDisclaimer: This tool should not be used as a 100%% accurate way to determine your best region or a possible network issue. Please work with official VRCDN support to determine the cause of any issues you may be having.\n\n")
// Print warning about traceroute.
fmt.Printf("%s%s%s\n", Yellow, "WARNING: Traceroute results contain SENSITIVE data. Please share with care!", Reset)
}
func main() {
fmt.Printf("VRCDN Network Test v1.0, built with %s\n", runtime.Version())
fmt.Print("Created by sticksdev. This tool is not affiliated with VRCDN and is community-made. See LICENSE for more information.\n\n")
fmt.Print("This tool will test your connection to the VRCDN network. Please select your closest region.\n")
// Print regions.
for i, region := range regions {
fmt.Printf("%d. %s\n", i+1, region)
}
// Get user input.
var region int
var errorInput error
// Keep asking for input until a valid region is selected.
for {
fmt.Print("Please enter the number of your region: ")
fmt.Scan(&region, &errorInput)
// If the input is valid, break out of the loop.
if region > 0 && region <= len(regions) {
break
} else {
fmt.Print("\nInvalid region selected. Please try again.")
// Sleep for .5 seconds
time.Sleep(500 * time.Millisecond)
}
if errorInput != nil {
fmt.Print(Red + "[!]" + Reset + " a error occurred while reading your input. Please try again.\n")
fmt.Print(errorInput.Error() + "\n")
// Sleep for .5 seconds
time.Sleep(500 * time.Millisecond)
}
}
// Test the region.
var regionResults []regionResult = testRegionPing(regions[region-1])
if len(regionResults) == 0 {
fmt.Printf("%s[!]%s No results were returned from the previous test. Please try again.\n", Red, Reset)
os.Exit(1)
}
fmt.Printf("[ping_test] Running dynamic ingest test... (ingest.vrcdn.live)\n")
var dynamicIngestResultData dynamicIngestResult = testDynamicIngestPing()
if dynamicIngestResultData == (dynamicIngestResult{}) {
fmt.Printf("%s[!]%s No results were returned from the previous test. Please try again.\n", Red, Reset)
os.Exit(1)
}
var traceRegionResults []string = Traceroute("region", regions[region-1])
if len(traceRegionResults) == 0 {
fmt.Printf("%s[!]%s No results were returned from the previous test. Please try again.\n", Red, Reset)
os.Exit(1)
}
fmt.Printf("[traceroute_test] Running dynamic ingest test... (ingest.vrcdn.live)\n")
var traceDynamicIngestResults []string = Traceroute("dynamicIngest", "")
if len(traceDynamicIngestResults) == 0 {
fmt.Printf("%s[!]%s No results were returned from the previous test. Please try again.\n", Red, Reset)
os.Exit(1)
}
fmt.Printf("%s[...]%s Saving traceroute results to traceroute_region.txt and traceroute_dynamicIngest.txt\n", Yellow, Reset)
// Save the traceroute results to a file.
var traceRegionFile, traceDynamicIngestFile *os.File
// If we have old results, delete them.
if _, err := os.Stat("traceroute_region.txt"); err == nil {
os.Remove("traceroute_region.txt")
}
if _, err := os.Stat("traceroute_dynamicIngest.txt"); err == nil {
os.Remove("traceroute_dynamicIngest.txt")
}
// Define error.
var err error
traceRegionFile, err = os.OpenFile("traceroute_region.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Printf("%s[!]%s Failed to open traceroute_region.txt. Please try again.\n", Red, Reset)
os.Exit(1)
}
traceDynamicIngestFile, err = os.OpenFile("traceroute_dynamicIngest.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
fmt.Printf("%s[!]%s Failed to open traceroute_dynamicIngest.txt. Please try again.\n", Red, Reset)
os.Exit(1)
}
// Write the results to the files.
for _, traceRegionResult := range traceRegionResults {
traceRegionFile.WriteString(traceRegionResult)
}
for _, traceDynamicIngestResult := range traceDynamicIngestResults {
traceDynamicIngestFile.WriteString(traceDynamicIngestResult)
}
// Close the files.
traceRegionFile.Close()
traceDynamicIngestFile.Close()
fmt.Printf("%s[i]%s Save complete. Showing results!\n", Green, Reset)
printResults(regionResults, dynamicIngestResultData)
// Sleep for 5 seconds.
time.Sleep(5 * time.Second)
// Exit.
os.Exit(0)
}

31
readme.md Normal file
View File

@ -0,0 +1,31 @@
# VRCDN Network Test
This is a simple tool to help you test your connection to the VRCDNs network. This can be useful to determine if you are having issues connecting to the VRCDN network.
## How does it work?
This tool will run two tests to test your connectivity to the VRCDN network. The first is a ping test, which will ping
each of the VRCDN ingest nodes for your area and report the results. The second is traceroute, which will show you the
path your connection takes to reach the VRCDN network.
Trace route is a useful tool to determine if you are having issues connecting to the VRCDN network. If you see a lot of
packet loss or high latency in the trace route, this could indicate an issue with your connection to the VRCDN network.
## How do I use it?
You can download the latest release from the [releases page]() - or you can build it yourself.
Once you have the binary, you can simply double click it to run it. Results will be shown for 5 seconds, and then the
program will exit. We recommend opening a command prompt and running the program from there so you can see the results
after the program exits, or use one of our pre-built scripts to run the program for you.
A sample output from the program is shown below:
![Sample output](https://img.sticks.ovh/9Loapvy8u.png)
## Compiling from source
This project uses [Go](https://golang.org/) to compile. You can download Go from [here](https://golang.org/dl/). Once
you have Go installed, you can compile the project by running `go build` in the root directory of the project. This will
create a binary called `vrcdn-nettest` in the root directory of the project that is ready to run. Alternatively, you can
run `go run .` to run the program without compiling it first.