1 /** @file
2 
3   SSLNextProtocolAccept
4 
5   @section license License
6 
7   Licensed to the Apache Software Foundation (ASF) under one
8   or more contributor license agreements.  See the NOTICE file
9   distributed with this work for additional information
10   regarding copyright ownership.  The ASF licenses this file
11   to you under the Apache License, Version 2.0 (the
12   "License"); you may not use this file except in compliance
13   with the License.  You may obtain a copy of the License at
14 
15       http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22  */
23 
24 #include "P_SSLNextProtocolAccept.h"
25 
26 static void
send_plugin_event(Continuation * plugin,int event,void * edata)27 send_plugin_event(Continuation *plugin, int event, void *edata)
28 {
29   if (plugin->mutex) {
30     SCOPED_MUTEX_LOCK(lock, plugin->mutex, this_ethread());
31     plugin->handleEvent(event, edata);
32   } else {
33     plugin->handleEvent(event, edata);
34   }
35 }
36 
37 static SSLNetVConnection *
ssl_netvc_cast(int event,void * edata)38 ssl_netvc_cast(int event, void *edata)
39 {
40   union {
41     VIO *vio;
42     NetVConnection *vc;
43   } ptr;
44 
45   switch (event) {
46   case NET_EVENT_ACCEPT:
47     ptr.vc = static_cast<NetVConnection *>(edata);
48     return dynamic_cast<SSLNetVConnection *>(ptr.vc);
49   case VC_EVENT_INACTIVITY_TIMEOUT:
50   case VC_EVENT_READ_COMPLETE:
51   case VC_EVENT_ERROR:
52     ptr.vio = static_cast<VIO *>(edata);
53     return dynamic_cast<SSLNetVConnection *>(ptr.vio->vc_server);
54   default:
55     return nullptr;
56   }
57 }
58 
59 // SSLNextProtocolTrampoline is the receiver of the I/O event generated when we perform a 0-length read on the new SSL
60 // connection. The 0-length read forces the SSL handshake, which allows us to bind an endpoint that is selected by the
61 // NPN extension. The Continuation that receives the read event *must* have a mutex, but we don't want to take a global
62 // lock across the handshake, so we make a trampoline to bounce the event from the SSL acceptor to the ultimate session
63 // acceptor.
64 struct SSLNextProtocolTrampoline : public Continuation {
SSLNextProtocolTrampolineSSLNextProtocolTrampoline65   SSLNextProtocolTrampoline(const SSLNextProtocolAccept *npn, Ptr<ProxyMutex> &mutex) : Continuation(mutex), npnParent(npn)
66   {
67     SET_HANDLER(&SSLNextProtocolTrampoline::ioCompletionEvent);
68   }
69 
70   int
ioCompletionEventSSLNextProtocolTrampoline71   ioCompletionEvent(int event, void *edata)
72   {
73     VIO *vio;
74     Continuation *plugin;
75     SSLNetVConnection *netvc;
76 
77     vio   = static_cast<VIO *>(edata);
78     netvc = dynamic_cast<SSLNetVConnection *>(vio->vc_server);
79     ink_assert(netvc != nullptr);
80 
81     switch (event) {
82     case VC_EVENT_EOS:
83     case VC_EVENT_ERROR:
84     case VC_EVENT_ACTIVE_TIMEOUT:
85     case VC_EVENT_INACTIVITY_TIMEOUT:
86       // Cancel the read before we have a chance to delete the continuation
87       netvc->do_io_read(nullptr, 0, nullptr);
88       netvc->do_io_close();
89       delete this;
90       return EVENT_ERROR;
91     case VC_EVENT_READ_COMPLETE:
92       break;
93     default:
94       return EVENT_ERROR;
95     }
96 
97     // Cancel the action, so later timeouts and errors don't try to
98     // send the event to the Accept object.  After this point, the accept
99     // object does not care.
100     netvc->set_action(nullptr);
101 
102     // Cancel the read before we have a chance to delete the continuation
103     netvc->do_io_read(nullptr, 0, nullptr);
104     plugin = netvc->endpoint();
105     if (plugin) {
106       send_plugin_event(plugin, NET_EVENT_ACCEPT, netvc);
107     } else if (npnParent->endpoint) {
108       // Route to the default endpoint
109       send_plugin_event(npnParent->endpoint, NET_EVENT_ACCEPT, netvc);
110     } else {
111       // No handler, what should we do? Best to just kill the VC while we can.
112       netvc->do_io_close();
113     }
114 
115     delete this;
116     return EVENT_CONT;
117   }
118 
119   const SSLNextProtocolAccept *npnParent;
120 };
121 
122 int
mainEvent(int event,void * edata)123 SSLNextProtocolAccept::mainEvent(int event, void *edata)
124 {
125   SSLNetVConnection *netvc = ssl_netvc_cast(event, edata);
126 
127   Debug("ssl", "[SSLNextProtocolAccept:mainEvent] event %d netvc %p", event, netvc);
128   switch (event) {
129   case NET_EVENT_ACCEPT:
130     ink_release_assert(netvc != nullptr);
131 
132     netvc->setTransparentPassThrough(transparent_passthrough);
133 
134     // Register our protocol set with the VC and kick off a zero-length read to
135     // force the SSLNetVConnection to complete the SSL handshake. Don't tell
136     // the endpoint that there is an accept to handle until the read completes
137     // and we know which protocol was negotiated.
138     netvc->registerNextProtocolSet(&this->protoset, this->protoenabled);
139     netvc->do_io_read(new SSLNextProtocolTrampoline(this, netvc->mutex), 0, this->buffer);
140     return EVENT_CONT;
141   default:
142     if (netvc) {
143       netvc->do_io_close();
144     }
145     return EVENT_DONE;
146   }
147 }
148 
149 bool
accept(NetVConnection *,MIOBuffer *,IOBufferReader *)150 SSLNextProtocolAccept::accept(NetVConnection *, MIOBuffer *, IOBufferReader *)
151 {
152   ink_release_assert(0);
153   return false;
154 }
155 
156 bool
registerEndpoint(const char * protocol,Continuation * handler)157 SSLNextProtocolAccept::registerEndpoint(const char *protocol, Continuation *handler)
158 {
159   return this->protoset.registerEndpoint(protocol, handler);
160 }
161 
162 void
enableProtocols(const SessionProtocolSet & protos)163 SSLNextProtocolAccept::enableProtocols(const SessionProtocolSet &protos)
164 {
165   this->protoenabled = protos;
166 }
167 
SSLNextProtocolAccept(Continuation * ep,bool transparent_passthrough)168 SSLNextProtocolAccept::SSLNextProtocolAccept(Continuation *ep, bool transparent_passthrough)
169   : SessionAccept(nullptr), buffer(new_empty_MIOBuffer()), endpoint(ep), transparent_passthrough(transparent_passthrough)
170 {
171   SET_HANDLER(&SSLNextProtocolAccept::mainEvent);
172 }
173 
174 SSLNextProtocolSet *
getProtoSet()175 SSLNextProtocolAccept::getProtoSet()
176 {
177   return &this->protoset;
178 }
179 
~SSLNextProtocolAccept()180 SSLNextProtocolAccept::~SSLNextProtocolAccept()
181 {
182   free_MIOBuffer(this->buffer);
183 }
184