bio.go 6.57 KB
Newer Older
1
// Copyright (C) 2017. See AUTHORS.
JT Olds's avatar
JT Olds committed
2 3 4 5 6 7 8 9 10 11 12 13 14
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

JT Olds's avatar
JT Olds committed
15 16
package openssl

Andrew Harding's avatar
Andrew Harding committed
17
// #include "shim.h"
JT Olds's avatar
JT Olds committed
18 19 20
import "C"

import (
21 22 23 24 25
	"errors"
	"io"
	"reflect"
	"sync"
	"unsafe"
JT Olds's avatar
JT Olds committed
26 27 28
)

const (
29
	SSLRecordSize = 16 * 1024
JT Olds's avatar
JT Olds committed
30 31 32
)

func nonCopyGoBytes(ptr uintptr, length int) []byte {
33 34 35 36 37 38
	var slice []byte
	header := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
	header.Cap = length
	header.Len = length
	header.Data = ptr
	return slice
JT Olds's avatar
JT Olds committed
39 40 41
}

func nonCopyCString(data *C.char, size C.int) []byte {
42
	return nonCopyGoBytes(uintptr(unsafe.Pointer(data)), int(size))
JT Olds's avatar
JT Olds committed
43 44
}

Jeff Wendling's avatar
Jeff Wendling committed
45 46
var writeBioMapping = newMapping()

JT Olds's avatar
JT Olds committed
47
type writeBio struct {
JT Olds's avatar
JT Olds committed
48 49 50 51
	data_mtx        sync.Mutex
	op_mtx          sync.Mutex
	buf             []byte
	release_buffers bool
JT Olds's avatar
JT Olds committed
52 53 54
}

func loadWritePtr(b *C.BIO) *writeBio {
Andrew Harding's avatar
Andrew Harding committed
55 56
	t := token(C.X_BIO_get_data(b))
	return (*writeBio)(writeBioMapping.Get(t))
JT Olds's avatar
JT Olds committed
57 58
}

JT Olds's avatar
JT Olds committed
59
func bioClearRetryFlags(b *C.BIO) {
Andrew Harding's avatar
Andrew Harding committed
60
	C.X_BIO_clear_flags(b, C.BIO_FLAGS_RWS|C.BIO_FLAGS_SHOULD_RETRY)
JT Olds's avatar
JT Olds committed
61 62 63
}

func bioSetRetryRead(b *C.BIO) {
Andrew Harding's avatar
Andrew Harding committed
64
	C.X_BIO_set_flags(b, C.BIO_FLAGS_READ|C.BIO_FLAGS_SHOULD_RETRY)
JT Olds's avatar
JT Olds committed
65 66
}

Andrew Harding's avatar
Andrew Harding committed
67 68
//export go_write_bio_write
func go_write_bio_write(b *C.BIO, data *C.char, size C.int) (rc C.int) {
69 70 71 72 73 74
	defer func() {
		if err := recover(); err != nil {
			logger.Critf("openssl: writeBioWrite panic'd: %v", err)
			rc = -1
		}
	}()
75 76 77 78 79 80
	ptr := loadWritePtr(b)
	if ptr == nil || data == nil || size < 0 {
		return -1
	}
	ptr.data_mtx.Lock()
	defer ptr.data_mtx.Unlock()
JT Olds's avatar
JT Olds committed
81
	bioClearRetryFlags(b)
82 83
	ptr.buf = append(ptr.buf, nonCopyCString(data, size)...)
	return size
JT Olds's avatar
JT Olds committed
84 85
}

Andrew Harding's avatar
Andrew Harding committed
86 87
//export go_write_bio_ctrl
func go_write_bio_ctrl(b *C.BIO, cmd C.int, arg1 C.long, arg2 unsafe.Pointer) (
88 89 90 91 92 93 94
	rc C.long) {
	defer func() {
		if err := recover(); err != nil {
			logger.Critf("openssl: writeBioCtrl panic'd: %v", err)
			rc = -1
		}
	}()
95 96 97 98 99 100 101 102
	switch cmd {
	case C.BIO_CTRL_WPENDING:
		return writeBioPending(b)
	case C.BIO_CTRL_DUP, C.BIO_CTRL_FLUSH:
		return 1
	default:
		return 0
	}
JT Olds's avatar
JT Olds committed
103 104 105
}

func writeBioPending(b *C.BIO) C.long {
106 107 108 109 110 111 112
	ptr := loadWritePtr(b)
	if ptr == nil {
		return 0
	}
	ptr.data_mtx.Lock()
	defer ptr.data_mtx.Unlock()
	return C.long(len(ptr.buf))
JT Olds's avatar
JT Olds committed
113 114 115
}

