1 /** @file
2 
3   ProtocolProbeSessionAccept
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_Net.h"
25 #include "I_Machine.h"
26 #include "ProtocolProbeSessionAccept.h"
27 #include "http2/HTTP2.h"
28 #include "ProxyProtocol.h"
29 #include "I_NetVConnection.h"
30 
31 static bool
proto_is_http2(IOBufferReader * reader)32 proto_is_http2(IOBufferReader *reader)
33 {
34   char buf[HTTP2_CONNECTION_PREFACE_LEN];
35   char *end;
36   ptrdiff_t nbytes;
37 
38   end    = reader->memcpy(buf, sizeof(buf), 0 /* offset */);
39   nbytes = end - buf;
40 
41   // Client must send at least 4 bytes to get a reasonable match.
42   if (nbytes < 4) {
43     return false;
44   }
45 
46   ink_assert(nbytes <= (int64_t)HTTP2_CONNECTION_PREFACE_LEN);
47   return memcmp(HTTP2_CONNECTION_PREFACE, buf, nbytes) == 0;
48 }
49 
50 struct ProtocolProbeTrampoline : public Continuation, public ProtocolProbeSessionAcceptEnums {
51   static const size_t minimum_read_size   = 1;
52   static const unsigned buffer_size_index = CLIENT_CONNECTION_FIRST_READ_BUFFER_SIZE_INDEX;
53   IOBufferReader *reader;
54 
ProtocolProbeTrampolineProtocolProbeTrampoline55   explicit ProtocolProbeTrampoline(const ProtocolProbeSessionAccept *probe, Ptr<ProxyMutex> &mutex, MIOBuffer *buffer,
56                                    IOBufferReader *reader)
57     : Continuation(mutex), probeParent(probe)
58   {
59     this->iobuf  = buffer ? buffer : new_MIOBuffer(buffer_size_index);
60     this->reader = reader ? reader : iobuf->alloc_reader(); // reader must be allocated only on a new MIOBuffer.
61     SET_HANDLER(&ProtocolProbeTrampoline::ioCompletionEvent);
62   }
63 
64   int
ioCompletionEventProtocolProbeTrampoline65   ioCompletionEvent(int event, void *edata)
66   {
67     VIO *vio;
68     NetVConnection *netvc;
69     SessionAccept *acceptor = nullptr;
70     ProtoGroupKey key       = N_PROTO_GROUPS; // use this as an invalid value.
71 
72     vio   = static_cast<VIO *>(edata);
73     netvc = static_cast<NetVConnection *>(vio->vc_server);
74 
75     switch (event) {
76     case VC_EVENT_EOS:
77     case VC_EVENT_ERROR:
78     case VC_EVENT_ACTIVE_TIMEOUT:
79     case VC_EVENT_INACTIVITY_TIMEOUT:
80       // Error ....
81       goto done;
82     case VC_EVENT_READ_READY:
83     case VC_EVENT_READ_COMPLETE:
84       break;
85     default:
86       return EVENT_ERROR;
87     }
88 
89     ink_assert(netvc != nullptr);
90 
91     if (!reader->is_read_avail_more_than(minimum_read_size - 1)) {
92       // Not enough data read. Well, that sucks.
93       goto done;
94     }
95 
96     // if proxy_protocol is enabled via port descriptor AND the src IP is in
97     // the trusted whitelist for proxy protocol, then check to see if it is
98     // present
99 
100     IpMap *pp_ipmap;
101     pp_ipmap = probeParent->proxy_protocol_ipmap;
102 
103     if (netvc->get_is_proxy_protocol()) {
104       Debug("proxyprotocol", "ioCompletionEvent: proxy protocol is enabled on this port");
105       if (pp_ipmap->count() > 0) {
106         Debug("proxyprotocol", "ioCompletionEvent: proxy protocol has a configured whitelist of trusted IPs - checking");
107         void *payload = nullptr;
108         if (!pp_ipmap->contains(netvc->get_remote_addr(), &payload)) {
109           Debug("proxyprotocol",
110                 "ioCompletionEvent: proxy protocol src IP is NOT in the configured whitelist of trusted IPs - closing connection");
111           goto done;
112         } else {
113           char new_host[INET6_ADDRSTRLEN];
114           Debug("proxyprotocol", "ioCompletionEvent: Source IP [%s] is trusted in the whitelist for proxy protocol",
115                 ats_ip_ntop(netvc->get_remote_addr(), new_host, sizeof(new_host)));
116         }
117       } else {
118         Debug("proxyprotocol",
119               "ioCompletionEvent: proxy protocol DOES NOT have a configured whitelist of trusted IPs but proxy protocol is "
120               "ernabled on this port - processing all connections");
121       }
122 
123       if (http_has_proxy_v1(reader, netvc)) {
124         Debug("proxyprotocol", "ioCompletionEvent: http has proxy_v1 header");
125         netvc->set_remote_addr(netvc->get_proxy_protocol_src_addr());
126       } else {
127         Debug("proxyprotocol",
128               "ioCompletionEvent: proxy protocol was enabled, but required header was not present in the transaction - "
129               "closing connection");
130         goto done;
131       }
132     } // end of Proxy Protocol processing
133 
134     if (proto_is_http2(reader)) {
135       key = PROTO_HTTP2;
136     } else {
137       key = PROTO_HTTP;
138     }
139 
140     acceptor = probeParent->endpoint[key];
141     if (acceptor == nullptr) {
142       Warning("Unregistered protocol type %d", key);
143       goto done;
144     }
145 
146     // Disable the read IO that we started.
147     netvc->do_io_read(acceptor, 0, nullptr);
148 
149     // Directly invoke the session acceptor, letting it take ownership of the input buffer.
150     if (!acceptor->accept(netvc, this->iobuf, reader)) {
151       // IPAllow check fails in XxxSessionAccept::accept() if false returned.
152       goto done;
153     }
154     delete this;
155     return EVENT_CONT;
156 
157   done:
158     netvc->do_io_close();
159     free_MIOBuffer(this->iobuf);
160     this->iobuf = nullptr;
161     delete this;
162     return EVENT_CONT;
163   }
164 
165   MIOBuffer *iobuf;
166   const ProtocolProbeSessionAccept *probeParent;
167 };
168 
169 int
mainEvent(int event,void * data)170 ProtocolProbeSessionAccept::mainEvent(int event, void *data)
171 {
172   if (event == NET_EVENT_ACCEPT) {
173     ink_assert(data);
174 
175     VIO *vio;
176     NetVConnection *netvc          = static_cast<NetVConnection *>(data);
177     ProtocolProbeTrampoline *probe = new ProtocolProbeTrampoline(this, netvc->mutex, nullptr, nullptr);
178 
179     // XXX we need to apply accept inactivity timeout here ...
180 
181     if (!probe->reader->is_read_avail_more_than(0)) {
182       Debug("http", "probe needs data, read..");
183       vio = netvc->do_io_read(probe, BUFFER_SIZE_FOR_INDEX(ProtocolProbeTrampoline::buffer_size_index), probe->iobuf);
184       vio->reenable();
185     } else {
186       Debug("http", "probe already has data, call ioComplete directly..");
187       vio = netvc->do_io_read(this, 0, nullptr);
188       probe->ioCompletionEvent(VC_EVENT_READ_COMPLETE, (void *)vio);
189     }
190     return EVENT_CONT;
191   }
192 
193   ink_abort("Protocol probe received a fatal error: errno = %d", -(static_cast<int>((intptr_t)data)));
194   return EVENT_CONT;
195 }
196 
197 bool
accept(NetVConnection *,MIOBuffer *,IOBufferReader *)198 ProtocolProbeSessionAccept::accept(NetVConnection *, MIOBuffer *, IOBufferReader *)
199 {
200   ink_release_assert(0);
201   return false;
202 }
203 
204 void
registerEndpoint(ProtoGroupKey key,SessionAccept * ap)205 ProtocolProbeSessionAccept::registerEndpoint(ProtoGroupKey key, SessionAccept *ap)
206 {
207   ink_release_assert(endpoint[key] == nullptr);
208   this->endpoint[key] = ap;
209 }
210