Golang Go语言中如何调用基础库 golang.org/x/crypto 的私有结构和方法?

发布于 1周前 作者 yuanlaile 来自 Go语言

开源项目 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©; 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.connectionssh.sshConnssh.newMux 依赖很深,发现很难直接 copy 出来。


Golang Go语言中如何调用基础库 golang.org/x/crypto 的私有结构和方法?

更多关于Golang Go语言中如何调用基础库 golang.org/x/crypto 的私有结构和方法?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

26 回复

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

更多关于Golang Go语言中如何调用基础库 golang.org/x/crypto 的私有结构和方法?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


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

你问问看?

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

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

#1 link 要在 golang.org/x/crypto 里面写

其实可以用 go 汇编,可以直接根据包名﹒变量名引用(这个不安全)

不需要的,外面写一个相同定义的函数 link 原函数就行了


//go:linkname func1 pkg.func1
func 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

https://github.com/golang/go/blob/master/src/runtime/timestub.go#L14

连接到 time now ,linkname 是写在 runtime 而不是 time

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

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

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

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

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

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

unsafe.Pointer + 偏移量 可以做到

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

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
}

接口可以
//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)
}

不过整体也不是很优雅就是了,兼容性很差

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

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

在Go语言中,直接调用golang.org/x/crypto库中的私有结构和方法通常是不被推荐的,也是不符合Go语言封装和模块化设计原则的。私有结构和方法的设计意图是限制外部直接访问,以保证库的内部实现细节不被破坏或误用。

如果你确实需要使用golang.org/x/crypto中的某些功能,但发现它们被设计为私有,通常有以下几种解决方案:

  1. 使用公共接口:检查该库是否提供了公共接口或函数来实现你需要的功能。这是最直接和推荐的方式。

  2. 提交功能请求:如果现有公共接口无法满足你的需求,可以考虑向库的维护者提交功能请求或issue,询问是否可以添加你需要的公共方法或接口。

  3. Fork并修改:在极端情况下,如果上述方法都不可行,你可以考虑Fork该库,并在自己的分支中修改私有结构和方法为公共。但这通常会导致你维护一个与官方库不同步的分支,增加了长期维护的复杂性。

  4. 使用反射:虽然理论上可以使用Go的反射机制来访问私有字段和方法,但这通常被视为一种非常规手段,且容易破坏库的稳定性和安全性,因此不推荐使用。

总之,遵循库的设计原则和接口规范是最佳实践,这样可以确保你的代码更加健壮和易于维护。

回到顶部