开源项目 https://github.com/trzsz/trzsz-ssh 想支持 ssh ControlMaster 的功能。
go 基础库 golang.org/x/crypto 有个 PR https://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 出来。
1
Nazz 2023-10-25 08:16:27 +08:00 via Android 1
用 go:linkname 把私有函数链出来; unsafe 读写私有结构
|
3
liuidetmks 2023-10-25 08:44:37 +08:00
chatGPT ?
|
4
LonnyWong OP @liuidetmks 你问问看?
|
6
Junian 2023-10-25 09:02:25 +08:00 via iPhone
@LonnyWong 可以看 https://github.com/dolthub/maphash 这玩意的实现,其实就是自己整个和私有结构体一模一样的,然后把指针映射到自己的结构体上
|
7
lysS 2023-10-25 09:20:52 +08:00
|
8
kkbblzq 2023-10-25 10:15:44 +08:00 2
@lysS 不需要的,外面写一个相同定义的函数 link 原函数就行了
//go:linkname func1 pkg.func1 func func1(a string) string 记得 import _ "unsafe" |
9
LonnyWong OP @kkbblzq 现在要调用的函数是
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 } |
10
lysS 2023-10-25 11:24:46 +08:00
@kkbblzq #8 你这反了,你看 runtime now
https://github.com/golang/go/blob/master/src/runtime/timestub.go#L14 连接到 time now ,linkname 是写在 runtime 而不是 time |
11
learningman 2023-10-25 11:29:07 +08:00
最好别拿 linkname 搞,升级 go 版本的时候容易炸。
还是乖乖开个 fork 吧 |
12
lysS 2023-10-25 11:34:36 +08:00
|
13
LonnyWong OP @learningman fork golang.org/x/crypto 也不好啊。
|
14
777777 2023-10-25 13:47:38 +08:00
Go 创始人说:最好的方法就是复制
|
18
chimission 2023-10-25 16:18:23 +08:00
可以复制, 毕竟标准库里为了解决循环引用 也搞了很多复制
|
19
bruce0 2023-10-25 16:34:45 +08:00
用 `unsafe.Pointer` + 偏移量 可以做到
|
21
kkbblzq 2023-10-25 17:20:17 +08:00 1
@kkbblzq mux ,骚一点就是。。
//go:linkname newMux golang.org/x/crypto/ssh.newMux func newMux(p packetConn) *mux type 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 } |
22
kkbblzq 2023-10-25 17:26:59 +08:00 1
@kkbblzq 接口可以
//go:linkname SendRequest golang.org/x/crypto/ssh.(*mux).SendRequest func 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) } 不过整体也不是很优雅就是了,兼容性很差 |
23
matrix1010 2023-10-25 17:27:52 +08:00
我觉得最优雅的办法就是 fork 。fork 完 release 个新版本,等 ssh 真的支持你想要的,再发布个新版本换回原来的
|
24
learningman 2023-10-26 00:03:29 +08:00
@LonnyWong 但是这样 go 版本更新的时候你可以显式的 review 改了什么,用 linkname 炸的无声无息,stacktrace 都没有那种
|
25
LonnyWong OP |