Skip to content
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
35 changes: 23 additions & 12 deletions cgi.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,7 @@ var cStringHTTPMethods = map[string]*C.char{
func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) {
request := fc.request
// Separate remote IP and port; more lenient than net.SplitHostPort
var ip, port string
if idx := strings.LastIndex(request.RemoteAddr, ":"); idx > -1 {
ip = request.RemoteAddr[:idx]
port = request.RemoteAddr[idx+1:]
} else {
ip = request.RemoteAddr
}

// Remove [] from IPv6 addresses
if len(ip) > 0 && ip[0] == '[' {
ip = ip[1 : len(ip)-1]
}
ip, port := splitRemoteAddr(request.RemoteAddr)

var rs, https, sslProtocol *C.zend_string
var sslCipher string
Expand Down Expand Up @@ -354,6 +343,28 @@ func sanitizedPathJoin(root, reqPath string) string {
return path
}

// splitRemoteAddr splits "host:port" leniently: a missing port is accepted.
// A malformed value such as "[" must not panic, as that would unwind out of
// the go_register_server_variables cgo callback and crash the whole process.
func splitRemoteAddr(remoteAddr string) (ip, port string) {
if host, p, err := net.SplitHostPort(remoteAddr); err == nil {
return host, p
}

if idx := strings.LastIndex(remoteAddr, ":"); idx > -1 {
ip = remoteAddr[:idx]
port = remoteAddr[idx+1:]
} else {
ip = remoteAddr
}

if len(ip) >= 2 && ip[0] == '[' && ip[len(ip)-1] == ']' {
ip = ip[1 : len(ip)-1]
}

return ip, port
}

const separator = string(filepath.Separator)

func ensureLeadingSlash(path string) string {
Expand Down
31 changes: 31 additions & 0 deletions cgi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,37 @@ func TestEnsureLeadingSlash(t *testing.T) {
}
}

func TestSplitRemoteAddr(t *testing.T) {
t.Parallel()

tests := []struct {
name string
addr string
wantIP string
wantPort string
}{
{"ipv4 with port", "1.2.3.4:5", "1.2.3.4", "5"},
{"ipv6 bracketed with port", "[::1]:443", "::1", "443"},
{"ipv6 zone bracketed with port", "[fe80::1%eth0]:443", "fe80::1%eth0", "443"},
{"ipv4 without port", "192.168.0.1", "192.168.0.1", ""},
{"empty", "", "", ""},
// Must not panic: would crash the process via the cgo callback.
{"lone open bracket", "[", "[", ""},
{"open bracket with port", "[:9000", "[", "9000"},
{"empty brackets", "[]", "", ""},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

ip, port := splitRemoteAddr(tt.addr)
assert.Equal(t, tt.wantIP, ip, "splitRemoteAddr(%q) ip", tt.addr)
assert.Equal(t, tt.wantPort, port, "splitRemoteAddr(%q) port", tt.addr)
})
}
}

func TestSplitPos(t *testing.T) {
tests := []struct {
name string
Expand Down
Loading