1 /*
2 * tcpconnect.cpp
3 *
4 * Created on: 27.02.2010
5 * Author: ed
6 */
7
8 #include <sys/select.h>
9
10 #define LOCAL_DEBUG
11 #include "debug.h"
12
13 #include "meta.h"
14 #include "tcpconnect.h"
15
16 #include "acfg.h"
17 #include "caddrinfo.h"
18 #include <signal.h>
19 #include "fileio.h"
20 #include "fileitem.h"
21 #include "cleaner.h"
22 #include <tuple>
23
24 using namespace std;
25
26 //#warning FIXME, hack
27 //#define NOCONCACHE
28
29 #ifdef DEBUG
30 #include <atomic>
31 atomic_int nConCount(0), nDisconCount(0), nReuseCount(0);
32 #endif
33
34 #ifdef HAVE_SSL
35 #include <openssl/evp.h>
36 #include "openssl/bio.h"
37 #include "openssl/ssl.h"
38 #include "openssl/err.h"
39 #include <openssl/rand.h>
40 #include <openssl/sha.h>
41 #include <openssl/crypto.h>
42 #include <openssl/x509_vfy.h>
43 #include <openssl/x509v3.h>
44 #ifndef HAVE_SSL_HOST_VALIDATION
45 extern "C"
46 {
47 #include "oldssl-workaround/openssl_hostname_validation.h"
48 }
49 #endif
50 #endif
51
52 namespace acng
53 {
54
55 std::atomic_uint dl_con_factory::g_nconns(0);
56 dl_con_factory g_tcp_con_factory;
57
tcpconnect(cfg::tRepoData::IHookHandler * pObserver)58 tcpconnect::tcpconnect(cfg::tRepoData::IHookHandler *pObserver) : m_pStateObserver(pObserver)
59 {
60 if(cfg::maxdlspeed != cfg::RESERVED_DEFVAL)
61 dl_con_factory::g_nconns.fetch_add(1);
62 if(pObserver)
63 pObserver->OnAccess();
64 }
65
~tcpconnect()66 tcpconnect::~tcpconnect()
67 {
68 LOGSTART("tcpconnect::~tcpconnect, terminating outgoing connection class");
69 Disconnect();
70 if(cfg::maxdlspeed != cfg::RESERVED_DEFVAL)
71 dl_con_factory::g_nconns.fetch_add(-1);
72 #ifdef HAVE_SSL
73 if(m_ctx)
74 {
75 SSL_CTX_free(m_ctx);
76 m_ctx=0;
77 }
78 #endif
79 if(m_pStateObserver)
80 {
81 m_pStateObserver->OnRelease();
82 m_pStateObserver=nullptr;
83
84 }
85 }
86
87 /*! \brief Helper to flush data stream contents reliable and close the connection then
88 * DUDES, who write TCP implementations... why can this just not be done easy and reliable? Why do we need hacks like the method below?
89 For details, see: http://blog.netherlabs.nl/articles/2009/01/18/the-ultimate-so_linger-page-or-why-is-my-tcp-not-reliable
90 *
91 */
termsocket(int fd)92 void termsocket(int fd)
93 {
94 LOGSTART2s("::termsocket", fd);
95 if (fd < 0)
96 return;
97
98 fcntl(fd, F_SETFL, ~O_NONBLOCK & fcntl(fd, F_GETFL));
99 ::shutdown(fd, SHUT_WR);
100 char buf[40];
101 LOG("waiting for peer to react");
102 while(true)
103 {
104 int r=recv(fd, buf, 40, MSG_WAITALL);
105 if(0 == r)
106 break;
107 if(r < 0)
108 {
109 if(errno == EINTR)
110 continue;
111 break; // XXX error case, actually
112 }
113 }
114
115 while (0 != ::close(fd))
116 {
117 if (errno != EINTR)
118 break;
119 };
120 }
121
connect_timeout(int sockfd,const struct sockaddr * addr,socklen_t addrlen,time_t timeout,bool bAssumeNonBlock)122 static int connect_timeout(int sockfd, const struct sockaddr *addr, socklen_t addrlen, time_t timeout, bool bAssumeNonBlock)
123 {
124 long stflags;
125 struct timeval tv;
126 fd_set wfds;
127 int res;
128
129 tv.tv_sec = timeout;
130 tv.tv_usec = 0;
131
132 if(!bAssumeNonBlock)
133 {
134 if ((stflags = fcntl(sockfd, F_GETFL, nullptr)) < 0)
135 return -1;
136
137 // Set to non-blocking mode.
138 if (fcntl(sockfd, F_SETFL, stflags | O_NONBLOCK) < 0)
139 return -1;
140 }
141 res = connect(sockfd, addr, addrlen);
142 if (res < 0) {
143 if (EINPROGRESS == errno)
144 {
145 for (;;) {
146 // Wait for connection.
147 FD_ZERO(&wfds);
148 FD_SET(sockfd, &wfds);
149 res = select(sockfd+1, nullptr, &wfds, nullptr, &tv);
150 if (res < 0)
151 {
152 if (EINTR != errno)
153 return -1;
154 }
155 else if (res > 0)
156 {
157 // Socket selected for writing.
158 int err;
159 socklen_t optlen = sizeof(err);
160
161 if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &optlen) < 0)
162 return -1;
163
164 if (err)
165 {
166 errno = err;
167 return -1;
168 }
169
170 break;
171 } else {
172 // Timeout.
173 errno = ETIMEDOUT;
174 return -1;
175 }
176 }
177 } else {
178 return -1;
179 }
180 }
181
182 if(!bAssumeNonBlock && fcntl(sockfd, F_SETFL, stflags) < 0) // Set back to original mode
183 return -1;
184
185 return 0;
186 }
187
_Connect(string & sErrorMsg,int timeout)188 inline bool tcpconnect::_Connect(string & sErrorMsg, int timeout)
189 {
190 LOGSTART2("tcpconnect::_Connect", "hostname: " << m_sHostName);
191
192 auto dns = CAddrInfo::CachedResolve(m_sHostName, m_sPort, sErrorMsg);
193
194 if(!dns)
195 {
196 USRDBG(sErrorMsg);
197 return false; // sErrorMsg got the info already, no other chance to fix it
198 }
199
200 ::signal(SIGPIPE, SIG_IGN);
201
202 // always consider first family, afterwards stop when no more specified
203 for (unsigned i=0; i< _countof(cfg::conprotos) && (0==i || cfg::conprotos[i]!=PF_UNSPEC); ++i)
204 {
205 for (auto pInfo = dns->m_addrInfo; pInfo; pInfo = pInfo->ai_next)
206 {
207 if (cfg::conprotos[i] != PF_UNSPEC && cfg::conprotos[i] != pInfo->ai_family)
208 continue;
209
210 ldbg("Creating socket for " << m_sHostName);
211
212 if (pInfo->ai_socktype != SOCK_STREAM || pInfo->ai_protocol != IPPROTO_TCP)
213 continue;
214
215 Disconnect();
216
217 m_conFd = ::socket(pInfo->ai_family, pInfo->ai_socktype, pInfo->ai_protocol);
218 if (m_conFd < 0)
219 continue;
220
221 #ifndef NO_TCP_TUNNING
222 {
223 int yes(1);
224 ::setsockopt(m_conFd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
225 ::setsockopt(m_conFd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes));
226 }
227 #endif
228 set_nb(m_conFd);
229 if (acng::connect_timeout(m_conFd, pInfo->ai_addr, pInfo->ai_addrlen, timeout, true) < 0)
230 {
231 if(errno==ETIMEDOUT)
232 sErrorMsg="Connection timeout";
233 #ifndef MINIBUILD
234 USRDBG(tErrnoFmter("Outgoing connection for ") << m_sHostName << ", Port: " << m_sPort );
235 #endif
236 continue;
237 }
238 #ifdef DEBUG
239 nConCount.fetch_add(1);
240 #endif
241 ldbg("connect() ok");
242
243 return true;
244 }
245 }
246
247 #ifdef MINIBUILD
248 sErrorMsg = "500 Connection failure";
249 #else
250 // format the last available error message for the user
251 sErrorMsg=tErrnoFmter("500 Connection failure: ");
252 #endif
253 ldbg("Force reconnect, con. failure");
254 Disconnect();
255 return false;
256 }
257
Disconnect()258 void tcpconnect::Disconnect()
259 {
260 LOGSTART("tcpconnect::_Disconnect");
261
262 #ifdef DEBUG
263 nDisconCount.fetch_add(m_conFd >=0);
264 #endif
265
266 #ifdef HAVE_SSL
267 if(m_bio)
268 BIO_free_all(m_bio), m_bio=nullptr;
269 #endif
270
271 m_lastFile.reset();
272
273 termsocket_quick(m_conFd);
274 }
275 acmutex spareConPoolMx;
276 multimap<tuple<string,string SSL_OPT_ARG(bool) >,
277 std::pair<tDlStreamHandle, time_t> > spareConPool;
278
CreateConnected(cmstring & sHostname,cmstring & sPort,mstring & sErrOut,bool * pbSecondHand,cfg::tRepoData::IHookHandler * pStateTracker,bool bSsl,int timeout,bool nocache)279 tDlStreamHandle dl_con_factory::CreateConnected(cmstring &sHostname, cmstring &sPort,
280 mstring &sErrOut, bool *pbSecondHand, cfg::tRepoData::IHookHandler *pStateTracker
281 ,bool bSsl, int timeout, bool nocache)
282 {
283 LOGSTART2s("tcpconnect::CreateConnected", "hostname: " << sHostname << ", port: " << sPort
284 << (bSsl?" with ssl":" , no ssl"));
285
286 tDlStreamHandle p;
287 #ifndef HAVE_SSL
288 if(bSsl)
289 {
290 log::err("E_NOTIMPLEMENTED: SSL");
291 return p;
292 }
293 #endif
294
295 bool bReused=false;
296 auto key = make_tuple(sHostname, sPort SSL_OPT_ARG(bSsl) );
297
298 #ifdef NOCONCACHE
299 p.reset(new tcpconnect(pStateTracker));
300 if(p)
301 {
302 if(!p->_Connect(sHostname, sPort, sErrOut) || p->GetFD()<0) // failed or worthless
303 p.reset();
304 }
305 #else
306 if(!nocache)
307 {
308 // mutex context
309 lockguard __g(spareConPoolMx);
310 auto it=spareConPool.find(key);
311 if(spareConPool.end() != it)
312 {
313 p=it->second.first;
314 spareConPool.erase(it);
315 bReused = true;
316 ldbg("got connection " << p.get() << " from the idle pool");
317
318 // it was reset in connection recycling, restart now
319 if(pStateTracker)
320 {
321 p->m_pStateObserver = pStateTracker;
322 pStateTracker->OnAccess();
323 }
324 #ifdef DEBUG
325 nReuseCount.fetch_add(1);
326 #endif
327 }
328 }
329 #endif
330
331 if(!p)
332 {
333 p.reset(new tcpconnect(pStateTracker));
334 if(p)
335 {
336 p->m_sHostName=sHostname;
337 p->m_sPort=sPort;
338 }
339
340 if(!p || !p->_Connect(sErrOut, timeout) || p->GetFD()<0) // failed or worthless
341 p.reset();
342 #ifdef HAVE_SSL
343 else if(bSsl)
344 {
345 if(!p->SSLinit(sErrOut, sHostname, sPort))
346 {
347 p.reset();
348 LOG("ssl init error");
349 }
350 }
351 #endif
352 }
353
354 if(pbSecondHand)
355 *pbSecondHand = bReused;
356
357 return p;
358 }
359
RecycleIdleConnection(tDlStreamHandle & handle)360 void dl_con_factory::RecycleIdleConnection(tDlStreamHandle & handle)
361 {
362 if(!handle)
363 return;
364
365 LOGSTART2s("tcpconnect::RecycleIdleConnection", handle->m_sHostName);
366
367 if(handle->m_pStateObserver)
368 {
369 handle->m_pStateObserver->OnRelease();
370 handle->m_pStateObserver = nullptr;
371 }
372
373 if(! cfg::persistoutgoing)
374 {
375 ldbg("not caching outgoing connections, drop " << handle.get());
376 handle.reset();
377 return;
378 }
379
380 #ifdef DEBUG
381 if(check_read_state(handle->GetFD()))
382 {
383 acbuf checker;
384 checker.setsize(300000);
385 checker.sysread(handle->GetFD());
386
387 }
388 #endif
389
390 auto& host = handle->GetHostname();
391 if (!host.empty())
392 {
393 #ifndef NOCONCACHE
394 time_t now = GetTime();
395 lockguard __g(spareConPoolMx);
396 ldbg("caching connection " << handle.get());
397
398 // a DOS?
399 if (spareConPool.size() < 50)
400 {
401 EMPLACE_PAIR_COMPAT(spareConPool, make_tuple(host, handle->GetPort()
402 SSL_OPT_ARG(handle->m_bio) ), make_pair(handle, now));
403 #ifndef MINIBUILD
404 g_victor.ScheduleFor(now + TIME_SOCKET_EXPIRE_CLOSE, cleaner::TYPE_EXCONNS);
405 #endif
406 }
407 #endif
408 }
409
410 handle.reset();
411 }
412
BackgroundCleanup()413 time_t dl_con_factory::BackgroundCleanup()
414 {
415 lockguard __g(spareConPoolMx);
416 time_t now=GetTime();
417
418 fd_set rfds;
419 FD_ZERO(&rfds);
420 int nMaxFd=0;
421
422 // either drop the old ones, or stuff them into a quick select call to find the good sockets
423 for (auto it = spareConPool.begin(); it != spareConPool.end();)
424 {
425 if (now >= (it->second.second + TIME_SOCKET_EXPIRE_CLOSE))
426 it = spareConPool.erase(it);
427 else
428 {
429 int fd = it->second.first->GetFD();
430 FD_SET(fd, &rfds);
431 nMaxFd = max(nMaxFd, fd);
432 ++it;
433 }
434 }
435 // if they have to send something, that must the be the CLOSE signal
436 struct timeval tv;
437 tv.tv_sec = 0;
438 tv.tv_usec = 1;
439 int r=select(nMaxFd + 1, &rfds, nullptr, nullptr, &tv);
440 // on error, also do nothing, or stop when r fds are processed
441 for (auto it = spareConPool.begin(); r>0 && it != spareConPool.end(); r--)
442 {
443 if(FD_ISSET(it->second.first->GetFD(), &rfds))
444 it = spareConPool.erase(it);
445 else
446 ++it;
447 }
448
449 return spareConPool.empty() ? END_OF_TIME : GetTime()+TIME_SOCKET_EXPIRE_CLOSE/4+1;
450 }
451
KillLastFile()452 void tcpconnect::KillLastFile()
453 {
454 #ifndef MINIBUILD
455 tFileItemPtr p = m_lastFile.lock();
456 if (!p)
457 return;
458 p->SetupClean(true);
459 #endif
460 }
461
dump_status()462 void dl_con_factory::dump_status()
463 {
464 lockguard __g(spareConPoolMx);
465 tSS msg;
466 msg << "TCP connection cache:\n";
467 for (const auto& x : spareConPool)
468 {
469 if(! x.second.first)
470 {
471 msg << "[BAD HANDLE] recycle at " << x.second.second << "\n";
472 continue;
473 }
474
475 msg << x.second.first->m_conFd << ": for "
476 << get<0>(x.first) << ":" << get<1>(x.first)
477 << ", recycled at " << x.second.second
478 << "\n";
479 }
480 #ifdef DEBUG
481 msg << "dbg counts, con: " << nConCount.load()
482 << " , discon: " << nDisconCount.load()
483 << " , reuse: " << nReuseCount.load() << "\n";
484 #endif
485
486 log::err(msg);
487 }
488 #ifdef HAVE_SSL
SSLinit(mstring & sErr,cmstring & sHostname,cmstring & sPort)489 bool tcpconnect::SSLinit(mstring &sErr, cmstring &sHostname, cmstring &sPort)
490 {
491 SSL * ssl(nullptr);
492 mstring ebuf;
493
494 auto withSslError = [&sErr](const char *perr)
495 {
496 sErr="500 SSL error: ";
497 sErr+=(perr?perr:"Generic SSL failure");
498 return false;
499 };
500 auto withLastSslError = [&withSslError]()
501 {
502 return withSslError(ERR_reason_error_string(ERR_get_error()));
503 };
504 auto withRetCode = [&withSslError, &ssl](int hret)
505 {
506 return withSslError(ERR_reason_error_string(SSL_get_error(ssl, hret)));
507 };
508
509 // cleaned up in the destructor on EOL
510 if(!m_ctx)
511 {
512 m_ctx = SSL_CTX_new(SSLv23_client_method());
513 if (!m_ctx) return withLastSslError();
514
515 SSL_CTX_load_verify_locations(m_ctx,
516 cfg::cafile.empty() ? nullptr : cfg::cafile.c_str(),
517 cfg::capath.empty() ? nullptr : cfg::capath.c_str());
518 }
519
520 ssl = SSL_new(m_ctx);
521 if (!m_ctx) return withLastSslError();
522
523 // for SNI
524 SSL_set_tlsext_host_name(ssl, sHostname.c_str());
525
526 {
527 #ifdef HAVE_SSL_HOST_VALIDATION
528 auto param = SSL_get0_param(ssl);
529 /* Enable automatic hostname checks */
530 X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
531 X509_VERIFY_PARAM_set1_host(param, sHostname.c_str(), 0);
532 #endif
533 /* Configure a non-zero callback if desired */
534 SSL_set_verify(ssl, SSL_VERIFY_PEER, 0);
535 }
536
537 // mark it connected and prepare for non-blocking mode
538 SSL_set_connect_state(ssl);
539 SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY
540 | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER
541 | SSL_MODE_ENABLE_PARTIAL_WRITE);
542
543 auto hret=SSL_set_fd(ssl, m_conFd);
544 if(hret != 1) return withRetCode(hret);
545
546 while(true)
547 {
548 hret=SSL_connect(ssl);
549 if(hret == 1 )
550 break;
551 if(hret == 0)
552 return withRetCode(hret);
553
554 fd_set rfds, wfds;
555 FD_ZERO(&rfds);
556 FD_ZERO(&wfds);
557 switch(SSL_get_error(ssl, hret))
558 {
559 case SSL_ERROR_WANT_READ:
560 FD_SET(m_conFd, &rfds);
561 break;
562 case SSL_ERROR_WANT_WRITE:
563 FD_SET(m_conFd, &wfds);
564 break;
565 default:
566 return withRetCode(hret);
567 }
568 struct timeval tv;
569 tv.tv_sec = cfg::nettimeout;
570 tv.tv_usec = 0;
571 int nReady=select(m_conFd+1, &rfds, &wfds, nullptr, &tv);
572 if(!nReady) return withSslError("Socket timeout");
573 if (nReady<0)
574 {
575 #ifndef MINIBUILD
576 ebuf=tErrnoFmter("Socket error");
577 return withSslError(ebuf.c_str());
578 #else
579 return withSslError("Socket error");
580 #endif
581 }
582 }
583
584 m_bio = BIO_new(BIO_f_ssl());
585 if(!m_bio) return withSslError("IO initialization error");
586 // not sure we need it but maybe the handshake can access this data
587 BIO_set_conn_hostname(m_bio, sHostname.c_str());
588 BIO_set_conn_port(m_bio, sPort.c_str());
589
590 BIO_set_ssl(m_bio, ssl, BIO_NOCLOSE);
591
592 BIO_set_nbio(m_bio, 1);
593 set_nb(m_conFd);
594
595 if(!cfg::nsafriendly)
596 {
597 X509* server_cert = nullptr;
598 hret=SSL_get_verify_result(ssl);
599 if( hret != X509_V_OK)
600 return withSslError(X509_verify_cert_error_string(hret));
601 server_cert = SSL_get_peer_certificate(ssl);
602 if(server_cert)
603 {
604 // XXX: maybe extract the real name to a buffer and report it additionally?
605 // X509_NAME_oneline(X509_get_subject_name (server_cert), cert_str, sizeof (cert_str));
606 #ifndef HAVE_SSL_HOST_VALIDATION
607 auto hcResult=validate_hostname(sHostname.c_str(), server_cert);
608 X509_free(server_cert);
609 if(hcResult != HostnameValidationResult::MatchFound)
610 return withSslError("Incorrect remote certificate configuration");
611 #else
612 X509_free(server_cert);
613 #endif
614 }
615 else // The handshake was successful although the server did not provide a certificate
616 return withSslError("Incompatible remote certificate");
617 }
618 return true;
619 }
620
621 //! Global initialization helper (might be non-reentrant)
globalSslInit()622 void globalSslInit()
623 {
624 static bool inited=false;
625 if(inited)
626 return;
627 inited = true;
628 SSL_load_error_strings();
629 ERR_load_BIO_strings();
630 ERR_load_crypto_strings();
631 ERR_load_SSL_strings();
632 OpenSSL_add_all_algorithms();
633 SSL_library_init();
634 }
635
636 #endif
637
StartTunnel(const tHttpUrl & realTarget,mstring & sError,cmstring * psAuthorization,bool bDoSSL)638 bool tcpconnect::StartTunnel(const tHttpUrl& realTarget, mstring& sError,
639 cmstring *psAuthorization, bool bDoSSL)
640 {
641 /*
642 CONNECT server.example.com:80 HTTP/1.1
643 Host: server.example.com:80
644 Proxy-Authorization: basic aGVsbG86d29ybGQ=
645 */
646 tSS fmt;
647 fmt << "CONNECT " << realTarget.sHost << ":" << realTarget.GetPort()
648 << " HTTP/1.1\r\nHost: " << realTarget.sHost << ":" << realTarget.GetPort()
649 << "\r\n";
650 if(psAuthorization && !psAuthorization->empty())
651 {
652 fmt << "Proxy-Authorization: Basic "
653 << EncodeBase64Auth(*psAuthorization) << "\r\n";
654 }
655 fmt << "\r\n";
656
657 try
658 {
659 if (!fmt.send(m_conFd, &sError))
660 return false;
661
662 fmt.clear();
663 while (true)
664 {
665 fmt.setsize(4000);
666 if (!fmt.recv(m_conFd, &sError))
667 return false;
668 if(fmt.freecapa()<=0)
669 {
670 sError = "503 Remote proxy error";
671 return false;
672 }
673
674 header h;
675 auto n = h.Load(fmt.rptr(), fmt.size());
676 if(!n)
677 continue;
678
679 auto st = h.getStatus();
680 if (n <= 0 || st == 404 /* just be sure it doesn't send crap */)
681 {
682 sError = "503 Tunnel setup failed";
683 return false;
684 }
685
686 if (st < 200 || st >= 300)
687 {
688 sError = h.frontLine;
689 return false;
690 }
691 break;
692 }
693
694 m_sHostName = realTarget.sHost;
695 m_sPort = realTarget.GetPort();
696 #ifdef HAVE_SSL
697 if (bDoSSL && !SSLinit(sError, m_sHostName, m_sPort))
698 {
699 m_sHostName.clear();
700 return false;
701 }
702 #else
703 (void) bDoSSL;
704 #endif
705 }
706 catch(...)
707 {
708 return false;
709 }
710 return true;
711 }
712
713
714
715 }
716