Golang Go语言中如何调用基础库 golang.org/x/crypto 的私有结构和方法?
开源项目 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.connection
、ssh.sshConn
和 ssh.newMux
依赖很深,发现很难直接 copy 出来。
Golang Go语言中如何调用基础库 golang.org/x/crypto 的私有结构和方法?
更多关于Golang Go语言中如何调用基础库 golang.org/x/crypto 的私有结构和方法?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
用 go:linkname 把私有函数链出来; unsafe 读写私有结构
更多关于Golang Go语言中如何调用基础库 golang.org/x/crypto 的私有结构和方法?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
unsafe 能引用私有结构吗?具体怎么做?
chatGPT ?
你问问看?
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 吧
fork golang.org/x/crypto 也不好啊。
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语言中,直接调用golang.org/x/crypto
库中的私有结构和方法通常是不被推荐的,也是不符合Go语言封装和模块化设计原则的。私有结构和方法的设计意图是限制外部直接访问,以保证库的内部实现细节不被破坏或误用。
如果你确实需要使用golang.org/x/crypto
中的某些功能,但发现它们被设计为私有,通常有以下几种解决方案:
-
使用公共接口:检查该库是否提供了公共接口或函数来实现你需要的功能。这是最直接和推荐的方式。
-
提交功能请求:如果现有公共接口无法满足你的需求,可以考虑向库的维护者提交功能请求或issue,询问是否可以添加你需要的公共方法或接口。
-
Fork并修改:在极端情况下,如果上述方法都不可行,你可以考虑Fork该库,并在自己的分支中修改私有结构和方法为公共。但这通常会导致你维护一个与官方库不同步的分支,增加了长期维护的复杂性。
-
使用反射:虽然理论上可以使用Go的反射机制来访问私有字段和方法,但这通常被视为一种非常规手段,且容易破坏库的稳定性和安全性,因此不推荐使用。
总之,遵循库的设计原则和接口规范是最佳实践,这样可以确保你的代码更加健壮和易于维护。