Просмотр исходного кода

Fix delayed reestablishment after "stream closed" write error

Rod Hynes 2 лет назад
Родитель
Сommit
4437220911
1 измененных файлов с 25 добавлено и 0 удалено
  1. 25 0
      psiphon/common/refraction/refraction.go

+ 25 - 0
psiphon/common/refraction/refraction.go

@@ -28,6 +28,7 @@ package refraction
 
 import (
 	"context"
+	std_errors "errors"
 	"fmt"
 	"io/ioutil"
 	"net"
@@ -42,6 +43,7 @@ import (
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol"
 	"github.com/armon/go-proxyproto"
 	lrucache "github.com/cognusion/go-cache-lru"
+	"github.com/pion/sctp"
 	refraction_networking_assets "github.com/refraction-networking/conjure/pkg/client/assets"
 	refraction_networking_registration "github.com/refraction-networking/conjure/pkg/registrars/registration"
 	refraction_networking_transports "github.com/refraction-networking/conjure/pkg/transports/client"
@@ -981,6 +983,29 @@ type refractionConn struct {
 	conjureMetricSTUNServerAddress string
 }
 
+func (conn *refractionConn) Write(p []byte) (int, error) {
+	n, err := conn.Conn.Write(p)
+
+	// For the DTLS transport, underlying SCTP conn writes may fail
+	// with "stream closed" -- which indicates a permanent failure of the
+	// transport -- without closing the conn. Explicitly close the conn on
+	// this error, which will trigger Psiphon to reconnect faster via
+	// IsClosed checks on port forward failures.
+	//
+	// The close is invoked asynchronously to avoid possible deadlocks due to
+	// a hypothetical panic in the Close call: for a port forward, the unwind
+	// will invoke a deferred ssh.channel.Close which reenters Write;
+	// meanwhile, the underlying ssh.channel.writePacket acquires a
+	// ssh.channel.writeMu lock but does not defer the unlock.
+
+	if std_errors.Is(err, sctp.ErrStreamClosed) {
+		go func() {
+			_ = conn.Close()
+		}()
+	}
+	return n, err
+}
+
 func (conn *refractionConn) Close() error {
 	conn.manager.close()
 	err := conn.Conn.Close()