func (b *writeBio) WriteTo(w io.Writer) (rv int64, err error) {
116 117
	b.op_mtx.Lock()
	defer b.op_mtx.Unlock()
JT Olds's avatar
JT Olds committed
118

119 120 121 122
	// write whatever data we currently have
	b.data_mtx.Lock()
	data := b.buf
	b.data_mtx.Unlock()
JT Olds's avatar
JT Olds committed
123

124 125 126 127
	if len(data) == 0 {
		return 0, nil
	}
	n, err := w.Write(data)
JT Olds's avatar
JT Olds committed
128

129 130 131
	// subtract however much data we wrote from the buffer
	b.data_mtx.Lock()
	b.buf = b.buf[:copy(b.buf, b.buf[n:])]
JT Olds's avatar
JT Olds committed
132 133 134
	if b.release_buffers && len(b.buf) == 0 {
		b.buf = nil
	}
135
	b.data_mtx.Unlock()
JT Olds's avatar
JT Olds committed
136

137
	return int64(n), err
JT Olds's avatar
JT Olds committed
138 139 140
}

func (self *writeBio) Disconnect(b *C.BIO) {
141
	if loadWritePtr(b) == self {
Andrew Harding's avatar
Andrew Harding committed
142 143
		writeBioMapping.Del(token(C.X_BIO_get_data(b)))
		C.X_BIO_set_data(b, nil)
144
	}
JT Olds's avatar
JT Olds committed
145 146 147
}

func (b *writeBio) MakeCBIO() *C.BIO {
Andrew Harding's avatar
Andrew Harding committed
148
	rv := C.X_BIO_new_write_bio()
Jeff Wendling's avatar
Jeff Wendling committed
149
	token := writeBioMapping.Add(unsafe.Pointer(b))
Andrew Harding's avatar
Andrew Harding committed
150
	C.X_BIO_set_data(rv, unsafe.Pointer(token))
151
	return rv
JT Olds's avatar
JT Olds committed
152 153
}

Jeff Wendling's avatar
Jeff Wendling committed
154 155
var readBioMapping = newMapping()

JT Olds's avatar
JT Olds committed
156
type readBio struct {
JT Olds's avatar
JT Olds committed
157 158 159 160 161
	data_mtx        sync.Mutex
	op_mtx          sync.Mutex
	buf             []byte
	eof             bool
	release_buffers bool
JT Olds's avatar
JT Olds committed
162 163 164
}

func loadReadPtr(b *C.BIO) *readBio {
Andrew Harding's avatar
Andrew Harding committed
165
	return (*readBio)(readBioMapping.Get(token(C.X_BIO_get_data(b))))
JT Olds's avatar
JT Olds committed
166 167
}

Andrew Harding's avatar
Andrew Harding committed
168 169
//export go_read_bio_read
func go_read_bio_read(b *C.BIO, data *C.char, size C.int) (rc C.int) {
170 171
	defer func() {
		if err := recover(); err != nil {
Andrew Harding's avatar
Andrew Harding committed
172
			logger.Critf("openssl: go_read_bio_read panic'd: %v", err)
173 174 175
			rc = -1
		}
	}()
176 177 178 179 180 181
	ptr := loadReadPtr(b)
	if ptr == nil || size < 0 {
		return -1
	}
	ptr.data_mtx.Lock()
	defer ptr.data_mtx.Unlock()
JT Olds's avatar
JT Olds committed
182
	bioClearRetryFlags(b)
183 184 185 186
	if len(ptr.buf) == 0 {
		if ptr.eof {
			return 0
		}
JT Olds's avatar
JT Olds committed
187
		bioSetRetryRead(b)
188 189 190 191 192 193 194
		return -1
	}
	if size == 0 || data == nil {
		return C.int(len(ptr.buf))
	}
	n := copy(nonCopyCString(data, size), ptr.buf)
	ptr.buf = ptr.buf[:copy(ptr.buf, ptr.buf[n:])]
JT Olds's avatar
JT Olds committed
195 196 197
	if ptr.release_buffers && len(ptr.buf) == 0 {
		ptr.buf = nil
	}
198
	return C.int(n)
JT Olds's avatar
JT Olds committed
199 200
}

