| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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. |
| */ |
| |
| /** |
| * Provides a SSL BIO implementation wrapping a Thrift transport. |
| * |
| * This way, SSL I/O can be relayed over Thrift transport without introducing |
| * an additional layer of buffering, especially for the non-blocking |
| * transports. |
| * |
| * For the Thrift transport incarnations of the SSL entities, "tt" is used as |
| * prefix for clarity. |
| */ |
| module thrift.internal.ssl_bio; |
| |
| import core.stdc.config; |
| import core.stdc.string : strlen; |
| import core.memory : GC; |
| import deimos.openssl.bio; |
| import deimos.openssl.err; |
| import thrift.base; |
| import thrift.internal.ssl; |
| import thrift.transport.base; |
| |
| /** |
| * Creates an SSL BIO object wrapping the given transport. |
| * |
| * Exceptions thrown by the transport are pushed onto the OpenSSL error stack, |
| * using the location/reason values from thrift.internal.ssl.ERR_*_D_EXCEPTION. |
| * |
| * The transport is assumed to be ready for reading and writing when the BIO |
| * functions are called, it is not opened by the implementation. |
| * |
| * Params: |
| * transport = The transport to wrap. |
| * closeTransport = Whether the close the transport when the SSL BIO is |
| * closed. |
| */ |
| BIO* createTTransportBIO(TTransport transport, bool closeTransport) { |
| auto result = BIO_new(cast(BIO_METHOD*)&ttBioMethod); |
| if (!result) return null; |
| |
| GC.addRoot(cast(void*)transport); |
| BIO_set_fd(result, closeTransport, cast(c_long)cast(void*)transport); |
| |
| return result; |
| } |
| |
| private { |
| // Helper to get the Thrift transport assigned with the given BIO. |
| TTransport trans(BIO* b) nothrow { |
| auto result = cast(TTransport)b.ptr; |
| assert(result); |
| return result; |
| } |
| |
| void setError(Exception e) nothrow { |
| ERR_put_error(ERR_LIB_D_EXCEPTION, ERR_F_D_EXCEPTION, ERR_R_D_EXCEPTION, |
| ERR_FILE_D_EXCEPTION, ERR_LINE_D_EXCEPTION); |
| try { GC.addRoot(cast(void*)e); } catch (Throwable) {} |
| ERR_set_error_data(cast(char*)e, ERR_FLAGS_D_EXCEPTION); |
| } |
| |
| extern(C) int ttWrite(BIO* b, const(char)* data, int length) nothrow { |
| assert(b); |
| if (!data || length <= 0) return 0; |
| try { |
| trans(b).write((cast(ubyte*)data)[0 .. length]); |
| return length; |
| } catch (Exception e) { |
| setError(e); |
| return -1; |
| } |
| } |
| |
| extern(C) int ttRead(BIO* b, char* data, int length) nothrow { |
| assert(b); |
| if (!data || length <= 0) return 0; |
| try { |
| return cast(int)trans(b).read((cast(ubyte*)data)[0 .. length]); |
| } catch (Exception e) { |
| setError(e); |
| return -1; |
| } |
| } |
| |
| extern(C) int ttPuts(BIO* b, const(char)* str) nothrow { |
| return ttWrite(b, str, cast(int)strlen(str)); |
| } |
| |
| extern(C) c_long ttCtrl(BIO* b, int cmd, c_long num, void* ptr) nothrow { |
| assert(b); |
| |
| switch (cmd) { |
| case BIO_C_SET_FD: |
| // Note that close flag and "fd" are actually reversed here because we |
| // need 64 bit width for the pointer – should probably drop BIO_set_fd |
| // altogether. |
| ttDestroy(b); |
| b.ptr = cast(void*)num; |
| b.shutdown = cast(int)ptr; |
| b.init_ = 1; |
| return 1; |
| case BIO_C_GET_FD: |
| if (!b.init_) return -1; |
| *(cast(void**)ptr) = b.ptr; |
| return cast(c_long)b.ptr; |
| case BIO_CTRL_GET_CLOSE: |
| return b.shutdown; |
| case BIO_CTRL_SET_CLOSE: |
| b.shutdown = cast(int)num; |
| return 1; |
| case BIO_CTRL_FLUSH: |
| try { |
| trans(b).flush(); |
| return 1; |
| } catch (Exception e) { |
| setError(e); |
| return -1; |
| } |
| case BIO_CTRL_DUP: |
| // Seems like we have nothing to do on duplication, but couldn't find |
| // any documentation if this actually ever happens during normal SSL |
| // usage. |
| return 1; |
| default: |
| return 0; |
| } |
| } |
| |
| extern(C) int ttCreate(BIO* b) nothrow { |
| assert(b); |
| b.init_ = 0; |
| b.num = 0; // User-defined number field, unused here. |
| b.ptr = null; |
| b.flags = 0; |
| return 1; |
| } |
| |
| extern(C) int ttDestroy(BIO* b) nothrow { |
| if (!b) return 0; |
| |
| int rc = 1; |
| if (b.shutdown) { |
| if (b.init_) { |
| try { |
| trans(b).close(); |
| GC.removeRoot(cast(void*)trans(b)); |
| b.ptr = null; |
| } catch (Exception e) { |
| setError(e); |
| rc = -1; |
| } |
| } |
| b.init_ = 0; |
| b.flags = 0; |
| } |
| |
| return rc; |
| } |
| |
| immutable BIO_METHOD ttBioMethod = { |
| BIO_TYPE_SOURCE_SINK, |
| "TTransport", |
| &ttWrite, |
| &ttRead, |
| &ttPuts, |
| null, // gets |
| &ttCtrl, |
| &ttCreate, |
| &ttDestroy, |
| null // callback_ctrl |
| }; |
| } |