1 /* $Id: http_server_transport.cpp 629837 2021-04-22 12:47:49Z ivanov $
2 * ===========================================================================
3 *
4 * PUBLIC DOMAIN NOTICE
5 * National Center for Biotechnology Information
6 *
7 * This software/database is a "United States Government Work" under the
8 * terms of the United States Copyright Act. It was written as part of
9 * the author's official duties as a United States Government employee and
10 * thus cannot be copyrighted. This software/database is freely available
11 * to the public for use. The National Library of Medicine and the U.S.
12 * Government have not placed any restriction on its use or reproduction.
13 *
14 * Although all reasonable efforts have been taken to ensure the accuracy
15 * and reliability of the software and data, the NLM and the U.S.
16 * Government do not and cannot warrant the performance or results that
17 * may be obtained by using this software or data. The NLM and the U.S.
18 * Government disclaim all warranties, express or implied, including
19 * warranties of performance, merchantability or fitness for any particular
20 * purpose.
21 *
22 * Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Authors: Dmitri Dmitrienko
27 *
28 * File Description:
29 *
30 */
31
32 #include <ncbi_pch.hpp>
33
34 #include <vector>
35
36 #include <connect/ext/ncbi_localnet.h>
37
38 #include "pending_operation.hpp"
39 #include "http_server_transport.hpp"
40 #include "pubseq_gateway.hpp"
41
42 #include <h2o.h>
43
44
45 using namespace std;
46
47
48 #define HTTP_RAW_PARAM_BUF_SIZE 8192
49
50
s_HttpUrlDecode(const char * what,size_t len,char * buf,size_t buf_size,size_t * result_len)51 static bool s_HttpUrlDecode(const char * what, size_t len, char * buf,
52 size_t buf_size, size_t * result_len)
53 {
54 const char * pch = what;
55 const char * end = what + len;
56 char * dest = buf;
57 char * dest_end = buf + buf_size;
58
59 while (pch < end) {
60 char ch = *pch;
61 unsigned char v;
62 switch (ch) {
63 case '%':
64 ++pch;
65 if (pch >= end - 1 || dest >= dest_end)
66 return false;
67 ch = *pch;
68 // To avoid CLang static analizer report on dead assignment
69 // v = 0;
70 if (ch >= '0' && ch <= '9')
71 v = (ch - '0') * 16;
72 else if (ch >= 'A' && ch <= 'F')
73 v = (ch - 'A' + 10) * 16;
74 else if (ch >= 'a' && ch <= 'f')
75 v = (ch - 'a' + 10) * 16;
76 else
77 return false;
78 ++pch;
79 ch = *pch;
80 if (ch >= '0' && ch <= '9')
81 v += (ch - '0');
82 else if (ch >= 'A' && ch <= 'F')
83 v += (ch - 'A' + 10);
84 else if (ch >= 'a' && ch <= 'f')
85 v += (ch - 'a' + 10);
86 else
87 return false;
88 *dest = v;
89 ++dest;
90 break;
91 case '+':
92 if (dest >= dest_end)
93 return false;
94 *dest = ' ';
95 ++dest;
96 break;
97 default:
98 if (dest >= dest_end)
99 return false;
100 *dest = ch;
101 ++dest;
102 }
103 ++pch;
104 }
105 *result_len = dest - buf;
106 if (dest < dest_end)
107 *dest = 0;
108 return true;
109 }
110
111
112 /** CHttpGetParser */
113
Parse(CHttpRequest & Req,char * Data,size_t Length) const114 void CHttpGetParser::Parse(CHttpRequest & Req, char * Data,
115 size_t Length) const
116 {
117 bool has_encodings;
118 char * buf = nullptr;
119 ssize_t bufsize = 0;
120
121 Req.GetRawBuffer(&buf, &bufsize);
122 bufsize--;
123
124 char * begin = Data;
125 const char * end = Data + Length;
126
127 if (*begin == '?')
128 begin++;
129
130 char * ch = begin;
131 bool is_name = true;
132
133 CQueryParam * prm = nullptr;
134 while (ch < end) {
135 const char * c_begin = ch;
136 has_encodings = false;
137 // NAME
138 while (ch < end) {
139 switch (*ch) {
140 case '%':
141 case '+':
142 has_encodings = true;
143 break;
144 case '=':
145 if (is_name)
146 goto done;
147 break;
148 case '&':
149 goto done;
150 }
151 ch++;
152 }
153 done:
154 if (is_name)
155 prm = Req.AddParam();
156 if (prm == nullptr)
157 break;
158
159 if (!has_encodings) {
160 *ch = '\0';
161 if (is_name) {
162 prm->m_Name = c_begin;
163 prm->m_NameLen = ch - c_begin;
164 prm->m_ValLen = 0;
165 prm->m_Val = nullptr;
166 }
167 else {
168 prm->m_Val = c_begin;
169 prm->m_ValLen = ch - c_begin;
170 }
171 } else {
172 size_t len;
173 if (!s_HttpUrlDecode(c_begin, ch - c_begin, buf, bufsize, &len))
174 return;
175 if (is_name) {
176 prm->m_Name = buf;
177 prm->m_NameLen = len;
178 prm->m_ValLen = 0;
179 prm->m_Val = nullptr;
180 }
181 else {
182 prm->m_Val = buf;
183 prm->m_ValLen = len;
184 }
185
186 buf += len;
187 bufsize -= len;
188 if (bufsize > 0) {
189 *buf = '\0';
190 ++buf;
191 --bufsize;
192 }
193 else {
194 Req.RevokeParam();
195 return;
196 }
197 }
198 is_name = !is_name;
199 ++ch;
200 }
201 }
202
203
204 /** CHttpRequest */
205
ParseParams(void)206 void CHttpRequest::ParseParams(void)
207 {
208 m_ParamParsed = true;
209 m_ParamCount = 0;
210
211 if (h2o_memis(m_Req->method.base, m_Req->method.len,
212 H2O_STRLIT("POST")) && m_PostParser) {
213 ssize_t cursor = h2o_find_header(&m_Req->headers,
214 H2O_TOKEN_CONTENT_TYPE, -1);
215 if (cursor == -1)
216 return;
217
218 if (m_PostParser->Supports(m_Req->headers.entries[cursor].value.base,
219 m_Req->headers.entries[cursor].value.len))
220 m_PostParser->Parse(*this, m_Req->entity.base, m_Req->entity.len);
221 } else if (h2o_memis(m_Req->method.base, m_Req->method.len,
222 H2O_STRLIT("GET")) && m_GetParser) {
223 if (m_Req->query_at == SIZE_MAX || m_Req->query_at >= m_Req->path.len)
224 return;
225
226 char * begin = &m_Req->path.base[m_Req->query_at];
227 const char * end = &m_Req->path.base[m_Req->path.len];
228 m_GetParser->Parse(*this, begin, end - begin);
229 }
230 }
231
232
GetParam(const char * name,size_t len,bool required,const char ** value,size_t * value_len)233 bool CHttpRequest::GetParam(const char * name, size_t len, bool required,
234 const char ** value, size_t * value_len)
235 {
236 if (!m_ParamParsed)
237 ParseParams();
238
239 for (size_t i = 0; i < m_ParamCount; ++i) {
240 if (m_Params[i].m_NameLen == len &&
241 memcmp(m_Params[i].m_Name, name, len) == 0) {
242 *value = m_Params[i].m_Val;
243 if (value_len)
244 *value_len = m_Params[i].m_ValLen;
245 return true;
246 }
247 }
248 *value = nullptr;
249 if (value_len)
250 *value_len = 0;
251
252 return !required;
253 }
254
255
GetMultipleValuesParam(const char * name,size_t len,vector<string> & values)256 bool CHttpRequest::GetMultipleValuesParam(const char * name, size_t len,
257 vector<string> & values)
258 {
259 if (!m_ParamParsed)
260 ParseParams();
261
262 bool found = false;
263 for (size_t i = 0; i < m_ParamCount; ++i) {
264 if (m_Params[i].m_NameLen == len &&
265 memcmp(m_Params[i].m_Name, name, len) == 0) {
266 values.push_back(string(m_Params[i].m_Val,
267 m_Params[i].m_ValLen));
268 found = true;
269 }
270 }
271 return found;
272 }
273
274
PrintParams(CDiagContext_Extra & extra)275 CDiagContext_Extra & CHttpRequest::PrintParams(CDiagContext_Extra & extra)
276 {
277 if (!m_ParamParsed)
278 ParseParams();
279
280 for (size_t i = 0; i < m_ParamCount; i++) {
281 extra.Print(string(m_Params[i].m_Name, m_Params[i].m_NameLen),
282 string(m_Params[i].m_Val, m_Params[i].m_ValLen));
283 }
284 return extra;
285 }
286
287
PrintLogFields(const CNcbiLogFields & log_fields)288 void CHttpRequest::PrintLogFields(const CNcbiLogFields & log_fields)
289 {
290 map<string, string> env;
291
292 for (size_t index = 0; index < m_Req->headers.size; ++index) {
293 string name(m_Req->headers.entries[index].name->base,
294 m_Req->headers.entries[index].name->len);
295 NStr::ToLower(name);
296 NStr::ReplaceInPlace(name, "_", "-");
297 env[name] = string(m_Req->headers.entries[index].value.base,
298 m_Req->headers.entries[index].value.len);
299 }
300
301 log_fields.LogFields(env);
302 }
303
304
GetPath(void)305 string CHttpRequest::GetPath(void)
306 {
307 return string(m_Req->path_normalized.base,
308 m_Req->path_normalized.len);
309 }
310
311
GetHeaderValue(const string & name)312 string CHttpRequest::GetHeaderValue(const string & name)
313 {
314 string value;
315 size_t name_size = name.size();
316
317 for (size_t index = 0; index < m_Req->headers.size; ++index) {
318 if (m_Req->headers.entries[index].name->len == name_size) {
319 if (strncasecmp(m_Req->headers.entries[index].name->base,
320 name.data(), name_size) == 0) {
321 value.assign(m_Req->headers.entries[index].value.base,
322 m_Req->headers.entries[index].value.len);
323 break;
324 }
325 }
326 }
327 return value;
328 }
329
330
331 // The method to extract the IP address needs an array of pointers to the
332 // strings like <name>=<value>. The h2o headers are stored in a different
333 // format so a conversion is required
GetClientIP(void)334 TNCBI_IPv6Addr CHttpRequest::GetClientIP(void)
335 {
336 // "There are no headers larger than 50k", so
337 const size_t buf_size = 50 * 1024;
338
339 const char * tracking_env[m_Req->headers.size + 1];
340 unique_ptr<char[]> buffer(new char[buf_size]);
341 char * raw_buffer = buffer.get();
342
343 size_t pos = 0;
344 for (size_t index = 0; index < m_Req->headers.size; ++index) {
345 size_t name_val_size = m_Req->headers.entries[index].name->len +
346 m_Req->headers.entries[index].value.len +
347 2; // '=' and '\0'
348 if (pos + name_val_size > buf_size) {
349 PSG_WARNING("The buffer for request headers is too small (" <<
350 buf_size << " bytes)");
351 return TNCBI_IPv6Addr{0};
352 }
353
354 tracking_env[index] = raw_buffer + pos;
355 memcpy(raw_buffer + pos, m_Req->headers.entries[index].name->base,
356 m_Req->headers.entries[index].name->len);
357 pos += m_Req->headers.entries[index].name->len;
358 raw_buffer[pos] = '=';
359 ++pos;
360 memcpy(raw_buffer + pos, m_Req->headers.entries[index].value.base,
361 m_Req->headers.entries[index].value.len);
362 pos += m_Req->headers.entries[index].value.len;
363 raw_buffer[pos] = '\0';
364 ++pos;
365 }
366 tracking_env[m_Req->headers.size] = nullptr;
367
368 return NcbiGetCgiClientIPv6(eCgiClientIP_TryMost, tracking_env);
369 }
370
371
GetPeerIP(void)372 string CHttpRequest::GetPeerIP(void)
373 {
374 struct sockaddr sock_addr;
375 if (m_Req->conn->callbacks->get_peername(m_Req->conn, &sock_addr) == 0)
376 return kEmptyStr;
377
378 char buf[256];
379 switch (sock_addr.sa_family) {
380 case AF_INET:
381 if (inet_ntop(AF_INET,
382 &(((struct sockaddr_in *)&sock_addr)->sin_addr),
383 buf, 256) == NULL)
384 return kEmptyStr;
385 break;
386 case AF_INET6:
387 if (inet_ntop(AF_INET6,
388 &(((struct sockaddr_in6 *)&sock_addr)->sin6_addr),
389 buf, 256) == NULL)
390 return kEmptyStr;
391 break;
392 default:
393 return kEmptyStr;
394 }
395
396 return buf;
397 }
398
399
GetSSLSettings(bool & enabled,string & cert_file,string & key_file,string & ciphers)400 void GetSSLSettings(bool & enabled,
401 string & cert_file,
402 string & key_file,
403 string & ciphers)
404 {
405 auto * app = CPubseqGatewayApp::GetInstance();
406 enabled = app->GetSSLEnable();
407 cert_file = app->GetSSLCertFile();
408 key_file = app->GetSSLKeyFile();
409 ciphers = app->GetSSLCiphers();
410 }
411
412