@@ -226,6 +226,43 @@ int SelectALPNCallback(
226226 unsigned int inlen,
227227 void * arg) {
228228 TLSWrap* w = static_cast <TLSWrap*>(arg);
229+ if (w->alpn_callback_enabled_ ) {
230+ Environment* env = w->env ();
231+
232+ Local<Value> callback_arg =
233+ Buffer::Copy (env, reinterpret_cast <const char *>(in), inlen)
234+ .ToLocalChecked ();
235+
236+ MaybeLocal<Value> maybe_callback_result =
237+ w->MakeCallback (env->alpn_callback_string (), 1 , &callback_arg);
238+
239+ if (UNLIKELY (maybe_callback_result.IsEmpty ())) {
240+ // Implies the callback didn't return, because some exception was thrown
241+ // during processing, e.g. if callback returned an invalid ALPN value.
242+ return SSL_TLSEXT_ERR_ALERT_FATAL;
243+ }
244+
245+ Local<Value> callback_result = maybe_callback_result.ToLocalChecked ();
246+
247+ if (callback_result->IsUndefined ()) {
248+ // If you set an ALPN callback, but you return undefined for an ALPN
249+ // request, you're rejecting all proposed ALPN protocols, and so we send
250+ // a fatal alert:
251+ return SSL_TLSEXT_ERR_ALERT_FATAL;
252+ }
253+
254+ CHECK (callback_result->IsNumber ());
255+ unsigned int result_int = callback_result.As <v8::Number>()->Value ();
256+
257+ // The callback returns an offset into the given buffer, for the selected
258+ // protocol that should be returned. We then set outlen & out to point
259+ // to the selected input length & value directly:
260+ *outlen = *(in + result_int);
261+ *out = (in + result_int + 1 );
262+
263+ return SSL_TLSEXT_ERR_OK;
264+ }
265+
229266 const std::vector<unsigned char >& alpn_protos = w->alpn_protos_ ;
230267
231268 if (alpn_protos.empty ()) return SSL_TLSEXT_ERR_NOACK;
@@ -1233,6 +1270,15 @@ void TLSWrap::OnClientHelloParseEnd(void* arg) {
12331270 c->Cycle ();
12341271}
12351272
1273+ void TLSWrap::EnableALPNCb (const FunctionCallbackInfo<Value>& args) {
1274+ TLSWrap* wrap;
1275+ ASSIGN_OR_RETURN_UNWRAP (&wrap, args.Holder ());
1276+ wrap->alpn_callback_enabled_ = true ;
1277+
1278+ SSL* ssl = wrap->ssl_ .get ();
1279+ SSL_CTX_set_alpn_select_cb (SSL_get_SSL_CTX (ssl), SelectALPNCallback, wrap);
1280+ }
1281+
12361282void TLSWrap::GetServername (const FunctionCallbackInfo<Value>& args) {
12371283 Environment* env = Environment::GetCurrent (args);
12381284
@@ -2044,6 +2090,7 @@ void TLSWrap::Initialize(
20442090 SetProtoMethod (isolate, t, " certCbDone" , CertCbDone);
20452091 SetProtoMethod (isolate, t, " destroySSL" , DestroySSL);
20462092 SetProtoMethod (isolate, t, " enableCertCb" , EnableCertCb);
2093+ SetProtoMethod (isolate, t, " enableALPNCb" , EnableALPNCb);
20472094 SetProtoMethod (isolate, t, " endParser" , EndParser);
20482095 SetProtoMethod (isolate, t, " enableKeylogCallback" , EnableKeylogCallback);
20492096 SetProtoMethod (isolate, t, " enableSessionCallbacks" , EnableSessionCallbacks);
@@ -2109,6 +2156,7 @@ void TLSWrap::RegisterExternalReferences(ExternalReferenceRegistry* registry) {
21092156 registry->Register (CertCbDone);
21102157 registry->Register (DestroySSL);
21112158 registry->Register (EnableCertCb);
2159+ registry->Register (EnableALPNCb);
21122160 registry->Register (EndParser);
21132161 registry->Register (EnableKeylogCallback);
21142162 registry->Register (EnableSessionCallbacks);
0 commit comments