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