// 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 { ctx context.Context reader io.Reader net.Conn } 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') } else { zerolog.Ctx(l.ctx).Trace().Bytes("line", line). Msg("malformed request found, stripped 0x0a") } if len(line) == 0 { break } } if scanner.Err() != nil { zerolog.Ctx(l.ctx).Err(scanner.Err()).Send() } zerolog.Ctx(l.ctx).Trace(). Int("numBytes", len(newData)). Bytes("request", newData). Msg("stripping conn complete") // Use a multi-reader to prepend the modified request finalReader := io.MultiReader(bytes.NewReader(newData), conn) return &LFStrippingConn{ Conn: conn, ctx: l.ctx, reader: finalReader, } } func NewAWNMutatingListener(ctx context.Context, listen string) net.Listener { rawListener, err := net.Listen("tcp", listen) if err != nil { panic(err) } // Encapsulate the raw listener with ours return &LFStrippingListener{ Listener: rawListener, ctx: ctx, } } func (l *LFStrippingListener) Accept() (net.Conn, error) { conn, err := l.Listener.Accept() if err != nil { return nil, err } return l.WrapConn(conn), nil } func (l *LFStrippingListener) Close() error { return l.Listener.Close() } func (l *LFStrippingListener) Addr() net.Addr { return l.Listener.Addr() } func (c *LFStrippingConn) Read(b []byte) (int, error) { return c.reader.Read(b) }