Andrew Harding's avatar
Andrew Harding committed
201 202
//export go_read_bio_ctrl
func go_read_bio_ctrl(b *C.BIO, cmd C.int, arg1 C.long, arg2 unsafe.Pointer) (
203 204 205 206 207 208 209 210
	rc C.long) {

	defer func() {
		if err := recover(); err != nil {
			logger.Critf("openssl: readBioCtrl panic'd: %v", err)
			rc = -1
		}
	}()
211 212 213 214 215 216 217 218
	switch cmd {
	case C.BIO_CTRL_PENDING:
		return readBioPending(b)
	case C.BIO_CTRL_DUP, C.BIO_CTRL_FLUSH:
		return 1
	default:
		return 0
	}
JT Olds's avatar
JT Olds committed
219 220 221
}

func readBioPending(b *C.BIO) C.long {
222 223 224 225 226 227 228
	ptr := loadReadPtr(b)
	if ptr == nil {
		return 0
	}
	ptr.data_mtx.Lock()
	defer ptr.data_mtx.Unlock()
	return C.long(len(ptr.buf))
JT Olds's avatar
JT Olds committed
229 230 231
}

func (b *readBio) ReadFromOnce(r io.Reader) (n int, err error) {
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
	b.op_mtx.Lock()
	defer b.op_mtx.Unlock()

	// make sure we have a destination that fits at least one SSL record
	b.data_mtx.Lock()
	if cap(b.buf) < len(b.buf)+SSLRecordSize {
		new_buf := make([]byte, len(b.buf), len(b.buf)+SSLRecordSize)
		copy(new_buf, b.buf)
		b.buf = new_buf
	}
	dst := b.buf[len(b.buf):cap(b.buf)]
	dst_slice := b.buf
	b.data_mtx.Unlock()

	n, err = r.Read(dst)
	b.data_mtx.Lock()
	defer b.data_mtx.Unlock()
	if n > 0 {
		if len(dst_slice) != len(b.buf) {
			// someone shrunk the buffer, so we read in too far ahead and we
			// need to slide backwards
			copy(b.buf[len(b.buf):len(b.buf)+n], dst)
		}
		b.buf = b.buf[:len(b.buf)+n]
	}
	return n, err
JT Olds's avatar
JT Olds committed
258 259 260
}

func (b *readBio) MakeCBIO() *C.BIO {
Andrew Harding's avatar
Andrew Harding committed
261
	rv := C.X_BIO_new_read_bio()
Jeff Wendling's avatar
Jeff Wendling committed
262
	token := readBioMapping.Add(unsafe.Pointer(b))
Andrew Harding's avatar
Andrew Harding committed
263
	C.X_BIO_set_data(rv, unsafe.Pointer(token))
264
	return rv
JT Olds's avatar
JT Olds committed
265 266 267
}

func (self *readBio) Disconnect(b *C.BIO) {
268
	if loadReadPtr(b) == self {
Andrew Harding's avatar
Andrew Harding committed
269 270
		readBioMapping.Del(token(C.X_BIO_get_data(b)))
		C.X_BIO_set_data(b, nil)
271
	}
JT Olds's avatar
JT Olds committed
272 273 274
}

func (b *readBio) MarkEOF() {
275 276 277
	b.data_mtx.Lock()
	defer b.data_mtx.Unlock()
	b.eof = true
JT Olds's avatar
JT Olds committed
278 279 280 281 282 283 284
}

type anyBio C.BIO

func asAnyBio(b *C.BIO) *anyBio { return (*anyBio)(b) }

func (b *anyBio) Read(buf []byte) (n int, err error) {
JT Olds's avatar
JT Olds committed
285 286 287
	if len(buf) == 0 {
		return 0, nil
	}
Andrew Harding's avatar
Andrew Harding committed
288
	n = int(C.X_BIO_read((*C.BIO)(b), unsafe.Pointer(&buf[0]), C.int(len(buf))))
289 290 291 292
	if n <= 0 {
		return 0, io.EOF
	}
	return n, nil
JT Olds's avatar
JT Olds committed
293 294 295
}

func (b *anyBio) Write(buf []byte) (written int, err error) {
JT Olds's avatar
JT Olds committed
296 297 298
	if len(buf) == 0 {
		return 0, nil
	}
Andrew Harding's avatar
Andrew Harding committed
299
	n := int(C.X_BIO_write((*C.BIO)(b), unsafe.Pointer(&buf[0]),
300 301 302 303 304
		C.int(len(buf))))
	if n != len(buf) {
		return n, errors.New("BIO write failed")
	}
	return n, nil
JT Olds's avatar
JT Olds committed
305
}