mirror of
https://github.com/owncloud/ocis
synced 2026-04-25 17:25:21 +02:00
fix: Add IPv6 handling to handlers.FailSaveAddress()
On IPv6-only deployment I encountered two problem with collaboration service and CheckHandler: * getOutBoundIP() did not allow IPv6 addresses to be used as outbound * FailSaveAddress() did double interpolation of the "::" literal on GUA IPv6 addresses containing "::"
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -11,21 +12,26 @@ import (
|
||||
)
|
||||
|
||||
// NewHTTPCheck checks the reachability of a http server.
|
||||
func NewHTTPCheck(url string) func(context.Context) error {
|
||||
func NewHTTPCheck(rawUrl string) func(context.Context) error {
|
||||
return func(_ context.Context) error {
|
||||
url, err := handlers.FailSaveAddress(url)
|
||||
if !strings.HasPrefix(rawUrl, "http://") && !strings.HasPrefix(rawUrl, "https://") {
|
||||
rawUrl = "http://" + rawUrl
|
||||
}
|
||||
|
||||
parsedUrl, err := url.Parse(rawUrl)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
|
||||
url = "http://" + url
|
||||
parsedUrl.Host, err = handlers.FailSaveAddress(parsedUrl.Host)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c := http.Client{
|
||||
Timeout: 3 * time.Second,
|
||||
}
|
||||
resp, err := c.Get(url)
|
||||
resp, err := c.Get(parsedUrl.String())
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not connect to http server: %v", err)
|
||||
}
|
||||
|
||||
@@ -115,34 +115,75 @@ func (h *CheckHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// FailSaveAddress replaces wildcard addresses with the outbound IP.
|
||||
// FailSaveAddress replaces unspecified addresses with the outbound IP.
|
||||
func FailSaveAddress(address string) (string, error) {
|
||||
if strings.Contains(address, "0.0.0.0") || strings.Contains(address, "::") {
|
||||
outboundIp, err := getOutBoundIP()
|
||||
host, port := SplitHostPort(address)
|
||||
|
||||
hostIP := net.ParseIP(host)
|
||||
|
||||
if host == "" || (hostIP != nil && hostIP.IsUnspecified()) {
|
||||
outboundIP, err := getOutBoundIP()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
address = strings.Replace(address, "0.0.0.0", outboundIp, 1)
|
||||
address = strings.Replace(address, "::", "["+outboundIp+"]", 1)
|
||||
address = strings.Replace(address, "[::]", "["+outboundIp+"]", 1)
|
||||
|
||||
host = outboundIP.String()
|
||||
}
|
||||
return address, nil
|
||||
|
||||
if port != "" {
|
||||
if strings.Contains(host, ":") {
|
||||
host = "[" + host + "]"
|
||||
}
|
||||
return host + ":" + port, nil
|
||||
}
|
||||
|
||||
return host, nil
|
||||
}
|
||||
|
||||
// SplitHostPort returns host and port of the address.
|
||||
// Contrary to the net.SplitHostPort the port is not mandatory.
|
||||
func SplitHostPort(address string) (string, string) {
|
||||
columns := strings.Split(address, ":")
|
||||
brackets := strings.Split(address, "]")
|
||||
|
||||
switch {
|
||||
case len(columns) == 1 && len(brackets) == 1: // 10.10.10.10
|
||||
return address, ""
|
||||
case len(columns) == 2 && len(brackets) == 1: // 10.10.10.10:80
|
||||
return columns[0], columns[1]
|
||||
case len(columns) > 2 && len(brackets) == 1: // 2a01::a
|
||||
return address, ""
|
||||
case len(brackets) == 2 && brackets[1] == "": // [2a01::a]
|
||||
return brackets[0][1:], ""
|
||||
case len(brackets) == 2: // [2a01::a]:10
|
||||
return brackets[0][1:], columns[len(columns)-1]
|
||||
}
|
||||
|
||||
return address, ""
|
||||
}
|
||||
|
||||
// getOutBoundIP returns the outbound IP address.
|
||||
func getOutBoundIP() (string, error) {
|
||||
func getOutBoundIP() (net.IP, error) {
|
||||
interfacesAddresses, err := net.InterfaceAddrs()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var fallbackIpv6 net.IP
|
||||
for _, address := range interfacesAddresses {
|
||||
if ipNet, ok := address.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
|
||||
if ipNet.IP.To4() != nil {
|
||||
return ipNet.IP.String(), nil
|
||||
return ipNet.IP, nil
|
||||
}
|
||||
if ipNet.IP.To16() != nil && !ipNet.IP.IsLinkLocalUnicast() {
|
||||
fallbackIpv6 = ipNet.IP.To16()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("no IP found")
|
||||
if fallbackIpv6 != nil {
|
||||
return fallbackIpv6, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("no IP found")
|
||||
}
|
||||
|
||||
@@ -125,3 +125,21 @@ func TestCheckHandler(t *testing.T) {
|
||||
require.Equal(t, 2, len(slices.DeleteFunc(errs, func(err error) bool { return err != nil })))
|
||||
})
|
||||
}
|
||||
|
||||
func TestSplitHostPort(t *testing.T) {
|
||||
address, port := handlers.SplitHostPort("10.10.10.10")
|
||||
require.Equal(t, address, "10.10.10.10")
|
||||
require.Equal(t, port, "")
|
||||
|
||||
address, port = handlers.SplitHostPort("10.10.10.10:20")
|
||||
require.Equal(t, address, "10.10.10.10")
|
||||
require.Equal(t, port, "20")
|
||||
|
||||
address, port = handlers.SplitHostPort("2a01::1")
|
||||
require.Equal(t, address, "2a01::1")
|
||||
require.Equal(t, port, "")
|
||||
|
||||
address, port = handlers.SplitHostPort("[2a01::1]:10")
|
||||
require.Equal(t, address, "2a01::1")
|
||||
require.Equal(t, port, "10")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user