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 
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       netvc->do_io_close();
87       delete this;
88       return EVENT_ERROR;
89     case VC_EVENT_READ_COMPLETE:
90       break;
91     default:
92       return EVENT_ERROR;
93     }
94 
95     // Cancel the action, so later timeouts and errors don't try to
96     // send the event to the Accept object.  After this point, the accept
97     // object does not care.
98     netvc->set_action(nullptr);
99 
100     Continuation *endpoint_cont = netvc->endpoint();
101     if (!endpoint_cont) {
102       // Route to the default endpoint
103       endpoint_cont = npnParent->endpoint;
104     }
105 
106     if (endpoint_cont) {
107       // disable read io, send events to endpoint
108       netvc->do_io_read(endpoint_cont, 0, nullptr);
109 
110       send_plugin_event(endpoint_cont, NET_EVENT_ACCEPT, netvc);
111     } else {
112       // No handler, what should we do? Best to just kill the VC while we can.
113       netvc->do_io_close();
114     }
115 
116     delete this;
117     return EVENT_CONT;
118   }
119 
120   const SSLNextProtocolAccept *npnParent;
121 };
122 
123 int
mainEvent(int event,void * edata)124 SSLNextProtocolAccept::mainEvent(int event, void *edata)
125 {
126   SSLNetVConnection *netvc = ssl_netvc_cast(event, edata);
127 
128   Debug("ssl", "[SSLNextProtocolAccept:mainEvent] event %d netvc %p", event, netvc);
129   switch (event) {
130   case NET_EVENT_ACCEPT:
131     ink_release_assert(netvc != nullptr);
132 
133     netvc->setTransparentPassThrough(transparent_passthrough);
134 
135     // Register our protocol set with the VC and kick off a zero-length read to
136     // force the SSLNetVConnection to complete the SSL handshake. Don't tell
137     // the endpoint that there is an accept to handle until the read completes
138     // and we know which protocol was negotiated.
139     netvc->registerNextProtocolSet(&this->protoset, this->protoenabled);
140     netvc->do_io_read(new SSLNextProtocolTrampoline(this, netvc->mutex), 0, this->buffer);
141     return EVENT_CONT;
142   default:
143     if (netvc) {
144       netvc->do_io_close();
145     }
146     return EVENT_DONE;
147   }
148 }
149 
150 bool
accept(NetVConnection *,MIOBuffer *,IOBufferReader *)151 SSLNextProtocolAccept::accept(NetVConnection *, MIOBuffer *, IOBufferReader *)
152 {
153   ink_release_assert(0);
154   return false;
155 }
156 
157 bool
registerEndpoint(const char * protocol,Continuation * handler)158 SSLNextProtocolAccept::registerEndpoint(const char *protocol, Continuation *handler)
159 {
160   return this->protoset.registerEndpoint(protocol, handler);
161 }
162 
163 void
enableProtocols(const SessionProtocolSet & protos)164 SSLNextProtocolAccept::enableProtocols(const SessionProtocolSet &protos)
165 {
166   this->protoenabled = protos;
167 }
168 
SSLNextProtocolAccept(Continuation * ep,bool transparent_passthrough)169 SSLNextProtocolAccept::SSLNextProtocolAccept(Continuation *ep, bool transparent_passthrough)
170   : SessionAccept(nullptr), buffer(new_empty_MIOBuffer()), endpoint(ep), transparent_passthrough(transparent_passthrough)
171 {
172   SET_HANDLER(&SSLNextProtocolAccept::mainEvent);
173 }
174 
175 SSLNextProtocolSet *
getProtoSet()176 SSLNextProtocolAccept::getProtoSet()
177 {
178   return &this->protoset;
179 }
180 
~SSLNextProtocolAccept()181 SSLNextProtocolAccept::~SSLNextProtocolAccept()
182 {
183   free_MIOBuffer(this->buffer);
184 }
185