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