[WIP] Use Go's TLS stream instead of whatever the library ships with #372
4
git.go
4
git.go
|
@ -129,6 +129,10 @@ func init() {
|
||||||
panic("libgit2 was not built with threading support")
|
panic("libgit2 was not built with threading support")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := RegisterManagedTls(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
// This is not something we should be doing, as we may be
|
// This is not something we should be doing, as we may be
|
||||||
// stomping all over someone else's setup. The user should do
|
// stomping all over someone else's setup. The user should do
|
||||||
// this themselves or use some binding/wrapper which does it
|
// this themselves or use some binding/wrapper which does it
|
||||||
|
|
18
remote.go
18
remote.go
|
@ -10,6 +10,7 @@ extern void _go_git_setup_callbacks(git_remote_callbacks *callbacks);
|
||||||
import "C"
|
import "C"
|
||||||
import (
|
import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"errors"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -163,6 +164,20 @@ type Certificate struct {
|
||||||
Hostkey HostkeyCertificate
|
Hostkey HostkeyCertificate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *Certificate) toC() (*C.git_cert, error) {
|
||||||
|
switch self.Kind {
|
||||||
|
case CertificateX509:
|
||||||
|
ccert := (*C.git_cert_x509)(C.calloc(1, C.size_t(unsafe.Sizeof(C.git_cert_x509{}))))
|
||||||
|
ccert.parent.cert_type = C.GIT_CERT_X509
|
||||||
|
rawCert := self.X509.Raw
|
||||||
|
ccert.len = C.size_t(len(rawCert))
|
||||||
|
ccert.data = C.CBytes(rawCert)
|
||||||
|
return (*C.git_cert)(unsafe.Pointer(ccert)), nil
|
||||||
|
default:
|
||||||
|
return nil, errors.New("not supported")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type HostkeyKind uint
|
type HostkeyKind uint
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -734,13 +749,12 @@ func (o *Remote) Connect(direction ConnectDirection, callbacks *RemoteCallbacks,
|
||||||
var cproxy C.git_proxy_options
|
var cproxy C.git_proxy_options
|
||||||
populateProxyOptions(&cproxy, proxyOpts)
|
populateProxyOptions(&cproxy, proxyOpts)
|
||||||
defer freeProxyOptions(&cproxy)
|
defer freeProxyOptions(&cproxy)
|
||||||
|
|
||||||
cheaders := C.git_strarray{}
|
cheaders := C.git_strarray{}
|
||||||
cheaders.count = C.size_t(len(headers))
|
cheaders.count = C.size_t(len(headers))
|
||||||
cheaders.strings = makeCStringsFromStrings(headers)
|
cheaders.strings = makeCStringsFromStrings(headers)
|
||||||
defer freeStrarray(&cheaders)
|
defer freeStrarray(&cheaders)
|
||||||
|
|
||||||
|
|
||||||
runtime.LockOSThread()
|
runtime.LockOSThread()
|
||||||
defer runtime.UnlockOSThread()
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,251 @@
|
||||||
|
package git
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <git2.h>
|
||||||
|
#include <git2/sys/stream.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
git_stream parent;
|
||||||
|
void *ptr;
|
||||||
|
} managed_stream;
|
||||||
|
|
||||||
|
extern int _go_git_register_tls(void);
|
||||||
|
extern void _go_git_setup_stream(managed_stream* s, int encrypted, int proxy_support, void *ptr);
|
||||||
|
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Network stream for libgit2 to use
|
||||||
|
type Stream interface {
|
||||||
|
Encrypted() bool
|
||||||
|
ProxySupport() bool
|
||||||
|
Connect() error
|
||||||
|
Certificate() (Certificate, error)
|
||||||
|
SetProxy(ProxyOptions) error
|
||||||
|
io.ReadWriteCloser
|
||||||
|
}
|
||||||
|
|
||||||
|
type ManagedStream struct {
|
||||||
|
host string
|
||||||
|
port string
|
||||||
|
conn *tls.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ManagedStream) Encrypted() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ManagedStream) ProxySupport() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ManagedStream) Connect() error {
|
||||||
|
conn, err := tls.Dial("tcp", fmt.Sprintf("%s:%s", self.host, self.port), nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
self.conn = conn
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ManagedStream) Certificate() (Certificate, error) {
|
||||||
|
connState := self.conn.ConnectionState()
|
||||||
|
cert := Certificate{
|
||||||
|
Kind: CertificateX509,
|
||||||
|
X509: connState.PeerCertificates[0],
|
||||||
|
}
|
||||||
|
|
||||||
|
return cert, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ManagedStream) SetProxy(opts ProxyOptions) error {
|
||||||
|
return errors.New("proxy not supported")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ManagedStream) Read(p []byte) (int, error) {
|
||||||
|
return self.conn.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ManagedStream) Write(p []byte) (int, error) {
|
||||||
|
return self.conn.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *ManagedStream) Close() error {
|
||||||
|
return self.conn.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
var errNotStream = errors.New("passed object does not implement Stream")
|
||||||
|
|
||||||
|
// getStreamInterface extracts the Stream interface from the pointers we passed
|
||||||
|
// to the C code.
|
||||||
|
func getStreamInterface(_s *C.git_stream) (Stream, error) {
|
||||||
|
// For type compatibility we accept C.git_stream but we know we pass
|
||||||
|
// C.managed_stream so force the casting to that.
|
||||||
|
wrapperPtr := (*C.managed_stream)(unsafe.Pointer(_s))
|
||||||
|
|
||||||
|
// Inside we've stored a handle to the actual type, which must implement
|
||||||
|
// Stream.
|
||||||
|
stream, ok := pointerHandles.Get(wrapperPtr.ptr).(Stream)
|
||||||
|
if !ok {
|
||||||
|
return nil, errNotStream
|
||||||
|
}
|
||||||
|
|
||||||
|
return stream, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//export streamCertificate
|
||||||
|
func streamCertificate(out **C.git_cert, _s *C.git_stream) C.int {
|
||||||
|
stream, err := getStreamInterface(_s)
|
||||||
|
if err != nil {
|
||||||
|
return setLibgit2Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cert, err := stream.Certificate()
|
||||||
|
if err != nil {
|
||||||
|
return setLibgit2Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ccert, err := cert.toC()
|
||||||
|
if err != nil {
|
||||||
|
return setLibgit2Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = ccert
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
//export streamSetProxy
|
||||||
|
func streamSetProxy(s *C.git_stream, proxy_opts *C.git_proxy_options) C.int {
|
||||||
|
setLibgit2Error(errors.New("proxy not supported"))
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
//export streamConnect
|
||||||
|
func streamConnect(_s *C.git_stream) C.int {
|
||||||
|
stream, err := getStreamInterface(_s)
|
||||||
|
if err != nil {
|
||||||
|
return setLibgit2Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = stream.Connect()
|
||||||
|
if err != nil {
|
||||||
|
return setLibgit2Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
//export streamRead
|
||||||
|
func streamRead(_s *C.git_stream, data unsafe.Pointer, l C.size_t) C.ssize_t {
|
||||||
|
stream, err := getStreamInterface(_s)
|
||||||
|
if err != nil {
|
||||||
|
setLibgit2Error(err)
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
var p []byte
|
||||||
|
header := (*reflect.SliceHeader)(unsafe.Pointer(&p))
|
||||||
|
header.Cap = int(l)
|
||||||
|
header.Len = int(l)
|
||||||
|
header.Data = uintptr(data)
|
||||||
|
|
||||||
|
n, err := stream.Read(p)
|
||||||
|
if err != nil {
|
||||||
|
setLibgit2Error(err)
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return C.ssize_t(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export streamWrite
|
||||||
|
func streamWrite(_s *C.git_stream, data unsafe.Pointer, l C.size_t, _f C.int) C.ssize_t {
|
||||||
|
stream, err := getStreamInterface(_s)
|
||||||
|
if err != nil {
|
||||||
|
setLibgit2Error(err)
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
var p []byte
|
||||||
|
header := (*reflect.SliceHeader)(unsafe.Pointer(&p))
|
||||||
|
header.Cap = int(l)
|
||||||
|
header.Len = int(l)
|
||||||
|
header.Data = uintptr(data)
|
||||||
|
|
||||||
|
n, err := stream.Write(p)
|
||||||
|
if err != nil {
|
||||||
|
setLibgit2Error(err)
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return C.ssize_t(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
//export streamClose
|
||||||
|
func streamClose(_s *C.git_stream) C.int {
|
||||||
|
stream, err := getStreamInterface(_s)
|
||||||
|
if err != nil {
|
||||||
|
return setLibgit2Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = stream.Close()
|
||||||
|
if err != nil {
|
||||||
|
return setLibgit2Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
//export streamFree
|
||||||
|
func streamFree(_s *C.git_stream) {
|
||||||
|
wrapperPtr := (*C.managed_stream)(unsafe.Pointer(_s))
|
||||||
|
pointerHandles.Untrack(wrapperPtr.ptr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newManagedStream(host, port string) *ManagedStream {
|
||||||
|
return &ManagedStream{
|
||||||
|
host: host,
|
||||||
|
port: port,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//export streamCallbackCb
|
||||||
|
func streamCallbackCb(out **C.git_stream, chost, cport *C.char) C.int {
|
||||||
|
stream := C.calloc(1, C.size_t(unsafe.Sizeof(C.managed_stream{})))
|
||||||
|
managed := newManagedStream(C.GoString(chost), C.GoString(cport))
|
||||||
|
managedPtr := pointerHandles.Track(managed)
|
||||||
|
C._go_git_setup_stream(stream, 1, 0, managedPtr)
|
||||||
|
|
||||||
|
*out = (*C.git_stream)(stream)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func setLibgit2Error(err error) C.int {
|
||||||
|
cstr := C.CString(err.Error())
|
||||||
|
defer C.free(unsafe.Pointer(cstr))
|
||||||
|
C.giterr_set_str(C.GITERR_NET, cstr)
|
||||||
|
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterManagedTls() error {
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
|
||||||
|
if err := C._go_git_register_tls(); err != 0 {
|
||||||
|
return MakeGitError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
23
wrapper.c
23
wrapper.c
|
@ -180,4 +180,27 @@ void _go_git_writestream_free(git_writestream *stream)
|
||||||
stream->free(stream);
|
stream->free(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int _go_git_register_tls(void)
|
||||||
|
{
|
||||||
|
return git_stream_register_tls((git_stream_cb) streamCallbackCb);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef int (*set_proxy_cb)(struct git_stream *, const git_proxy_options *proxy_opts);
|
||||||
|
typedef ssize_t (*write_cb)(struct git_stream *, const char *, size_t, int);
|
||||||
|
|
||||||
|
void _go_git_setup_stream(managed_stream* s, int encrypted, int proxy_support, void *ptr)
|
||||||
|
{
|
||||||
|
s->parent.version = GIT_STREAM_VERSION;
|
||||||
|
s->parent.encrypted = encrypted;
|
||||||
|
s->parent.proxy_support = proxy_support;
|
||||||
|
s->parent.connect = streamConnect;
|
||||||
|
s->parent.certificate = streamCertificate;
|
||||||
|
s->parent.set_proxy = (set_proxy_cb) streamSetProxy;
|
||||||
|
s->parent.read = streamRead;
|
||||||
|
s->parent.write = (write_cb) streamWrite;
|
||||||
|
s->parent.close = streamClose;
|
||||||
|
s->parent.free = streamFree;
|
||||||
|
s->ptr = ptr;
|
||||||
|
}
|
||||||
|
|
||||||
/* EOF */
|
/* EOF */
|
||||||
|
|
Loading…
Reference in New Issue