Repository: caddyserver/caddy (language: go)

Issue:
Proxy protocol v2, as declared in reverse_proxy / transport http, fails due to unitialized command byte
Using this caddyfile fragment as an example,
```
:80 {
    reverse_proxy {
        to unix//data/socket
        transport http {
            proxy_protocol v2
        }
    }
}
```
the generated stream is prefixed with the proxy protocol sequence as defined by [haproxy](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt).
See this hex dump:
```
0d 0a 0d 0a 00 0d 0a 51 55 49 54 0a 20 11 00 0c c0 a8 58 5b 00 00 00 00 00 00 00 00
```
The 13th byte ("20") is the protocol version and command. The lowest four bits default to 0, meaning LOCAL, but should be initialized to 1, meaning PROXY. Only in this case the receiver uses the information provided in the protocol block to get the original address.

As it stands, the remote address encoded in the header is being ignored by the counterpart.

The related code section is in caddyhttp / reverseproxy / httptransport.go, in function [NewTransport()](https://github.com/caddyserver/caddy/blob/7e2510ef43d3439c682d56b580e4013a0cc9cc3e/modules/caddyhttp/reverseproxy/httptransport.go#L228).
The section about ProxyProtocol could be simplified by using function HeaderProxyFromAddrs() from the [go-proxyproto](https://github.com/pires/go-proxyproto/blob/8a2480a3966f69776d99be21dd09b345fb199ee6/header.go#L47) module.


Discussion / hints:

Fix patch:
diff --git a/modules/caddyhttp/reverseproxy/httptransport.go b/modules/caddyhttp/reverseproxy/httptransport.go
index 895873b9dd8..93ed84ad6c5 100644
--- a/modules/caddyhttp/reverseproxy/httptransport.go
+++ b/modules/caddyhttp/reverseproxy/httptransport.go
@@ -225,41 +225,47 @@ func (h *HTTPTransport) NewTransport(caddyCtx caddy.Context) (*http.Transport, e
 			if !ok {
 				return nil, fmt.Errorf("failed to get proxy protocol info from context")
 			}
-			header := proxyproto.Header{
-				SourceAddr: &net.TCPAddr{
-					IP:   proxyProtocolInfo.AddrPort.Addr().AsSlice(),
-					Port: int(proxyProtocolInfo.AddrPort.Port()),
-					Zone: proxyProtocolInfo.AddrPort.Addr().Zone(),
-				},
+			var proxyv byte
+			switch h.ProxyProtocol {
+			case "v1":
+				proxyv = 1
+			case "v2":
+				proxyv = 2
+			default:
+				return nil, fmt.Errorf("unexpected proxy protocol version")
 			}
+
 			// The src and dst have to be of the same address family. As we don't know the original
 			// dst address (it's kind of impossible to know) and this address is generally of very
 			// little interest, we just set it to all zeros.
+			var destAddr net.Addr
 			switch {
 			case proxyProtocolInfo.AddrPort.Addr().Is4():
-				header.TransportProtocol = proxyproto.TCPv4
-				header.DestinationAddr = &net.TCPAddr{
+				destAddr = &net.TCPAddr{
 					IP: net.IPv4zero,
 				}
 			case proxyProtocolInfo.AddrPort.Addr().Is6():
-				header.TransportProtocol = proxyproto.TCPv6
-				header.DestinationAddr = &net.TCPAddr{
+				destAddr = &net.TCPAddr{
 					IP: net.IPv6zero,
 				}
 			default:
 				return nil, fmt.Errorf("unexpected remote addr type in proxy protocol info")
 			}
+			sourceAddr := &net.TCPAddr{
+				IP:   proxyProtocolInfo.AddrPort.Addr().AsSlice(),
+				Port: int(proxyProtocolInfo.AddrPort.Port()),
+				Zone: proxyProtocolInfo.AddrPort.Addr().Zone(),
+			}
+			header := proxyproto.HeaderProxyFromAddrs(proxyv, sourceAddr, destAddr)
 
+			// retain the log message structure
 			switch h.ProxyProtocol {
 			case "v1":
-				header.Version = 1
 				caddyCtx.Logger().Debug("sending proxy protocol header v1", zap.Any("header", header))
 			case "v2":
-				header.Version = 2
 				caddyCtx.Logger().Debug("sending proxy protocol header v2", zap.Any("header", header))
-			default:
-				return nil, fmt.Errorf("unexpected proxy protocol version")
 			}
+
 			_, err = header.WriteTo(conn)
 			if err != nil {
 				// identify this error as one that occurred during


Test patch: