1 /** @file
2 
3   An example plugin for accept object protocol set API.
4 
5   This clones the protocol sets attached to all the accept objects and unregisters HTTP/2 from those
6   copies.  The protocol set for incoming connections that match a list of domains are replaced with
7   the copy, effectively disabling HTTP/2 for those domains.
8 
9   @section license License
10 
11   Licensed to the Apache Software Foundation (ASF) under one
12   or more contributor license agreements.  See the NOTICE file
13   distributed with this work for additional information
14   regarding copyright ownership.  The ASF licenses this file
15   to you under the Apache License, Version 2.0 (the
16   "License"); you may not use this file except in compliance
17   with the License.  You may obtain a copy of the License at
18 
19       http://www.apache.org/licenses/LICENSE-2.0
20 
21   Unless required by applicable law or agreed to in writing, software
22   distributed under the License is distributed on an "AS IS" BASIS,
23   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24   See the License for the specific language governing permissions and
25   limitations under the License.
26  */
27 
28 #include <ts/ts.h>
29 
30 #include <unordered_set>
31 #include <string>
32 #include <cstring>
33 #include <openssl/ssl.h>
34 
35 #define PLUGIN_NAME "disable_http2"
36 
37 // Map of domains to tweak.
38 using DomainSet = std::unordered_set<std::string>;
39 DomainSet Domains;
40 
41 int
CB_SNI(TSCont contp,TSEvent,void * cb_data)42 CB_SNI(TSCont contp, TSEvent, void *cb_data)
43 {
44   auto vc                  = static_cast<TSVConn>(cb_data);
45   TSSslConnection ssl_conn = TSVConnSslConnectionGet(vc);
46   auto *ssl                = reinterpret_cast<SSL *>(ssl_conn);
47   char const *sni          = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
48   if (sni) {
49     if (Domains.find(sni) != Domains.end()) {
50       TSDebug(PLUGIN_NAME, "Disable H2 for SNI=%s", sni);
51       TSVConnProtocolDisable(vc, TS_ALPN_PROTOCOL_HTTP_2_0);
52     }
53   }
54 
55   TSVConnReenable(vc);
56   return TS_SUCCESS;
57 }
58 
59 void
TSPluginInit(int argc,char const * argv[])60 TSPluginInit(int argc, char const *argv[])
61 {
62   int ret;
63   TSPluginRegistrationInfo info;
64 
65   info.plugin_name   = PLUGIN_NAME;
66   info.vendor_name   = "Apache Software Foundation";
67   info.support_email = "dev@trafficserver.apache.org";
68   ret                = TSPluginRegister(&info);
69 
70   if (ret != TS_SUCCESS) {
71     TSError("[%s] registration failed", PLUGIN_NAME);
72     return;
73   } else if (argc < 2) {
74     TSError("[%s] Usage %s.so servername1 servername2 ... ", PLUGIN_NAME, PLUGIN_NAME);
75     return;
76   } else {
77     TSDebug(PLUGIN_NAME, "registration succeeded");
78   }
79 
80   for (int i = 1; i < argc; i++) {
81     TSDebug(PLUGIN_NAME, "%s added to the No-H2 list", argv[i]);
82     Domains.emplace(std::string(argv[i], strlen(argv[i])));
83   }
84   // These callbacks do not modify any state so no lock is needed.
85   TSCont cb_sni = TSContCreate(&CB_SNI, nullptr);
86 
87   TSHttpHookAdd(TS_SSL_SERVERNAME_HOOK, cb_sni);
88 }
89