开源项目 github.com/trzsz/trzsz-ssh 想支持 ssh ControlMaster 的功能。
go 基础库 golang.org/x/crypto 有个 PR go-review.googlesource.com/c/crypto/+/383374 还没合入。
有办法在不 fork 基础库 golang.org/x/crypto 的情况下,优雅地在自己的项目实现这个 PR 的逻辑吗?
import "golang.org/x/crypto/ssh"

func NewControlClientConn(c net.Conn) (ssh.Conn, <-chan ssh.NewChannel, <-chan *ssh.Request, error) {
conn := &ssh.connection{ ■ undefined: ssh.connection
sshConn: ssh.sshConn{conn: c}, ■ undefined: ssh.sshConn
}
var err error
if conn.transport, err = handshakeControlProxy(c); err != nil {
return nil, nil, nil, fmt.Errorf("ssh: control proxy handshake failed; %v", err)
}
conn.mux = ssh.newMux(conn.transport) ■ undefined: ssh.newMux
return conn, conn.mux.incomingChannels, conn.mux.incomingRequests, nil
}

handshakeControlProxy 这个函数很好办,直接从 PR 中复制出来就行了。
问题:上面的 ssh.connection、ssh.sshConn 和 ssh.newMux 依赖很深,发现很难直接 copy 出来。

用 go:linkname 把私有函数链出来; unsafe 读写私有结构

unsafe 能引用私有结构吗?具体怎么做?

chatGPT ?

你问问看?

V2EX 不允许 gpt 回答的,会 ban 账号的。

可以看 github.com/dolthub/maphash 这玩意的实现,其实就是自己整个和私有结构体一模一样的,然后把指针映射到自己的结构体上

#1 link 要在 golang.org/x/crypto 里面写其实可以用 go 汇编,可以直接根据包名﹒变量名引用(这个不安全)

不需要的,外面写一个相同定义的函数 link 原函数就行了//go:linkname func1 pkg.func1func func1(a string) string记得 import _ "unsafe"

现在要调用的函数是func newMux(p packetConn) mux问题是 packetConn 和 mux 都是私有的,怎么用 go:linkname 定义呢?type packetConn interface { // Encrypt and send a packet of data to the remote peer. writePacket(packet []byte) error // Read a packet from the connection. The read is blocking, // i.e. if error is nil, then the returned byte slice is // always non-empty. readPacket() ([]byte, error) // Close closes the write-side of the connection. Close() error}type mux struct { conn packetConn chanList chanList incomingChannels chan NewChannel globalSentMu sync.Mutex globalResponses chan interface{} incomingRequests chan Request errCond *sync.Cond err error}

#8 你这反了,你看 runtime now github.com/golang/go/blob/master/src/runtime/timestub.go#L14连接到 time now ,linkname 是写在 runtime 而不是 time

最好别拿 linkname 搞,升级 go 版本的时候容易炸。还是乖乖开个 fork 吧

汇编可以参考这个 github.com/golang/go/blob/master/src/sync/atomic/asm.s#L10

fork golang.org/x/crypto 也不好啊。

Go 创始人说:最好的方法就是复制

#14 反对他。我只是要加一点点功能,我可没有能力和精力去维护那么大一个库。

可以算偏移量直接读写; 或者定义一个相同结构的 struct, unsafe 强转 (我不清楚是否安全)

#10 ,linkname 在哪边写都是可以的

可以复制, 毕竟标准库里为了解决循环引用 也搞了很多复制

unsafe.Pointer + 偏移量 可以做到

packetConn 是个 interface 吧,直接拷贝出来就是了,

mux ,骚一点就是。。//go:linkname newMux golang.org/x/crypto/ssh.newMuxfunc newMux(p packetConn) muxtype mux struct { conn packetConn _ [40]byte incomingChannels chan ssh.NewChannel globalSentMu sync.Mutex globalResponses chan interface{} incomingRequests chan ssh.Request errCond *sync.Cond err error}

接口可以//go:linkname SendRequest golang.org/x/crypto/ssh.(mux).SendRequestfunc SendRequest(mux mux, name string, wantReply bool, payload []byte) (bool, []byte, error)func (c *connection) SendRequest(name string, wantReply bool, payload []byte) (bool, []byte, error) { return SendRequest(c.mux, name, wantReply, payload)}不过整体也不是很优雅就是了,兼容性很差

我觉得最优雅的办法就是 fork 。fork 完 release 个新版本,等 ssh 真的支持你想要的,再发布个新版本换回原来的

但是这样 go 版本更新的时候你可以显式的 review 改了什么,用 linkname 炸的无声无息,stacktrace 都没有那种