fix(chain): Copy returns wrong pointer, connection leaks in Connect/Handshake, deferred cancel accumulation in retry loops
- transport.go: Copy() returned original pointer instead of the allocated copy, causing data races and incorrect routing for multiplexed chains. - route.go: Connect and Handshake failures in intermediate nodes leaked the returned connection. Handshake fix preserves the original Connect result (guaranteed non-nil) and closes both the Handshake return value and input. - router.go: defer cancel() inside retry loops accumulated context timers across iterations. Wrapped each loop body in an IIFE so defer scopes per-iteration and timers are released immediately.
This commit is contained in:
+10
-1
@@ -271,20 +271,29 @@ func (r *chainRoute) connect(ctx context.Context, logger logger.Logger) (conn ne
|
|||||||
}
|
}
|
||||||
cc, err = preNode.Options().Transport.Connect(ctx, cn, "tcp", addr)
|
cc, err = preNode.Options().Transport.Connect(ctx, cn, "tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if cc != nil {
|
||||||
|
cc.Close()
|
||||||
|
}
|
||||||
cn.Close()
|
cn.Close()
|
||||||
if marker != nil {
|
if marker != nil {
|
||||||
marker.Mark()
|
marker.Mark()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cc, err = node.Options().Transport.Handshake(ctx, cc)
|
var cc2 net.Conn
|
||||||
|
cc2, err = node.Options().Transport.Handshake(ctx, cc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if cc2 != nil {
|
||||||
|
cc2.Close()
|
||||||
|
}
|
||||||
|
cc.Close()
|
||||||
cn.Close()
|
cn.Close()
|
||||||
if marker != nil {
|
if marker != nil {
|
||||||
marker.Mark()
|
marker.Mark()
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
cc = cc2
|
||||||
if marker != nil {
|
if marker != nil {
|
||||||
marker.Reset()
|
marker.Reset()
|
||||||
}
|
}
|
||||||
|
|||||||
+86
-76
@@ -90,58 +90,63 @@ func (r *Router) dial(ctx context.Context, network, address string, log logger.L
|
|||||||
log.Debugf("dial %s/%s", address, network)
|
log.Debugf("dial %s/%s", address, network)
|
||||||
|
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
ctx := ctx
|
func() {
|
||||||
if r.options.Timeout > 0 {
|
ctx := ctx
|
||||||
var cancel context.CancelFunc
|
if r.options.Timeout > 0 {
|
||||||
ctx, cancel = context.WithTimeout(ctx, r.options.Timeout)
|
var cancel context.CancelFunc
|
||||||
defer cancel()
|
ctx, cancel = context.WithTimeout(ctx, r.options.Timeout)
|
||||||
}
|
defer cancel()
|
||||||
|
}
|
||||||
|
|
||||||
buf := ictx.BufferFromContext(ctx)
|
buf := ictx.BufferFromContext(ctx)
|
||||||
if buf != nil {
|
if buf != nil {
|
||||||
buf.Reset()
|
buf.Reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
var ipAddr string
|
var ipAddr string
|
||||||
ipAddr, err = xnet.Resolve(ctx, "ip", address, r.options.Resolver, r.options.HostMapper, log)
|
ipAddr, err = xnet.Resolve(ctx, "ip", address, r.options.Resolver, r.options.HostMapper, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if buf != nil {
|
||||||
|
buf.Reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
var route chain.Route
|
||||||
|
if r.options.Chain != nil {
|
||||||
|
route = r.options.Chain.Route(ctx, network, ipAddr, chain.WithHostRouteOption(address))
|
||||||
|
}
|
||||||
|
|
||||||
|
if buf == nil {
|
||||||
|
buf = &bytes.Buffer{}
|
||||||
|
}
|
||||||
|
for _, node := range routePath(route) {
|
||||||
|
fmt.Fprintf(buf, "%s@%s > ", node.Name, node.Addr)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(buf, "%s", ipAddr)
|
||||||
|
log.Debugf("route(retry=%d) %s", i, buf.String())
|
||||||
|
|
||||||
|
if route == nil {
|
||||||
|
route = DefaultRoute
|
||||||
|
}
|
||||||
|
conn, err = route.Dial(ctx, network, ipAddr,
|
||||||
|
append([]chain.DialOption{
|
||||||
|
chain.InterfaceDialOption(r.options.IfceName),
|
||||||
|
chain.NetnsDialOption(r.options.Netns),
|
||||||
|
chain.SockOptsDialOption(r.options.SockOpts),
|
||||||
|
chain.LoggerDialOption(log),
|
||||||
|
}, callerOpts...)...,
|
||||||
|
)
|
||||||
|
if err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Errorf("route(retry=%d) %s", i, err)
|
||||||
|
}()
|
||||||
|
if conn != nil || err == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
if buf != nil {
|
|
||||||
buf.Reset()
|
|
||||||
}
|
|
||||||
|
|
||||||
var route chain.Route
|
|
||||||
if r.options.Chain != nil {
|
|
||||||
route = r.options.Chain.Route(ctx, network, ipAddr, chain.WithHostRouteOption(address))
|
|
||||||
}
|
|
||||||
|
|
||||||
if buf == nil {
|
|
||||||
buf = &bytes.Buffer{}
|
|
||||||
}
|
|
||||||
for _, node := range routePath(route) {
|
|
||||||
fmt.Fprintf(buf, "%s@%s > ", node.Name, node.Addr)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(buf, "%s", ipAddr)
|
|
||||||
log.Debugf("route(retry=%d) %s", i, buf.String())
|
|
||||||
|
|
||||||
if route == nil {
|
|
||||||
route = DefaultRoute
|
|
||||||
}
|
|
||||||
conn, err = route.Dial(ctx, network, ipAddr,
|
|
||||||
append([]chain.DialOption{
|
|
||||||
chain.InterfaceDialOption(r.options.IfceName),
|
|
||||||
chain.NetnsDialOption(r.options.Netns),
|
|
||||||
chain.SockOptsDialOption(r.options.SockOpts),
|
|
||||||
chain.LoggerDialOption(log),
|
|
||||||
}, callerOpts...)...,
|
|
||||||
)
|
|
||||||
if err == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
log.Errorf("route(retry=%d) %s", i, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -160,39 +165,44 @@ func (r *Router) Bind(ctx context.Context, network, address string, opts ...chai
|
|||||||
log.Debugf("bind on %s/%s", address, network)
|
log.Debugf("bind on %s/%s", address, network)
|
||||||
|
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
ctx := ctx
|
func() {
|
||||||
if r.options.Timeout > 0 {
|
ctx := ctx
|
||||||
var cancel context.CancelFunc
|
if r.options.Timeout > 0 {
|
||||||
ctx, cancel = context.WithTimeout(ctx, r.options.Timeout)
|
var cancel context.CancelFunc
|
||||||
defer cancel()
|
ctx, cancel = context.WithTimeout(ctx, r.options.Timeout)
|
||||||
}
|
defer cancel()
|
||||||
|
}
|
||||||
|
|
||||||
var route chain.Route
|
var route chain.Route
|
||||||
if r.options.Chain != nil {
|
if r.options.Chain != nil {
|
||||||
route = r.options.Chain.Route(ctx, network, address)
|
route = r.options.Chain.Route(ctx, network, address)
|
||||||
if route == nil || len(route.Nodes()) == 0 {
|
if route == nil || len(route.Nodes()) == 0 {
|
||||||
err = ErrEmptyRoute
|
err = ErrEmptyRoute
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if log.IsLevelEnabled(logger.DebugLevel) {
|
||||||
|
buf := bytes.Buffer{}
|
||||||
|
for _, node := range routePath(route) {
|
||||||
|
fmt.Fprintf(&buf, "%s@%s > ", node.Name, node.Addr)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(&buf, "%s", address)
|
||||||
|
log.Debugf("route(retry=%d) %s", i, buf.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if route == nil {
|
||||||
|
route = DefaultRoute
|
||||||
|
}
|
||||||
|
ln, err = route.Bind(ctx, network, address, opts...)
|
||||||
|
if err == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
log.Errorf("route(retry=%d) %s", i, err)
|
||||||
|
}()
|
||||||
if log.IsLevelEnabled(logger.DebugLevel) {
|
if ln != nil || err == nil {
|
||||||
buf := bytes.Buffer{}
|
|
||||||
for _, node := range routePath(route) {
|
|
||||||
fmt.Fprintf(&buf, "%s@%s > ", node.Name, node.Addr)
|
|
||||||
}
|
|
||||||
fmt.Fprintf(&buf, "%s", address)
|
|
||||||
log.Debugf("route(retry=%d) %s", i, buf.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
if route == nil {
|
|
||||||
route = DefaultRoute
|
|
||||||
}
|
|
||||||
ln, err = route.Bind(ctx, network, address, opts...)
|
|
||||||
if err == nil {
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
log.Errorf("route(retry=%d) %s", i, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|||||||
+1
-1
@@ -102,5 +102,5 @@ func (tr *Transport) Options() *chain.TransportOptions {
|
|||||||
func (tr *Transport) Copy() chain.Transporter {
|
func (tr *Transport) Copy() chain.Transporter {
|
||||||
tr2 := &Transport{}
|
tr2 := &Transport{}
|
||||||
*tr2 = *tr
|
*tr2 = *tr
|
||||||
return tr
|
return tr2
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user