@@ -40,3 +40,230 @@ ossl_membio2str(BIO *bio)
4040
4141 return ret ;
4242}
43+
44+ static BIO_METHOD * ossl_bio_meth ;
45+ static VALUE nonblock_kwargs , sym_wait_readable , sym_wait_writable ;
46+
47+ struct ossl_bio_ctx {
48+ VALUE io ;
49+ int state ;
50+ int eof ;
51+ };
52+
53+ BIO *
54+ ossl_bio_new (VALUE io )
55+ {
56+ BIO * bio = BIO_new (ossl_bio_meth );
57+ if (!bio )
58+ ossl_raise (eOSSLError , "BIO_new" );
59+ struct ossl_bio_ctx * ctx = BIO_get_data (bio );
60+ ctx -> io = io ;
61+ BIO_set_init (bio , 1 );
62+ return bio ;
63+ }
64+
65+ int
66+ ossl_bio_state (BIO * bio )
67+ {
68+ struct ossl_bio_ctx * ctx = BIO_get_data (bio );
69+ int state = ctx -> state ;
70+ ctx -> state = 0 ;
71+ return state ;
72+ }
73+
74+ static int
75+ bio_create (BIO * bio )
76+ {
77+ struct ossl_bio_ctx * ctx = OPENSSL_malloc (sizeof (* ctx ));
78+ if (!ctx )
79+ return 0 ;
80+ memset (ctx , 0 , sizeof (* ctx ));
81+ BIO_set_data (bio , ctx );
82+
83+ return 1 ;
84+ }
85+
86+ static int
87+ bio_destroy (BIO * bio )
88+ {
89+ struct ossl_bio_ctx * ctx = BIO_get_data (bio );
90+ if (ctx ) {
91+ OPENSSL_free (ctx );
92+ BIO_set_data (bio , NULL );
93+ }
94+
95+ return 1 ;
96+ }
97+
98+ struct bwrite_args {
99+ BIO * bio ;
100+ struct ossl_bio_ctx * ctx ;
101+ const char * data ;
102+ int dlen ;
103+ int written ;
104+ };
105+
106+ static VALUE
107+ bio_bwrite0 (VALUE args )
108+ {
109+ struct bwrite_args * p = (void * )args ;
110+ BIO_clear_retry_flags (p -> bio );
111+
112+ VALUE fargs [] = { rb_str_new_static (p -> data , p -> dlen ), nonblock_kwargs };
113+ VALUE ret = rb_funcallv_public_kw (p -> ctx -> io , rb_intern ("write_nonblock" ),
114+ 2 , fargs , RB_PASS_KEYWORDS );
115+
116+ if (RB_INTEGER_TYPE_P (ret )) {
117+ p -> written = NUM2INT (ret );
118+ return Qtrue ;
119+ }
120+ else if (ret == sym_wait_readable ) {
121+ BIO_set_retry_read (p -> bio );
122+ return Qfalse ;
123+ }
124+ else if (ret == sym_wait_writable ) {
125+ BIO_set_retry_write (p -> bio );
126+ return Qfalse ;
127+ }
128+ else {
129+ rb_raise (rb_eTypeError , "write_nonblock must return an Integer, "
130+ ":wait_readable, or :wait_writable" );
131+ }
132+ }
133+
134+ static int
135+ bio_bwrite (BIO * bio , const char * data , int dlen )
136+ {
137+ struct ossl_bio_ctx * ctx = BIO_get_data (bio );
138+ struct bwrite_args args = { bio , ctx , data , dlen , 0 };
139+ int state ;
140+
141+ if (ctx -> state )
142+ return -1 ;
143+
144+ VALUE ok = rb_protect (bio_bwrite0 , (VALUE )& args , & state );
145+ if (state ) {
146+ ctx -> state = state ;
147+ return -1 ;
148+ }
149+ if (RTEST (ok ))
150+ return args .written ;
151+ return -1 ;
152+ }
153+
154+ struct bread_args {
155+ BIO * bio ;
156+ struct ossl_bio_ctx * ctx ;
157+ char * data ;
158+ int dlen ;
159+ int readbytes ;
160+ };
161+
162+ static VALUE
163+ bio_bread0 (VALUE args )
164+ {
165+ struct bread_args * p = (void * )args ;
166+ BIO_clear_retry_flags (p -> bio );
167+
168+ VALUE fargs [] = { INT2NUM (p -> dlen ), nonblock_kwargs };
169+ VALUE ret = rb_funcallv_public_kw (p -> ctx -> io , rb_intern ("read_nonblock" ),
170+ 2 , fargs , RB_PASS_KEYWORDS );
171+
172+ if (RB_TYPE_P (ret , T_STRING )) {
173+ int len = RSTRING_LENINT (ret );
174+ if (len > p -> dlen )
175+ rb_raise (rb_eTypeError , "read_nonblock returned too much data" );
176+ memcpy (p -> data , RSTRING_PTR (ret ), len );
177+ p -> readbytes = len ;
178+ return Qtrue ;
179+ }
180+ else if (NIL_P (ret )) {
181+ // In OpenSSL 3.0 or later: BIO_set_flags(p->bio, BIO_FLAGS_IN_EOF);
182+ p -> ctx -> eof = 1 ;
183+ return Qtrue ;
184+ }
185+ else if (ret == sym_wait_readable ) {
186+ BIO_set_retry_read (p -> bio );
187+ return Qfalse ;
188+ }
189+ else if (ret == sym_wait_writable ) {
190+ BIO_set_retry_write (p -> bio );
191+ return Qfalse ;
192+ }
193+ else {
194+ rb_raise (rb_eTypeError , "write_nonblock must return an Integer, "
195+ ":wait_readable, or :wait_writable" );
196+ }
197+ }
198+
199+ static int
200+ bio_bread (BIO * bio , char * data , int dlen )
201+ {
202+ struct ossl_bio_ctx * ctx = BIO_get_data (bio );
203+ struct bread_args args = { bio , ctx , data , dlen , 0 };
204+ int state ;
205+
206+ if (ctx -> state )
207+ return -1 ;
208+
209+ VALUE ok = rb_protect (bio_bread0 , (VALUE )& args , & state );
210+ if (state ) {
211+ ctx -> state = state ;
212+ return -1 ;
213+ }
214+ if (RTEST (ok ))
215+ return args .readbytes ;
216+ return -1 ;
217+ }
218+
219+ static VALUE
220+ bio_flush0 (VALUE vctx )
221+ {
222+ struct ossl_bio_ctx * ctx = (void * )vctx ;
223+ return rb_funcallv_public (ctx -> io , rb_intern ("flush" ), 0 , NULL );
224+ }
225+
226+ static long
227+ bio_ctrl (BIO * bio , int cmd , long larg , void * parg )
228+ {
229+ struct ossl_bio_ctx * ctx = BIO_get_data (bio );
230+ int state ;
231+
232+ if (ctx -> state )
233+ return 0 ;
234+
235+ switch (cmd ) {
236+ case BIO_CTRL_EOF :
237+ return ctx -> eof ;
238+ case BIO_CTRL_FLUSH :
239+ rb_protect (bio_flush0 , (VALUE )ctx , & state );
240+ ctx -> state = state ;
241+ return !state ;
242+ default :
243+ return 0 ;
244+ }
245+ }
246+
247+ void
248+ Init_ossl_bio (void )
249+ {
250+ ossl_bio_meth = BIO_meth_new (BIO_TYPE_SOURCE_SINK , "Ruby IO-like object" );
251+ if (!ossl_bio_meth )
252+ ossl_raise (eOSSLError , "BIO_meth_new" );
253+ if (!BIO_meth_set_create (ossl_bio_meth , bio_create ) ||
254+ !BIO_meth_set_destroy (ossl_bio_meth , bio_destroy ) ||
255+ !BIO_meth_set_write (ossl_bio_meth , bio_bwrite ) ||
256+ !BIO_meth_set_read (ossl_bio_meth , bio_bread ) ||
257+ !BIO_meth_set_ctrl (ossl_bio_meth , bio_ctrl )) {
258+ BIO_meth_free (ossl_bio_meth );
259+ ossl_bio_meth = NULL ;
260+ ossl_raise (eOSSLError , "BIO_meth_set_*" );
261+ }
262+
263+ nonblock_kwargs = rb_hash_new ();
264+ rb_hash_aset (nonblock_kwargs , ID2SYM (rb_intern_const ("exception" )), Qfalse );
265+ rb_global_variable (& nonblock_kwargs );
266+
267+ sym_wait_readable = ID2SYM (rb_intern_const ("wait_readable" ));
268+ sym_wait_writable = ID2SYM (rb_intern_const ("wait_writable" ));
269+ }
0 commit comments