Skip to content

Added changes for IPv6 structure #1606

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 30 additions & 76 deletions assets/golang/site.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,117 +4,71 @@ import (
"fmt"
"io"
"log"
"net"
"net/http"
"os"
"time"
)

type EndpointType struct {
validationName string
path string
}

var endpointTypeMap = map[string]EndpointType{
"api.ipify.org": {
validationName: "IPv4",
path: "/ipv4-test",
},
"api6.ipify.org": {
validationName: "IPv6",
path: "/ipv6-test",
},
"api64.ipify.org": {
validationName: "Dual stack",
path: "/dual-stack-test",
},
var endpointMap = map[string]string{
"/ipv4-test": "https://api.ipify.org",
"/ipv6-test": "https://api6.ipify.org",
"/dual-stack-test": "https://api64.ipify.org",
}

func main() {
http.HandleFunc("/", handleRequest)
http.HandleFunc("/", hello)
http.HandleFunc("/requesturi/", echo)

log.Printf("Starting server on %s\n", os.Getenv("PORT"))

server := &http.Server{
Addr: fmt.Sprintf(":%s", os.Getenv("PORT")),
for path, apiURL := range endpointMap {
http.HandleFunc(path, createIPHandler(apiURL))
}

if err := server.ListenAndServe(); err != nil {
log.Fatalf("Could not start server: %s\n", err)
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
}

func handleRequest(res http.ResponseWriter, req *http.Request) {
for endpoint, data := range endpointTypeMap {
if req.URL.Path == data.path {
testEndpoint(res, endpoint, data.validationName)
return
}
fmt.Printf("Listening on port %s...\n", port)
err := http.ListenAndServe(":"+port, nil)
if err != nil {
log.Fatalf("Could not start server: %v\n", err)
}

hello(res, req)
}

func hello(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, "Hello go, world")
}

func echo(res http.ResponseWriter, req *http.Request) {
fmt.Fprintln(res, fmt.Sprintf("Request URI is [%s]\nQuery String is [%s]", req.RequestURI, req.URL.RawQuery))
fmt.Fprintf(res, "Request URI is [%s]\nQuery String is [%s]\n", req.RequestURI, req.URL.RawQuery)
}

func testEndpoint(res http.ResponseWriter, endpoint, validationName string) {
client := &http.Client{
Timeout: 5 * time.Second,
func createIPHandler(apiURL string) http.HandlerFunc {
return func(res http.ResponseWriter, req *http.Request) {
fetchAndWriteIP(res, apiURL)
}
}

func fetchAndWriteIP(res http.ResponseWriter, apiURL string) {
client := &http.Client{Timeout: 5 * time.Second}

resp, err := client.Get(fmt.Sprintf("http://%s", endpoint))
resp, err := client.Get(apiURL)
if err != nil {
log.Printf("Failed to reach %s: %v\n", endpoint, err)
writeTestResponse(res, validationName, false, "Unknown", err.Error())
log.Printf("Error fetching from %s: %v\n", apiURL, err)
res.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(res, "Error: %v", err)
return
}
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
log.Printf("Error reading response: %v\n", err)
writeTestResponse(res, validationName, false, "Unknown", err.Error())
res.WriteHeader(http.StatusInternalServerError)
fmt.Fprintf(res, "Error: %v", err)
return
}

ipType := determineIPType(string(body))
success := resp.StatusCode == http.StatusOK

writeTestResponse(res, validationName, success, ipType, "")
}

func writeTestResponse(res http.ResponseWriter, validationName string, success bool, ipType, errorMsg string) {
responseCode := http.StatusInternalServerError
if success {
responseCode = http.StatusOK
}
res.WriteHeader(responseCode)

if errorMsg == "" {
errorMsg = "none"
}

message := fmt.Sprintf("%s validation resulted in %s. Detected IP type is %s. Error message: %s.\n",
validationName, map[bool]string{true: "success", false: "failure"}[success], ipType, errorMsg)
res.Write([]byte(message))
}

func determineIPType(ipString string) string {
ip := net.ParseIP(ipString)
if ip == nil {
return "Invalid IP"
}

if ip.To4() != nil {
return "IPv4"
}

return "IPv6"
res.WriteHeader(resp.StatusCode)
fmt.Fprintf(res, "%s", string(body))
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.net.InetAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
Expand All @@ -31,9 +30,9 @@ class IPv6TesterController {

static {
ENDPOINT_TYPE_MAP = new HashMap<>();
ENDPOINT_TYPE_MAP.put("api.ipify.org", new EndpointInfo("IPv4", "/ipv4-test"));
ENDPOINT_TYPE_MAP.put("api6.ipify.org", new EndpointInfo("IPv6", "/ipv6-test"));
ENDPOINT_TYPE_MAP.put("api64.ipify.org", new EndpointInfo("Dual stack", "/dual-stack-test"));
ENDPOINT_TYPE_MAP.put("api.ipify.org", new EndpointInfo("/ipv4-test"));
ENDPOINT_TYPE_MAP.put("api6.ipify.org", new EndpointInfo("/ipv6-test"));
ENDPOINT_TYPE_MAP.put("api64.ipify.org", new EndpointInfo("/dual-stack-test"));
}

@GetMapping("/")
Expand Down Expand Up @@ -64,40 +63,21 @@ private String testEndpoint(String endpoint) {
URI uri = new URI("http://" + endpoint + "/");

String response = restTemplate.getForObject(uri, String.class);
if (response == null || response.isEmpty()) {
throw new RuntimeException("Empty response from " + endpoint);
}

String ipType = determineIpType(response);
boolean success = response != null && !response.isEmpty();
String resultMessage = success ? "success" : "failure";

logger.info(endpointInfo.validationName + " validation " + resultMessage);
return response;

return endpointInfo.validationName + " validation resulted in " + resultMessage +
". Detected IP type is " + ipType + ".";
} catch (URISyntaxException e) {
logger.severe("URI syntax issue with " + endpoint + ": " + e.getMessage());
return endpointInfo.validationName +
" validation resulted in failure due to URI issue. Error message: " + e.getMessage() + ".";
return e.getMessage();
} catch (Exception e) {
logger.severe("Failed to reach " + endpoint + ": " + e.getMessage());
return endpointInfo.validationName +
" validation resulted in failure. Error message: " + e.getMessage() + ".";
return e.getMessage();
}
}

private String determineIpType(String ipString) {
try {
InetAddress inetAddress = InetAddress.getByName(ipString);
if (inetAddress instanceof java.net.Inet4Address) {
return "IPv4";
} else if (inetAddress instanceof java.net.Inet6Address) {
return "IPv6";
}
} catch (Exception e) {
logger.severe("Invalid IP format or unexpected error for: " + ipString + ". Error: " + e.getMessage());
}
return "Invalid IP";
}

private static record EndpointInfo(String validationName, String path) {
private static record EndpointInfo(String path) {
}
}
}
Binary file modified assets/java-spring/target/spring-boot-trivial-app-0.0.1.jar
Binary file not shown.
9 changes: 4 additions & 5 deletions assets/nginx-ipv6/nginx.conf
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,21 @@ http {
}

location /ipv4-test {
proxy_pass https://api4.ipify.org/?format=json;
proxy_pass https://api4.ipify.org/;
proxy_set_header Host api4.ipify.org;
proxy_ssl_server_name on;
}

location /ipv6-test {
proxy_pass https://api6.ipify.org/?format=json;
proxy_pass https://api6.ipify.org/;
proxy_set_header Host api6.ipify.org;
proxy_ssl_server_name on;
}

location /dual-stack-test {
proxy_pass https://api64.ipify.org/?format=json;
proxy_pass https://api64.ipify.org/;
proxy_set_header Host api64.ipify.org;
proxy_ssl_server_name on;
}
}
}

}
94 changes: 18 additions & 76 deletions assets/node/server.js
Original file line number Diff line number Diff line change
@@ -1,95 +1,37 @@
var http = require('http');
var https = require('https');
var url = require('url');
var ip = require('ip');

HOST = null;

var host = "0.0.0.0";
var port = process.env.PORT || 3000;

const ENDPOINT_TYPE_MAP = {
'api.ipify.org': {
validation_name: "IPv4",
path: "/ipv4-test"
},
'api6.ipify.org': {
validation_name: "IPv6",
path: "/ipv6-test"
},
'api64.ipify.org': {
validation_name: "Dual stack",
path: "/dual-stack-test"
}
};
http.createServer(async function (req, res) {
const path = req.url;
const endpoints = {
'/ipv4-test': 'api.ipify.org',
'/ipv6-test': 'api6.ipify.org',
'/dual-stack-test': 'api64.ipify.org'
};

function testIPAddress(endpoint, expectedType) {
return new Promise((resolve) => {
const endpoint = endpoints[path];

if (endpoint) {
https.get(`https://${endpoint}`, (resp) => {
let data = '';

resp.on('data', (chunk) => { data += chunk; });
resp.on('data', (chunk) => data += chunk);
resp.on('end', () => {
let success = false;
let detectedType = 'unknown';

if (expectedType === "IPv4" && ip.isV4Format(data)) {
success = true;
detectedType = "IPv4";
} else if (expectedType === "IPv6" && ip.isV6Format(data)) {
success = true;
detectedType = "IPv6";
} else if (expectedType === "Dual stack") {
if (ip.isV4Format(data)) {
success = true;
detectedType = "IPv4";
} else if (ip.isV6Format(data)) {
success = true;
detectedType = "IPv6";
}
}

resolve({
endpoint,
success,
ip_type: detectedType,
error: success ? 'none' : `Expected ${expectedType}, but got ${data}`
});
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end(data.trim());
});

}).on("error", (err) => {
resolve({ endpoint, success: false, ip_type: 'unknown', error: err.message });
console.error(`Error fetching from ${endpoint}: ${err.message}`);
res.writeHead(500, { 'Content-Type': 'text/plain' });
res.end(err.message);
});
});
}

http.createServer(async function (req, res) {
const parsedUrl = url.parse(req.url, true);
const path = parsedUrl.pathname;

let endpoint = null;
for (const [ep, { path: epPath }] of Object.entries(ENDPOINT_TYPE_MAP)) {
if (path === epPath) {
endpoint = ep;
break;
}
}

if (endpoint) {
const expectedType = ENDPOINT_TYPE_MAP[endpoint].validation_name;
const result = await testIPAddress(endpoint, expectedType);

const responseCode = result.success ? 200 : 500;
res.writeHead(responseCode, { 'Content-Type': 'text/plain' });

const responseMessage = `${expectedType} validation resulted in ${result.success ? 'success' : 'failure'}. Detected IP type is ${result.ip_type}. Error message: ${result.error}.`;

res.end(responseMessage);

} else {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('<h1>Hello from a node app! ');
res.write('via: ' + host + ':' + port);
res.end('</h1>');
res.end('Hello from a node app! ');
}

}).listen(port, host, () => {
Expand Down
2 changes: 2 additions & 0 deletions assets/php/htdocs/.htaccess
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
RewriteEngine On
RewriteRule ^(ipv4|ipv6|dual-stack)-test$ ip-test.php?target=$1 [L]
Loading
Loading