openssl/ssl/
callbacks.rs

1use cfg_if::cfg_if;
2use foreign_types::ForeignType;
3use foreign_types::ForeignTypeRef;
4#[cfg(any(ossl111, not(osslconf = "OPENSSL_NO_PSK")))]
5use libc::c_char;
6#[cfg(ossl111)]
7use libc::size_t;
8use libc::{c_int, c_uchar, c_uint, c_void};
9#[cfg(any(ossl111, not(osslconf = "OPENSSL_NO_PSK")))]
10use std::ffi::CStr;
11use std::mem;
12use std::ptr;
13#[cfg(any(ossl111, boringssl, awslc))]
14use std::str;
15use std::sync::Arc;
16
17use crate::dh::Dh;
18#[cfg(all(ossl102, not(ossl110)))]
19use crate::ec::EcKey;
20use crate::error::ErrorStack;
21use crate::pkey::Params;
22use crate::ssl::AlpnError;
23use crate::ssl::{
24    try_get_session_ctx_index, SniError, Ssl, SslAlert, SslContext, SslContextRef, SslRef,
25    SslSession, SslSessionRef,
26};
27#[cfg(ossl111)]
28use crate::ssl::{ClientHelloResponse, ExtensionContext};
29use crate::util;
30#[cfg(any(ossl111, boringssl, awslc))]
31use crate::util::ForeignTypeRefExt;
32#[cfg(ossl111)]
33use crate::x509::X509Ref;
34use crate::x509::{X509StoreContext, X509StoreContextRef};
35
36pub extern "C" fn raw_verify<F>(preverify_ok: c_int, x509_ctx: *mut ffi::X509_STORE_CTX) -> c_int
37where
38    F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send,
39{
40    unsafe {
41        let ctx = X509StoreContextRef::from_ptr_mut(x509_ctx);
42        let ssl_idx = X509StoreContext::ssl_idx().expect("BUG: store context ssl index missing");
43        let verify_idx = SslContext::cached_ex_index::<F>();
44
45        // raw pointer shenanigans to break the borrow of ctx
46        // the callback can't mess with its own ex_data slot so this is safe
47        let verify = ctx
48            .ex_data(ssl_idx)
49            .expect("BUG: store context missing ssl")
50            .ssl_context()
51            .ex_data(verify_idx)
52            .expect("BUG: verify callback missing") as *const F;
53
54        (*verify)(preverify_ok != 0, ctx) as c_int
55    }
56}
57
58#[cfg(not(osslconf = "OPENSSL_NO_PSK"))]
59pub extern "C" fn raw_client_psk<F>(
60    ssl: *mut ffi::SSL,
61    hint: *const c_char,
62    identity: *mut c_char,
63    max_identity_len: c_uint,
64    psk: *mut c_uchar,
65    max_psk_len: c_uint,
66) -> c_uint
67where
68    F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8], &mut [u8]) -> Result<usize, ErrorStack>
69        + 'static
70        + Sync
71        + Send,
72{
73    unsafe {
74        let ssl = SslRef::from_ptr_mut(ssl);
75        let callback_idx = SslContext::cached_ex_index::<F>();
76
77        let callback = ssl
78            .ssl_context()
79            .ex_data(callback_idx)
80            .expect("BUG: psk callback missing") as *const F;
81        let hint = if !hint.is_null() {
82            Some(CStr::from_ptr(hint).to_bytes())
83        } else {
84            None
85        };
86        // Give the callback mutable slices into which it can write the identity and psk.
87        let identity_sl = util::from_raw_parts_mut(identity as *mut u8, max_identity_len as usize);
88        #[allow(clippy::unnecessary_cast)]
89        let psk_sl = util::from_raw_parts_mut(psk as *mut u8, max_psk_len as usize);
90        match (*callback)(ssl, hint, identity_sl, psk_sl) {
91            Ok(psk_len) => psk_len as u32,
92            Err(e) => {
93                e.put();
94                0
95            }
96        }
97    }
98}
99
100#[cfg(not(osslconf = "OPENSSL_NO_PSK"))]
101pub extern "C" fn raw_server_psk<F>(
102    ssl: *mut ffi::SSL,
103    identity: *const c_char,
104    psk: *mut c_uchar,
105    max_psk_len: c_uint,
106) -> c_uint
107where
108    F: Fn(&mut SslRef, Option<&[u8]>, &mut [u8]) -> Result<usize, ErrorStack>
109        + 'static
110        + Sync
111        + Send,
112{
113    unsafe {
114        let ssl = SslRef::from_ptr_mut(ssl);
115        let callback_idx = SslContext::cached_ex_index::<F>();
116
117        let callback = ssl
118            .ssl_context()
119            .ex_data(callback_idx)
120            .expect("BUG: psk callback missing") as *const F;
121        let identity = if identity.is_null() {
122            None
123        } else {
124            Some(CStr::from_ptr(identity).to_bytes())
125        };
126        // Give the callback mutable slices into which it can write the psk.
127        #[allow(clippy::unnecessary_cast)]
128        let psk_sl = util::from_raw_parts_mut(psk as *mut u8, max_psk_len as usize);
129        match (*callback)(ssl, identity, psk_sl) {
130            Ok(psk_len) => psk_len as u32,
131            Err(e) => {
132                e.put();
133                0
134            }
135        }
136    }
137}
138
139pub extern "C" fn ssl_raw_verify<F>(
140    preverify_ok: c_int,
141    x509_ctx: *mut ffi::X509_STORE_CTX,
142) -> c_int
143where
144    F: Fn(bool, &mut X509StoreContextRef) -> bool + 'static + Sync + Send,
145{
146    unsafe {
147        let ctx = X509StoreContextRef::from_ptr_mut(x509_ctx);
148        let ssl_idx = X509StoreContext::ssl_idx().expect("BUG: store context ssl index missing");
149        let callback_idx = Ssl::cached_ex_index::<Arc<F>>();
150
151        let callback = ctx
152            .ex_data(ssl_idx)
153            .expect("BUG: store context missing ssl")
154            .ex_data(callback_idx)
155            .expect("BUG: ssl verify callback missing")
156            .clone();
157
158        callback(preverify_ok != 0, ctx) as c_int
159    }
160}
161
162pub extern "C" fn raw_sni<F>(ssl: *mut ffi::SSL, al: *mut c_int, arg: *mut c_void) -> c_int
163where
164    F: Fn(&mut SslRef, &mut SslAlert) -> Result<(), SniError> + 'static + Sync + Send,
165{
166    unsafe {
167        let ssl = SslRef::from_ptr_mut(ssl);
168        let callback = arg as *const F;
169        let mut alert = SslAlert(*al);
170
171        let r = (*callback)(ssl, &mut alert);
172        *al = alert.0;
173        match r {
174            Ok(()) => ffi::SSL_TLSEXT_ERR_OK,
175            Err(e) => e.0,
176        }
177    }
178}
179
180pub extern "C" fn raw_alpn_select<F>(
181    ssl: *mut ffi::SSL,
182    out: *mut *const c_uchar,
183    outlen: *mut c_uchar,
184    inbuf: *const c_uchar,
185    inlen: c_uint,
186    _arg: *mut c_void,
187) -> c_int
188where
189    F: for<'a> Fn(&mut SslRef, &'a [u8]) -> Result<&'a [u8], AlpnError> + 'static + Sync + Send,
190{
191    unsafe {
192        let ssl = SslRef::from_ptr_mut(ssl);
193        let callback = ssl
194            .ssl_context()
195            .ex_data(SslContext::cached_ex_index::<F>())
196            .expect("BUG: alpn callback missing") as *const F;
197        #[allow(clippy::unnecessary_cast)]
198        let protos = util::from_raw_parts(inbuf as *const u8, inlen as usize);
199
200        match (*callback)(ssl, protos) {
201            Ok(proto) => {
202                *out = proto.as_ptr() as *const c_uchar;
203                *outlen = proto.len() as c_uchar;
204                ffi::SSL_TLSEXT_ERR_OK
205            }
206            Err(e) => e.0,
207        }
208    }
209}
210
211pub unsafe extern "C" fn raw_tmp_dh<F>(
212    ssl: *mut ffi::SSL,
213    is_export: c_int,
214    keylength: c_int,
215) -> *mut ffi::DH
216where
217    F: Fn(&mut SslRef, bool, u32) -> Result<Dh<Params>, ErrorStack> + 'static + Sync + Send,
218{
219    let ssl = SslRef::from_ptr_mut(ssl);
220    let callback = ssl
221        .ssl_context()
222        .ex_data(SslContext::cached_ex_index::<F>())
223        .expect("BUG: tmp dh callback missing") as *const F;
224
225    match (*callback)(ssl, is_export != 0, keylength as u32) {
226        Ok(dh) => {
227            let ptr = dh.as_ptr();
228            mem::forget(dh);
229            ptr
230        }
231        Err(e) => {
232            e.put();
233            ptr::null_mut()
234        }
235    }
236}
237
238#[cfg(all(ossl102, not(ossl110)))]
239pub unsafe extern "C" fn raw_tmp_ecdh<F>(
240    ssl: *mut ffi::SSL,
241    is_export: c_int,
242    keylength: c_int,
243) -> *mut ffi::EC_KEY
244where
245    F: Fn(&mut SslRef, bool, u32) -> Result<EcKey<Params>, ErrorStack> + 'static + Sync + Send,
246{
247    let ssl = SslRef::from_ptr_mut(ssl);
248    let callback = ssl
249        .ssl_context()
250        .ex_data(SslContext::cached_ex_index::<F>())
251        .expect("BUG: tmp ecdh callback missing") as *const F;
252
253    match (*callback)(ssl, is_export != 0, keylength as u32) {
254        Ok(ec_key) => {
255            let ptr = ec_key.as_ptr();
256            mem::forget(ec_key);
257            ptr
258        }
259        Err(e) => {
260            e.put();
261            ptr::null_mut()
262        }
263    }
264}
265
266pub unsafe extern "C" fn raw_tmp_dh_ssl<F>(
267    ssl: *mut ffi::SSL,
268    is_export: c_int,
269    keylength: c_int,
270) -> *mut ffi::DH
271where
272    F: Fn(&mut SslRef, bool, u32) -> Result<Dh<Params>, ErrorStack> + 'static + Sync + Send,
273{
274    let ssl = SslRef::from_ptr_mut(ssl);
275    let callback = ssl
276        .ex_data(Ssl::cached_ex_index::<Arc<F>>())
277        .expect("BUG: ssl tmp dh callback missing")
278        .clone();
279
280    match callback(ssl, is_export != 0, keylength as u32) {
281        Ok(dh) => {
282            let ptr = dh.as_ptr();
283            mem::forget(dh);
284            ptr
285        }
286        Err(e) => {
287            e.put();
288            ptr::null_mut()
289        }
290    }
291}
292
293#[cfg(all(ossl102, not(ossl110)))]
294pub unsafe extern "C" fn raw_tmp_ecdh_ssl<F>(
295    ssl: *mut ffi::SSL,
296    is_export: c_int,
297    keylength: c_int,
298) -> *mut ffi::EC_KEY
299where
300    F: Fn(&mut SslRef, bool, u32) -> Result<EcKey<Params>, ErrorStack> + 'static + Sync + Send,
301{
302    let ssl = SslRef::from_ptr_mut(ssl);
303    let callback = ssl
304        .ex_data(Ssl::cached_ex_index::<Arc<F>>())
305        .expect("BUG: ssl tmp ecdh callback missing")
306        .clone();
307
308    match callback(ssl, is_export != 0, keylength as u32) {
309        Ok(ec_key) => {
310            let ptr = ec_key.as_ptr();
311            mem::forget(ec_key);
312            ptr
313        }
314        Err(e) => {
315            e.put();
316            ptr::null_mut()
317        }
318    }
319}
320
321pub unsafe extern "C" fn raw_tlsext_status<F>(ssl: *mut ffi::SSL, _: *mut c_void) -> c_int
322where
323    F: Fn(&mut SslRef) -> Result<bool, ErrorStack> + 'static + Sync + Send,
324{
325    let ssl = SslRef::from_ptr_mut(ssl);
326    let callback = ssl
327        .ssl_context()
328        .ex_data(SslContext::cached_ex_index::<F>())
329        .expect("BUG: ocsp callback missing") as *const F;
330    let ret = (*callback)(ssl);
331
332    if ssl.is_server() {
333        match ret {
334            Ok(true) => ffi::SSL_TLSEXT_ERR_OK,
335            Ok(false) => ffi::SSL_TLSEXT_ERR_NOACK,
336            Err(e) => {
337                e.put();
338                ffi::SSL_TLSEXT_ERR_ALERT_FATAL
339            }
340        }
341    } else {
342        match ret {
343            Ok(true) => 1,
344            Ok(false) => 0,
345            Err(e) => {
346                e.put();
347                -1
348            }
349        }
350    }
351}
352
353pub unsafe extern "C" fn raw_new_session<F>(
354    ssl: *mut ffi::SSL,
355    session: *mut ffi::SSL_SESSION,
356) -> c_int
357where
358    F: Fn(&mut SslRef, SslSession) + 'static + Sync + Send,
359{
360    let session_ctx_index =
361        try_get_session_ctx_index().expect("BUG: session context index initialization failed");
362    let ssl = SslRef::from_ptr_mut(ssl);
363    let callback = ssl
364        .ex_data(*session_ctx_index)
365        .expect("BUG: session context missing")
366        .ex_data(SslContext::cached_ex_index::<F>())
367        .expect("BUG: new session callback missing") as *const F;
368    let session = SslSession::from_ptr(session);
369
370    (*callback)(ssl, session);
371
372    // the return code doesn't indicate error vs success, but whether or not we consumed the session
373    1
374}
375
376pub unsafe extern "C" fn raw_remove_session<F>(
377    ctx: *mut ffi::SSL_CTX,
378    session: *mut ffi::SSL_SESSION,
379) where
380    F: Fn(&SslContextRef, &SslSessionRef) + 'static + Sync + Send,
381{
382    let ctx = SslContextRef::from_ptr(ctx);
383    let callback = ctx
384        .ex_data(SslContext::cached_ex_index::<F>())
385        .expect("BUG: remove session callback missing");
386    let session = SslSessionRef::from_ptr(session);
387
388    callback(ctx, session)
389}
390
391cfg_if! {
392    if #[cfg(any(ossl110, libressl, boringssl, awslc))] {
393        type DataPtr = *const c_uchar;
394    } else {
395        type DataPtr = *mut c_uchar;
396    }
397}
398
399pub unsafe extern "C" fn raw_get_session<F>(
400    ssl: *mut ffi::SSL,
401    data: DataPtr,
402    len: c_int,
403    copy: *mut c_int,
404) -> *mut ffi::SSL_SESSION
405where
406    F: Fn(&mut SslRef, &[u8]) -> Option<SslSession> + 'static + Sync + Send,
407{
408    let session_ctx_index =
409        try_get_session_ctx_index().expect("BUG: session context index initialization failed");
410    let ssl = SslRef::from_ptr_mut(ssl);
411    let callback = ssl
412        .ex_data(*session_ctx_index)
413        .expect("BUG: session context missing")
414        .ex_data(SslContext::cached_ex_index::<F>())
415        .expect("BUG: get session callback missing") as *const F;
416    #[allow(clippy::unnecessary_cast)]
417    let data = util::from_raw_parts(data as *const u8, len as usize);
418
419    match (*callback)(ssl, data) {
420        Some(session) => {
421            let p = session.as_ptr();
422            mem::forget(session);
423            *copy = 0;
424            p
425        }
426        None => ptr::null_mut(),
427    }
428}
429
430#[cfg(any(ossl111, boringssl, awslc))]
431pub unsafe extern "C" fn raw_keylog<F>(ssl: *const ffi::SSL, line: *const c_char)
432where
433    F: Fn(&SslRef, &str) + 'static + Sync + Send,
434{
435    let ssl = SslRef::from_const_ptr(ssl);
436    let callback = ssl
437        .ssl_context()
438        .ex_data(SslContext::cached_ex_index::<F>())
439        .expect("BUG: get session callback missing");
440    let line = CStr::from_ptr(line).to_bytes();
441    let line = str::from_utf8_unchecked(line);
442
443    callback(ssl, line);
444}
445
446#[cfg(ossl111)]
447pub unsafe extern "C" fn raw_stateless_cookie_generate<F>(
448    ssl: *mut ffi::SSL,
449    cookie: *mut c_uchar,
450    cookie_len: *mut size_t,
451) -> c_int
452where
453    F: Fn(&mut SslRef, &mut [u8]) -> Result<usize, ErrorStack> + 'static + Sync + Send,
454{
455    let ssl = SslRef::from_ptr_mut(ssl);
456    let callback = ssl
457        .ssl_context()
458        .ex_data(SslContext::cached_ex_index::<F>())
459        .expect("BUG: stateless cookie generate callback missing") as *const F;
460    #[allow(clippy::unnecessary_cast)]
461    let slice = util::from_raw_parts_mut(cookie as *mut u8, ffi::SSL_COOKIE_LENGTH as usize);
462    match (*callback)(ssl, slice) {
463        Ok(len) => {
464            *cookie_len = len as size_t;
465            1
466        }
467        Err(e) => {
468            e.put();
469            0
470        }
471    }
472}
473
474#[cfg(ossl111)]
475pub unsafe extern "C" fn raw_stateless_cookie_verify<F>(
476    ssl: *mut ffi::SSL,
477    cookie: *const c_uchar,
478    cookie_len: size_t,
479) -> c_int
480where
481    F: Fn(&mut SslRef, &[u8]) -> bool + 'static + Sync + Send,
482{
483    let ssl = SslRef::from_ptr_mut(ssl);
484    let callback = ssl
485        .ssl_context()
486        .ex_data(SslContext::cached_ex_index::<F>())
487        .expect("BUG: stateless cookie verify callback missing") as *const F;
488    #[allow(clippy::unnecessary_cast)]
489    let slice = util::from_raw_parts(cookie as *const c_uchar as *const u8, cookie_len);
490    (*callback)(ssl, slice) as c_int
491}
492
493#[cfg(not(any(boringssl, awslc)))]
494pub extern "C" fn raw_cookie_generate<F>(
495    ssl: *mut ffi::SSL,
496    cookie: *mut c_uchar,
497    cookie_len: *mut c_uint,
498) -> c_int
499where
500    F: Fn(&mut SslRef, &mut [u8]) -> Result<usize, ErrorStack> + 'static + Sync + Send,
501{
502    unsafe {
503        let ssl = SslRef::from_ptr_mut(ssl);
504        let callback = ssl
505            .ssl_context()
506            .ex_data(SslContext::cached_ex_index::<F>())
507            .expect("BUG: cookie generate callback missing") as *const F;
508        // We subtract 1 from DTLS1_COOKIE_LENGTH as the ostensible value, 256, is erroneous but retained for
509        // compatibility. See comments in dtls1.h.
510        #[allow(clippy::unnecessary_cast)]
511        let slice =
512            util::from_raw_parts_mut(cookie as *mut u8, ffi::DTLS1_COOKIE_LENGTH as usize - 1);
513        match (*callback)(ssl, slice) {
514            Ok(len) => {
515                *cookie_len = len as c_uint;
516                1
517            }
518            Err(e) => {
519                e.put();
520                0
521            }
522        }
523    }
524}
525
526#[cfg(not(any(boringssl, awslc)))]
527cfg_if! {
528    if #[cfg(any(ossl110, libressl))] {
529        type CookiePtr = *const c_uchar;
530    } else {
531        type CookiePtr = *mut c_uchar;
532    }
533}
534
535#[cfg(not(any(boringssl, awslc)))]
536pub extern "C" fn raw_cookie_verify<F>(
537    ssl: *mut ffi::SSL,
538    cookie: CookiePtr,
539    cookie_len: c_uint,
540) -> c_int
541where
542    F: Fn(&mut SslRef, &[u8]) -> bool + 'static + Sync + Send,
543{
544    unsafe {
545        let ssl = SslRef::from_ptr_mut(ssl);
546        let callback = ssl
547            .ssl_context()
548            .ex_data(SslContext::cached_ex_index::<F>())
549            .expect("BUG: cookie verify callback missing") as *const F;
550        #[allow(clippy::unnecessary_cast)]
551        let slice =
552            util::from_raw_parts(cookie as *const c_uchar as *const u8, cookie_len as usize);
553        (*callback)(ssl, slice) as c_int
554    }
555}
556
557#[cfg(ossl111)]
558pub struct CustomExtAddState<T>(Option<T>);
559
560#[cfg(ossl111)]
561pub extern "C" fn raw_custom_ext_add<F, T>(
562    ssl: *mut ffi::SSL,
563    _: c_uint,
564    context: c_uint,
565    out: *mut *const c_uchar,
566    outlen: *mut size_t,
567    x: *mut ffi::X509,
568    chainidx: size_t,
569    al: *mut c_int,
570    _: *mut c_void,
571) -> c_int
572where
573    F: Fn(&mut SslRef, ExtensionContext, Option<(usize, &X509Ref)>) -> Result<Option<T>, SslAlert>
574        + 'static
575        + Sync
576        + Send,
577    T: AsRef<[u8]> + 'static + Sync + Send,
578{
579    unsafe {
580        let ssl = SslRef::from_ptr_mut(ssl);
581        let callback = ssl
582            .ssl_context()
583            .ex_data(SslContext::cached_ex_index::<F>())
584            .expect("BUG: custom ext add callback missing") as *const F;
585        let ectx = ExtensionContext::from_bits_truncate(context);
586        let cert = if ectx.contains(ExtensionContext::TLS1_3_CERTIFICATE) {
587            Some((chainidx, X509Ref::from_ptr(x)))
588        } else {
589            None
590        };
591        match (*callback)(ssl, ectx, cert) {
592            Ok(None) => 0,
593            Ok(Some(buf)) => {
594                *outlen = buf.as_ref().len();
595                *out = buf.as_ref().as_ptr();
596
597                let idx = Ssl::cached_ex_index::<CustomExtAddState<T>>();
598                let mut buf = Some(buf);
599                let new = match ssl.ex_data_mut(idx) {
600                    Some(state) => {
601                        state.0 = buf.take();
602                        false
603                    }
604                    None => true,
605                };
606                if new {
607                    ssl.set_ex_data(idx, CustomExtAddState(buf));
608                }
609                1
610            }
611            Err(alert) => {
612                *al = alert.0;
613                -1
614            }
615        }
616    }
617}
618
619#[cfg(ossl111)]
620pub extern "C" fn raw_custom_ext_free<T>(
621    ssl: *mut ffi::SSL,
622    _: c_uint,
623    _: c_uint,
624    _: *const c_uchar,
625    _: *mut c_void,
626) where
627    T: 'static + Sync + Send,
628{
629    unsafe {
630        let ssl = SslRef::from_ptr_mut(ssl);
631        let idx = Ssl::cached_ex_index::<CustomExtAddState<T>>();
632        if let Some(state) = ssl.ex_data_mut(idx) {
633            state.0 = None;
634        }
635    }
636}
637
638#[cfg(ossl111)]
639pub extern "C" fn raw_custom_ext_parse<F>(
640    ssl: *mut ffi::SSL,
641    _: c_uint,
642    context: c_uint,
643    input: *const c_uchar,
644    inlen: size_t,
645    x: *mut ffi::X509,
646    chainidx: size_t,
647    al: *mut c_int,
648    _: *mut c_void,
649) -> c_int
650where
651    F: Fn(&mut SslRef, ExtensionContext, &[u8], Option<(usize, &X509Ref)>) -> Result<(), SslAlert>
652        + 'static
653        + Sync
654        + Send,
655{
656    unsafe {
657        let ssl = SslRef::from_ptr_mut(ssl);
658        let callback = ssl
659            .ssl_context()
660            .ex_data(SslContext::cached_ex_index::<F>())
661            .expect("BUG: custom ext parse callback missing") as *const F;
662        let ectx = ExtensionContext::from_bits_truncate(context);
663        #[allow(clippy::unnecessary_cast)]
664        let slice = util::from_raw_parts(input as *const u8, inlen);
665        let cert = if ectx.contains(ExtensionContext::TLS1_3_CERTIFICATE) {
666            Some((chainidx, X509Ref::from_ptr(x)))
667        } else {
668            None
669        };
670        match (*callback)(ssl, ectx, slice, cert) {
671            Ok(()) => 1,
672            Err(alert) => {
673                *al = alert.0;
674                0
675            }
676        }
677    }
678}
679
680#[cfg(ossl111)]
681pub unsafe extern "C" fn raw_client_hello<F>(
682    ssl: *mut ffi::SSL,
683    al: *mut c_int,
684    arg: *mut c_void,
685) -> c_int
686where
687    F: Fn(&mut SslRef, &mut SslAlert) -> Result<ClientHelloResponse, ErrorStack>
688        + 'static
689        + Sync
690        + Send,
691{
692    let ssl = SslRef::from_ptr_mut(ssl);
693    let callback = arg as *const F;
694    let mut alert = SslAlert(*al);
695
696    let r = (*callback)(ssl, &mut alert);
697    *al = alert.0;
698    match r {
699        Ok(c) => c.0,
700        Err(e) => {
701            e.put();
702            ffi::SSL_CLIENT_HELLO_ERROR
703        }
704    }
705}