Invalid AWN Request Fixer
This commit is contained in:
101
pkg/ambient/ambienthttp/ambienthttp.go
Normal file
101
pkg/ambient/ambienthttp/ambienthttp.go
Normal file
@ -0,0 +1,101 @@
|
||||
// This package exists purely to override the net.Listener used
|
||||
// by the application's http server. This is necessary for certain versions
|
||||
// of firmware which errantly put an 0x0a (LF) following PASSKEY for
|
||||
// AmbientWeather type http reporting.
|
||||
//
|
||||
// This needs to be fixed upstream by Ambient Weather and is a complete
|
||||
// hack that should never be necessary. Without this, the http server
|
||||
// will silently crank back an HTTP:400
|
||||
package ambienthttp
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"net"
|
||||
"regexp"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
// Invalid Request Pattern
|
||||
var badReqURI = regexp.MustCompile(`PASSKEY=[^\n&]{16,}$`)
|
||||
|
||||
// Listener encapsulates LFStrippingConn to perform
|
||||
// infuriating strip of newline character present after PASSKEY
|
||||
// sent errantly by specific versions of firmware sending updates
|
||||
// in AmbientWeather protocol
|
||||
type LFStrippingListener struct {
|
||||
ctx context.Context
|
||||
net.Listener
|
||||
}
|
||||
|
||||
type LFStrippingConn struct {
|
||||
net.Conn
|
||||
reader io.Reader
|
||||
}
|
||||
|
||||
func (l *LFStrippingListener) WrapConn(conn net.Conn) net.Conn {
|
||||
buf := new(bytes.Buffer)
|
||||
reader := io.TeeReader(conn, buf)
|
||||
|
||||
scanner := bufio.NewScanner(reader)
|
||||
var newData []byte
|
||||
for scanner.Scan() {
|
||||
line := scanner.Bytes()
|
||||
newData = append(newData, line...)
|
||||
|
||||
// Only restore newline if not a bad request
|
||||
if !badReqURI.Match(line) {
|
||||
newData = append(newData, '\n')
|
||||
}
|
||||
}
|
||||
if scanner.Err() != nil {
|
||||
zerolog.Ctx(l.ctx).Err(scanner.Err()).Send()
|
||||
}
|
||||
|
||||
// Use a multi-reader to prepend the modified request
|
||||
finalReader := io.MultiReader(bytes.NewReader(newData), conn)
|
||||
|
||||
return &LFStrippingConn{
|
||||
Conn: conn,
|
||||
reader: finalReader,
|
||||
}
|
||||
}
|
||||
|
||||
func NewLFStrippingListener(ctx context.Context, listen string) net.Listener {
|
||||
// Create the underlying TCP listener
|
||||
rawListener, err := net.Listen("tcp", listen)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &LFStrippingListener{
|
||||
Listener: rawListener,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// Accept waits for and returns the next connection to the listener.
|
||||
func (l *LFStrippingListener) Accept() (net.Conn, error) {
|
||||
conn, err := l.Listener.Accept()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return l.WrapConn(conn), nil
|
||||
}
|
||||
|
||||
// Close closes the listener.
|
||||
// Any blocked Accept operations will be unblocked and return errors.
|
||||
func (l *LFStrippingListener) Close() error {
|
||||
return l.Listener.Close()
|
||||
}
|
||||
|
||||
// Addr returns the listener's network address.
|
||||
func (l *LFStrippingListener) Addr() net.Addr {
|
||||
return l.Listener.Addr()
|
||||
}
|
||||
|
||||
func (c *LFStrippingConn) Read(b []byte) (int, error) {
|
||||
return c.reader.Read(b)
|
||||
}
|
Reference in New Issue
Block a user