Skip to content

Commit

Permalink
Add ReadReqMethodHostPath,
Browse files Browse the repository at this point in the history
optimize read request,
add error "First byte does not looks like TLS handshake or HTTP request",
modify ScriptRedirect
  • Loading branch information
bddjr committed Jun 21, 2024
1 parent 8d1fd94 commit bf1690e
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 30 deletions.
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

This mod implements a feature by hijacking `net.Conn` :
If a user accesses an https port using http, the server returns 302 redirection.
Adapted from `net/http` .
Adapted from `net/http` and `crypto/tls` .

Related Issue:
[net/http: configurable error message for Client sent an HTTP request to an HTTPS server. #49310](https://github.com/golang/go/issues/49310)
Expand Down Expand Up @@ -73,18 +73,20 @@ go build
HTTPS Server Start -> Hijacking net.Listener.Accept

### Client HTTPS
Accept hijacking net.Conn.Read -> Not looks like HTTP -> ✅Continue...
Accept hijacking net.Conn.Read -> First byte 0x16 looks like TLS handshake -> ✅Continue...

### Client HTTP/1.1
Accept hijacking net.Conn.Read -> Looks like HTTP -> HttpOnHttpsPortErrorHandler
Accept hijacking net.Conn.Read -> First byte looks like HTTP -> HttpOnHttpsPortErrorHandler

If handler nil -> Read Host header and path -> 🔄302 Redirect.

### Client HTTP/???
Accept hijacking net.Conn.Read -> Looks like HTTP -> HttpOnHttpsPortErrorHandler
Accept hijacking net.Conn.Read -> First byte looks like HTTP -> HttpOnHttpsPortErrorHandler

If handler nil -> Missing Host header -> ❌400 ScriptRedirect.

### Client ???
Accept hijacking net.Conn.Read -> First byte does not looks like TLS handshake or HTTP request -> Close.

***
## Option Example
Expand Down Expand Up @@ -184,6 +186,12 @@ var rb []byte
host, path, ok := hlfhr.ReadReqHostPath(rb)
```

#### ReadReqMethodHostPath
```go
var rb []byte
method, host, path, ok := hlfhr.ReadReqMethodHostPath(rb)
```

#### ReadReq
```go
var rb []byte
Expand Down
39 changes: 18 additions & 21 deletions conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,11 @@ import (
"errors"
"fmt"
"net"
"regexp"
)

// hlfhr: Client sent an HTTP request to an HTTPS server
var ErrHttpOnHttpsPort = errors.New("hlfhr: Client sent an HTTP request to an HTTPS server")

var compiledRegexp_tlsRecordHeaderLooksLikeHTTP = regexp.MustCompile(`^(GET /|HEAD |POST |PUT /|OPTIO|DELET|CONNE|TRACE|PATCH)`)

type HttpOnHttpsPortErrorHandler func(rb []byte, conn net.Conn)

type conn struct {
Expand Down Expand Up @@ -51,43 +48,43 @@ func (c *conn) Read(b []byte) (n int, err error) {
c.hlfhr_isNotFirstRead = true

// Default 576 Bytes
if len(b) <= 5 {
if len(b) <= 1 {
// Never run this
return c.Conn.Read(b)
}

// Read 5 Bytes Header
rb5n := 0
rb5 := b[:5]
for rb5n < 5 {
n, err := c.Conn.Read(rb5[rb5n:])
if err != nil {
return 0, err
}
rb5n += n
// Read 1 Byte Header
_, err = c.Conn.Read(b[:1])
if err != nil {
return
}

if !compiledRegexp_tlsRecordHeaderLooksLikeHTTP.Match(rb5) {
// HTTPS
n, err = c.Conn.Read(b[rb5n:])
switch b[0] {
case 0x16:
// Looks like TLS handshake.
n, err = c.Conn.Read(b[1:])
if err == nil {
n += rb5n
n += 1
}
return
case 'G', 'H', 'P', 'O', 'D', 'C', 'T':
// Looks like HTTP.
default:
return 0, fmt.Errorf("hlfhr: First byte %#02x does not looks like TLS handshake or HTTP request", b[0])
}

// HTTP Read 4096 Bytes Cache for redirect
if c.hlfhr_readFirstRequestBytesLen > len(b) {
b = append(b, make([]byte, c.hlfhr_readFirstRequestBytesLen-len(b))...)
nb := make([]byte, c.hlfhr_readFirstRequestBytesLen)
nb[0] = b[0]
b = nb
}
bn, err := c.Conn.Read(b[rb5n:])
bn, err := c.Conn.Read(b[1:])
if err != nil {
return
}
b = b[:bn]

// Write and Close
defer c.Close()
err = ErrHttpOnHttpsPort

// handler
Expand Down
12 changes: 9 additions & 3 deletions readreq.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,25 @@ import (

// "GET /index.html HTTP/1.1\r\nHost: localhost:5678\r\nUser-Agent: curl/8.7.1\r\nAccept: */*\r\n\r\n"
// ["GET /index.html HTTP/1.1\r\nHost: localhost:5678\r" "/index.html" "localhost:5678"]
var compiledRegexp_ReadReq = regexp.MustCompile(`^[A-Z]{3,7} (/\S*) HTTP/1\.[01]\r\nHost: (\S+)\r`)
var compiledRegexp_ReadReq = regexp.MustCompile(`^(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH) (/\S*) HTTP/1\.[01]\r\nHost: (\S+)\r`)

var compiledRegexp_ReadHostnamePort = regexp.MustCompile(`^(\S+?)(:(\d{1,5}))?$`)

// Parse the request Host header and path from Hlfhr_HttpOnHttpsPortErrorHandler.
// Suppose this request using HTTP/1.1
func ReadReqHostPath(b []byte) (host string, path string, ok bool) {
_, host, path, ok = ReadReqMethodHostPath(b)
return
}

func ReadReqMethodHostPath(b []byte) (method string, host string, path string, ok bool) {
fb := compiledRegexp_ReadReq.FindSubmatch(b)
if fb == nil {
return
}
path = string(fb[1])
host = string(fb[2])
method = string(fb[1])
path = string(fb[2])
host = string(fb[3])
ok = true
return
}
Expand Down
2 changes: 1 addition & 1 deletion response.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func (resp Response) ScriptRedirect() error {
resp.StatusCode = 400
resp.SetContentType("text/html")
return resp.Write(
"<noscript> ", ErrHttpOnHttpsPort, " </noscript>\n",
"<noscript> Client sent an HTTP request to an HTTPS server. </noscript>\n",
"<script> location.protocol = 'https:' </script>\n",
)
}
2 changes: 1 addition & 1 deletion server.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// HTTPS Listener For HTTP Redirect
//
// Adapted from net/http
// Adapted from net/http and crypto/tls
//
// BSD-3-clause license
package hlfhr
Expand Down

0 comments on commit bf1690e

Please sign in to comment.