/src/znc/build/src/Csocket.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /** |
2 | | * @file Csocket.cc |
3 | | * @author Jim Hull <csocket@jimloco.com> |
4 | | * |
5 | | * Copyright (c) 1999-2012 Jim Hull <csocket@jimloco.com> |
6 | | * All rights reserved |
7 | | * |
8 | | * Redistribution and use in source and binary forms, with or without modification, |
9 | | * are permitted provided that the following conditions are met: |
10 | | * Redistributions of source code must retain the above copyright notice, this |
11 | | * list of conditions and the following disclaimer. |
12 | | * Redistributions in binary form must reproduce the above copyright notice, this list |
13 | | * of conditions and the following disclaimer in the documentation and/or other materials |
14 | | * provided with the distribution. |
15 | | * Redistributions in any form must be accompanied by information on how to obtain |
16 | | * complete source code for this software and any accompanying software that uses this software. |
17 | | * The source code must either be included in the distribution or be available for no more than |
18 | | * the cost of distribution plus a nominal fee, and must be freely redistributable |
19 | | * under reasonable conditions. For an executable file, complete source code means the source |
20 | | * code for all modules it contains. It does not include source code for modules or files |
21 | | * that typically accompany the major components of the operating system on which the executable file runs. |
22 | | * |
23 | | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, |
24 | | * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, |
25 | | * OR NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OF THIS SOFTWARE BE LIABLE FOR ANY DIRECT, |
26 | | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
27 | | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
28 | | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR |
29 | | * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
30 | | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
31 | | */ |
32 | | |
33 | | /*** |
34 | | * doing this because there seems to be a bug that is losing the "short" on htons when in optimize mode turns into a macro |
35 | | * gcc 4.3.4 |
36 | | */ |
37 | | #if defined(__OPTIMIZE__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 3 |
38 | | #pragma GCC diagnostic warning "-Wconversion" |
39 | | #endif /* defined(__OPTIMIZE__) && __GNUC__ == 4 && __GNUC_MINOR__ >= 3 */ |
40 | | |
41 | | #include <znc/Csocket.h> |
42 | | #ifdef __NetBSD__ |
43 | | #include <sys/param.h> |
44 | | #endif /* __NetBSD__ */ |
45 | | |
46 | | #ifdef HAVE_LIBSSL |
47 | | #include <stdio.h> |
48 | | #include <openssl/ssl.h> |
49 | | #include <openssl/conf.h> |
50 | | #include <openssl/bn.h> |
51 | | #include <openssl/dh.h> |
52 | | #include <openssl/dsa.h> |
53 | | #include <openssl/rsa.h> |
54 | | #ifndef OPENSSL_NO_COMP |
55 | | #include <openssl/comp.h> |
56 | | #endif |
57 | | #ifndef OPENSSL_NO_ENGINE |
58 | | #include <openssl/engine.h> |
59 | | #endif |
60 | | #define HAVE_ERR_REMOVE_STATE |
61 | | #ifdef OPENSSL_VERSION_NUMBER |
62 | | # if OPENSSL_VERSION_NUMBER >= 0x10000000 |
63 | | # undef HAVE_ERR_REMOVE_STATE |
64 | | # define HAVE_ERR_REMOVE_THREAD_STATE |
65 | | # endif |
66 | | # if OPENSSL_VERSION_NUMBER < 0x10001000 |
67 | | # define OPENSSL_NO_TLS1_1 /* 1.0.1-pre~: openssl/openssl@637f374ad49d5f6d4f81d87d7cdd226428aa470c */ |
68 | | # define OPENSSL_NO_TLS1_2 /* 1.0.1-pre~: openssl/openssl@7409d7ad517650db332ae528915a570e4e0ab88b */ |
69 | | # endif |
70 | | # if !defined(LIBRESSL_VERSION_NUMBER) || (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER >= 0x03050000fL) |
71 | | # if OPENSSL_VERSION_NUMBER >= 0x10100000 |
72 | | # undef HAVE_ERR_REMOVE_THREAD_STATE /* 1.1.0-pre4: openssl/openssl@8509dcc9f319190c565ab6baad7c88d37a951d1c */ |
73 | | # undef OPENSSL_NO_SSL2 /* 1.1.0-pre4: openssl/openssl@e80381e1a3309f5d4a783bcaa508a90187a48882 */ |
74 | | # define OPENSSL_NO_SSL2 /* 1.1.0-pre1: openssl/openssl@45f55f6a5bdcec411ef08a6f8aae41d5d3d234ad */ |
75 | | # define HAVE_FLEXIBLE_TLS_METHOD /* 1.1.0-pre1: openssl/openssl@32ec41539b5b23bc42503589fcc5be65d648d1f5 */ |
76 | | # define HAVE_OPAQUE_SSL |
77 | | # endif |
78 | | # endif /* LIBRESSL_VERSION_NUMBER */ |
79 | | #endif /* OPENSSL_VERSION_NUMBER */ |
80 | | #endif /* HAVE_LIBSSL */ |
81 | | |
82 | | #ifdef HAVE_ICU |
83 | | #include <unicode/ustring.h> |
84 | | #include <unicode/errorcode.h> |
85 | | #include <unicode/ucnv_cb.h> |
86 | | #endif /* HAVE_ICU */ |
87 | | |
88 | | #include <list> |
89 | | #include <algorithm> |
90 | | #include <chrono> |
91 | | |
92 | | #define CS_SRANDBUFFER 128 |
93 | | |
94 | | /* |
95 | | * timeradd/timersub is missing on solaris' sys/time.h, provide |
96 | | * some fallback macros |
97 | | */ |
98 | | #ifndef timeradd |
99 | | #define timeradd(a, b, result) \ |
100 | | do { \ |
101 | | (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \ |
102 | | (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \ |
103 | | if ((result)->tv_usec >= 1000000) { \ |
104 | | ++(result)->tv_sec; \ |
105 | | (result)->tv_usec -= 1000000; \ |
106 | | } \ |
107 | | } while (0) |
108 | | #endif |
109 | | |
110 | | #ifndef timersub |
111 | | #define timersub(a, b, result) \ |
112 | | do { \ |
113 | | (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ |
114 | | (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ |
115 | | if ((result)->tv_usec < 0) { \ |
116 | | --(result)->tv_sec; \ |
117 | | (result)->tv_usec += 1000000; \ |
118 | | } \ |
119 | | } while (0) |
120 | | #endif |
121 | | |
122 | | using std::stringstream; |
123 | | using std::ostream; |
124 | | using std::endl; |
125 | | using std::min; |
126 | | using std::vector; |
127 | | |
128 | | #define CREATE_ARES_VER( a, b, c ) ((a<<16)|(b<<8)|c) |
129 | | |
130 | | #ifndef _NO_CSOCKET_NS // some people may not want to use a namespace |
131 | | namespace Csocket |
132 | | { |
133 | | #endif /* _NO_CSOCKET_NS */ |
134 | | |
135 | | static int s_iCsockSSLIdx = 0; //!< this gets setup once in InitSSL |
136 | | int GetCsockSSLIdx() |
137 | 0 | { |
138 | 0 | return( s_iCsockSSLIdx ); |
139 | 0 | } |
140 | | |
141 | | #ifdef _WIN32 |
142 | | |
143 | | #if defined(_WIN32) && (!defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0600)) |
144 | | //! thanks to KiNgMaR @ #znc for this wrapper |
145 | | static int inet_pton( int af, const char *src, void *dst ) |
146 | | { |
147 | | sockaddr_storage aAddress; |
148 | | int iAddrLen = sizeof( sockaddr_storage ); |
149 | | memset( &aAddress, 0, iAddrLen ); |
150 | | char * pTmp = strdup( src ); |
151 | | aAddress.ss_family = af; // this is important: |
152 | | // The function fails if the sin_family member of the SOCKADDR_IN structure is not set to AF_INET or AF_INET6. |
153 | | int iRet = WSAStringToAddressA( pTmp, af, NULL, ( sockaddr * )&aAddress, &iAddrLen ); |
154 | | free( pTmp ); |
155 | | if( iRet == 0 ) |
156 | | { |
157 | | if( af == AF_INET6 ) |
158 | | memcpy( dst, &( ( sockaddr_in6 * ) &aAddress )->sin6_addr, sizeof( in6_addr ) ); |
159 | | else |
160 | | memcpy( dst, &( ( sockaddr_in * ) &aAddress )->sin_addr, sizeof( in_addr ) ); |
161 | | return( 1 ); |
162 | | } |
163 | | return( -1 ); |
164 | | } |
165 | | #endif |
166 | | |
167 | | static inline void set_non_blocking( cs_sock_t fd ) |
168 | | { |
169 | | u_long iOpts = 1; |
170 | | ioctlsocket( fd, FIONBIO, &iOpts ); |
171 | | } |
172 | | |
173 | | /* |
174 | | * not used by anything anymore |
175 | | static inline void set_blocking(cs_sock_t fd) |
176 | | { |
177 | | u_long iOpts = 0; |
178 | | ioctlsocket( fd, FIONBIO, &iOpts ); |
179 | | } |
180 | | */ |
181 | | |
182 | | static inline void set_close_on_exec( cs_sock_t fd ) |
183 | | { |
184 | | // TODO add this for windows |
185 | | // see http://gcc.gnu.org/ml/java-patches/2002-q1/msg00696.html |
186 | | // for infos on how to do this |
187 | | } |
188 | | |
189 | | #else // _WIN32 |
190 | | |
191 | | static inline void set_non_blocking( cs_sock_t fd ) |
192 | 0 | { |
193 | 0 | int fdflags = fcntl( fd, F_GETFL, 0 ); |
194 | 0 | if( fdflags < 0 ) |
195 | 0 | return; // Ignore errors |
196 | 0 | fcntl( fd, F_SETFL, fdflags|O_NONBLOCK ); |
197 | 0 | } |
198 | | |
199 | | /* |
200 | | * not used by anything anymore |
201 | | static inline void set_blocking(cs_sock_t fd) |
202 | | { |
203 | | int fdflags = fcntl(fd, F_GETFL, 0); |
204 | | if( fdflags < 0 ) |
205 | | return; // Ignore errors |
206 | | fdflags &= ~O_NONBLOCK; |
207 | | fcntl( fd, F_SETFL, fdflags ); |
208 | | } |
209 | | */ |
210 | | |
211 | | static inline void set_close_on_exec( cs_sock_t fd ) |
212 | 0 | { |
213 | 0 | int fdflags = fcntl( fd, F_GETFD, 0 ); |
214 | 0 | if( fdflags < 0 ) |
215 | 0 | return; // Ignore errors |
216 | 0 | fcntl( fd, F_SETFD, fdflags|FD_CLOEXEC ); |
217 | 0 | } |
218 | | #endif /* _WIN32 */ |
219 | | |
220 | | void CSSockAddr::SinFamily() |
221 | 0 | { |
222 | | #ifdef HAVE_IPV6 |
223 | | m_saddr6.sin6_family = PF_INET6; |
224 | | #endif /* HAVE_IPV6 */ |
225 | 0 | m_saddr.sin_family = PF_INET; |
226 | 0 | } |
227 | | |
228 | | void CSSockAddr::SinPort( uint16_t iPort ) |
229 | 0 | { |
230 | | #ifdef HAVE_IPV6 |
231 | | m_saddr6.sin6_port = htons( iPort ); |
232 | | #endif /* HAVE_IPV6 */ |
233 | 0 | m_saddr.sin_port = htons( iPort ); |
234 | 0 | } |
235 | | |
236 | | void CSSockAddr::SetIPv6( bool b ) |
237 | 0 | { |
238 | 0 | #ifndef HAVE_IPV6 |
239 | 0 | if( b ) |
240 | 0 | { |
241 | 0 | CS_DEBUG( "-DHAVE_IPV6 must be set during compile time to enable this feature" ); |
242 | 0 | m_bIsIPv6 = false; |
243 | 0 | return; |
244 | 0 | } |
245 | 0 | #endif /* HAVE_IPV6 */ |
246 | 0 | m_bIsIPv6 = b; |
247 | 0 | SinFamily(); |
248 | 0 | } |
249 | | |
250 | | |
251 | | #ifdef HAVE_LIBSSL |
252 | | static int _PemPassCB( char *pBuff, int iBuffLen, int rwflag, void * pcSocket ) |
253 | | { |
254 | | Csock * pSock = static_cast<Csock *>( pcSocket ); |
255 | | const CS_STRING & sPassword = pSock->GetPemPass(); |
256 | | if( iBuffLen <= 0 ) |
257 | | return( 0 ); |
258 | | memset( pBuff, '\0', iBuffLen ); |
259 | | if( sPassword.empty() ) |
260 | | return( 0 ); |
261 | | int iUseBytes = min( iBuffLen - 1, ( int )sPassword.length() ); |
262 | | memcpy( pBuff, sPassword.data(), iUseBytes ); |
263 | | return( iUseBytes ); |
264 | | } |
265 | | |
266 | | static int _CertVerifyCB( int preverify_ok, X509_STORE_CTX *x509_ctx ) |
267 | | { |
268 | | Csock * pSock = GetCsockFromCTX( x509_ctx ); |
269 | | if( pSock ) |
270 | | return( pSock->VerifyPeerCertificate( preverify_ok, x509_ctx ) ); |
271 | | |
272 | | return( preverify_ok ); |
273 | | } |
274 | | |
275 | | static void _InfoCallback( const SSL * pSSL, int where, int ret ) |
276 | | { |
277 | | if( ( where & SSL_CB_HANDSHAKE_DONE ) && ret != 0 ) |
278 | | { |
279 | | Csock * pSock = static_cast<Csock *>( SSL_get_ex_data( pSSL, GetCsockSSLIdx() ) ); |
280 | | if( pSock ) |
281 | | pSock->SSLHandShakeFinished(); |
282 | | } |
283 | | } |
284 | | |
285 | | |
286 | | Csock * GetCsockFromCTX( X509_STORE_CTX * pCTX ) |
287 | | { |
288 | | Csock * pSock = NULL; |
289 | | SSL * pSSL = ( SSL * ) X509_STORE_CTX_get_ex_data( pCTX, SSL_get_ex_data_X509_STORE_CTX_idx() ); |
290 | | if( pSSL ) |
291 | | pSock = ( Csock * ) SSL_get_ex_data( pSSL, GetCsockSSLIdx() ); |
292 | | return( pSock ); |
293 | | } |
294 | | #endif /* HAVE_LIBSSL */ |
295 | | |
296 | | |
297 | | #ifdef USE_GETHOSTBYNAME |
298 | | |
299 | | // this issue here is getaddrinfo has a significant behavior difference when dealing with round robin dns on an |
300 | | // ipv4 network. This is not desirable IMHO. so when this is compiled without ipv6 support backwards compatibility |
301 | | // is maintained. |
302 | | static int __GetHostByName( const CS_STRING & sHostName, struct in_addr * paddr, u_int iNumRetries ) |
303 | | { |
304 | | int iReturn = HOST_NOT_FOUND; |
305 | | struct hostent * hent = NULL; |
306 | | #ifdef __linux__ |
307 | | char hbuff[2048]; |
308 | | struct hostent hentbuff; |
309 | | |
310 | | int err; |
311 | | for( u_int a = 0; a < iNumRetries; ++a ) |
312 | | { |
313 | | memset( ( char * ) hbuff, '\0', 2048 ); |
314 | | iReturn = gethostbyname_r( sHostName.c_str(), &hentbuff, hbuff, 2048, &hent, &err ); |
315 | | |
316 | | if( iReturn == 0 ) |
317 | | break; |
318 | | |
319 | | if( iReturn != TRY_AGAIN ) |
320 | | { |
321 | | CS_DEBUG( "gethostyname_r: " << hstrerror( h_errno ) ); |
322 | | break; |
323 | | } |
324 | | } |
325 | | if( !hent && iReturn == 0 ) |
326 | | iReturn = HOST_NOT_FOUND; |
327 | | #else |
328 | | for( u_int a = 0; a < iNumRetries; ++a ) |
329 | | { |
330 | | iReturn = HOST_NOT_FOUND; |
331 | | hent = gethostbyname( sHostName.c_str() ); |
332 | | |
333 | | if( hent ) |
334 | | { |
335 | | iReturn = 0; |
336 | | break; |
337 | | } |
338 | | |
339 | | if( h_errno != TRY_AGAIN ) |
340 | | { |
341 | | #ifndef _WIN32 |
342 | | CS_DEBUG( "gethostyname: " << hstrerror( h_errno ) ); |
343 | | #endif /* _WIN32 */ |
344 | | break; |
345 | | } |
346 | | } |
347 | | |
348 | | #endif /* __linux__ */ |
349 | | |
350 | | if( iReturn == 0 ) |
351 | | memcpy( &paddr->s_addr, hent->h_addr_list[0], sizeof( paddr->s_addr ) ); |
352 | | |
353 | | return( iReturn == TRY_AGAIN ? EAGAIN : iReturn ); |
354 | | } |
355 | | #endif /* !USE_GETHOSTBYNAME */ |
356 | | |
357 | | |
358 | | #ifdef HAVE_C_ARES |
359 | | void Csock::FreeAres() |
360 | | { |
361 | | if( m_pARESChannel ) |
362 | | { |
363 | | ares_destroy( m_pARESChannel ); |
364 | | m_pARESChannel = NULL; |
365 | | } |
366 | | } |
367 | | |
368 | | static void AresHostCallback( void * pArg, int status, int timeouts, struct hostent *hent ) |
369 | | { |
370 | | Csock * pSock = ( Csock * )pArg; |
371 | | if( status == ARES_SUCCESS && hent && hent->h_addr_list[0] != NULL ) |
372 | | { |
373 | | CSSockAddr * pSockAddr = pSock->GetCurrentAddr(); |
374 | | if( hent->h_addrtype == AF_INET ) |
375 | | { |
376 | | pSock->SetIPv6( false ); |
377 | | memcpy( pSockAddr->GetAddr(), hent->h_addr_list[0], sizeof( *( pSockAddr->GetAddr() ) ) ); |
378 | | } |
379 | | #ifdef HAVE_IPV6 |
380 | | else if( hent->h_addrtype == AF_INET6 ) |
381 | | { |
382 | | pSock->SetIPv6( true ); |
383 | | memcpy( pSockAddr->GetAddr6(), hent->h_addr_list[0], sizeof( *( pSockAddr->GetAddr6() ) ) ); |
384 | | } |
385 | | #endif /* HAVE_IPV6 */ |
386 | | else |
387 | | { |
388 | | status = ARES_ENOTFOUND; |
389 | | } |
390 | | } |
391 | | else |
392 | | { |
393 | | CS_DEBUG( ares_strerror( status ) ); |
394 | | if( status == ARES_SUCCESS ) |
395 | | { |
396 | | CS_DEBUG( "Received ARES_SUCCESS without any useful reply, using NODATA instead" ); |
397 | | status = ARES_ENODATA; |
398 | | } |
399 | | } |
400 | | pSock->SetAresFinished( status ); |
401 | | } |
402 | | #endif /* HAVE_C_ARES */ |
403 | | |
404 | | CGetAddrInfo::CGetAddrInfo( const CS_STRING & sHostname, Csock * pSock, CSSockAddr & csSockAddr ) |
405 | 0 | : m_pSock( pSock ), m_csSockAddr( csSockAddr ) |
406 | 0 | { |
407 | 0 | m_sHostname = sHostname; |
408 | 0 | m_pAddrRes = NULL; |
409 | 0 | m_iRet = ETIMEDOUT; |
410 | 0 | } |
411 | | |
412 | | CGetAddrInfo::~CGetAddrInfo() |
413 | 0 | { |
414 | 0 | if( m_pAddrRes ) |
415 | 0 | freeaddrinfo( m_pAddrRes ); |
416 | 0 | m_pAddrRes = NULL; |
417 | 0 | } |
418 | | |
419 | | void CGetAddrInfo::Init() |
420 | 0 | { |
421 | 0 | memset( ( struct addrinfo * )&m_cHints, '\0', sizeof( m_cHints ) ); |
422 | 0 | m_cHints.ai_family = m_csSockAddr.GetAFRequire(); |
423 | |
|
424 | 0 | m_cHints.ai_socktype = SOCK_STREAM; |
425 | 0 | m_cHints.ai_protocol = IPPROTO_TCP; |
426 | 0 | #ifdef AI_ADDRCONFIG |
427 | | // this is suppose to eliminate host from appearing that this system can not support |
428 | 0 | m_cHints.ai_flags = AI_ADDRCONFIG; |
429 | 0 | #endif /* AI_ADDRCONFIG */ |
430 | |
|
431 | 0 | if( m_pSock && ( m_pSock->GetType() == Csock::LISTENER || m_pSock->GetConState() == Csock::CST_BINDVHOST ) ) |
432 | 0 | { |
433 | | // when doing a dns for bind only, set the AI_PASSIVE flag as suggested by the man page |
434 | 0 | m_cHints.ai_flags |= AI_PASSIVE; |
435 | 0 | } |
436 | 0 | } |
437 | | |
438 | | int CGetAddrInfo::Process() |
439 | 0 | { |
440 | 0 | m_iRet = getaddrinfo( m_sHostname.c_str(), NULL, &m_cHints, &m_pAddrRes ); |
441 | 0 | if( m_iRet == EAI_AGAIN ) |
442 | 0 | return( EAGAIN ); |
443 | 0 | else if( m_iRet == 0 ) |
444 | 0 | return( 0 ); |
445 | 0 | return( ETIMEDOUT ); |
446 | 0 | } |
447 | | |
448 | | int CGetAddrInfo::Finish() |
449 | 0 | { |
450 | 0 | if( m_iRet == 0 && m_pAddrRes ) |
451 | 0 | { |
452 | 0 | std::list<struct addrinfo *> lpTryAddrs; |
453 | 0 | bool bFound = false; |
454 | 0 | for( struct addrinfo * pRes = m_pAddrRes; pRes; pRes = pRes->ai_next ) |
455 | 0 | { |
456 | | // pass through the list building out a lean list of candidates to try. AI_CONFIGADDR doesn't always seem to work |
457 | | #ifdef __sun |
458 | | if( ( pRes->ai_socktype != SOCK_STREAM ) || ( pRes->ai_protocol != IPPROTO_TCP && pRes->ai_protocol != IPPROTO_IP ) ) |
459 | | #else |
460 | 0 | if( ( pRes->ai_socktype != SOCK_STREAM ) || ( pRes->ai_protocol != IPPROTO_TCP ) ) |
461 | 0 | #endif /* __sun work around broken impl of getaddrinfo */ |
462 | 0 | continue; |
463 | | |
464 | 0 | if( ( m_csSockAddr.GetAFRequire() != CSSockAddr::RAF_ANY ) && ( pRes->ai_family != m_csSockAddr.GetAFRequire() ) ) |
465 | 0 | continue; // they requested a special type, so be certain we woop past anything unwanted |
466 | 0 | lpTryAddrs.push_back( pRes ); |
467 | 0 | } |
468 | 0 | for( std::list<struct addrinfo *>::iterator it = lpTryAddrs.begin(); it != lpTryAddrs.end(); ) |
469 | 0 | { |
470 | | // cycle through these, leaving the last iterator for the outside caller to call, so if there is an error it can call the events |
471 | 0 | struct addrinfo * pRes = *it; |
472 | 0 | bool bTryConnect = false; |
473 | 0 | if( pRes->ai_family == AF_INET ) |
474 | 0 | { |
475 | 0 | if( m_pSock ) |
476 | 0 | m_pSock->SetIPv6( false ); |
477 | 0 | m_csSockAddr.SetIPv6( false ); |
478 | 0 | struct sockaddr_in * pTmp = ( struct sockaddr_in * )pRes->ai_addr; |
479 | 0 | memcpy( m_csSockAddr.GetAddr(), &( pTmp->sin_addr ), sizeof( *( m_csSockAddr.GetAddr() ) ) ); |
480 | 0 | if( m_pSock && m_pSock->GetConState() == Csock::CST_DESTDNS && m_pSock->GetType() == Csock::OUTBOUND ) |
481 | 0 | { |
482 | 0 | bTryConnect = true; |
483 | 0 | } |
484 | 0 | else |
485 | 0 | { |
486 | 0 | bFound = true; |
487 | 0 | break; |
488 | 0 | } |
489 | 0 | } |
490 | | #ifdef HAVE_IPV6 |
491 | | else if( pRes->ai_family == AF_INET6 ) |
492 | | { |
493 | | if( m_pSock ) |
494 | | m_pSock->SetIPv6( true ); |
495 | | m_csSockAddr.SetIPv6( true ); |
496 | | struct sockaddr_in6 * pTmp = ( struct sockaddr_in6 * )pRes->ai_addr; |
497 | | memcpy( m_csSockAddr.GetAddr6(), &( pTmp->sin6_addr ), sizeof( *( m_csSockAddr.GetAddr6() ) ) ); |
498 | | if( m_pSock && m_pSock->GetConState() == Csock::CST_DESTDNS && m_pSock->GetType() == Csock::OUTBOUND ) |
499 | | { |
500 | | bTryConnect = true; |
501 | | } |
502 | | else |
503 | | { |
504 | | bFound = true; |
505 | | break; |
506 | | } |
507 | | } |
508 | | #endif /* HAVE_IPV6 */ |
509 | | |
510 | 0 | ++it; // increment the iterator her so we know if its the last element or not |
511 | |
|
512 | 0 | if( bTryConnect && it != lpTryAddrs.end() ) |
513 | 0 | { |
514 | | // save the last attempt for the outer loop, the issue then becomes that the error is thrown on the last failure |
515 | 0 | if( m_pSock->CreateSocksFD() && m_pSock->Connect() ) |
516 | 0 | { |
517 | 0 | m_pSock->SetSkipConnect( true ); // this tells the socket that the connection state has been started |
518 | 0 | bFound = true; |
519 | 0 | break; |
520 | 0 | } |
521 | 0 | m_pSock->CloseSocksFD(); |
522 | 0 | } |
523 | 0 | else if( bTryConnect ) |
524 | 0 | { |
525 | 0 | bFound = true; |
526 | 0 | } |
527 | 0 | } |
528 | |
|
529 | 0 | if( bFound ) // the data pointed to here is invalid now, but the pointer itself is a good test |
530 | 0 | { |
531 | 0 | return( 0 ); |
532 | 0 | } |
533 | 0 | } |
534 | 0 | return( ETIMEDOUT ); |
535 | 0 | } |
536 | | |
537 | | int CS_GetAddrInfo( const CS_STRING & sHostname, Csock * pSock, CSSockAddr & csSockAddr ) |
538 | 0 | { |
539 | | #ifdef USE_GETHOSTBYNAME |
540 | | if( pSock ) |
541 | | pSock->SetIPv6( false ); |
542 | | csSockAddr.SetIPv6( false ); |
543 | | int iRet = __GetHostByName( sHostname, csSockAddr.GetAddr(), 3 ); |
544 | | return( iRet ); |
545 | | #else |
546 | 0 | CGetAddrInfo cInfo( sHostname, pSock, csSockAddr ); |
547 | 0 | cInfo.Init(); |
548 | 0 | int iRet = cInfo.Process(); |
549 | 0 | if( iRet != 0 ) |
550 | 0 | return( iRet ); |
551 | 0 | return( cInfo.Finish() ); |
552 | 0 | #endif /* USE_GETHOSTBYNAME */ |
553 | 0 | } |
554 | | |
555 | | int Csock::ConvertAddress( const struct sockaddr_storage * pAddr, socklen_t iAddrLen, CS_STRING & sIP, uint16_t * piPort ) const |
556 | 0 | { |
557 | 0 | char szHostname[NI_MAXHOST]; |
558 | 0 | char szServ[NI_MAXSERV]; |
559 | 0 | int iRet = getnameinfo( ( const struct sockaddr * )pAddr, iAddrLen, szHostname, NI_MAXHOST, szServ, NI_MAXSERV, NI_NUMERICHOST|NI_NUMERICSERV ); |
560 | 0 | if( iRet == 0 ) |
561 | 0 | { |
562 | 0 | sIP = szHostname; |
563 | 0 | if( piPort ) |
564 | 0 | *piPort = ( uint16_t )atoi( szServ ); |
565 | 0 | } |
566 | 0 | return( iRet ); |
567 | 0 | } |
568 | | |
569 | | bool InitCsocket() |
570 | 0 | { |
571 | | #ifdef _WIN32 |
572 | | WSADATA wsaData; |
573 | | int iResult = WSAStartup( MAKEWORD( 2, 2 ), &wsaData ); |
574 | | if( iResult != NO_ERROR ) |
575 | | return( false ); |
576 | | #endif /* _WIN32 */ |
577 | | #ifdef HAVE_C_ARES |
578 | | #if ARES_VERSION >= CREATE_ARES_VER( 1, 6, 1 ) |
579 | | if( ares_library_init( ARES_LIB_INIT_ALL ) != 0 ) |
580 | | return( false ); |
581 | | #endif /* ARES_VERSION >= CREATE_ARES_VER( 1, 6, 1 ) */ |
582 | | #endif /* HAVE_C_ARES */ |
583 | | #ifdef HAVE_LIBSSL |
584 | | if( !InitSSL() ) |
585 | | return( false ); |
586 | | #endif /* HAVE_LIBSSL */ |
587 | 0 | return( true ); |
588 | 0 | } |
589 | | |
590 | | void ShutdownCsocket() |
591 | 0 | { |
592 | | #ifdef HAVE_LIBSSL |
593 | | #if defined( HAVE_ERR_REMOVE_THREAD_STATE ) |
594 | | ERR_remove_thread_state( NULL ); |
595 | | #elif defined( HAVE_ERR_REMOVE_STATE ) |
596 | | ERR_remove_state( 0 ); |
597 | | #endif |
598 | | #ifndef OPENSSL_NO_ENGINE |
599 | | ENGINE_cleanup(); |
600 | | #endif |
601 | | #ifndef OPENSSL_IS_BORINGSSL |
602 | | CONF_modules_unload( 1 ); |
603 | | #endif |
604 | | ERR_free_strings(); |
605 | | EVP_cleanup(); |
606 | | CRYPTO_cleanup_all_ex_data(); |
607 | | #endif /* HAVE_LIBSSL */ |
608 | | #ifdef HAVE_C_ARES |
609 | | #if ARES_VERSION >= CREATE_ARES_VER( 1, 6, 1 ) |
610 | | ares_library_cleanup(); |
611 | | #endif /* ARES_VERSION >= CREATE_ARES_VER( 1, 6, 1 ) */ |
612 | | #endif /* HAVE_C_ARES */ |
613 | | #ifdef _WIN32 |
614 | | WSACleanup(); |
615 | | #endif /* _WIN32 */ |
616 | 0 | } |
617 | | |
618 | | #ifdef HAVE_LIBSSL |
619 | | bool InitSSL( ECompType eCompressionType ) |
620 | | { |
621 | | SSL_load_error_strings(); |
622 | | if( SSL_library_init() != 1 ) |
623 | | { |
624 | | CS_DEBUG( "SSL_library_init() failed!" ); |
625 | | return( false ); |
626 | | } |
627 | | |
628 | | #ifndef _WIN32 |
629 | | if( access( "/dev/urandom", R_OK ) == 0 ) |
630 | | { |
631 | | RAND_load_file( "/dev/urandom", 1024 ); |
632 | | } |
633 | | else if( access( "/dev/random", R_OK ) == 0 ) |
634 | | { |
635 | | RAND_load_file( "/dev/random", 1024 ); |
636 | | } |
637 | | else |
638 | | { |
639 | | CS_DEBUG( "Unable to locate entropy location! Tried /dev/urandom and /dev/random" ); |
640 | | return( false ); |
641 | | } |
642 | | #endif /* _WIN32 */ |
643 | | |
644 | | #ifndef OPENSSL_NO_COMP |
645 | | COMP_METHOD *cm = NULL; |
646 | | |
647 | | if( CT_ZLIB & eCompressionType ) |
648 | | { |
649 | | cm = COMP_zlib(); |
650 | | if( cm ) |
651 | | SSL_COMP_add_compression_method( CT_ZLIB, cm ); |
652 | | } |
653 | | #endif |
654 | | |
655 | | // setting this up once in the begining |
656 | | s_iCsockSSLIdx = SSL_get_ex_new_index( 0, NULL, NULL, NULL, NULL ); |
657 | | |
658 | | return( true ); |
659 | | } |
660 | | |
661 | | void SSLErrors( const char *filename, u_int iLineNum ) |
662 | | { |
663 | | unsigned long iSSLError = 0; |
664 | | while( ( iSSLError = ERR_get_error() ) != 0 ) |
665 | | { |
666 | | CS_DEBUG( "at " << filename << ":" << iLineNum ); |
667 | | char szError[512]; |
668 | | memset( ( char * ) szError, '\0', 512 ); |
669 | | ERR_error_string_n( iSSLError, szError, 511 ); |
670 | | if( *szError ) |
671 | | CS_DEBUG( szError ); |
672 | | } |
673 | | } |
674 | | #endif /* HAVE_LIBSSL */ |
675 | | |
676 | | void CSAdjustTVTimeout( struct timeval & tv, long iTimeoutMS ) |
677 | 0 | { |
678 | 0 | if( iTimeoutMS >= 0 ) |
679 | 0 | { |
680 | 0 | long iCurTimeout = tv.tv_usec / 1000; |
681 | 0 | iCurTimeout += tv.tv_sec * 1000; |
682 | 0 | if( iCurTimeout > iTimeoutMS ) |
683 | 0 | { |
684 | 0 | tv.tv_sec = iTimeoutMS / 1000; |
685 | 0 | tv.tv_usec = iTimeoutMS % 1000; |
686 | 0 | } |
687 | 0 | } |
688 | 0 | } |
689 | | |
690 | 0 | #define CS_UNKNOWN_ERROR "Unknown Error" |
691 | | static const char * CS_StrError( int iErrno, char * pszBuff, size_t uBuffLen ) |
692 | 0 | { |
693 | | #if defined( sgi ) || defined(__sun) || (defined(__NetBSD_Version__) && __NetBSD_Version__ < 4000000000) |
694 | | return( strerror( iErrno ) ); |
695 | | #else |
696 | 0 | memset( pszBuff, '\0', uBuffLen ); |
697 | | #if defined( _WIN32 ) |
698 | | if ( strerror_s( pszBuff, uBuffLen, iErrno ) == 0 ) |
699 | | return( pszBuff ); |
700 | | #elif !defined( _GNU_SOURCE ) || !defined(__GLIBC__) || defined( __FreeBSD__ ) |
701 | | if( strerror_r( iErrno, pszBuff, uBuffLen ) == 0 ) |
702 | | return( pszBuff ); |
703 | | #else |
704 | 0 | return( strerror_r( iErrno, pszBuff, uBuffLen ) ); |
705 | 0 | #endif /* (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !defined( _GNU_SOURCE ) */ |
706 | 0 | #endif /* defined( sgi ) || defined(__sun) || defined(_WIN32) || (defined(__NetBSD_Version__) && __NetBSD_Version__ < 4000000000) */ |
707 | 0 | return( CS_UNKNOWN_ERROR ); |
708 | 0 | } |
709 | | |
710 | | void __Perror( const CS_STRING & s, const char * pszFile, u_int iLineNo ) |
711 | 0 | { |
712 | 0 | char szBuff[0xff]; |
713 | 0 | std::cerr << s << "(" << pszFile << ":" << iLineNo << "): " << CS_StrError( GetSockError(), szBuff, 0xff ) << endl; |
714 | 0 | } |
715 | | |
716 | 0 | static uint64_t millitime() { |
717 | 0 | std::chrono::time_point<std::chrono::steady_clock> time = std::chrono::steady_clock::now(); |
718 | 0 | return std::chrono::duration_cast<std::chrono::milliseconds>(time.time_since_epoch()).count(); |
719 | 0 | } |
720 | | |
721 | 0 | #define CS_GETTIMEOFDAY cxx11_gettimeofday |
722 | 0 | static int cxx11_gettimeofday(struct timeval* now, void*) { |
723 | 0 | std::chrono::time_point<std::chrono::steady_clock> time = std::chrono::steady_clock::now(); |
724 | 0 | long long micros = std::chrono::duration_cast<std::chrono::microseconds>(time.time_since_epoch()).count(); |
725 | 0 | now->tv_sec = micros / 1000000LL; |
726 | 0 | now->tv_usec = micros % 1000000LL; |
727 | 0 | return 0; |
728 | 0 | } |
729 | | |
730 | | #ifndef _NO_CSOCKET_NS // some people may not want to use a namespace |
731 | | } |
732 | | using namespace Csocket; |
733 | | #endif /* _NO_CSOCKET_NS */ |
734 | | |
735 | | CCron::CCron() |
736 | 0 | { |
737 | 0 | m_iCycles = 0; |
738 | 0 | m_iMaxCycles = 0; |
739 | 0 | m_bActive = true; |
740 | 0 | timerclear( &m_tTime ); |
741 | 0 | m_tTimeSequence.tv_sec = 60; |
742 | 0 | m_tTimeSequence.tv_usec = 0; |
743 | 0 | m_bPause = false; |
744 | 0 | m_bRunOnNextCall = false; |
745 | 0 | } |
746 | | |
747 | | void CCron::run( timeval & tNow ) |
748 | 0 | { |
749 | 0 | if( m_bPause ) |
750 | 0 | return; |
751 | | |
752 | 0 | if( !timerisset( &tNow ) ) |
753 | 0 | CS_GETTIMEOFDAY( &tNow, NULL ); |
754 | |
|
755 | 0 | if( m_bActive && ( !timercmp( &tNow, &m_tTime, < ) || m_bRunOnNextCall ) ) |
756 | 0 | { |
757 | 0 | m_bRunOnNextCall = false; // Setting this here because RunJob() could set it back to true |
758 | 0 | RunJob(); |
759 | |
|
760 | 0 | if( m_iMaxCycles > 0 && ++m_iCycles >= m_iMaxCycles ) |
761 | 0 | m_bActive = false; |
762 | 0 | else |
763 | 0 | timeradd( &tNow, &m_tTimeSequence, &m_tTime ); |
764 | 0 | } |
765 | 0 | } |
766 | | |
767 | | void CCron::StartMaxCycles( double dTimeSequence, u_int iMaxCycles ) |
768 | 0 | { |
769 | 0 | timeval tNow; |
770 | 0 | m_tTimeSequence.tv_sec = ( time_t ) dTimeSequence; |
771 | | // this could be done with modf(), but we're avoiding bringing in libm just for the one function. |
772 | 0 | m_tTimeSequence.tv_usec = ( suseconds_t )( ( dTimeSequence - ( double )( ( time_t ) dTimeSequence ) ) * 1000000 ); |
773 | 0 | CS_GETTIMEOFDAY( &tNow, NULL ); |
774 | 0 | timeradd( &tNow, &m_tTimeSequence, &m_tTime ); |
775 | 0 | m_iMaxCycles = iMaxCycles; |
776 | 0 | m_bActive = true; |
777 | 0 | } |
778 | | |
779 | | void CCron::StartMaxCycles( const timeval& tTimeSequence, u_int iMaxCycles ) |
780 | 0 | { |
781 | 0 | timeval tNow; |
782 | 0 | m_tTimeSequence = tTimeSequence; |
783 | 0 | CS_GETTIMEOFDAY( &tNow, NULL ); |
784 | 0 | timeradd( &tNow, &m_tTimeSequence, &m_tTime ); |
785 | 0 | m_iMaxCycles = iMaxCycles; |
786 | 0 | m_bActive = true; |
787 | 0 | } |
788 | | |
789 | | void CCron::Start( double dTimeSequence ) |
790 | 0 | { |
791 | 0 | StartMaxCycles( dTimeSequence, 0 ); |
792 | 0 | } |
793 | | |
794 | | void CCron::Start( const timeval& tTimeSequence ) |
795 | 0 | { |
796 | 0 | StartMaxCycles( tTimeSequence, 0 ); |
797 | 0 | } |
798 | | |
799 | | void CCron::Stop() |
800 | 0 | { |
801 | 0 | m_bActive = false; |
802 | 0 | } |
803 | | |
804 | | void CCron::Pause() |
805 | 0 | { |
806 | 0 | m_bPause = true; |
807 | 0 | } |
808 | | |
809 | | void CCron::UnPause() |
810 | 0 | { |
811 | 0 | m_bPause = false; |
812 | 0 | } |
813 | | |
814 | | void CCron::Reset() |
815 | 0 | { |
816 | 0 | Stop(); |
817 | 0 | Start(m_tTimeSequence); |
818 | 0 | } |
819 | | |
820 | 0 | timeval CCron::GetInterval() const { return( m_tTimeSequence ); } |
821 | 0 | u_int CCron::GetMaxCycles() const { return( m_iMaxCycles ); } |
822 | 0 | u_int CCron::GetCyclesLeft() const { return( ( m_iMaxCycles > m_iCycles ? ( m_iMaxCycles - m_iCycles ) : 0 ) ); } |
823 | | |
824 | 0 | bool CCron::isValid() const { return( m_bActive ); } |
825 | 0 | const CS_STRING & CCron::GetName() const { return( m_sName ); } |
826 | 0 | void CCron::SetName( const CS_STRING & sName ) { m_sName = sName; } |
827 | 0 | void CCron::RunJob() { CS_DEBUG( "This should be overridden" ); } |
828 | | |
829 | | bool CSMonitorFD::GatherFDsForSelect( std::map< cs_sock_t, short > & miiReadyFds, long & iTimeoutMS ) |
830 | 0 | { |
831 | 0 | iTimeoutMS = -1; // don't bother changing anything in the default implementation |
832 | 0 | for( std::map< cs_sock_t, short >::iterator it = m_miiMonitorFDs.begin(); it != m_miiMonitorFDs.end(); ++it ) |
833 | 0 | { |
834 | 0 | miiReadyFds[it->first] = it->second; |
835 | 0 | } |
836 | 0 | return( m_bEnabled ); |
837 | 0 | } |
838 | | |
839 | | bool CSMonitorFD::CheckFDs( const std::map< cs_sock_t, short > & miiReadyFds ) |
840 | 0 | { |
841 | 0 | std::map< cs_sock_t, short > miiTriggerdFds; |
842 | 0 | for( std::map< cs_sock_t, short >::iterator it = m_miiMonitorFDs.begin(); it != m_miiMonitorFDs.end(); ++it ) |
843 | 0 | { |
844 | 0 | std::map< cs_sock_t, short >::const_iterator itFD = miiReadyFds.find( it->first ); |
845 | 0 | if( itFD != miiReadyFds.end() ) |
846 | 0 | miiTriggerdFds[itFD->first] = itFD->second; |
847 | 0 | } |
848 | 0 | if( !miiTriggerdFds.empty() ) |
849 | 0 | return( FDsThatTriggered( miiTriggerdFds ) ); |
850 | 0 | return( m_bEnabled ); |
851 | 0 | } |
852 | | |
853 | | CSockCommon::~CSockCommon() |
854 | 0 | { |
855 | | // delete any left over crons |
856 | 0 | CleanupCrons(); |
857 | 0 | CleanupFDMonitors(); |
858 | 0 | } |
859 | | |
860 | | void CSockCommon::CleanupCrons() |
861 | 0 | { |
862 | 0 | for( size_t a = 0; a < m_vcCrons.size(); ++a ) |
863 | 0 | CS_Delete( m_vcCrons[a] ); |
864 | 0 | m_vcCrons.clear(); |
865 | 0 | } |
866 | | |
867 | | void CSockCommon::CleanupFDMonitors() |
868 | 0 | { |
869 | 0 | for( size_t a = 0; a < m_vcMonitorFD.size(); ++a ) |
870 | 0 | CS_Delete( m_vcMonitorFD[a] ); |
871 | 0 | m_vcMonitorFD.clear(); |
872 | 0 | } |
873 | | |
874 | | void CSockCommon::CheckFDs( const std::map< cs_sock_t, short > & miiReadyFds ) |
875 | 0 | { |
876 | 0 | for( size_t uMon = 0; uMon < m_vcMonitorFD.size(); ++uMon ) |
877 | 0 | { |
878 | 0 | if( !m_vcMonitorFD[uMon]->IsEnabled() || !m_vcMonitorFD[uMon]->CheckFDs( miiReadyFds ) ) |
879 | 0 | m_vcMonitorFD.erase( m_vcMonitorFD.begin() + uMon-- ); |
880 | 0 | } |
881 | 0 | } |
882 | | |
883 | | void CSockCommon::AssignFDs( std::map< cs_sock_t, short > & miiReadyFds, struct timeval * tvtimeout ) |
884 | 0 | { |
885 | 0 | for( size_t uMon = 0; uMon < m_vcMonitorFD.size(); ++uMon ) |
886 | 0 | { |
887 | 0 | long iTimeoutMS = -1; |
888 | 0 | if( m_vcMonitorFD[uMon]->IsEnabled() && m_vcMonitorFD[uMon]->GatherFDsForSelect( miiReadyFds, iTimeoutMS ) ) |
889 | 0 | { |
890 | 0 | CSAdjustTVTimeout( *tvtimeout, iTimeoutMS ); |
891 | 0 | } |
892 | 0 | else |
893 | 0 | { |
894 | 0 | CS_Delete( m_vcMonitorFD[uMon] ); |
895 | 0 | m_vcMonitorFD.erase( m_vcMonitorFD.begin() + uMon-- ); |
896 | 0 | } |
897 | 0 | } |
898 | 0 | } |
899 | | |
900 | | |
901 | | void CSockCommon::Cron() |
902 | 0 | { |
903 | 0 | timeval tNow; |
904 | 0 | timerclear( &tNow ); |
905 | |
|
906 | 0 | for( vector<CCron *>::size_type a = 0; a < m_vcCrons.size(); ++a ) |
907 | 0 | { |
908 | 0 | CCron * pcCron = m_vcCrons[a]; |
909 | |
|
910 | 0 | if( !pcCron->isValid() ) |
911 | 0 | { |
912 | 0 | CS_Delete( pcCron ); |
913 | 0 | m_vcCrons.erase( m_vcCrons.begin() + a-- ); |
914 | 0 | } |
915 | 0 | else |
916 | 0 | { |
917 | 0 | pcCron->run( tNow ); |
918 | 0 | } |
919 | 0 | } |
920 | 0 | } |
921 | | |
922 | | void CSockCommon::AddCron( CCron * pcCron ) |
923 | 0 | { |
924 | 0 | m_vcCrons.push_back( pcCron ); |
925 | 0 | } |
926 | | |
927 | | void CSockCommon::DelCron( const CS_STRING & sName, bool bDeleteAll, bool bCaseSensitive ) |
928 | 0 | { |
929 | 0 | for( size_t a = 0; a < m_vcCrons.size(); ++a ) |
930 | 0 | { |
931 | 0 | int ( *Cmp )( const char *, const char * ) = ( bCaseSensitive ? strcmp : strcasecmp ); |
932 | 0 | if( Cmp( m_vcCrons[a]->GetName().c_str(), sName.c_str() ) == 0 ) |
933 | 0 | { |
934 | 0 | m_vcCrons[a]->Stop(); |
935 | 0 | CS_Delete( m_vcCrons[a] ); |
936 | 0 | m_vcCrons.erase( m_vcCrons.begin() + a-- ); |
937 | 0 | if( !bDeleteAll ) |
938 | 0 | break; |
939 | 0 | } |
940 | 0 | } |
941 | 0 | } |
942 | | |
943 | | void CSockCommon::DelCron( u_int iPos ) |
944 | 0 | { |
945 | 0 | if( iPos < m_vcCrons.size() ) |
946 | 0 | { |
947 | 0 | m_vcCrons[iPos]->Stop(); |
948 | 0 | CS_Delete( m_vcCrons[iPos] ); |
949 | 0 | m_vcCrons.erase( m_vcCrons.begin() + iPos ); |
950 | 0 | } |
951 | 0 | } |
952 | | |
953 | | void CSockCommon::DelCronByAddr( CCron * pcCron ) |
954 | 0 | { |
955 | 0 | for( size_t a = 0; a < m_vcCrons.size(); ++a ) |
956 | 0 | { |
957 | 0 | if( m_vcCrons[a] == pcCron ) |
958 | 0 | { |
959 | 0 | m_vcCrons[a]->Stop(); |
960 | 0 | CS_Delete( m_vcCrons[a] ); |
961 | 0 | m_vcCrons.erase( m_vcCrons.begin() + a ); |
962 | 0 | return; |
963 | 0 | } |
964 | 0 | } |
965 | 0 | } |
966 | | |
967 | 0 | Csock::Csock( int iTimeout ) : CSockCommon() |
968 | 0 | { |
969 | | #ifdef HAVE_LIBSSL |
970 | | m_pCerVerifyCB = _CertVerifyCB; |
971 | | #endif /* HAVE_LIBSSL */ |
972 | 0 | Init( "", 0, iTimeout ); |
973 | 0 | } |
974 | | |
975 | 0 | Csock::Csock( const CS_STRING & sHostname, uint16_t iport, int iTimeout ) : CSockCommon() |
976 | 0 | { |
977 | | #ifdef HAVE_LIBSSL |
978 | | m_pCerVerifyCB = _CertVerifyCB; |
979 | | #endif /* HAVE_LIBSSL */ |
980 | 0 | Init( sHostname, iport, iTimeout ); |
981 | 0 | } |
982 | | |
983 | | // override this for accept sockets |
984 | | Csock *Csock::GetSockObj( const CS_STRING & sHostname, uint16_t iPort ) |
985 | 0 | { |
986 | 0 | return( NULL ); |
987 | 0 | } |
988 | | |
989 | | #ifdef HAVE_LIBSSL |
990 | | bool Csock::SNIConfigureClient( CS_STRING & sHostname ) |
991 | | { |
992 | | if( m_shostname.empty() ) |
993 | | return( false ); |
994 | | sHostname = m_shostname; |
995 | | return( true ); |
996 | | } |
997 | | #endif |
998 | | |
999 | | #ifdef _WIN32 |
1000 | | #define CS_CLOSE closesocket |
1001 | | #else |
1002 | 0 | #define CS_CLOSE close |
1003 | | #endif /* _WIN32 */ |
1004 | | |
1005 | | Csock::~Csock() |
1006 | 0 | { |
1007 | | #ifdef _WIN32 |
1008 | | // prevent successful closesocket() calls and such from |
1009 | | // overwriting any possible previous errors. |
1010 | | int iOldError = ::WSAGetLastError(); |
1011 | | #endif /* _WIN32 */ |
1012 | |
|
1013 | | #ifdef HAVE_ICU |
1014 | | if( m_cnvExt ) ucnv_close( m_cnvExt ); |
1015 | | if( m_cnvInt ) ucnv_close( m_cnvInt ); |
1016 | | #endif |
1017 | |
|
1018 | | #ifdef HAVE_C_ARES |
1019 | | if( m_pARESChannel ) |
1020 | | ares_cancel( m_pARESChannel ); |
1021 | | FreeAres(); |
1022 | | #endif /* HAVE_C_ARES */ |
1023 | |
|
1024 | | #ifdef HAVE_LIBSSL |
1025 | | FREE_SSL(); |
1026 | | FREE_CTX(); |
1027 | | #endif /* HAVE_LIBSSL */ |
1028 | |
|
1029 | 0 | CloseSocksFD(); |
1030 | |
|
1031 | 0 | #ifdef HAVE_UNIX_SOCKET |
1032 | 0 | if (m_bUnixListen) { |
1033 | 0 | ::unlink(m_sBindHost.c_str()); |
1034 | 0 | } |
1035 | 0 | #endif |
1036 | |
|
1037 | | #ifdef _WIN32 |
1038 | | ::WSASetLastError( iOldError ); |
1039 | | #endif /* _WIN32 */ |
1040 | 0 | } |
1041 | | |
1042 | | void Csock::CloseSocksFD() |
1043 | 0 | { |
1044 | 0 | if( m_iReadSock != m_iWriteSock ) |
1045 | 0 | { |
1046 | 0 | if( m_iReadSock != CS_INVALID_SOCK ) |
1047 | 0 | CS_CLOSE( m_iReadSock ); |
1048 | 0 | if( m_iWriteSock != CS_INVALID_SOCK ) |
1049 | 0 | CS_CLOSE( m_iWriteSock ); |
1050 | 0 | } |
1051 | 0 | else if( m_iReadSock != CS_INVALID_SOCK ) |
1052 | 0 | { |
1053 | 0 | CS_CLOSE( m_iReadSock ); |
1054 | 0 | } |
1055 | |
|
1056 | 0 | m_iReadSock = CS_INVALID_SOCK; |
1057 | 0 | m_iWriteSock = CS_INVALID_SOCK; |
1058 | 0 | } |
1059 | | |
1060 | | |
1061 | | void Csock::Dereference() |
1062 | 0 | { |
1063 | 0 | m_iWriteSock = m_iReadSock = CS_INVALID_SOCK; |
1064 | |
|
1065 | | #ifdef HAVE_LIBSSL |
1066 | | m_ssl = NULL; |
1067 | | m_ssl_ctx = NULL; |
1068 | | #endif /* HAVE_LIBSSL */ |
1069 | | |
1070 | | // don't delete and erase, just erase since they were moved to the copied sock |
1071 | 0 | m_vcCrons.clear(); |
1072 | 0 | m_vcMonitorFD.clear(); |
1073 | 0 | Close( CLT_DEREFERENCE ); |
1074 | 0 | } |
1075 | | |
1076 | | void Csock::Copy( const Csock & cCopy ) |
1077 | 0 | { |
1078 | 0 | m_iTcount = cCopy.m_iTcount; |
1079 | 0 | m_iLastCheckTimeoutTime = cCopy.m_iLastCheckTimeoutTime; |
1080 | 0 | m_uPort = cCopy.m_uPort; |
1081 | 0 | m_iRemotePort = cCopy.m_iRemotePort; |
1082 | 0 | m_iLocalPort = cCopy.m_iLocalPort; |
1083 | 0 | m_iReadSock = cCopy.m_iReadSock; |
1084 | 0 | m_iWriteSock = cCopy.m_iWriteSock; |
1085 | 0 | m_iTimeout = cCopy.m_iTimeout; |
1086 | 0 | m_iMaxConns = cCopy.m_iMaxConns; |
1087 | 0 | m_iConnType = cCopy.m_iConnType; |
1088 | 0 | m_iMethod = cCopy.m_iMethod; |
1089 | 0 | m_bUseSSL = cCopy.m_bUseSSL; |
1090 | 0 | m_bIsConnected = cCopy.m_bIsConnected; |
1091 | 0 | m_bsslEstablished = cCopy.m_bsslEstablished; |
1092 | 0 | m_bEnableReadLine = cCopy.m_bEnableReadLine; |
1093 | 0 | m_bPauseRead = cCopy.m_bPauseRead; |
1094 | 0 | m_shostname = cCopy.m_shostname; |
1095 | 0 | m_sbuffer = cCopy.m_sbuffer; |
1096 | 0 | m_sSockName = cCopy.m_sSockName; |
1097 | 0 | m_sKeyFile = cCopy.m_sKeyFile; |
1098 | 0 | m_sDHParamFile = cCopy.m_sDHParamFile; |
1099 | 0 | m_sPemFile = cCopy.m_sPemFile; |
1100 | 0 | m_sCipherType = cCopy.m_sCipherType; |
1101 | 0 | m_sParentName = cCopy.m_sParentName; |
1102 | 0 | m_sSend = cCopy.m_sSend; |
1103 | 0 | m_sPemPass = cCopy.m_sPemPass; |
1104 | 0 | m_sLocalIP = cCopy.m_sLocalIP; |
1105 | 0 | m_sRemoteIP = cCopy.m_sRemoteIP; |
1106 | 0 | m_eCloseType = cCopy.m_eCloseType; |
1107 | |
|
1108 | 0 | m_iMaxMilliSeconds = cCopy.m_iMaxMilliSeconds; |
1109 | 0 | m_iLastSendTime = cCopy.m_iLastSendTime; |
1110 | 0 | m_iBytesRead = cCopy.m_iBytesRead; |
1111 | 0 | m_iBytesWritten = cCopy.m_iBytesWritten; |
1112 | 0 | m_iStartTime = cCopy.m_iStartTime; |
1113 | 0 | m_iMaxBytes = cCopy.m_iMaxBytes; |
1114 | 0 | m_iLastSend = cCopy.m_iLastSend; |
1115 | 0 | m_uSendBufferPos = cCopy.m_uSendBufferPos; |
1116 | 0 | m_iMaxStoredBufferLength = cCopy.m_iMaxStoredBufferLength; |
1117 | 0 | m_iTimeoutType = cCopy.m_iTimeoutType; |
1118 | |
|
1119 | 0 | m_address = cCopy.m_address; |
1120 | 0 | m_bindhost = cCopy.m_bindhost; |
1121 | 0 | m_bIsIPv6 = cCopy.m_bIsIPv6; |
1122 | 0 | m_bSkipConnect = cCopy.m_bSkipConnect; |
1123 | | #ifdef HAVE_C_ARES |
1124 | | FreeAres(); // Not copying this state, but making sure its nulled out |
1125 | | m_iARESStatus = -1; // set it to unitialized |
1126 | | m_pCurrAddr = NULL; |
1127 | | #endif /* HAVE_C_ARES */ |
1128 | |
|
1129 | | #ifdef HAVE_LIBSSL |
1130 | | m_bNoSSLCompression = cCopy.m_bNoSSLCompression; |
1131 | | m_bSSLCipherServerPreference = cCopy.m_bSSLCipherServerPreference; |
1132 | | m_uDisableProtocols = cCopy.m_uDisableProtocols; |
1133 | | m_iRequireClientCertFlags = cCopy.m_iRequireClientCertFlags; |
1134 | | m_sSSLBuffer = cCopy.m_sSSLBuffer; |
1135 | | |
1136 | | FREE_SSL(); |
1137 | | FREE_CTX(); // be sure to remove anything that was already here |
1138 | | m_ssl = cCopy.m_ssl; |
1139 | | m_ssl_ctx = cCopy.m_ssl_ctx; |
1140 | | |
1141 | | m_pCerVerifyCB = cCopy.m_pCerVerifyCB; |
1142 | | |
1143 | | if( m_ssl ) |
1144 | | { |
1145 | | SSL_set_ex_data( m_ssl, GetCsockSSLIdx(), this ); |
1146 | | #if defined( SSL_CTX_set_tlsext_servername_callback ) |
1147 | | SSL_CTX_set_tlsext_servername_arg( m_ssl_ctx, this ); |
1148 | | #endif /* SSL_CTX_set_tlsext_servername_callback */ |
1149 | | } |
1150 | | |
1151 | | #endif /* HAVE_LIBSSL */ |
1152 | 0 | #ifdef HAVE_UNIX_SOCKET |
1153 | 0 | m_bUnixListen = cCopy.m_bUnixListen; |
1154 | 0 | #endif |
1155 | |
|
1156 | | #ifdef HAVE_ICU |
1157 | | SetEncoding(cCopy.m_sEncoding); |
1158 | | #endif |
1159 | |
|
1160 | 0 | CleanupCrons(); |
1161 | 0 | CleanupFDMonitors(); |
1162 | 0 | m_vcCrons = cCopy.m_vcCrons; |
1163 | 0 | m_vcMonitorFD = cCopy.m_vcMonitorFD; |
1164 | |
|
1165 | 0 | m_eConState = cCopy.m_eConState; |
1166 | 0 | m_sBindHost = cCopy.m_sBindHost; |
1167 | 0 | m_iCurBindCount = cCopy.m_iCurBindCount; |
1168 | 0 | m_iDNSTryCount = cCopy.m_iDNSTryCount; |
1169 | |
|
1170 | 0 | } |
1171 | | |
1172 | | Csock & Csock::operator<<( const CS_STRING & s ) |
1173 | 0 | { |
1174 | 0 | Write( s ); |
1175 | 0 | return( *this ); |
1176 | 0 | } |
1177 | | |
1178 | | Csock & Csock::operator<<( ostream & ( *io )( ostream & ) ) |
1179 | 0 | { |
1180 | 0 | Write( "\r\n" ); |
1181 | 0 | return( *this ); |
1182 | 0 | } |
1183 | | |
1184 | | Csock & Csock::operator<<( int32_t i ) |
1185 | 0 | { |
1186 | 0 | stringstream s; |
1187 | 0 | s << i; |
1188 | 0 | Write( s.str() ); |
1189 | 0 | return( *this ); |
1190 | 0 | } |
1191 | | |
1192 | | Csock & Csock::operator<<( uint32_t i ) |
1193 | 0 | { |
1194 | 0 | stringstream s; |
1195 | 0 | s << i; |
1196 | 0 | Write( s.str() ); |
1197 | 0 | return( *this ); |
1198 | 0 | } |
1199 | | |
1200 | | Csock & Csock::operator<<( int64_t i ) |
1201 | 0 | { |
1202 | 0 | stringstream s; |
1203 | 0 | s << i; |
1204 | 0 | Write( s.str() ); |
1205 | 0 | return( *this ); |
1206 | 0 | } |
1207 | | |
1208 | | Csock & Csock::operator<<( uint64_t i ) |
1209 | 0 | { |
1210 | 0 | stringstream s; |
1211 | 0 | s << i; |
1212 | 0 | Write( s.str() ); |
1213 | 0 | return( *this ); |
1214 | 0 | } |
1215 | | |
1216 | | Csock & Csock::operator<<( float i ) |
1217 | 0 | { |
1218 | 0 | stringstream s; |
1219 | 0 | s << i; |
1220 | 0 | Write( s.str() ); |
1221 | 0 | return( *this ); |
1222 | 0 | } |
1223 | | |
1224 | | Csock & Csock::operator<<( double i ) |
1225 | 0 | { |
1226 | 0 | stringstream s; |
1227 | 0 | s << i; |
1228 | 0 | Write( s.str() ); |
1229 | 0 | return( *this ); |
1230 | 0 | } |
1231 | | |
1232 | | bool Csock::Connect() |
1233 | 0 | { |
1234 | 0 | if( m_bSkipConnect ) |
1235 | 0 | { |
1236 | | // this was already called, so skipping now. this is to allow easy pass through |
1237 | 0 | if( m_eConState != CST_OK ) |
1238 | 0 | { |
1239 | 0 | m_eConState = CST_CONNECTWAIT; |
1240 | 0 | } |
1241 | 0 | return( true ); |
1242 | 0 | } |
1243 | | |
1244 | 0 | #ifndef _WIN32 |
1245 | 0 | set_non_blocking( m_iReadSock ); |
1246 | | #else |
1247 | | if( !GetIPv6() ) |
1248 | | set_non_blocking( m_iReadSock ); |
1249 | | // non-blocking sockets on Win32 do *not* return ENETUNREACH/EHOSTUNREACH if there's no IPv6 gateway. |
1250 | | // we need those error codes for the v4 fallback in GetAddrInfo! |
1251 | | #endif /* _WIN32 */ |
1252 | |
|
1253 | 0 | m_iConnType = OUTBOUND; |
1254 | |
|
1255 | 0 | int ret = -1; |
1256 | 0 | if( !GetIPv6() ) |
1257 | 0 | ret = connect( m_iReadSock, ( struct sockaddr * )m_address.GetSockAddr(), m_address.GetSockAddrLen() ); |
1258 | | #ifdef HAVE_IPV6 |
1259 | | else |
1260 | | ret = connect( m_iReadSock, ( struct sockaddr * )m_address.GetSockAddr6(), m_address.GetSockAddrLen6() ); |
1261 | | #endif /* HAVE_IPV6 */ |
1262 | 0 | #ifndef _WIN32 |
1263 | 0 | if( ret == -1 && GetSockError() != EINPROGRESS ) |
1264 | | #else |
1265 | | if( ret == -1 && GetSockError() != EINPROGRESS && GetSockError() != WSAEWOULDBLOCK ) |
1266 | | #endif /* _WIN32 */ |
1267 | | |
1268 | 0 | { |
1269 | 0 | CS_DEBUG( "Connect Failed. ERRNO [" << GetSockError() << "] FD [" << m_iReadSock << "]" ); |
1270 | 0 | return( false ); |
1271 | 0 | } |
1272 | | |
1273 | | #ifdef _WIN32 |
1274 | | // do what we didn't do above since connect() is now over! |
1275 | | if( GetIPv6() ) |
1276 | | set_non_blocking( m_iReadSock ); |
1277 | | #endif /* _WIN32 */ |
1278 | | |
1279 | 0 | if( m_eConState != CST_OK ) |
1280 | 0 | { |
1281 | 0 | m_eConState = CST_CONNECTWAIT; |
1282 | 0 | } |
1283 | |
|
1284 | 0 | return( true ); |
1285 | 0 | } |
1286 | | |
1287 | | |
1288 | | #ifdef HAVE_UNIX_SOCKET |
1289 | | static bool prepare_sockaddr(struct sockaddr_un * addr, const CS_STRING & sPath) |
1290 | 0 | { |
1291 | 0 | if (sPath.empty()) { |
1292 | 0 | return false; |
1293 | 0 | } |
1294 | 0 | memset( addr, 0, sizeof(*addr) ); |
1295 | 0 | addr->sun_family = AF_UNIX; |
1296 | 0 | auto length = sPath.length(); |
1297 | 0 | if( sizeof(addr->sun_path) <= length ) |
1298 | 0 | return( false ); |
1299 | 0 | memcpy( &addr->sun_path, sPath.c_str(), length + 1 ); |
1300 | | // Linux abstract namespace is null followed by name. |
1301 | 0 | if (sPath[0] == '@') addr->sun_path[0] = 0; |
1302 | 0 | return true; |
1303 | 0 | } |
1304 | | |
1305 | | bool Csock::ConnectUnixInternal( const CS_STRING & sPath ) |
1306 | 0 | { |
1307 | 0 | if( m_iReadSock != m_iWriteSock ) |
1308 | 0 | return( false ); |
1309 | 0 | if( m_iReadSock == CS_INVALID_SOCK ) |
1310 | 0 | m_iReadSock = m_iWriteSock = CreateSocket( false, true ); |
1311 | |
|
1312 | 0 | #ifndef __CYGWIN__ |
1313 | | // Cygwin emulates unix sockets using IP sockets, so connect() actually |
1314 | | // needs to be blocking, otherwise it returns SockError(119 Operation now in |
1315 | | // progress). Therefore for cygwin do it after connect(). |
1316 | 0 | set_non_blocking( m_iReadSock ); |
1317 | 0 | #endif |
1318 | 0 | m_iConnType = OUTBOUND; |
1319 | |
|
1320 | 0 | struct sockaddr_un addr; |
1321 | 0 | if( !prepare_sockaddr( &addr, sPath) ) |
1322 | 0 | { |
1323 | 0 | CallSockError( EADDRNOTAVAIL ); |
1324 | 0 | return( false ); |
1325 | 0 | } |
1326 | 0 | if( connect( m_iReadSock, ( struct sockaddr * )&addr, sizeof(addr) ) == -1) |
1327 | 0 | { |
1328 | 0 | int e = GetSockError(); |
1329 | 0 | CS_DEBUG( "Connect Failed. ERRNO [" << e << "] FD [" << m_iReadSock << "]" ); |
1330 | 0 | if( e == ECONNREFUSED ) |
1331 | 0 | ConnectionRefused(); |
1332 | 0 | else |
1333 | 0 | CallSockError( e ); |
1334 | 0 | return( false ); |
1335 | 0 | } |
1336 | | |
1337 | | #ifdef __CYGWIN__ |
1338 | | set_non_blocking( m_iReadSock ); |
1339 | | #endif |
1340 | 0 | m_eConState = ( GetSSL() ? CST_CONNECTSSL : CST_OK ); |
1341 | |
|
1342 | 0 | return( true ); |
1343 | 0 | } |
1344 | | |
1345 | | bool Csock::ListenUnixInternal( const CS_STRING & sBindFile, int iMaxConns, u_int iTimeout ) |
1346 | 0 | { |
1347 | 0 | m_iConnType = LISTENER; |
1348 | 0 | m_iTimeout = iTimeout; |
1349 | 0 | m_sBindHost = sBindFile; |
1350 | 0 | m_iMaxConns = iMaxConns; |
1351 | |
|
1352 | 0 | SetConState( Csock::CST_OK ); |
1353 | | |
1354 | | // Should m_address be set up somehow? |
1355 | |
|
1356 | 0 | struct sockaddr_un addr; |
1357 | 0 | if( !prepare_sockaddr( &addr, sBindFile) ) |
1358 | 0 | { |
1359 | 0 | CallSockError( EADDRNOTAVAIL ); |
1360 | 0 | CS_DEBUG("Csock::ListenUnixInternal(): prepare_sockaddr failed"); |
1361 | 0 | return( false ); |
1362 | 0 | } |
1363 | | |
1364 | 0 | m_iReadSock = m_iWriteSock = CreateSocket( true, true ); |
1365 | |
|
1366 | 0 | if( m_iReadSock == CS_INVALID_SOCK ) |
1367 | 0 | { |
1368 | 0 | CallSockError( EBADF ); |
1369 | 0 | CS_DEBUG("Csock::ListenUnixInternal(): CreateSocket failed"); |
1370 | 0 | return( false ); |
1371 | 0 | } |
1372 | | |
1373 | 0 | if( bind( m_iReadSock, ( struct sockaddr * ) &addr, sizeof(addr) ) == -1 ) |
1374 | 0 | { |
1375 | 0 | CallSockError( GetSockError() ); |
1376 | 0 | CS_DEBUG("Csock::ListenUnixInternal(): bind failed"); |
1377 | 0 | return( false ); |
1378 | 0 | } |
1379 | | |
1380 | 0 | if( listen( m_iReadSock, iMaxConns ) == -1 ) |
1381 | 0 | { |
1382 | 0 | CallSockError( GetSockError() ); |
1383 | 0 | CS_DEBUG("Csock::ListenUnixInternal(): listen failed"); |
1384 | 0 | return( false ); |
1385 | 0 | } |
1386 | | |
1387 | | // set it none blocking |
1388 | 0 | set_non_blocking( m_iReadSock ); |
1389 | | |
1390 | | // TODO: The following callback makes no sense here; should a |
1391 | | // ListeningUnix() be added? We aren't doing anything asynchronous... |
1392 | | //Listening( m_sBindHost, m_uPort ); |
1393 | |
|
1394 | 0 | m_bUnixListen = true; |
1395 | 0 | return( true ); |
1396 | 0 | } |
1397 | | #endif |
1398 | | |
1399 | | bool Csock::Listen( uint16_t iPort, int iMaxConns, const CS_STRING & sBindHost, u_int iTimeout, bool bDetach ) |
1400 | 0 | { |
1401 | 0 | m_iConnType = LISTENER; |
1402 | 0 | m_iTimeout = iTimeout; |
1403 | 0 | m_sBindHost = sBindHost; |
1404 | 0 | m_iMaxConns = iMaxConns; |
1405 | |
|
1406 | 0 | SetConState( Csock::CST_OK ); |
1407 | 0 | if( !m_sBindHost.empty() ) |
1408 | 0 | { |
1409 | 0 | if( bDetach ) |
1410 | 0 | { |
1411 | 0 | int iRet = GetAddrInfo( m_sBindHost, m_address ); |
1412 | 0 | if( iRet == ETIMEDOUT ) |
1413 | 0 | { |
1414 | 0 | CallSockError( EADDRNOTAVAIL ); |
1415 | 0 | return( false ); |
1416 | 0 | } |
1417 | 0 | else if( iRet == EAGAIN ) |
1418 | 0 | { |
1419 | 0 | SetConState( Csock::CST_BINDVHOST ); |
1420 | 0 | return( true ); |
1421 | 0 | } |
1422 | 0 | } |
1423 | 0 | else |
1424 | 0 | { |
1425 | | // if not detaching, then must block to do DNS resolution, so might as well use internal resolver |
1426 | 0 | if( ::CS_GetAddrInfo( m_sBindHost, this, m_address ) != 0 ) |
1427 | 0 | { |
1428 | 0 | CallSockError( EADDRNOTAVAIL ); |
1429 | 0 | return( false ); |
1430 | 0 | } |
1431 | 0 | } |
1432 | 0 | } |
1433 | | |
1434 | 0 | m_iReadSock = m_iWriteSock = CreateSocket( true ); |
1435 | |
|
1436 | 0 | if( m_iReadSock == CS_INVALID_SOCK ) |
1437 | 0 | { |
1438 | 0 | CallSockError( EBADF ); |
1439 | 0 | return( false ); |
1440 | 0 | } |
1441 | | |
1442 | | #ifdef HAVE_IPV6 |
1443 | | # ifdef _WIN32 |
1444 | | # ifndef IPPROTO_IPV6 |
1445 | | # define IPPROTO_IPV6 41 /* define for apps with _WIN32_WINNT < 0x0501 (XP) */ |
1446 | | # endif /* !IPPROTO_IPV6 */ |
1447 | | # ifndef IPV6_V6ONLY |
1448 | | # define IPV6_V6ONLY 27 |
1449 | | # endif |
1450 | | /* check for IPV6_V6ONLY support at runtime: only supported on Windows Vista or later */ |
1451 | | OSVERSIONINFOEX osvi = { 0 }; |
1452 | | DWORDLONG dwlConditionMask = 0; |
1453 | | |
1454 | | osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); |
1455 | | osvi.dwMajorVersion = 6; |
1456 | | |
1457 | | VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL); |
1458 | | |
1459 | | if( VerifyVersionInfo( &osvi, VER_MAJORVERSION, dwlConditionMask ) ) |
1460 | | { |
1461 | | # endif /* _WIN32 */ |
1462 | | # ifdef IPV6_V6ONLY |
1463 | | if( GetIPv6() ) |
1464 | | { |
1465 | | // per RFC3493#5.3 |
1466 | | const int on = ( m_address.GetAFRequire() == CSSockAddr::RAF_INET6 ? 1 : 0 ); |
1467 | | if( setsockopt( m_iReadSock, IPPROTO_IPV6, IPV6_V6ONLY, ( char * )&on, sizeof( on ) ) != 0 ) |
1468 | | PERROR( "IPV6_V6ONLY" ); |
1469 | | } |
1470 | | # endif /* IPV6_V6ONLY */ |
1471 | | # ifdef _WIN32 |
1472 | | } |
1473 | | # endif /* _WIN32 */ |
1474 | | #endif /* HAVE_IPV6 */ |
1475 | | |
1476 | 0 | m_address.SinFamily(); |
1477 | 0 | m_address.SinPort( iPort ); |
1478 | 0 | if( !GetIPv6() ) |
1479 | 0 | { |
1480 | 0 | if( bind( m_iReadSock, ( struct sockaddr * ) m_address.GetSockAddr(), m_address.GetSockAddrLen() ) == -1 ) |
1481 | 0 | { |
1482 | 0 | CallSockError( GetSockError() ); |
1483 | 0 | return( false ); |
1484 | 0 | } |
1485 | 0 | } |
1486 | | #ifdef HAVE_IPV6 |
1487 | | else |
1488 | | { |
1489 | | if( bind( m_iReadSock, ( struct sockaddr * ) m_address.GetSockAddr6(), m_address.GetSockAddrLen6() ) == -1 ) |
1490 | | { |
1491 | | CallSockError( GetSockError() ); |
1492 | | return( false ); |
1493 | | } |
1494 | | } |
1495 | | #endif /* HAVE_IPV6 */ |
1496 | | |
1497 | 0 | if( listen( m_iReadSock, iMaxConns ) == -1 ) |
1498 | 0 | { |
1499 | 0 | CallSockError( GetSockError() ); |
1500 | 0 | return( false ); |
1501 | 0 | } |
1502 | | |
1503 | | // set it none blocking |
1504 | 0 | set_non_blocking( m_iReadSock ); |
1505 | 0 | if( m_uPort == 0 || !m_sBindHost.empty() ) |
1506 | 0 | { |
1507 | 0 | struct sockaddr_storage cAddr; |
1508 | 0 | socklen_t iAddrLen = sizeof( cAddr ); |
1509 | 0 | if( getsockname( m_iReadSock, ( struct sockaddr * )&cAddr, &iAddrLen ) == 0 ) |
1510 | 0 | { |
1511 | 0 | ConvertAddress( &cAddr, iAddrLen, m_sBindHost, &m_uPort ); |
1512 | 0 | } |
1513 | 0 | } |
1514 | 0 | Listening( m_sBindHost, m_uPort ); |
1515 | |
|
1516 | 0 | return( true ); |
1517 | 0 | } |
1518 | | |
1519 | | cs_sock_t Csock::Accept( CS_STRING & sHost, uint16_t & iRPort ) |
1520 | 0 | { |
1521 | 0 | cs_sock_t iSock = CS_INVALID_SOCK; |
1522 | 0 | struct sockaddr_storage cAddr; |
1523 | 0 | socklen_t iAddrLen = sizeof( cAddr ); |
1524 | 0 | iSock = accept( m_iReadSock, ( struct sockaddr * )&cAddr, &iAddrLen ); |
1525 | 0 | if( iSock != CS_INVALID_SOCK && getpeername( iSock, ( struct sockaddr * )&cAddr, &iAddrLen ) == 0 ) |
1526 | 0 | { |
1527 | 0 | ConvertAddress( &cAddr, iAddrLen, sHost, &iRPort ); |
1528 | 0 | } |
1529 | |
|
1530 | 0 | if( iSock != CS_INVALID_SOCK ) |
1531 | 0 | { |
1532 | | // Make it close-on-exec |
1533 | 0 | set_close_on_exec( iSock ); |
1534 | | |
1535 | | // make it none blocking |
1536 | 0 | set_non_blocking( iSock ); |
1537 | |
|
1538 | 0 | if( !ConnectionFrom( sHost, iRPort ) ) |
1539 | 0 | { |
1540 | 0 | CS_CLOSE( iSock ); |
1541 | 0 | iSock = CS_INVALID_SOCK; |
1542 | 0 | } |
1543 | |
|
1544 | 0 | } |
1545 | |
|
1546 | 0 | return( iSock ); |
1547 | 0 | } |
1548 | | |
1549 | | #ifdef HAVE_LIBSSL |
1550 | | #if defined( SSL_CTX_set_tlsext_servername_callback ) |
1551 | | static int __SNICallBack( SSL *pSSL, int *piAD, void *pData ) |
1552 | | { |
1553 | | if( !pSSL || !pData ) |
1554 | | return( SSL_TLSEXT_ERR_NOACK ); |
1555 | | |
1556 | | const char * pServerName = SSL_get_servername( pSSL, TLSEXT_NAMETYPE_host_name ); |
1557 | | if( !pServerName ) |
1558 | | return( SSL_TLSEXT_ERR_NOACK ); |
1559 | | |
1560 | | Csock * pSock = static_cast<Csock *>( pData ); |
1561 | | |
1562 | | CS_STRING sDHParamFile, sKeyFile, sPemFile, sPemPass; |
1563 | | if( !pSock->SNIConfigureServer( pServerName, sPemFile, sPemPass ) ) |
1564 | | return( SSL_TLSEXT_ERR_NOACK ); |
1565 | | |
1566 | | pSock->SetDHParamLocation( sDHParamFile ); |
1567 | | pSock->SetKeyLocation( sKeyFile ); |
1568 | | pSock->SetPemLocation( sPemFile ); |
1569 | | pSock->SetPemPass( sPemPass ); |
1570 | | SSL_CTX * pCTX = pSock->SetupServerCTX(); |
1571 | | SSL_set_SSL_CTX( pSSL, pCTX ); |
1572 | | pSock->SetCTXObject( pCTX, true ); |
1573 | | return( SSL_TLSEXT_ERR_OK ); |
1574 | | } |
1575 | | #endif /* SSL_CTX_set_tlsext_servername_callback */ |
1576 | | #endif /* HAVE_LIBSSL */ |
1577 | | |
1578 | | bool Csock::AcceptSSL() |
1579 | 0 | { |
1580 | | #ifdef HAVE_LIBSSL |
1581 | | if( !m_ssl ) |
1582 | | if( !SSLServerSetup() ) |
1583 | | return( false ); |
1584 | | |
1585 | | #if defined( SSL_CTX_set_tlsext_servername_callback ) |
1586 | | SSL_CTX_set_tlsext_servername_callback( m_ssl_ctx, __SNICallBack ); |
1587 | | SSL_CTX_set_tlsext_servername_arg( m_ssl_ctx, this ); |
1588 | | #endif /* SSL_CTX_set_tlsext_servername_callback */ |
1589 | | |
1590 | | int err = SSL_accept( m_ssl ); |
1591 | | |
1592 | | if( err == 1 ) |
1593 | | { |
1594 | | return( true ); |
1595 | | } |
1596 | | |
1597 | | int sslErr = SSL_get_error( m_ssl, err ); |
1598 | | |
1599 | | if( sslErr == SSL_ERROR_WANT_READ || sslErr == SSL_ERROR_WANT_WRITE ) |
1600 | | return( true ); |
1601 | | |
1602 | | SSLErrors( __FILE__, __LINE__ ); |
1603 | | |
1604 | | #endif /* HAVE_LIBSSL */ |
1605 | |
|
1606 | 0 | return( false ); |
1607 | 0 | } |
1608 | | |
1609 | | #ifdef HAVE_LIBSSL |
1610 | | bool Csock::ConfigureCTXOptions( SSL_CTX * pCTX ) |
1611 | | { |
1612 | | if( pCTX ) |
1613 | | { |
1614 | | if( SSL_CTX_set_cipher_list( pCTX, m_sCipherType.c_str() ) <= 0 ) |
1615 | | { |
1616 | | CS_DEBUG( "Could not assign cipher [" << m_sCipherType << "]" ); |
1617 | | return( false ); |
1618 | | } |
1619 | | |
1620 | | long uCTXOptions = 0; |
1621 | | if( m_uDisableProtocols > 0 ) |
1622 | | { |
1623 | | #ifdef SSL_OP_NO_SSLv2 |
1624 | | if( EDP_SSLv2 & m_uDisableProtocols ) |
1625 | | uCTXOptions |= SSL_OP_NO_SSLv2; |
1626 | | #endif /* SSL_OP_NO_SSLv2 */ |
1627 | | #ifdef SSL_OP_NO_SSLv3 |
1628 | | if( EDP_SSLv3 & m_uDisableProtocols ) |
1629 | | uCTXOptions |= SSL_OP_NO_SSLv3; |
1630 | | #endif /* SSL_OP_NO_SSLv3 */ |
1631 | | #ifdef SSL_OP_NO_TLSv1 |
1632 | | if( EDP_TLSv1 & m_uDisableProtocols ) |
1633 | | uCTXOptions |= SSL_OP_NO_TLSv1; |
1634 | | #endif /* SSL_OP_NO_TLSv1 */ |
1635 | | #ifdef SSL_OP_NO_TLSv1_1 |
1636 | | if( EDP_TLSv1_1 & m_uDisableProtocols ) |
1637 | | uCTXOptions |= SSL_OP_NO_TLSv1_1; |
1638 | | #endif /* SSL_OP_NO_TLSv1 */ |
1639 | | #ifdef SSL_OP_NO_TLSv1_2 |
1640 | | if( EDP_TLSv1_2 & m_uDisableProtocols ) |
1641 | | uCTXOptions |= SSL_OP_NO_TLSv1_2; |
1642 | | #endif /* SSL_OP_NO_TLSv1_2 */ |
1643 | | } |
1644 | | #ifdef SSL_OP_NO_COMPRESSION |
1645 | | if( m_bNoSSLCompression ) |
1646 | | uCTXOptions |= SSL_OP_NO_COMPRESSION; |
1647 | | #endif /* SSL_OP_NO_COMPRESSION */ |
1648 | | #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE |
1649 | | if( m_bSSLCipherServerPreference ) |
1650 | | uCTXOptions |= SSL_OP_CIPHER_SERVER_PREFERENCE; |
1651 | | #endif /* SSL_OP_CIPHER_SERVER_PREFERENCE */ |
1652 | | if( uCTXOptions ) |
1653 | | SSL_CTX_set_options( pCTX, uCTXOptions ); |
1654 | | } |
1655 | | return true; |
1656 | | } |
1657 | | #endif /* HAVE_LIBSSL */ |
1658 | | |
1659 | | |
1660 | | #ifdef HAVE_LIBSSL |
1661 | | static SSL_CTX * GetSSLCTX( int iMethod ) |
1662 | | { |
1663 | | const SSL_METHOD *pMethod = NULL; |
1664 | | |
1665 | | #ifdef HAVE_FLEXIBLE_TLS_METHOD |
1666 | | int iProtoVersion = 0; |
1667 | | pMethod = TLS_method(); |
1668 | | #else |
1669 | | pMethod = SSLv23_method(); |
1670 | | #endif // HAVE_FLEXIBLE_TLS_METHOD |
1671 | | |
1672 | | switch( iMethod ) |
1673 | | { |
1674 | | case Csock::TLS: |
1675 | | break; // defaults already set above, anything else can either match a case or fall through and use defaults anyway |
1676 | | #ifdef HAVE_FLEXIBLE_TLS_METHOD |
1677 | | #ifndef OPENSSL_NO_TLS1_2 |
1678 | | case Csock::TLS12: |
1679 | | iProtoVersion = TLS1_2_VERSION; |
1680 | | break; |
1681 | | #endif /* OPENSSL_NO_TLS1_2 */ |
1682 | | #ifndef OPENSSL_NO_TLS1_1 |
1683 | | case Csock::TLS11: |
1684 | | iProtoVersion = TLS1_1_VERSION; |
1685 | | break; |
1686 | | #endif /* OPENSSL_NO_TLS1_1 */ |
1687 | | #ifndef OPENSSL_NO_TLS1 |
1688 | | case Csock::TLS1: |
1689 | | iProtoVersion = TLS1_VERSION; |
1690 | | break; |
1691 | | #endif /* OPENSSL_NO_TLS1 */ |
1692 | | #ifndef OPENSSL_NO_SSL3 |
1693 | | case Csock::SSL3: |
1694 | | iProtoVersion = SSL3_VERSION; |
1695 | | break; |
1696 | | #endif /* OPENSSL_NO_SSL3 */ |
1697 | | #ifndef OPENSSL_NO_SSL2 |
1698 | | case Csock::SSL2: |
1699 | | pMethod = SSLv2_method(); |
1700 | | break; |
1701 | | #endif /* OPENSSL_NO_SSL2 */ |
1702 | | |
1703 | | |
1704 | | #else /* HAVE_FLEXIBLE_TLS_METHOD */ |
1705 | | |
1706 | | |
1707 | | #ifndef OPENSSL_NO_TLS1_2 |
1708 | | case Csock::TLS12: |
1709 | | pMethod = TLSv1_2_method(); |
1710 | | break; |
1711 | | #endif /* OPENSSL_NO_TLS1_2 */ |
1712 | | #ifndef OPENSSL_NO_TLS1_1 |
1713 | | case Csock::TLS11: |
1714 | | pMethod = TLSv1_1_method(); |
1715 | | break; |
1716 | | #endif /* OPENSSL_NO_TLS1_1 */ |
1717 | | #ifndef OPENSSL_NO_TLS1 |
1718 | | case Csock::TLS1: |
1719 | | pMethod = TLSv1_method(); |
1720 | | break; |
1721 | | #endif /* OPENSSL_NO_TLS1 */ |
1722 | | #ifndef OPENSSL_NO_SSL3 |
1723 | | case Csock::SSL3: |
1724 | | pMethod = SSLv3_method(); |
1725 | | break; |
1726 | | #endif /* OPENSSL_NO_SSL3 */ |
1727 | | #ifndef OPENSSL_NO_SSL2 |
1728 | | case Csock::SSL2: |
1729 | | pMethod = SSLv2_method(); |
1730 | | break; |
1731 | | #endif /* OPENSSL_NO_SSL2 */ |
1732 | | #endif /* HAVE_FLEXIBLE_TLS_METHOD */ |
1733 | | |
1734 | | default: |
1735 | | CS_DEBUG( "WARNING: SSL Client Method other than SSLv23 specified, but has passed through" ); |
1736 | | break; |
1737 | | } |
1738 | | |
1739 | | SSL_CTX * pCTX = SSL_CTX_new( pMethod ); |
1740 | | if( !pCTX ) |
1741 | | { |
1742 | | CS_DEBUG( "WARNING: GetSSLCTX failed!" ); |
1743 | | return( NULL ); |
1744 | | } |
1745 | | |
1746 | | #ifdef HAVE_FLEXIBLE_TLS_METHOD |
1747 | | if( iProtoVersion ) |
1748 | | { |
1749 | | SSL_CTX_set_min_proto_version( pCTX, iProtoVersion ); |
1750 | | SSL_CTX_set_max_proto_version( pCTX, iProtoVersion ); |
1751 | | } |
1752 | | #endif /* HAVE_FLEXIBLE_TLS_METHOD */ |
1753 | | |
1754 | | return( pCTX ); |
1755 | | } |
1756 | | #endif |
1757 | | |
1758 | | bool Csock::SSLClientSetup() |
1759 | 0 | { |
1760 | | #ifdef HAVE_LIBSSL |
1761 | | m_bUseSSL = true; |
1762 | | FREE_SSL(); |
1763 | | FREE_CTX(); |
1764 | | |
1765 | | #ifdef _WIN64 |
1766 | | if( m_iReadSock != ( int )m_iReadSock || m_iWriteSock != ( int )m_iWriteSock ) |
1767 | | { |
1768 | | // sanity check the FD to be sure its compatible with openssl |
1769 | | CS_DEBUG( "ERROR: sockfd larger than OpenSSL can handle" ); |
1770 | | return( false ); |
1771 | | } |
1772 | | #endif /* _WIN64 */ |
1773 | | |
1774 | | m_ssl_ctx = GetSSLCTX( m_iMethod ); |
1775 | | if( !m_ssl_ctx ) |
1776 | | { |
1777 | | CS_DEBUG( "WARNING: Failed to retrieve a valid ctx" ); |
1778 | | return( false ); |
1779 | | } |
1780 | | |
1781 | | SSL_CTX_set_default_verify_paths( m_ssl_ctx ); |
1782 | | |
1783 | | if( !m_sPemFile.empty() ) |
1784 | | { |
1785 | | // are we sending a client cerificate ? |
1786 | | SSL_CTX_set_default_passwd_cb( m_ssl_ctx, _PemPassCB ); |
1787 | | SSL_CTX_set_default_passwd_cb_userdata( m_ssl_ctx, ( void * )this ); |
1788 | | |
1789 | | // |
1790 | | // set up the CTX |
1791 | | if( SSL_CTX_use_certificate_file( m_ssl_ctx, m_sPemFile.c_str() , SSL_FILETYPE_PEM ) <= 0 ) |
1792 | | { |
1793 | | CS_DEBUG( "Error with SSLCert file [" << m_sPemFile << "]" ); |
1794 | | SSLErrors( __FILE__, __LINE__ ); |
1795 | | } |
1796 | | CS_STRING privKeyFile = m_sKeyFile.empty() ? m_sPemFile : m_sKeyFile; |
1797 | | if( SSL_CTX_use_PrivateKey_file( m_ssl_ctx, privKeyFile.c_str(), SSL_FILETYPE_PEM ) <= 0 ) |
1798 | | { |
1799 | | CS_DEBUG( "Error with SSLKey file [" << privKeyFile << "]" ); |
1800 | | SSLErrors( __FILE__, __LINE__ ); |
1801 | | } |
1802 | | } |
1803 | | |
1804 | | if( !ConfigureCTXOptions( m_ssl_ctx ) ) |
1805 | | { |
1806 | | SSL_CTX_free( m_ssl_ctx ); |
1807 | | m_ssl_ctx = NULL; |
1808 | | return( false ); |
1809 | | } |
1810 | | |
1811 | | m_ssl = SSL_new( m_ssl_ctx ); |
1812 | | if( !m_ssl ) |
1813 | | return( false ); |
1814 | | |
1815 | | SSL_set_rfd( m_ssl, ( int )m_iReadSock ); |
1816 | | SSL_set_wfd( m_ssl, ( int )m_iWriteSock ); |
1817 | | SSL_set_verify( m_ssl, SSL_VERIFY_PEER, m_pCerVerifyCB ); |
1818 | | SSL_set_info_callback( m_ssl, _InfoCallback ); |
1819 | | SSL_set_ex_data( m_ssl, GetCsockSSLIdx(), this ); |
1820 | | |
1821 | | #if defined( SSL_set_tlsext_host_name ) |
1822 | | CS_STRING sSNIHostname; |
1823 | | if( SNIConfigureClient( sSNIHostname ) ) |
1824 | | SSL_set_tlsext_host_name( m_ssl, sSNIHostname.c_str() ); |
1825 | | #endif /* SSL_set_tlsext_host_name */ |
1826 | | |
1827 | | SSLFinishSetup( m_ssl ); |
1828 | | return( true ); |
1829 | | #else |
1830 | 0 | return( false ); |
1831 | |
|
1832 | 0 | #endif /* HAVE_LIBSSL */ |
1833 | 0 | } |
1834 | | |
1835 | | #ifdef HAVE_LIBSSL |
1836 | | SSL_CTX * Csock::SetupServerCTX() |
1837 | | { |
1838 | | SSL_CTX * pCTX = GetSSLCTX( m_iMethod ); |
1839 | | if( !pCTX ) |
1840 | | { |
1841 | | CS_DEBUG( "WARNING: Failed to retrieve a valid ctx" ); |
1842 | | return( NULL ); |
1843 | | } |
1844 | | |
1845 | | SSL_CTX_set_default_verify_paths( pCTX ); |
1846 | | |
1847 | | // set the pemfile password |
1848 | | SSL_CTX_set_default_passwd_cb( pCTX, _PemPassCB ); |
1849 | | SSL_CTX_set_default_passwd_cb_userdata( pCTX, ( void * )this ); |
1850 | | |
1851 | | if( m_sPemFile.empty() || access( m_sPemFile.c_str(), R_OK ) != 0 ) |
1852 | | { |
1853 | | CS_DEBUG( "Empty, missing, or bad pemfile ... [" << m_sPemFile << "]" ); |
1854 | | SSL_CTX_free( pCTX ); |
1855 | | return( NULL ); |
1856 | | } |
1857 | | |
1858 | | if( ! m_sKeyFile.empty() && access( m_sKeyFile.c_str(), R_OK ) != 0 ) |
1859 | | { |
1860 | | CS_DEBUG( "Bad keyfile ... [" << m_sKeyFile << "]" ); |
1861 | | SSL_CTX_free( pCTX ); |
1862 | | return( NULL ); |
1863 | | } |
1864 | | |
1865 | | // |
1866 | | // set up the CTX |
1867 | | if( SSL_CTX_use_certificate_chain_file( pCTX, m_sPemFile.c_str() ) <= 0 ) |
1868 | | { |
1869 | | CS_DEBUG( "Error with SSLCert file [" << m_sPemFile << "]" ); |
1870 | | SSLErrors( __FILE__, __LINE__ ); |
1871 | | SSL_CTX_free( pCTX ); |
1872 | | return( NULL ); |
1873 | | } |
1874 | | |
1875 | | CS_STRING privKeyFile = m_sKeyFile.empty() ? m_sPemFile : m_sKeyFile; |
1876 | | if( SSL_CTX_use_PrivateKey_file( pCTX, privKeyFile.c_str(), SSL_FILETYPE_PEM ) <= 0 ) |
1877 | | { |
1878 | | CS_DEBUG( "Error with SSLKey file [" << privKeyFile << "]" ); |
1879 | | SSLErrors( __FILE__, __LINE__ ); |
1880 | | SSL_CTX_free( pCTX ); |
1881 | | return( NULL ); |
1882 | | } |
1883 | | |
1884 | | // check to see if this pem file contains a DH structure for use with DH key exchange |
1885 | | // https://github.com/znc/znc/pull/46 |
1886 | | CS_STRING DHParamFile = m_sDHParamFile.empty() ? m_sPemFile : m_sDHParamFile; |
1887 | | FILE *dhParamsFile = fopen( DHParamFile.c_str(), "r" ); |
1888 | | if( !dhParamsFile ) |
1889 | | { |
1890 | | CS_DEBUG( "Error with DHParam file [" << DHParamFile << "]" ); |
1891 | | SSL_CTX_free( pCTX ); |
1892 | | return( NULL ); |
1893 | | } |
1894 | | |
1895 | | DH * dhParams = PEM_read_DHparams( dhParamsFile, NULL, NULL, NULL ); |
1896 | | fclose( dhParamsFile ); |
1897 | | if( dhParams ) |
1898 | | { |
1899 | | SSL_CTX_set_options( pCTX, SSL_OP_SINGLE_DH_USE ); |
1900 | | if( !SSL_CTX_set_tmp_dh( pCTX, dhParams ) ) |
1901 | | { |
1902 | | CS_DEBUG( "Error setting ephemeral DH parameters from [" << m_sPemFile << "]" ); |
1903 | | SSLErrors( __FILE__, __LINE__ ); |
1904 | | DH_free( dhParams ); |
1905 | | SSL_CTX_free( pCTX ); |
1906 | | return( NULL ); |
1907 | | } |
1908 | | DH_free( dhParams ); |
1909 | | } |
1910 | | else |
1911 | | { |
1912 | | // Presumably PEM_read_DHparams failed, as there was no DH structure. Clearing those errors here so they are removed off the stack |
1913 | | ERR_clear_error(); |
1914 | | } |
1915 | | #ifndef OPENSSL_NO_ECDH |
1916 | | // Errors for the following block are non-fatal (ECDHE is nice to have |
1917 | | // but not a requirement) |
1918 | | #ifndef OPENSSL_IS_BORINGSSL |
1919 | | // BoringSSL does this thing automatically |
1920 | | #if defined( SSL_CTX_set_ecdh_auto ) |
1921 | | // Auto-select sensible curve |
1922 | | if( !SSL_CTX_set_ecdh_auto( pCTX , 1 ) ) |
1923 | | ERR_clear_error(); |
1924 | | #elif defined( SSL_CTX_set_tmp_ecdh ) |
1925 | | // Use a standard, widely-supported curve |
1926 | | EC_KEY * ecdh = EC_KEY_new_by_curve_name( NID_X9_62_prime256v1 ); |
1927 | | if( ecdh ) |
1928 | | { |
1929 | | if( !SSL_CTX_set_tmp_ecdh( pCTX, ecdh ) ) |
1930 | | ERR_clear_error(); |
1931 | | EC_KEY_free( ecdh ); |
1932 | | } |
1933 | | else |
1934 | | { |
1935 | | ERR_clear_error(); |
1936 | | } |
1937 | | #endif /* SSL_CTX_set_tmp_ecdh */ |
1938 | | #endif /* !OPENSSL_IS_BORINGSSL */ |
1939 | | #endif /* OPENSSL_NO_ECDH */ |
1940 | | |
1941 | | if( !ConfigureCTXOptions( pCTX ) ) |
1942 | | { |
1943 | | SSL_CTX_free( pCTX ); |
1944 | | return( NULL ); |
1945 | | } |
1946 | | return( pCTX ); |
1947 | | } |
1948 | | #endif /* HAVE_LIBSSL */ |
1949 | | |
1950 | | bool Csock::SSLServerSetup() |
1951 | 0 | { |
1952 | | #ifdef HAVE_LIBSSL |
1953 | | m_bUseSSL = true; |
1954 | | FREE_SSL(); |
1955 | | FREE_CTX(); |
1956 | | |
1957 | | #ifdef _WIN64 |
1958 | | if( m_iReadSock != ( int )m_iReadSock || m_iWriteSock != ( int )m_iWriteSock ) |
1959 | | { |
1960 | | // sanity check the FD to be sure its compatible with openssl |
1961 | | CS_DEBUG( "ERROR: sockfd larger than OpenSSL can handle" ); |
1962 | | return( false ); |
1963 | | } |
1964 | | #endif /* _WIN64 */ |
1965 | | |
1966 | | m_ssl_ctx = SetupServerCTX(); |
1967 | | |
1968 | | // |
1969 | | // setup the SSL |
1970 | | m_ssl = SSL_new( m_ssl_ctx ); |
1971 | | if( !m_ssl ) |
1972 | | return( false ); |
1973 | | |
1974 | | #if defined( SSL_MODE_SEND_FALLBACK_SCSV ) |
1975 | | SSL_set_mode( m_ssl, SSL_MODE_SEND_FALLBACK_SCSV ); |
1976 | | #endif /* SSL_MODE_SEND_FALLBACK_SCSV */ |
1977 | | |
1978 | | // Call for client Verification |
1979 | | SSL_set_rfd( m_ssl, ( int )m_iReadSock ); |
1980 | | SSL_set_wfd( m_ssl, ( int )m_iWriteSock ); |
1981 | | SSL_set_accept_state( m_ssl ); |
1982 | | if( m_iRequireClientCertFlags ) |
1983 | | { |
1984 | | SSL_set_verify( m_ssl, m_iRequireClientCertFlags, m_pCerVerifyCB ); |
1985 | | } |
1986 | | SSL_set_info_callback( m_ssl, _InfoCallback ); |
1987 | | SSL_set_ex_data( m_ssl, GetCsockSSLIdx(), this ); |
1988 | | |
1989 | | SSLFinishSetup( m_ssl ); |
1990 | | return( true ); |
1991 | | #else |
1992 | 0 | return( false ); |
1993 | 0 | #endif /* HAVE_LIBSSL */ |
1994 | 0 | } |
1995 | | |
1996 | | bool Csock::StartTLS() |
1997 | 0 | { |
1998 | 0 | if( m_iConnType == INBOUND ) |
1999 | 0 | return( AcceptSSL() ); |
2000 | 0 | if( m_iConnType == OUTBOUND ) |
2001 | 0 | return( ConnectSSL() ); |
2002 | 0 | CS_DEBUG( "Invalid connection type with StartTLS" ); |
2003 | 0 | return( false ); |
2004 | 0 | } |
2005 | | |
2006 | | bool Csock::ConnectSSL() |
2007 | 0 | { |
2008 | | #ifdef HAVE_LIBSSL |
2009 | | if( m_iReadSock == CS_INVALID_SOCK ) |
2010 | | return( false ); // this should be long passed at this point |
2011 | | if( !m_ssl && !SSLClientSetup() ) |
2012 | | return( false ); |
2013 | | |
2014 | | bool bPass = true; |
2015 | | |
2016 | | int iErr = SSL_connect( m_ssl ); |
2017 | | if( iErr != 1 ) |
2018 | | { |
2019 | | int sslErr = SSL_get_error( m_ssl, iErr ); |
2020 | | bPass = false; |
2021 | | if( sslErr == SSL_ERROR_WANT_READ || sslErr == SSL_ERROR_WANT_WRITE ) |
2022 | | bPass = true; |
2023 | | #ifdef _WIN32 |
2024 | | else if( sslErr == SSL_ERROR_SYSCALL && iErr < 0 && GetLastError() == WSAENOTCONN ) |
2025 | | { |
2026 | | // this seems to be an issue with win32 only. I've seen it happen on slow connections |
2027 | | // the issue is calling this before select(), which isn't a problem on unix. Allowing this |
2028 | | // to pass in this case is fine because subsequent ssl transactions will occur and the handshake |
2029 | | // will finish. At this point, its just instantiating the handshake. |
2030 | | bPass = true; |
2031 | | } |
2032 | | #endif /* _WIN32 */ |
2033 | | } |
2034 | | else |
2035 | | { |
2036 | | bPass = true; |
2037 | | } |
2038 | | |
2039 | | if( m_eConState != CST_OK ) |
2040 | | m_eConState = CST_OK; |
2041 | | return( bPass ); |
2042 | | #else |
2043 | 0 | return( false ); |
2044 | 0 | #endif /* HAVE_LIBSSL */ |
2045 | 0 | } |
2046 | | |
2047 | | #ifdef HAVE_ICU |
2048 | | inline bool icuConv( const CS_STRING& src, CS_STRING& tgt, UConverter* cnv_in, UConverter* cnv_out ) |
2049 | | { |
2050 | | const char* indata = src.c_str(); |
2051 | | const char* indataend = indata + src.length(); |
2052 | | tgt.clear(); |
2053 | | char buf[100]; |
2054 | | UChar pivotStart[100]; |
2055 | | UChar* pivotSource = pivotStart; |
2056 | | UChar* pivotTarget = pivotStart; |
2057 | | UChar* pivotLimit = pivotStart + sizeof pivotStart / sizeof pivotStart[0]; |
2058 | | const char* outdataend = buf + sizeof buf; |
2059 | | bool reset = true; |
2060 | | while( true ) |
2061 | | { |
2062 | | char* outdata = buf; |
2063 | | UErrorCode e = U_ZERO_ERROR; |
2064 | | ucnv_convertEx( cnv_out, cnv_in, &outdata, outdataend, &indata, indataend, pivotStart, &pivotSource, &pivotTarget, pivotLimit, reset, true, &e ); |
2065 | | reset = false; |
2066 | | if( U_SUCCESS( e ) ) |
2067 | | { |
2068 | | if( e != U_ZERO_ERROR ) |
2069 | | { |
2070 | | CS_DEBUG( "Warning during converting string encoding: " << u_errorName( e ) ); |
2071 | | } |
2072 | | tgt.append( buf, outdata - buf ); |
2073 | | break; |
2074 | | } |
2075 | | if( e == U_BUFFER_OVERFLOW_ERROR ) |
2076 | | { |
2077 | | tgt.append( buf, outdata - buf ); |
2078 | | continue; |
2079 | | } |
2080 | | CS_DEBUG( "Error during converting string encoding: " << u_errorName( e ) ); |
2081 | | return false; |
2082 | | } |
2083 | | return true; |
2084 | | } |
2085 | | |
2086 | | static bool isUTF8( const CS_STRING& src, CS_STRING& target) |
2087 | | { |
2088 | | UErrorCode e = U_ZERO_ERROR; |
2089 | | // Convert to UTF-16 without actually converting. This will still count |
2090 | | // the number of output characters, so we either get |
2091 | | // U_BUFFER_OVERFLOW_ERROR or U_INVALID_CHAR_FOUND. |
2092 | | u_strFromUTF8( NULL, 0, NULL, src.c_str(), (int32_t) src.length(), &e ); |
2093 | | if( e != U_BUFFER_OVERFLOW_ERROR) |
2094 | | return false; |
2095 | | target = src; |
2096 | | return true; |
2097 | | } |
2098 | | #endif /* HAVE_ICU */ |
2099 | | |
2100 | | bool Csock::AllowWrite( uint64_t & iNOW ) const |
2101 | 0 | { |
2102 | 0 | if( m_iMaxBytes > 0 && m_iMaxMilliSeconds > 0 ) |
2103 | 0 | { |
2104 | 0 | if( iNOW == 0 ) |
2105 | 0 | iNOW = millitime(); |
2106 | |
|
2107 | 0 | if( m_iLastSend < m_iMaxBytes ) |
2108 | 0 | return( true ); // allow sending if our out buffer was less than what we can send |
2109 | 0 | if( ( iNOW - m_iLastSendTime ) < m_iMaxMilliSeconds ) |
2110 | 0 | return( false ); |
2111 | 0 | } |
2112 | 0 | return( true ); |
2113 | 0 | } |
2114 | | |
2115 | | void Csock::ShrinkSendBuff() |
2116 | 0 | { |
2117 | 0 | if( m_uSendBufferPos > 0 ) |
2118 | 0 | { |
2119 | | // just doing this to keep m_sSend from growing out of control |
2120 | 0 | m_sSend.erase( 0, m_uSendBufferPos ); |
2121 | 0 | m_uSendBufferPos = 0; |
2122 | 0 | } |
2123 | 0 | } |
2124 | | void Csock::IncBuffPos( size_t uBytes ) |
2125 | 0 | { |
2126 | 0 | m_uSendBufferPos += uBytes; |
2127 | 0 | if( m_uSendBufferPos >= m_sSend.size() ) |
2128 | 0 | { |
2129 | 0 | m_uSendBufferPos = 0; |
2130 | 0 | m_sSend.clear(); |
2131 | 0 | } |
2132 | 0 | } |
2133 | | |
2134 | | bool Csock::Write( const char *data, size_t len ) |
2135 | 0 | { |
2136 | 0 | if( len > 0 ) |
2137 | 0 | { |
2138 | 0 | ShrinkSendBuff(); |
2139 | 0 | m_sSend.append( data, len ); |
2140 | 0 | } |
2141 | |
|
2142 | 0 | if( m_sSend.empty() ) |
2143 | 0 | return( true ); |
2144 | | |
2145 | 0 | if( m_eConState != CST_OK ) |
2146 | 0 | return( true ); |
2147 | | |
2148 | | // rate shaping |
2149 | 0 | size_t iBytesToSend = 0; |
2150 | |
|
2151 | 0 | size_t uBytesInSend = m_sSend.size() - m_uSendBufferPos; |
2152 | |
|
2153 | | #ifdef HAVE_LIBSSL |
2154 | | if( m_bUseSSL && m_sSSLBuffer.empty() && !m_bsslEstablished ) |
2155 | | { |
2156 | | // to keep openssl from spinning, just initiate the connection with 1 byte so the connection establishes faster |
2157 | | iBytesToSend = 1; |
2158 | | } |
2159 | | else |
2160 | | #endif /* HAVE_LIBSSL */ |
2161 | 0 | if( m_iMaxBytes > 0 && m_iMaxMilliSeconds > 0 ) |
2162 | 0 | { |
2163 | 0 | uint64_t iNOW = millitime(); |
2164 | | // figure out the shaping here |
2165 | | // if NOW - m_iLastSendTime > m_iMaxMilliSeconds then send a full length of ( iBytesToSend ) |
2166 | 0 | if( ( iNOW - m_iLastSendTime ) > m_iMaxMilliSeconds ) |
2167 | 0 | { |
2168 | 0 | m_iLastSendTime = iNOW; |
2169 | 0 | iBytesToSend = m_iMaxBytes; |
2170 | 0 | m_iLastSend = 0; |
2171 | 0 | } |
2172 | 0 | else // otherwise send m_iMaxBytes - m_iLastSend |
2173 | 0 | iBytesToSend = m_iMaxBytes - m_iLastSend; |
2174 | | |
2175 | | // take which ever is lesser |
2176 | 0 | if( uBytesInSend < iBytesToSend ) |
2177 | 0 | iBytesToSend = uBytesInSend; |
2178 | | |
2179 | | // add up the bytes sent |
2180 | 0 | m_iLastSend += iBytesToSend; |
2181 | | |
2182 | | // so, are we ready to send anything ? |
2183 | 0 | if( iBytesToSend == 0 ) |
2184 | 0 | return( true ); |
2185 | 0 | } |
2186 | 0 | else |
2187 | 0 | { |
2188 | 0 | iBytesToSend = uBytesInSend; |
2189 | 0 | } |
2190 | | |
2191 | | #ifdef HAVE_LIBSSL |
2192 | | if( m_bUseSSL ) |
2193 | | { |
2194 | | if( !m_ssl ) |
2195 | | { |
2196 | | CS_DEBUG( "SSL object is NULL but m_bUseSSL is true" ); |
2197 | | return( false ); |
2198 | | } |
2199 | | |
2200 | | if( m_sSSLBuffer.empty() ) // on retrying to write data, ssl wants the data in the SAME spot and the SAME size |
2201 | | m_sSSLBuffer.append( m_sSend.data() + m_uSendBufferPos, iBytesToSend ); |
2202 | | |
2203 | | int iErr = SSL_write( m_ssl, m_sSSLBuffer.data(), ( int )m_sSSLBuffer.length() ); |
2204 | | |
2205 | | if( iErr < 0 && GetSockError() == ECONNREFUSED ) |
2206 | | { |
2207 | | // If ret == -1, the underlying BIO reported an I/O error (man SSL_get_error) |
2208 | | ConnectionRefused(); |
2209 | | return( false ); |
2210 | | } |
2211 | | |
2212 | | switch( SSL_get_error( m_ssl, iErr ) ) |
2213 | | { |
2214 | | case SSL_ERROR_NONE: |
2215 | | m_bsslEstablished = true; |
2216 | | // all ok |
2217 | | break; |
2218 | | |
2219 | | case SSL_ERROR_ZERO_RETURN: |
2220 | | { |
2221 | | // weird closer alert |
2222 | | return( false ); |
2223 | | } |
2224 | | |
2225 | | case SSL_ERROR_WANT_READ: |
2226 | | // retry |
2227 | | break; |
2228 | | |
2229 | | case SSL_ERROR_WANT_WRITE: |
2230 | | // retry |
2231 | | break; |
2232 | | |
2233 | | case SSL_ERROR_SSL: |
2234 | | { |
2235 | | SSLErrors( __FILE__, __LINE__ ); |
2236 | | return( false ); |
2237 | | } |
2238 | | } |
2239 | | |
2240 | | if( iErr > 0 ) |
2241 | | { |
2242 | | m_sSSLBuffer.clear(); |
2243 | | IncBuffPos( ( size_t )iErr ); |
2244 | | // reset the timer on successful write (we have to set it here because the write |
2245 | | // bit might not always be set, so need to trigger) |
2246 | | if( TMO_WRITE & GetTimeoutType() ) |
2247 | | ResetTimer(); |
2248 | | |
2249 | | m_iBytesWritten += ( uint64_t )iErr; |
2250 | | } |
2251 | | |
2252 | | return( true ); |
2253 | | } |
2254 | | #endif /* HAVE_LIBSSL */ |
2255 | | #ifdef _WIN32 |
2256 | | cs_ssize_t bytes = send( m_iWriteSock, m_sSend.data() + m_uSendBufferPos, iBytesToSend, 0 ); |
2257 | | #else |
2258 | 0 | cs_ssize_t bytes = write( m_iWriteSock, m_sSend.data() + m_uSendBufferPos, iBytesToSend ); |
2259 | 0 | #endif /* _WIN32 */ |
2260 | |
|
2261 | 0 | if( bytes == -1 && GetSockError() == ECONNREFUSED ) |
2262 | 0 | { |
2263 | 0 | ConnectionRefused(); |
2264 | 0 | return( false ); |
2265 | 0 | } |
2266 | | |
2267 | | #ifdef _WIN32 |
2268 | | if( bytes <= 0 && GetSockError() != WSAEWOULDBLOCK ) |
2269 | | return( false ); |
2270 | | #else |
2271 | 0 | if( bytes <= 0 && GetSockError() != EAGAIN ) |
2272 | 0 | return( false ); |
2273 | 0 | #endif /* _WIN32 */ |
2274 | | |
2275 | | // delete the bytes we sent |
2276 | 0 | if( bytes > 0 ) |
2277 | 0 | { |
2278 | 0 | IncBuffPos( ( size_t )bytes ); |
2279 | 0 | if( TMO_WRITE & GetTimeoutType() ) |
2280 | 0 | ResetTimer(); // reset the timer on successful write |
2281 | 0 | m_iBytesWritten += ( uint64_t )bytes; |
2282 | 0 | } |
2283 | |
|
2284 | 0 | return( true ); |
2285 | 0 | } |
2286 | | |
2287 | | bool Csock::Write( const CS_STRING & sData ) |
2288 | 0 | { |
2289 | | #ifdef HAVE_ICU |
2290 | | if( m_cnvExt && !m_cnvSendUTF8 ) |
2291 | | { |
2292 | | CS_STRING sBinary; |
2293 | | if( icuConv( sData, sBinary, m_cnvInt, m_cnvExt ) ) |
2294 | | { |
2295 | | return( Write( sBinary.c_str(), sBinary.length() ) ); |
2296 | | } |
2297 | | } |
2298 | | // can't convert our UTF-8 string to that encoding, just put it as is... |
2299 | | #endif /* HAVE_ICU */ |
2300 | 0 | return( Write( sData.c_str(), sData.length() ) ); |
2301 | 0 | } |
2302 | | |
2303 | | cs_ssize_t Csock::Read( char *data, size_t len ) |
2304 | 0 | { |
2305 | 0 | cs_ssize_t bytes = 0; |
2306 | |
|
2307 | 0 | if( IsReadPaused() && SslIsEstablished() ) |
2308 | 0 | return( READ_EAGAIN ); // allow the handshake to complete first |
2309 | | |
2310 | | #ifdef HAVE_LIBSSL |
2311 | | if( m_bUseSSL && m_eConState != CST_CONNECTWAIT ) |
2312 | | { |
2313 | | if( !m_ssl ) |
2314 | | { |
2315 | | CS_DEBUG( "SSL object is NULL but m_bUseSSL is true" ); |
2316 | | return( READ_ERR ); |
2317 | | } |
2318 | | |
2319 | | bytes = SSL_read( m_ssl, data, ( int )len ); |
2320 | | if( bytes >= 0 ) |
2321 | | m_bsslEstablished = true; // this means all is good in the realm of ssl |
2322 | | } |
2323 | | else |
2324 | | #endif /* HAVE_LIBSSL */ |
2325 | | #ifdef _WIN32 |
2326 | | bytes = recv( m_iReadSock, data, len, 0 ); |
2327 | | #else |
2328 | 0 | bytes = read( m_iReadSock, data, len ); |
2329 | 0 | #endif /* _WIN32 */ |
2330 | 0 | if( bytes == -1 ) |
2331 | 0 | { |
2332 | 0 | if( GetSockError() == ECONNREFUSED ) |
2333 | 0 | return( READ_CONNREFUSED ); |
2334 | | |
2335 | 0 | if( GetSockError() == ETIMEDOUT ) |
2336 | 0 | return( READ_TIMEDOUT ); |
2337 | | |
2338 | 0 | if( GetSockError() == EINTR || GetSockError() == EAGAIN ) |
2339 | 0 | return( READ_EAGAIN ); |
2340 | | |
2341 | | #ifdef _WIN32 |
2342 | | if( GetSockError() == WSAEWOULDBLOCK ) |
2343 | | return( READ_EAGAIN ); |
2344 | | #endif /* _WIN32 */ |
2345 | | |
2346 | | #ifdef HAVE_LIBSSL |
2347 | | if( m_ssl ) |
2348 | | { |
2349 | | int iErr = SSL_get_error( m_ssl, ( int )bytes ); |
2350 | | if( iErr != SSL_ERROR_WANT_READ && iErr != SSL_ERROR_WANT_WRITE ) |
2351 | | return( READ_ERR ); |
2352 | | else |
2353 | | return( READ_EAGAIN ); |
2354 | | } |
2355 | | #else |
2356 | 0 | return( READ_ERR ); |
2357 | 0 | #endif /* HAVE_LIBSSL */ |
2358 | 0 | } |
2359 | | |
2360 | 0 | if( bytes > 0 ) // becareful not to add negative bytes :P |
2361 | 0 | m_iBytesRead += ( uint64_t )bytes; |
2362 | |
|
2363 | 0 | return( bytes ); |
2364 | 0 | } |
2365 | | |
2366 | | CS_STRING Csock::GetLocalIP() const |
2367 | 0 | { |
2368 | 0 | if( !m_sLocalIP.empty() ) |
2369 | 0 | return( m_sLocalIP ); |
2370 | | |
2371 | 0 | cs_sock_t iSock = GetSock(); |
2372 | |
|
2373 | 0 | if( iSock == CS_INVALID_SOCK ) |
2374 | 0 | return( "" ); |
2375 | | |
2376 | 0 | struct sockaddr_storage cAddr; |
2377 | 0 | socklen_t iAddrLen = sizeof( cAddr ); |
2378 | 0 | if( getsockname( iSock, ( struct sockaddr * )&cAddr, &iAddrLen ) == 0 ) |
2379 | 0 | { |
2380 | 0 | ConvertAddress( &cAddr, iAddrLen, m_sLocalIP, &m_iLocalPort ); |
2381 | 0 | } |
2382 | |
|
2383 | 0 | return( m_sLocalIP ); |
2384 | 0 | } |
2385 | | |
2386 | | CS_STRING Csock::GetRemoteIP() const |
2387 | 0 | { |
2388 | 0 | if( !m_sRemoteIP.empty() ) |
2389 | 0 | return( m_sRemoteIP ); |
2390 | | |
2391 | 0 | cs_sock_t iSock = GetSock(); |
2392 | |
|
2393 | 0 | if( iSock == CS_INVALID_SOCK ) |
2394 | 0 | return( "" ); |
2395 | | |
2396 | 0 | struct sockaddr_storage cAddr; |
2397 | 0 | socklen_t iAddrLen = sizeof( cAddr ); |
2398 | 0 | if( getpeername( iSock, ( struct sockaddr * )&cAddr, &iAddrLen ) == 0 ) |
2399 | 0 | { |
2400 | 0 | ConvertAddress( &cAddr, iAddrLen, m_sRemoteIP, &m_iRemotePort ); |
2401 | 0 | } |
2402 | |
|
2403 | 0 | return( m_sRemoteIP ); |
2404 | 0 | } |
2405 | | |
2406 | 0 | bool Csock::IsConnected() const { return( m_bIsConnected ); } |
2407 | 0 | void Csock::SetIsConnected(bool b) { |
2408 | 0 | m_bIsConnected = b; |
2409 | 0 | if (m_eConState == CST_CONNECTWAIT && b) { |
2410 | 0 | m_eConState = (GetSSL() ? CST_CONNECTSSL : CST_OK); |
2411 | 0 | } |
2412 | 0 | } |
2413 | | |
2414 | 0 | cs_sock_t & Csock::GetRSock() { return( m_iReadSock ); } |
2415 | 0 | const cs_sock_t & Csock::GetRSock() const { return( m_iReadSock ); } |
2416 | 0 | void Csock::SetRSock( cs_sock_t iSock ) { m_iReadSock = iSock; } |
2417 | 0 | cs_sock_t & Csock::GetWSock() { return( m_iWriteSock ); } |
2418 | 0 | const cs_sock_t & Csock::GetWSock() const { return( m_iWriteSock ); } |
2419 | 0 | void Csock::SetWSock( cs_sock_t iSock ) { m_iWriteSock = iSock; } |
2420 | 0 | void Csock::SetSock( cs_sock_t iSock ) { m_iWriteSock = iSock; m_iReadSock = iSock; } |
2421 | 0 | cs_sock_t & Csock::GetSock() { return( m_iReadSock ); } |
2422 | 0 | const cs_sock_t & Csock::GetSock() const { return( m_iReadSock ); } |
2423 | 0 | void Csock::ResetTimer() { m_iLastCheckTimeoutTime = 0; m_iTcount = 0; } |
2424 | 0 | void Csock::PauseRead() { m_bPauseRead = true; } |
2425 | 0 | bool Csock::IsReadPaused() const { return( m_bPauseRead ); } |
2426 | | |
2427 | | void Csock::UnPauseRead() |
2428 | 0 | { |
2429 | 0 | m_bPauseRead = false; |
2430 | 0 | ResetTimer(); |
2431 | 0 | PushBuff( "", 0, true ); |
2432 | 0 | } |
2433 | | |
2434 | | void Csock::SetTimeout( int iTimeout, u_int iTimeoutType ) |
2435 | 0 | { |
2436 | 0 | m_iTimeoutType = iTimeoutType; |
2437 | 0 | m_iTimeout = iTimeout; |
2438 | 0 | } |
2439 | | |
2440 | | void Csock::CallSockError( int iErrno, const CS_STRING & sDescription ) |
2441 | 0 | { |
2442 | 0 | if( !sDescription.empty() ) |
2443 | 0 | { |
2444 | 0 | SockError( iErrno, sDescription ); |
2445 | 0 | } |
2446 | 0 | else |
2447 | 0 | { |
2448 | 0 | char szBuff[0xff]; |
2449 | 0 | SockError( iErrno, CS_StrError( iErrno, szBuff, 0xff ) ); |
2450 | 0 | } |
2451 | 0 | } |
2452 | 0 | void Csock::SetTimeoutType( u_int iTimeoutType ) { m_iTimeoutType = iTimeoutType; } |
2453 | 0 | int Csock::GetTimeout() const { return m_iTimeout; } |
2454 | 0 | u_int Csock::GetTimeoutType() const { return( m_iTimeoutType ); } |
2455 | | |
2456 | | bool Csock::CheckTimeout( time_t iNow ) |
2457 | 0 | { |
2458 | 0 | if( m_iLastCheckTimeoutTime == 0 ) |
2459 | 0 | { |
2460 | 0 | m_iLastCheckTimeoutTime = iNow; |
2461 | 0 | return( false ); |
2462 | 0 | } |
2463 | | |
2464 | 0 | if( IsReadPaused() ) |
2465 | 0 | return( false ); |
2466 | | |
2467 | 0 | time_t iDiff = 0; |
2468 | 0 | if( iNow > m_iLastCheckTimeoutTime ) |
2469 | 0 | { |
2470 | 0 | iDiff = iNow - m_iLastCheckTimeoutTime; |
2471 | 0 | } |
2472 | 0 | else |
2473 | 0 | { |
2474 | | // this is weird, but its possible if someone changes a clock and it went back in time, this essentially has to reset the last check |
2475 | | // the worst case scenario is the timeout is about to it and the clock changes, it would then cause |
2476 | | // this to pass over the last half the time |
2477 | 0 | m_iLastCheckTimeoutTime = iNow; |
2478 | 0 | } |
2479 | |
|
2480 | 0 | if( m_iTimeout > 0 ) |
2481 | 0 | { |
2482 | | // this is basically to help stop a clock adjust ahead, stuff could reset immediatly on a clock jump |
2483 | | // otherwise |
2484 | 0 | time_t iRealTimeout = m_iTimeout; |
2485 | 0 | if( iRealTimeout <= 1 ) |
2486 | 0 | m_iTcount++; |
2487 | 0 | else if( m_iTcount == 0 ) |
2488 | 0 | iRealTimeout /= 2; |
2489 | 0 | if( iDiff >= iRealTimeout ) |
2490 | 0 | { |
2491 | 0 | if( m_iTcount == 0 ) |
2492 | 0 | m_iLastCheckTimeoutTime = iNow - iRealTimeout; |
2493 | 0 | if( m_iTcount++ >= 1 ) |
2494 | 0 | { |
2495 | 0 | Timeout(); |
2496 | 0 | return( true ); |
2497 | 0 | } |
2498 | 0 | } |
2499 | 0 | } |
2500 | 0 | return( false ); |
2501 | 0 | } |
2502 | | |
2503 | | void Csock::PushBuff( const char *data, size_t len, bool bStartAtZero ) |
2504 | 0 | { |
2505 | 0 | if( !m_bEnableReadLine ) |
2506 | 0 | return; // If the ReadLine event is disabled, just ditch here |
2507 | | |
2508 | 0 | size_t iStartPos = ( m_sbuffer.empty() || bStartAtZero ? 0 : m_sbuffer.length() - 1 ); |
2509 | |
|
2510 | 0 | if( data ) |
2511 | 0 | m_sbuffer.append( data, len ); |
2512 | |
|
2513 | 0 | while( !m_bPauseRead && GetCloseType() == CLT_DONT ) |
2514 | 0 | { |
2515 | 0 | CS_STRING::size_type iFind = m_sbuffer.find( "\n", iStartPos ); |
2516 | |
|
2517 | 0 | if( iFind != CS_STRING::npos ) |
2518 | 0 | { |
2519 | 0 | CS_STRING sBuff = m_sbuffer.substr( 0, iFind + 1 ); // read up to(including) the newline |
2520 | 0 | m_sbuffer.erase( 0, iFind + 1 ); // erase past the newline |
2521 | | #ifdef HAVE_ICU |
2522 | | if( m_cnvExt ) |
2523 | | { |
2524 | | CS_STRING sUTF8; |
2525 | | if( ( m_cnvTryUTF8 && isUTF8( sBuff, sUTF8 ) ) // maybe it's already UTF-8? |
2526 | | || icuConv( sBuff, sUTF8, m_cnvExt, m_cnvInt ) ) |
2527 | | { |
2528 | | ReadLine( sUTF8 ); |
2529 | | } |
2530 | | else |
2531 | | { |
2532 | | CS_DEBUG( "Can't convert received line to UTF-8" ); |
2533 | | } |
2534 | | } |
2535 | | else |
2536 | | #endif /* HAVE_ICU */ |
2537 | 0 | { |
2538 | 0 | ReadLine( sBuff ); |
2539 | 0 | } |
2540 | 0 | iStartPos = 0; // reset this back to 0, since we need to look for the next newline here. |
2541 | 0 | } |
2542 | 0 | else |
2543 | 0 | { |
2544 | 0 | break; |
2545 | 0 | } |
2546 | 0 | } |
2547 | |
|
2548 | 0 | if( m_iMaxStoredBufferLength > 0 && m_sbuffer.length() > m_iMaxStoredBufferLength ) |
2549 | 0 | ReachedMaxBuffer(); // call the max read buffer event |
2550 | 0 | } |
2551 | | |
2552 | | #ifdef HAVE_ICU |
2553 | | void Csock::IcuExtToUCallback( |
2554 | | UConverterToUnicodeArgs* toArgs, |
2555 | | const char* codeUnits, |
2556 | | int32_t length, |
2557 | | UConverterCallbackReason reason, |
2558 | | UErrorCode* err) |
2559 | | { |
2560 | | if( reason <= UCNV_IRREGULAR ) |
2561 | | { |
2562 | | *err = U_ZERO_ERROR; |
2563 | | ucnv_cbToUWriteSub( toArgs, 0, err ); |
2564 | | } |
2565 | | } |
2566 | | |
2567 | | void Csock::IcuExtFromUCallback( |
2568 | | UConverterFromUnicodeArgs* fromArgs, |
2569 | | const UChar* codeUnits, |
2570 | | int32_t length, |
2571 | | UChar32 codePoint, |
2572 | | UConverterCallbackReason reason, |
2573 | | UErrorCode* err) |
2574 | | { |
2575 | | if( reason <= UCNV_IRREGULAR ) |
2576 | | { |
2577 | | *err = U_ZERO_ERROR; |
2578 | | ucnv_cbFromUWriteSub( fromArgs, 0, err ); |
2579 | | } |
2580 | | } |
2581 | | |
2582 | | static void icuExtToUCallback( |
2583 | | const void* context, |
2584 | | UConverterToUnicodeArgs* toArgs, |
2585 | | const char* codeUnits, |
2586 | | int32_t length, |
2587 | | UConverterCallbackReason reason, |
2588 | | UErrorCode* err) |
2589 | | { |
2590 | | Csock* pcSock = (Csock*)context; |
2591 | | pcSock->IcuExtToUCallback(toArgs, codeUnits, length, reason, err); |
2592 | | } |
2593 | | |
2594 | | static void icuExtFromUCallback( |
2595 | | const void* context, |
2596 | | UConverterFromUnicodeArgs* fromArgs, |
2597 | | const UChar* codeUnits, |
2598 | | int32_t length, |
2599 | | UChar32 codePoint, |
2600 | | UConverterCallbackReason reason, |
2601 | | UErrorCode* err) |
2602 | | { |
2603 | | Csock* pcSock = (Csock*)context; |
2604 | | pcSock->IcuExtFromUCallback(fromArgs, codeUnits, length, codePoint, reason, err); |
2605 | | } |
2606 | | |
2607 | | void Csock::SetEncoding( const CS_STRING& sEncoding ) |
2608 | | { |
2609 | | if( m_cnvExt ) ucnv_close( m_cnvExt ); |
2610 | | m_cnvExt = NULL; |
2611 | | m_sEncoding = sEncoding; |
2612 | | if( !sEncoding.empty() ) |
2613 | | { |
2614 | | m_cnvTryUTF8 = sEncoding[0] == '*' || sEncoding[0] == '^'; |
2615 | | m_cnvSendUTF8 = sEncoding[0] == '^'; |
2616 | | const char* sEncodingName = sEncoding.c_str(); |
2617 | | if( m_cnvTryUTF8 ) |
2618 | | sEncodingName++; |
2619 | | UErrorCode e = U_ZERO_ERROR; |
2620 | | m_cnvExt = ucnv_open( sEncodingName, &e ); |
2621 | | if( U_FAILURE( e ) ) |
2622 | | { |
2623 | | CS_DEBUG( "Can't set encoding to " << sEncoding << ": " << u_errorName( e ) ); |
2624 | | } |
2625 | | if( m_cnvExt ) |
2626 | | { |
2627 | | ucnv_setToUCallBack( m_cnvExt, icuExtToUCallback, this, NULL, NULL, &e ); |
2628 | | ucnv_setFromUCallBack( m_cnvExt, icuExtFromUCallback, this, NULL, NULL, &e ); |
2629 | | } |
2630 | | } |
2631 | | } |
2632 | | #endif /* HAVE_ICU */ |
2633 | | |
2634 | 0 | CS_STRING & Csock::GetInternalReadBuffer() { return( m_sbuffer ); } |
2635 | | CS_STRING & Csock::GetInternalWriteBuffer() |
2636 | 0 | { |
2637 | | // in the event that some is grabbing this for some reason, we need to |
2638 | | // clean it up so it's what they are expecting. |
2639 | 0 | ShrinkSendBuff(); |
2640 | 0 | return( m_sSend ); |
2641 | 0 | } |
2642 | 0 | void Csock::SetMaxBufferThreshold( u_int iThreshold ) { m_iMaxStoredBufferLength = iThreshold; } |
2643 | 0 | u_int Csock::GetMaxBufferThreshold() const { return( m_iMaxStoredBufferLength ); } |
2644 | 0 | int Csock::GetType() const { return( m_iConnType ); } |
2645 | 0 | void Csock::SetType( int iType ) { m_iConnType = iType; } |
2646 | 0 | const CS_STRING & Csock::GetSockName() const { return( m_sSockName ); } |
2647 | 0 | void Csock::SetSockName( const CS_STRING & sName ) { m_sSockName = sName; } |
2648 | 0 | const CS_STRING & Csock::GetHostName() const { return( m_shostname ); } |
2649 | 0 | void Csock::SetHostName( const CS_STRING & sHostname ) { m_shostname = sHostname; } |
2650 | 0 | uint64_t Csock::GetStartTime() const { return( m_iStartTime ); } |
2651 | 0 | void Csock::ResetStartTime() { m_iStartTime = 0; } |
2652 | 0 | uint64_t Csock::GetBytesRead() const { return( m_iBytesRead ); } |
2653 | 0 | void Csock::ResetBytesRead() { m_iBytesRead = 0; } |
2654 | 0 | uint64_t Csock::GetBytesWritten() const { return( m_iBytesWritten ); } |
2655 | 0 | void Csock::ResetBytesWritten() { m_iBytesWritten = 0; } |
2656 | | |
2657 | | double Csock::GetAvgRead( uint64_t iSample ) const |
2658 | 0 | { |
2659 | 0 | uint64_t iDifference = ( millitime() - m_iStartTime ); |
2660 | |
|
2661 | 0 | if( m_iBytesRead == 0 || iSample > iDifference ) |
2662 | 0 | return( ( double )m_iBytesRead ); |
2663 | | |
2664 | 0 | return( ( ( double )m_iBytesRead / ( ( double )iDifference / ( double )iSample ) ) ); |
2665 | 0 | } |
2666 | | |
2667 | | double Csock::GetAvgWrite( uint64_t iSample ) const |
2668 | 0 | { |
2669 | 0 | uint64_t iDifference = ( millitime() - m_iStartTime ); |
2670 | |
|
2671 | 0 | if( m_iBytesWritten == 0 || iSample > iDifference ) |
2672 | 0 | return( ( double )m_iBytesWritten ); |
2673 | | |
2674 | 0 | return( ( ( double )m_iBytesWritten / ( ( double )iDifference / ( double )iSample ) ) ); |
2675 | 0 | } |
2676 | | |
2677 | | uint16_t Csock::GetRemotePort() const |
2678 | 0 | { |
2679 | 0 | if( m_iRemotePort > 0 ) |
2680 | 0 | return( m_iRemotePort ); |
2681 | 0 | GetRemoteIP(); |
2682 | 0 | return( m_iRemotePort ); |
2683 | 0 | } |
2684 | | |
2685 | | uint16_t Csock::GetLocalPort() const |
2686 | 0 | { |
2687 | 0 | if( m_iLocalPort > 0 ) |
2688 | 0 | return( m_iLocalPort ); |
2689 | 0 | GetLocalIP(); |
2690 | 0 | return( m_iLocalPort ); |
2691 | 0 | } |
2692 | | |
2693 | 0 | uint16_t Csock::GetPort() const { return( m_uPort ); } |
2694 | 0 | void Csock::SetPort( uint16_t iPort ) { m_uPort = iPort; } |
2695 | | void Csock::Close( ECloseType eCloseType ) |
2696 | 0 | { |
2697 | 0 | m_eCloseType = eCloseType; |
2698 | 0 | } |
2699 | | |
2700 | | void Csock::NonBlockingIO() |
2701 | 0 | { |
2702 | 0 | set_non_blocking( m_iReadSock ); |
2703 | |
|
2704 | 0 | if( m_iReadSock != m_iWriteSock ) |
2705 | 0 | { |
2706 | 0 | set_non_blocking( m_iWriteSock ); |
2707 | 0 | } |
2708 | 0 | } |
2709 | | |
2710 | 0 | bool Csock::GetSSL() const { return( m_bUseSSL ); } |
2711 | 0 | void Csock::SetSSL( bool b ) { m_bUseSSL = b; } |
2712 | | |
2713 | | #ifdef HAVE_LIBSSL |
2714 | | void Csock::SetCipher( const CS_STRING & sCipher ) { m_sCipherType = sCipher; } |
2715 | | const CS_STRING & Csock::GetCipher() const { return( m_sCipherType ); } |
2716 | | |
2717 | | void Csock::SetDHParamLocation( const CS_STRING & sDHParamFile ) { m_sDHParamFile = sDHParamFile; } |
2718 | | const CS_STRING & Csock::GetDHParamLocation() const { return( m_sDHParamFile ); } |
2719 | | |
2720 | | void Csock::SetKeyLocation( const CS_STRING & sKeyFile ) { m_sKeyFile = sKeyFile; } |
2721 | | const CS_STRING & Csock::GetKeyLocation() const { return( m_sKeyFile ); } |
2722 | | |
2723 | | void Csock::SetPemLocation( const CS_STRING & sPemFile ) { m_sPemFile = sPemFile; } |
2724 | | const CS_STRING & Csock::GetPemLocation() const { return( m_sPemFile ); } |
2725 | | |
2726 | | void Csock::SetPemPass( const CS_STRING & sPassword ) { m_sPemPass = sPassword; } |
2727 | | const CS_STRING & Csock::GetPemPass() const { return( m_sPemPass ); } |
2728 | | |
2729 | | void Csock::SetSSLMethod( int iMethod ) { m_iMethod = iMethod; } |
2730 | | int Csock::GetSSLMethod() const { return( m_iMethod ); } |
2731 | | void Csock::SetSSLObject( SSL *ssl, bool bDeleteExisting ) |
2732 | | { |
2733 | | if( bDeleteExisting ) |
2734 | | FREE_SSL(); |
2735 | | m_ssl = ssl; |
2736 | | } |
2737 | | SSL * Csock::GetSSLObject() const |
2738 | | { |
2739 | | if( m_ssl ) |
2740 | | return( m_ssl ); |
2741 | | |
2742 | | return( NULL ); |
2743 | | } |
2744 | | void Csock::SetCTXObject( SSL_CTX *sslCtx, bool bDeleteExisting ) |
2745 | | { |
2746 | | if( bDeleteExisting ) |
2747 | | FREE_CTX(); |
2748 | | m_ssl_ctx = sslCtx; |
2749 | | } |
2750 | | |
2751 | | SSL_SESSION * Csock::GetSSLSession() const |
2752 | | { |
2753 | | if( m_ssl ) |
2754 | | return( SSL_get_session( m_ssl ) ); |
2755 | | |
2756 | | return( NULL ); |
2757 | | } |
2758 | | #endif /* HAVE_LIBSSL */ |
2759 | | |
2760 | | bool Csock::HasWriteBuffer() const |
2761 | 0 | { |
2762 | | // the fact that this has data in it is good enough. Checking m_uSendBufferPos is a moot point |
2763 | | // since once m_uSendBufferPos is at the same position as m_sSend it's cleared (in Write) |
2764 | 0 | return( !m_sSend.empty() ); |
2765 | 0 | } |
2766 | 0 | void Csock::ClearWriteBuffer() { m_sSend.clear(); m_uSendBufferPos = 0; } |
2767 | 0 | bool Csock::SslIsEstablished() const { return ( m_bsslEstablished ); } |
2768 | | |
2769 | | bool Csock::ConnectInetd( bool bIsSSL, const CS_STRING & sHostname ) |
2770 | 0 | { |
2771 | 0 | if( !sHostname.empty() ) |
2772 | 0 | m_sSockName = sHostname; |
2773 | | |
2774 | | // set our hostname |
2775 | 0 | if( m_sSockName.empty() ) |
2776 | 0 | { |
2777 | 0 | struct sockaddr_in client; |
2778 | 0 | socklen_t clen = sizeof( client ); |
2779 | 0 | if( getpeername( 0, ( struct sockaddr * )&client, &clen ) < 0 ) |
2780 | 0 | { |
2781 | 0 | m_sSockName = "0.0.0.0:0"; |
2782 | 0 | } |
2783 | 0 | else |
2784 | 0 | { |
2785 | 0 | stringstream s; |
2786 | 0 | s << inet_ntoa( client.sin_addr ) << ":" << ntohs( client.sin_port ); |
2787 | 0 | m_sSockName = s.str(); |
2788 | 0 | } |
2789 | 0 | } |
2790 | |
|
2791 | 0 | return( ConnectFD( 0, 1, m_sSockName, bIsSSL, INBOUND ) ); |
2792 | 0 | } |
2793 | | |
2794 | | bool Csock::ConnectFD( int iReadFD, int iWriteFD, const CS_STRING & sName, bool bIsSSL, ETConn eDirection ) |
2795 | 0 | { |
2796 | 0 | if( eDirection == LISTENER ) |
2797 | 0 | { |
2798 | 0 | CS_DEBUG( "You can not use a LISTENER type here!" ); |
2799 | 0 | return( false ); |
2800 | 0 | } |
2801 | | |
2802 | | // set our socket type |
2803 | 0 | SetType( eDirection ); |
2804 | | |
2805 | | // set the hostname |
2806 | 0 | m_sSockName = sName; |
2807 | | |
2808 | | // set the file descriptors |
2809 | 0 | SetRSock( iReadFD ); |
2810 | 0 | SetWSock( iWriteFD ); |
2811 | | |
2812 | | // set it up as non-blocking io |
2813 | 0 | NonBlockingIO(); |
2814 | |
|
2815 | 0 | if( bIsSSL ) |
2816 | 0 | { |
2817 | 0 | if( eDirection == INBOUND && !AcceptSSL() ) |
2818 | 0 | return( false ); |
2819 | 0 | else if( eDirection == OUTBOUND && !ConnectSSL() ) |
2820 | 0 | return( false ); |
2821 | 0 | } |
2822 | | |
2823 | 0 | return( true ); |
2824 | 0 | } |
2825 | | |
2826 | | #ifdef HAVE_LIBSSL |
2827 | | X509 *Csock::GetX509() const |
2828 | | { |
2829 | | if( m_ssl ) |
2830 | | return( SSL_get_peer_certificate( m_ssl ) ); |
2831 | | |
2832 | | return( NULL ); |
2833 | | } |
2834 | | |
2835 | | CS_STRING Csock::GetPeerPubKey() const |
2836 | | { |
2837 | | CS_STRING sKey; |
2838 | | |
2839 | | X509 * pCert = GetX509(); |
2840 | | |
2841 | | if( pCert ) |
2842 | | { |
2843 | | EVP_PKEY * pKey = X509_get_pubkey( pCert ); |
2844 | | if( pKey ) |
2845 | | { |
2846 | | const BIGNUM * pPubKey = NULL; |
2847 | | #ifdef HAVE_OPAQUE_SSL |
2848 | | int iType = EVP_PKEY_base_id( pKey ); |
2849 | | #else |
2850 | | int iType = pKey->type; |
2851 | | #endif /* HAVE_OPAQUE_SSL */ |
2852 | | switch( iType ) |
2853 | | { |
2854 | | #ifndef OPENSSL_NO_RSA |
2855 | | case EVP_PKEY_RSA: |
2856 | | # ifdef HAVE_OPAQUE_SSL |
2857 | | RSA_get0_key( EVP_PKEY_get0_RSA( pKey ), &pPubKey, NULL, NULL ); |
2858 | | # else |
2859 | | pPubKey = pKey->pkey.rsa->n; |
2860 | | # endif /* HAVE_OPAQUE_SSL */ |
2861 | | break; |
2862 | | #endif /* OPENSSL_NO_RSA */ |
2863 | | #ifndef OPENSSL_NO_DSA |
2864 | | case EVP_PKEY_DSA: |
2865 | | # ifdef HAVE_OPAQUE_SSL |
2866 | | DSA_get0_key( EVP_PKEY_get0_DSA( pKey ), &pPubKey, NULL ); |
2867 | | # else |
2868 | | pPubKey = pKey->pkey.dsa->pub_key; |
2869 | | # endif /* HAVE_OPAQUE_SSL */ |
2870 | | break; |
2871 | | #endif /* OPENSSL_NO_DSA */ |
2872 | | default: |
2873 | | CS_DEBUG( "Not Prepared for Public Key Type [" << iType << "]" ); |
2874 | | break; |
2875 | | } |
2876 | | if( pPubKey ) |
2877 | | { |
2878 | | char *hxKey = BN_bn2hex( pPubKey ); |
2879 | | sKey = hxKey; |
2880 | | OPENSSL_free( hxKey ); |
2881 | | } |
2882 | | EVP_PKEY_free( pKey ); |
2883 | | } |
2884 | | X509_free( pCert ); |
2885 | | } |
2886 | | return( sKey ); |
2887 | | } |
2888 | | |
2889 | | long Csock::GetPeerFingerprint( CS_STRING & sFP ) const |
2890 | | { |
2891 | | sFP.clear(); |
2892 | | |
2893 | | if( !m_ssl ) |
2894 | | return( 0 ); |
2895 | | |
2896 | | X509 * pCert = GetX509(); |
2897 | | |
2898 | | #ifdef HAVE_OPAQUE_SSL |
2899 | | unsigned char sha1_hash[SHA_DIGEST_LENGTH]; |
2900 | | |
2901 | | if( pCert && X509_digest( pCert, EVP_sha1(), sha1_hash, NULL ) ) |
2902 | | #else |
2903 | | unsigned char * sha1_hash = NULL; |
2904 | | |
2905 | | // Inspired by charybdis |
2906 | | if( pCert && (sha1_hash = pCert->sha1_hash) ) |
2907 | | #endif /* HAVE_OPAQUE_SSL */ |
2908 | | { |
2909 | | for( int i = 0; i < SHA_DIGEST_LENGTH; i++ ) |
2910 | | { |
2911 | | char buf[3]; |
2912 | | snprintf( buf, 3, "%02x", sha1_hash[i] ); |
2913 | | sFP += buf; |
2914 | | } |
2915 | | } |
2916 | | X509_free( pCert ); |
2917 | | |
2918 | | return( SSL_get_verify_result( m_ssl ) ); |
2919 | | } |
2920 | | u_int Csock::GetRequireClientCertFlags() const { return( m_iRequireClientCertFlags ); } |
2921 | | void Csock::SetRequiresClientCert( bool bRequiresCert ) { m_iRequireClientCertFlags = ( bRequiresCert ? SSL_VERIFY_FAIL_IF_NO_PEER_CERT|SSL_VERIFY_PEER : 0 ); } |
2922 | | |
2923 | | #endif /* HAVE_LIBSSL */ |
2924 | | |
2925 | 0 | void Csock::SetParentSockName( const CS_STRING & sParentName ) { m_sParentName = sParentName; } |
2926 | 0 | const CS_STRING & Csock::GetParentSockName() const { return( m_sParentName ); } |
2927 | | |
2928 | | void Csock::SetRate( u_int iBytes, uint64_t iMilliseconds ) |
2929 | 0 | { |
2930 | 0 | m_iMaxBytes = iBytes; |
2931 | 0 | m_iMaxMilliSeconds = iMilliseconds; |
2932 | 0 | } |
2933 | | |
2934 | 0 | u_int Csock::GetRateBytes() const { return( m_iMaxBytes ); } |
2935 | 0 | uint64_t Csock::GetRateTime() const { return( m_iMaxMilliSeconds ); } |
2936 | | |
2937 | | |
2938 | 0 | void Csock::EnableReadLine() { m_bEnableReadLine = true; } |
2939 | | void Csock::DisableReadLine() |
2940 | 0 | { |
2941 | 0 | m_bEnableReadLine = false; |
2942 | 0 | m_sbuffer.clear(); |
2943 | 0 | } |
2944 | | |
2945 | | void Csock::ReachedMaxBuffer() |
2946 | 0 | { |
2947 | 0 | std::cerr << "Warning, Max Buffer length Warning Threshold has been hit" << endl; |
2948 | 0 | std::cerr << "If you don't care, then set SetMaxBufferThreshold to 0" << endl; |
2949 | 0 | } |
2950 | | |
2951 | | time_t Csock::GetTimeSinceLastDataTransaction( time_t iNow ) const |
2952 | 0 | { |
2953 | 0 | if( m_iLastCheckTimeoutTime == 0 ) |
2954 | 0 | return( 0 ); |
2955 | 0 | return( ( iNow > 0 ? iNow : time( NULL ) ) - m_iLastCheckTimeoutTime ); |
2956 | 0 | } |
2957 | | |
2958 | | time_t Csock::GetNextCheckTimeout( time_t iNow ) const |
2959 | 0 | { |
2960 | 0 | if( iNow == 0 ) |
2961 | 0 | iNow = time( NULL ); |
2962 | 0 | time_t iTimeout = m_iTimeout; |
2963 | 0 | time_t iDiff = iNow - m_iLastCheckTimeoutTime; |
2964 | | /* CheckTimeout() wants to be called after half the timeout */ |
2965 | 0 | if( m_iTcount == 0 ) |
2966 | 0 | iTimeout /= 2; |
2967 | 0 | if( iDiff > iTimeout ) |
2968 | 0 | iTimeout = 0; |
2969 | 0 | else |
2970 | 0 | iTimeout -= iDiff; |
2971 | 0 | return( iNow + iTimeout ); |
2972 | 0 | } |
2973 | | |
2974 | | int Csock::GetPending() const |
2975 | 0 | { |
2976 | | #ifdef HAVE_LIBSSL |
2977 | | if( m_ssl ) |
2978 | | { |
2979 | | // in v23 method, the pending function is initialized to ssl_undefined_const_function |
2980 | | // which throws SSL_UNDEFINED_CONST_FUNCTION on to the error stack |
2981 | | // this is one of the more stupid things in openssl, it seems bizarre that even though SSL_pending |
2982 | | // returns an int, they don't bother returning in error to notify us, so basically |
2983 | | // we have to always clear errors here generated by SSL_pending, otherwise the stack could |
2984 | | // have a lame error on it causing SSL_write to fail in certain instances. |
2985 | | #if defined( OPENSSL_VERSION_NUMBER ) && OPENSSL_VERSION_NUMBER >= 0x00908000 |
2986 | | ERR_set_mark(); |
2987 | | int iBytes = SSL_pending( m_ssl ); |
2988 | | ERR_pop_to_mark(); |
2989 | | return( iBytes ); |
2990 | | #else |
2991 | | int iBytes = SSL_pending( m_ssl ); |
2992 | | ERR_clear_error(); // to get safer handling, upgrade your openssl version! |
2993 | | return( iBytes ); |
2994 | | #endif /* OPENSSL_VERSION_NUMBER */ |
2995 | | } |
2996 | | else |
2997 | | return( 0 ); |
2998 | | #else |
2999 | 0 | return( 0 ); |
3000 | 0 | #endif /* HAVE_LIBSSL */ |
3001 | 0 | } |
3002 | | |
3003 | | bool Csock::CreateSocksFD() |
3004 | 0 | { |
3005 | 0 | if( m_iReadSock != CS_INVALID_SOCK ) |
3006 | 0 | return( true ); |
3007 | | |
3008 | 0 | m_iReadSock = m_iWriteSock = CreateSocket(); |
3009 | 0 | if( m_iReadSock == CS_INVALID_SOCK ) |
3010 | 0 | return( false ); |
3011 | | |
3012 | 0 | m_address.SinFamily(); |
3013 | 0 | m_address.SinPort( m_uPort ); |
3014 | |
|
3015 | 0 | return( true ); |
3016 | 0 | } |
3017 | | |
3018 | | |
3019 | | int Csock::GetAddrInfo( const CS_STRING & sHostname, CSSockAddr & csSockAddr ) |
3020 | 0 | { |
3021 | | #ifdef HAVE_IPV6 |
3022 | | if( csSockAddr.GetAFRequire() != AF_INET && inet_pton( AF_INET6, sHostname.c_str(), csSockAddr.GetAddr6() ) > 0 ) |
3023 | | { |
3024 | | SetIPv6( true ); |
3025 | | return( 0 ); |
3026 | | } |
3027 | | #endif /* HAVE_IPV6 */ |
3028 | 0 | if( inet_pton( AF_INET, sHostname.c_str(), csSockAddr.GetAddr() ) > 0 ) |
3029 | 0 | { |
3030 | | #ifdef HAVE_IPV6 |
3031 | | SetIPv6( false ); |
3032 | | #endif /* HAVE_IPV6 */ |
3033 | 0 | return( 0 ); |
3034 | 0 | } |
3035 | | |
3036 | | #ifdef HAVE_C_ARES |
3037 | | // need to compute this up here |
3038 | | if( !m_pARESChannel ) |
3039 | | { |
3040 | | if( ares_init( &m_pARESChannel ) != ARES_SUCCESS ) |
3041 | | { |
3042 | | // TODO throw some debug? |
3043 | | FreeAres(); |
3044 | | return( ETIMEDOUT ); |
3045 | | } |
3046 | | m_pCurrAddr = &csSockAddr; // flag its starting |
3047 | | |
3048 | | int iFamily = AF_INET; |
3049 | | #ifdef HAVE_IPV6 |
3050 | | #if ARES_VERSION >= CREATE_ARES_VER( 1, 7, 5 ) |
3051 | | // as of ares 1.7.5, it falls back to af_inet only when AF_UNSPEC is specified |
3052 | | // so this can finally let the code flow through as anticipated :) |
3053 | | iFamily = csSockAddr.GetAFRequire(); |
3054 | | #else |
3055 | | // as of ares 1.6.0 if it fails on af_inet6, it falls back to af_inet, |
3056 | | // this code was here in the previous Csocket version, just adding the comment as a reminder |
3057 | | iFamily = csSockAddr.GetAFRequire() == CSSockAddr::RAF_ANY ? AF_INET6 : csSockAddr.GetAFRequire(); |
3058 | | #endif /* CREATE_ARES_VER( 1, 7, 5 ) */ |
3059 | | #endif /* HAVE_IPV6 */ |
3060 | | ares_gethostbyname( m_pARESChannel, sHostname.c_str(), iFamily, AresHostCallback, this ); |
3061 | | } |
3062 | | if( !m_pCurrAddr ) |
3063 | | { |
3064 | | // this means its finished |
3065 | | FreeAres(); |
3066 | | #ifdef HAVE_IPV6 |
3067 | | if( GetType() != LISTENER && m_iARESStatus == ARES_SUCCESS && csSockAddr.GetAFRequire() == CSSockAddr::RAF_ANY && GetIPv6() ) |
3068 | | { |
3069 | | // this means that ares_host returned an ipv6 host, so try a connect right away |
3070 | | if( CreateSocksFD() && Connect() ) |
3071 | | { |
3072 | | SetSkipConnect( true ); |
3073 | | } |
3074 | | #ifndef _WIN32 |
3075 | | else if( GetSockError() == ENETUNREACH ) |
3076 | | #else |
3077 | | else if( GetSockError() == WSAENETUNREACH || GetSockError() == WSAEHOSTUNREACH ) |
3078 | | #endif /* !_WIN32 */ |
3079 | | { |
3080 | | // the Connect() failed, so throw a retry back in with ipv4, and let it process normally |
3081 | | CS_DEBUG( "Failed ipv6 connection with PF_UNSPEC, falling back to ipv4" ); |
3082 | | m_iARESStatus = -1; |
3083 | | CloseSocksFD(); |
3084 | | SetAFRequire( CSSockAddr::RAF_INET ); |
3085 | | return( GetAddrInfo( sHostname, csSockAddr ) ); |
3086 | | } |
3087 | | } |
3088 | | #if ARES_VERSION < CREATE_ARES_VER( 1, 5, 3 ) |
3089 | | if( m_iARESStatus != ARES_SUCCESS && csSockAddr.GetAFRequire() == CSSockAddr::RAF_ANY ) |
3090 | | { |
3091 | | // this is a workaround for ares < 1.5.3 where the builtin retry on failed AF_INET6 isn't there yet |
3092 | | CS_DEBUG( "Retry for older version of c-ares with AF_INET only" ); |
3093 | | // this means we tried previously with AF_INET6 and failed, so force AF_INET and retry |
3094 | | SetAFRequire( CSSockAddr::RAF_INET ); |
3095 | | return( GetAddrInfo( sHostname, csSockAddr ) ); |
3096 | | } |
3097 | | #endif /* ARES_VERSION < CREATE_ARES_VER( 1, 5, 3 ) */ |
3098 | | #endif /* HAVE_IPV6 */ |
3099 | | return( m_iARESStatus == ARES_SUCCESS ? 0 : ETIMEDOUT ); |
3100 | | } |
3101 | | return( EAGAIN ); |
3102 | | #else /* HAVE_C_ARES */ |
3103 | 0 | return( ::CS_GetAddrInfo( sHostname, this, csSockAddr ) ); |
3104 | 0 | #endif /* HAVE_C_ARES */ |
3105 | 0 | } |
3106 | | |
3107 | | int Csock::DNSLookup( EDNSLType eDNSLType ) |
3108 | 0 | { |
3109 | 0 | if( eDNSLType == DNS_VHOST ) |
3110 | 0 | { |
3111 | 0 | if( m_sBindHost.empty() ) |
3112 | 0 | { |
3113 | 0 | if( m_eConState != CST_OK ) |
3114 | 0 | m_eConState = CST_DESTDNS; // skip binding, there is no vhost |
3115 | 0 | return( 0 ); |
3116 | 0 | } |
3117 | | |
3118 | 0 | m_bindhost.SinFamily(); |
3119 | 0 | m_bindhost.SinPort( 0 ); |
3120 | 0 | } |
3121 | | |
3122 | 0 | int iRet = ETIMEDOUT; |
3123 | 0 | if( eDNSLType == DNS_VHOST ) |
3124 | 0 | { |
3125 | 0 | iRet = GetAddrInfo( m_sBindHost, m_bindhost ); |
3126 | | #ifdef HAVE_IPV6 |
3127 | | if( m_bindhost.GetIPv6() ) |
3128 | | { |
3129 | | SetAFRequire( CSSockAddr::RAF_INET6 ); |
3130 | | } |
3131 | | else |
3132 | | { |
3133 | | SetAFRequire( CSSockAddr::RAF_INET ); |
3134 | | } |
3135 | | #endif /* HAVE_IPV6 */ |
3136 | 0 | } |
3137 | 0 | else |
3138 | 0 | { |
3139 | 0 | iRet = GetAddrInfo( m_shostname, m_address ); |
3140 | 0 | } |
3141 | |
|
3142 | 0 | if( iRet == 0 ) |
3143 | 0 | { |
3144 | 0 | if( !CreateSocksFD() ) |
3145 | 0 | { |
3146 | 0 | m_iDNSTryCount = 0; |
3147 | 0 | return( ETIMEDOUT ); |
3148 | 0 | } |
3149 | 0 | if( m_eConState != CST_OK ) |
3150 | 0 | m_eConState = ( ( eDNSLType == DNS_VHOST ) ? CST_BINDVHOST : CST_CONNECT ); |
3151 | 0 | m_iDNSTryCount = 0; |
3152 | 0 | return( 0 ); |
3153 | 0 | } |
3154 | 0 | else if( iRet == EAGAIN ) |
3155 | 0 | { |
3156 | 0 | #ifndef HAVE_C_ARES |
3157 | 0 | m_iDNSTryCount++; |
3158 | 0 | if( m_iDNSTryCount > 20 ) |
3159 | 0 | { |
3160 | 0 | m_iDNSTryCount = 0; |
3161 | 0 | return( ETIMEDOUT ); |
3162 | 0 | } |
3163 | 0 | #endif /* HAVE_C_ARES */ |
3164 | 0 | return( EAGAIN ); |
3165 | 0 | } |
3166 | 0 | m_iDNSTryCount = 0; |
3167 | 0 | return( ETIMEDOUT ); |
3168 | 0 | } |
3169 | | |
3170 | | bool Csock::SetupVHost() |
3171 | 0 | { |
3172 | 0 | if( m_sBindHost.empty() ) |
3173 | 0 | { |
3174 | 0 | if( m_eConState != CST_OK ) |
3175 | 0 | m_eConState = CST_DESTDNS; |
3176 | 0 | return( true ); |
3177 | 0 | } |
3178 | 0 | int iRet = -1; |
3179 | 0 | if( !GetIPv6() ) |
3180 | 0 | iRet = bind( m_iReadSock, ( struct sockaddr * ) m_bindhost.GetSockAddr(), m_bindhost.GetSockAddrLen() ); |
3181 | | #ifdef HAVE_IPV6 |
3182 | | else |
3183 | | iRet = bind( m_iReadSock, ( struct sockaddr * ) m_bindhost.GetSockAddr6(), m_bindhost.GetSockAddrLen6() ); |
3184 | | #endif /* HAVE_IPV6 */ |
3185 | |
|
3186 | 0 | if( iRet == 0 ) |
3187 | 0 | { |
3188 | 0 | if( m_eConState != CST_OK ) |
3189 | 0 | m_eConState = CST_DESTDNS; |
3190 | 0 | return( true ); |
3191 | 0 | } |
3192 | 0 | m_iCurBindCount++; |
3193 | 0 | if( m_iCurBindCount > 3 ) |
3194 | 0 | { |
3195 | 0 | CS_DEBUG( "Failure to bind to " << m_sBindHost ); |
3196 | 0 | return( false ); |
3197 | 0 | } |
3198 | | |
3199 | 0 | return( true ); |
3200 | 0 | } |
3201 | | |
3202 | | #ifdef HAVE_LIBSSL |
3203 | | void Csock::FREE_SSL() |
3204 | | { |
3205 | | if( m_ssl ) |
3206 | | { |
3207 | | SSL_shutdown( m_ssl ); |
3208 | | SSL_free( m_ssl ); |
3209 | | } |
3210 | | m_ssl = NULL; |
3211 | | } |
3212 | | |
3213 | | void Csock::FREE_CTX() |
3214 | | { |
3215 | | if( m_ssl_ctx ) |
3216 | | SSL_CTX_free( m_ssl_ctx ); |
3217 | | |
3218 | | m_ssl_ctx = NULL; |
3219 | | } |
3220 | | |
3221 | | #endif /* HAVE_LIBSSL */ |
3222 | | |
3223 | | cs_sock_t Csock::CreateSocket( bool bListen, bool bUnix ) |
3224 | 0 | { |
3225 | 0 | int domain, protocol; |
3226 | 0 | if ( !bUnix ) |
3227 | 0 | { |
3228 | | #ifdef HAVE_IPV6 |
3229 | | domain = ( GetIPv6() ? PF_INET6 : PF_INET ); |
3230 | | #else |
3231 | 0 | domain = PF_INET; |
3232 | 0 | #endif /* HAVE_IPV6 */ |
3233 | 0 | protocol = IPPROTO_TCP; |
3234 | 0 | } |
3235 | 0 | else |
3236 | 0 | { |
3237 | 0 | #ifdef HAVE_UNIX_SOCKET |
3238 | 0 | domain = AF_UNIX; |
3239 | 0 | protocol = 0; |
3240 | | #else |
3241 | | return CS_INVALID_SOCK; |
3242 | | #endif |
3243 | 0 | } |
3244 | 0 | cs_sock_t iRet = socket( domain, SOCK_STREAM, protocol ); |
3245 | |
|
3246 | 0 | if( iRet != CS_INVALID_SOCK ) |
3247 | 0 | { |
3248 | 0 | set_close_on_exec( iRet ); |
3249 | |
|
3250 | 0 | if( bListen ) |
3251 | 0 | { |
3252 | 0 | const int on = 1; |
3253 | 0 | if( setsockopt( iRet, SOL_SOCKET, SO_REUSEADDR, ( char * ) &on, sizeof( on ) ) != 0 ) |
3254 | 0 | PERROR( "SO_REUSEADDR" ); |
3255 | 0 | } |
3256 | 0 | } |
3257 | 0 | else |
3258 | 0 | { |
3259 | 0 | PERROR( "socket" ); |
3260 | 0 | } |
3261 | 0 | return( iRet ); |
3262 | 0 | } |
3263 | | |
3264 | | void Csock::Init( const CS_STRING & sHostname, uint16_t uPort, int iTimeout ) |
3265 | 0 | { |
3266 | | #ifdef HAVE_LIBSSL |
3267 | | m_ssl = NULL; |
3268 | | m_ssl_ctx = NULL; |
3269 | | m_iRequireClientCertFlags = 0; |
3270 | | m_uDisableProtocols = 0; |
3271 | | m_bNoSSLCompression = false; |
3272 | | m_bSSLCipherServerPreference = false; |
3273 | | #endif /* HAVE_LIBSSL */ |
3274 | 0 | m_iTcount = 0; |
3275 | 0 | m_iReadSock = CS_INVALID_SOCK; |
3276 | 0 | m_iWriteSock = CS_INVALID_SOCK; |
3277 | 0 | m_iTimeout = iTimeout; |
3278 | 0 | m_iMaxConns = SOMAXCONN; |
3279 | 0 | m_bUseSSL = false; |
3280 | 0 | m_bIsConnected = false; |
3281 | 0 | m_uPort = uPort; |
3282 | 0 | m_shostname = sHostname; |
3283 | 0 | m_sbuffer.clear(); |
3284 | 0 | m_eCloseType = CLT_DONT; |
3285 | | /* |
3286 | | * While I appreciate the line ... |
3287 | | * "It's 2014, no idea how this made it as a default for the past 16 years..." |
3288 | | * TLS 1.2 was introduced in 2008. That being said, it's still not widely supported so I'm not |
3289 | | * ready to make it the default. SSL 3.0 is still the most widely supported standard and that's |
3290 | | * what a sane default is supposed to be. Additionally, OpenSSL is smart with SSLv23_client_method |
3291 | | * as it will check for TLS in addition to SSL (per the manual) which is the reason for its choice. |
3292 | | * |
3293 | | * https://www.openssl.org/docs/ssl/SSL_CTX_new.html |
3294 | | */ |
3295 | 0 | m_iMethod = SSL23; |
3296 | 0 | m_sCipherType = "ALL"; |
3297 | 0 | m_iMaxBytes = 0; |
3298 | 0 | m_iMaxMilliSeconds = 0; |
3299 | 0 | m_iLastSendTime = 0; |
3300 | 0 | m_iLastSend = 0; |
3301 | 0 | m_uSendBufferPos = 0; |
3302 | 0 | m_bsslEstablished = false; |
3303 | 0 | m_bEnableReadLine = false; |
3304 | 0 | m_iMaxStoredBufferLength = 1024; |
3305 | 0 | m_iConnType = INBOUND; |
3306 | 0 | m_iRemotePort = 0; |
3307 | 0 | m_iLocalPort = 0; |
3308 | 0 | m_iBytesRead = 0; |
3309 | 0 | m_iBytesWritten = 0; |
3310 | 0 | m_iStartTime = millitime(); |
3311 | 0 | m_bPauseRead = false; |
3312 | 0 | m_iTimeoutType = TMO_ALL; |
3313 | 0 | m_eConState = CST_OK; // default should be ok |
3314 | 0 | m_iDNSTryCount = 0; |
3315 | 0 | m_iCurBindCount = 0; |
3316 | 0 | m_bIsIPv6 = false; |
3317 | 0 | m_bSkipConnect = false; |
3318 | 0 | m_iLastCheckTimeoutTime = 0; |
3319 | | #ifdef HAVE_C_ARES |
3320 | | m_pARESChannel = NULL; |
3321 | | m_pCurrAddr = NULL; |
3322 | | m_iARESStatus = -1; |
3323 | | #endif /* HAVE_C_ARES */ |
3324 | | #ifdef HAVE_ICU |
3325 | | m_cnvTryUTF8 = false; |
3326 | | m_cnvSendUTF8 = false; |
3327 | | UErrorCode e = U_ZERO_ERROR; |
3328 | | m_cnvExt = NULL; |
3329 | | m_cnvInt = ucnv_open( "UTF-8", &e ); |
3330 | | #endif /* HAVE_ICU */ |
3331 | 0 | #ifdef HAVE_UNIX_SOCKET |
3332 | 0 | m_bUnixListen = false; |
3333 | 0 | #endif |
3334 | 0 | } |
3335 | | |
3336 | | ////////////////////////// CSocketManager ////////////////////////// |
3337 | 0 | CSocketManager::CSocketManager() : std::vector<Csock *>(), CSockCommon() |
3338 | 0 | { |
3339 | 0 | m_errno = SUCCESS; |
3340 | 0 | m_iCallTimeouts = millitime(); |
3341 | 0 | m_iSelectWait = 100000; // Default of 100 milliseconds |
3342 | 0 | m_iBytesRead = 0; |
3343 | 0 | m_iBytesWritten = 0; |
3344 | 0 | } |
3345 | | |
3346 | | CSocketManager::~CSocketManager() |
3347 | 0 | { |
3348 | 0 | clear(); |
3349 | 0 | } |
3350 | | |
3351 | | void CSocketManager::clear() |
3352 | 0 | { |
3353 | 0 | while( !this->empty() ) |
3354 | 0 | DelSock( 0 ); |
3355 | 0 | } |
3356 | | |
3357 | | void CSocketManager::Cleanup() |
3358 | 0 | { |
3359 | 0 | CleanupCrons(); |
3360 | 0 | CleanupFDMonitors(); |
3361 | 0 | clear(); |
3362 | 0 | } |
3363 | | |
3364 | | Csock * CSocketManager::GetSockObj( const CS_STRING & sHostname, uint16_t uPort, int iTimeout ) |
3365 | 0 | { |
3366 | 0 | return( new Csock( sHostname, uPort, iTimeout ) ); |
3367 | 0 | } |
3368 | | |
3369 | | void CSocketManager::Connect( const CSConnection & cCon, Csock * pcSock ) |
3370 | 0 | { |
3371 | | // create the new object |
3372 | 0 | if( !pcSock ) |
3373 | 0 | pcSock = GetSockObj( cCon.GetHostname(), cCon.GetPort(), cCon.GetTimeout() ); |
3374 | 0 | else |
3375 | 0 | { |
3376 | 0 | pcSock->SetHostName( cCon.GetHostname() ); |
3377 | 0 | pcSock->SetPort( cCon.GetPort() ); |
3378 | 0 | pcSock->SetTimeout( cCon.GetTimeout() ); |
3379 | 0 | } |
3380 | |
|
3381 | 0 | if( cCon.GetAFRequire() != CSSockAddr::RAF_ANY ) |
3382 | 0 | pcSock->SetAFRequire( cCon.GetAFRequire() ); |
3383 | | |
3384 | | // bind the vhost |
3385 | 0 | pcSock->SetBindHost( cCon.GetBindHost() ); |
3386 | |
|
3387 | | #ifdef HAVE_LIBSSL |
3388 | | pcSock->SetSSL( cCon.GetIsSSL() ); |
3389 | | if( cCon.GetIsSSL() ) |
3390 | | { |
3391 | | if( !cCon.GetPemLocation().empty() ) |
3392 | | { |
3393 | | pcSock->SetDHParamLocation( cCon.GetDHParamLocation() ); |
3394 | | pcSock->SetKeyLocation( cCon.GetKeyLocation() ); |
3395 | | pcSock->SetPemLocation( cCon.GetPemLocation() ); |
3396 | | pcSock->SetPemPass( cCon.GetPemPass() ); |
3397 | | } |
3398 | | if( !cCon.GetCipher().empty() ) |
3399 | | pcSock->SetCipher( cCon.GetCipher() ); |
3400 | | } |
3401 | | #endif /* HAVE_LIBSSL */ |
3402 | |
|
3403 | 0 | pcSock->SetType( Csock::OUTBOUND ); |
3404 | |
|
3405 | 0 | pcSock->SetConState( Csock::CST_START ); |
3406 | 0 | AddSock( pcSock, cCon.GetSockName() ); |
3407 | 0 | } |
3408 | | |
3409 | | bool CSocketManager::Listen( const CSListener & cListen, Csock * pcSock, uint16_t * piRandPort ) |
3410 | 0 | { |
3411 | 0 | if( !pcSock ) |
3412 | 0 | pcSock = GetSockObj( "", 0 ); |
3413 | |
|
3414 | 0 | if( cListen.GetAFRequire() != CSSockAddr::RAF_ANY ) |
3415 | 0 | { |
3416 | 0 | pcSock->SetAFRequire( cListen.GetAFRequire() ); |
3417 | | #ifdef HAVE_IPV6 |
3418 | | if( cListen.GetAFRequire() == CSSockAddr::RAF_INET6 ) |
3419 | | pcSock->SetIPv6( true ); |
3420 | | #endif /* HAVE_IPV6 */ |
3421 | 0 | } |
3422 | | #ifdef HAVE_IPV6 |
3423 | | else |
3424 | | { |
3425 | | pcSock->SetIPv6( true ); |
3426 | | } |
3427 | | #endif /* HAVE_IPV6 */ |
3428 | | #ifdef HAVE_LIBSSL |
3429 | | pcSock->SetSSL( cListen.GetIsSSL() ); |
3430 | | if( cListen.GetIsSSL() && !cListen.GetPemLocation().empty() ) |
3431 | | { |
3432 | | pcSock->SetDHParamLocation( cListen.GetDHParamLocation() ); |
3433 | | pcSock->SetKeyLocation( cListen.GetKeyLocation() ); |
3434 | | pcSock->SetPemLocation( cListen.GetPemLocation() ); |
3435 | | pcSock->SetPemPass( cListen.GetPemPass() ); |
3436 | | pcSock->SetCipher( cListen.GetCipher() ); |
3437 | | pcSock->SetRequireClientCertFlags( cListen.GetRequireClientCertFlags() ); |
3438 | | } |
3439 | | #endif /* HAVE_LIBSSL */ |
3440 | |
|
3441 | 0 | if( piRandPort ) |
3442 | 0 | *piRandPort = 0; |
3443 | |
|
3444 | 0 | bool bDetach = ( cListen.GetDetach() && !piRandPort ); // can't detach if we're waiting for the port to come up right now |
3445 | 0 | if( pcSock->Listen( cListen.GetPort(), cListen.GetMaxConns(), cListen.GetBindHost(), cListen.GetTimeout(), bDetach ) ) |
3446 | 0 | { |
3447 | 0 | AddSock( pcSock, cListen.GetSockName() ); |
3448 | 0 | if( piRandPort && cListen.GetPort() == 0 ) |
3449 | 0 | { |
3450 | 0 | cs_sock_t iSock = pcSock->GetSock(); |
3451 | |
|
3452 | 0 | if( iSock == CS_INVALID_SOCK ) |
3453 | 0 | { |
3454 | 0 | CS_DEBUG( "Failed to attain a valid file descriptor" ); |
3455 | 0 | pcSock->Close(); |
3456 | 0 | return( false ); |
3457 | 0 | } |
3458 | 0 | struct sockaddr_in mLocalAddr; |
3459 | 0 | socklen_t mLocalLen = sizeof( mLocalAddr ); |
3460 | 0 | getsockname( iSock, ( struct sockaddr * ) &mLocalAddr, &mLocalLen ); |
3461 | 0 | *piRandPort = ntohs( mLocalAddr.sin_port ); |
3462 | 0 | } |
3463 | 0 | return( true ); |
3464 | 0 | } |
3465 | | |
3466 | 0 | CS_Delete( pcSock ); |
3467 | 0 | return( false ); |
3468 | 0 | } |
3469 | | |
3470 | | |
3471 | | bool CSocketManager::HasFDs() const |
3472 | 0 | { |
3473 | 0 | return( !this->empty() || !m_vcMonitorFD.empty() ); |
3474 | 0 | } |
3475 | | |
3476 | | void CSocketManager::Loop() |
3477 | 0 | { |
3478 | 0 | for( size_t a = 0; a < this->size(); ++a ) |
3479 | 0 | { |
3480 | 0 | Csock * pcSock = this->at( a ); |
3481 | 0 | if( pcSock->GetType() != Csock::OUTBOUND || pcSock->GetConState() == Csock::CST_OK ) |
3482 | 0 | continue; |
3483 | 0 | if( pcSock->GetConState() == Csock::CST_DNS ) |
3484 | 0 | { |
3485 | 0 | if( pcSock->DNSLookup( Csock::DNS_VHOST ) == ETIMEDOUT ) |
3486 | 0 | { |
3487 | 0 | pcSock->CallSockError( EDOM, "DNS Lookup for bind host failed" ); |
3488 | 0 | DelSock( a-- ); |
3489 | 0 | continue; |
3490 | 0 | } |
3491 | 0 | } |
3492 | | |
3493 | 0 | if( pcSock->GetConState() == Csock::CST_BINDVHOST ) |
3494 | 0 | { |
3495 | 0 | if( !pcSock->SetupVHost() ) |
3496 | 0 | { |
3497 | 0 | pcSock->CallSockError( GetSockError(), "Failed to setup bind host" ); |
3498 | 0 | DelSock( a-- ); |
3499 | 0 | continue; |
3500 | 0 | } |
3501 | 0 | } |
3502 | | |
3503 | 0 | if( pcSock->GetConState() == Csock::CST_DESTDNS ) |
3504 | 0 | { |
3505 | 0 | if( pcSock->DNSLookup( Csock::DNS_DEST ) == ETIMEDOUT ) |
3506 | 0 | { |
3507 | 0 | pcSock->CallSockError( EADDRNOTAVAIL, "Unable to resolve requested address" ); |
3508 | 0 | DelSock( a-- ); |
3509 | 0 | continue; |
3510 | 0 | } |
3511 | 0 | } |
3512 | 0 | if( pcSock->GetConState() == Csock::CST_CONNECT ) |
3513 | 0 | { |
3514 | 0 | if( !pcSock->Connect() ) |
3515 | 0 | { |
3516 | 0 | if( GetSockError() == ECONNREFUSED ) |
3517 | 0 | pcSock->ConnectionRefused(); |
3518 | 0 | else |
3519 | 0 | pcSock->CallSockError( GetSockError() ); |
3520 | |
|
3521 | 0 | DelSock( a-- ); |
3522 | 0 | continue; |
3523 | 0 | } |
3524 | 0 | } |
3525 | | #ifdef HAVE_LIBSSL |
3526 | | if( pcSock->GetConState() == Csock::CST_CONNECTSSL ) |
3527 | | { |
3528 | | if( pcSock->GetSSL() ) |
3529 | | { |
3530 | | if( !pcSock->ConnectSSL() ) |
3531 | | { |
3532 | | if( GetSockError() == ECONNREFUSED ) |
3533 | | pcSock->ConnectionRefused(); |
3534 | | else |
3535 | | pcSock->CallSockError( GetSockError() == 0 ? ECONNABORTED : GetSockError() ); |
3536 | | |
3537 | | DelSock( a-- ); |
3538 | | continue; |
3539 | | } |
3540 | | } |
3541 | | } |
3542 | | #endif /* HAVE_LIBSSL */ |
3543 | 0 | } |
3544 | |
|
3545 | 0 | std::map<Csock *, EMessages> mpeSocks; |
3546 | 0 | Select( mpeSocks ); |
3547 | |
|
3548 | 0 | switch( m_errno ) |
3549 | 0 | { |
3550 | 0 | case SUCCESS: |
3551 | 0 | { |
3552 | 0 | for( std::map<Csock *, EMessages>::iterator itSock = mpeSocks.begin(); itSock != mpeSocks.end(); itSock++ ) |
3553 | 0 | { |
3554 | 0 | Csock * pcSock = itSock->first; |
3555 | 0 | EMessages iErrno = itSock->second; |
3556 | |
|
3557 | 0 | if( iErrno == SUCCESS ) |
3558 | 0 | { |
3559 | | // read in data |
3560 | | // if this is a |
3561 | 0 | int iLen = 0; |
3562 | |
|
3563 | 0 | if( pcSock->GetSSL() ) |
3564 | 0 | iLen = pcSock->GetPending(); |
3565 | |
|
3566 | 0 | if( iLen <= 0 ) |
3567 | 0 | iLen = CS_BLOCKSIZE; |
3568 | |
|
3569 | 0 | CSCharBuffer cBuff( iLen ); |
3570 | |
|
3571 | 0 | cs_ssize_t bytes = pcSock->Read( cBuff(), iLen ); |
3572 | |
|
3573 | 0 | if( bytes != Csock::READ_TIMEDOUT && bytes != Csock::READ_CONNREFUSED && bytes != Csock::READ_ERR && !pcSock->IsConnected() ) |
3574 | 0 | { |
3575 | 0 | pcSock->SetIsConnected( true ); |
3576 | 0 | pcSock->Connected(); |
3577 | 0 | } |
3578 | |
|
3579 | 0 | switch( bytes ) |
3580 | 0 | { |
3581 | 0 | case Csock::READ_EOF: |
3582 | 0 | { |
3583 | 0 | DelSockByAddr( pcSock ); |
3584 | 0 | break; |
3585 | 0 | } |
3586 | | |
3587 | 0 | case Csock::READ_ERR: |
3588 | 0 | { |
3589 | 0 | bool bHandled = false; |
3590 | | #ifdef HAVE_LIBSSL |
3591 | | if( pcSock->GetSSL() ) |
3592 | | { |
3593 | | unsigned long iSSLError = ERR_peek_error(); |
3594 | | if( iSSLError ) |
3595 | | { |
3596 | | char szError[512]; |
3597 | | memset( ( char * ) szError, '\0', 512 ); |
3598 | | ERR_error_string_n( iSSLError, szError, 511 ); |
3599 | | SSLErrors( __FILE__, __LINE__ ); |
3600 | | pcSock->CallSockError( GetSockError(), szError ); |
3601 | | bHandled = true; |
3602 | | } |
3603 | | } |
3604 | | #endif |
3605 | 0 | if( !bHandled ) |
3606 | 0 | pcSock->CallSockError( GetSockError() ); |
3607 | 0 | DelSockByAddr( pcSock ); |
3608 | 0 | break; |
3609 | 0 | } |
3610 | | |
3611 | 0 | case Csock::READ_EAGAIN: |
3612 | 0 | break; |
3613 | | |
3614 | 0 | case Csock::READ_CONNREFUSED: |
3615 | 0 | pcSock->ConnectionRefused(); |
3616 | 0 | DelSockByAddr( pcSock ); |
3617 | 0 | break; |
3618 | | |
3619 | 0 | case Csock::READ_TIMEDOUT: |
3620 | 0 | pcSock->Timeout(); |
3621 | 0 | DelSockByAddr( pcSock ); |
3622 | 0 | break; |
3623 | | |
3624 | 0 | default: |
3625 | 0 | { |
3626 | 0 | if( Csock::TMO_READ & pcSock->GetTimeoutType() ) |
3627 | 0 | pcSock->ResetTimer(); // reset the timeout timer |
3628 | |
|
3629 | 0 | pcSock->ReadData( cBuff(), bytes ); // Call ReadData() before PushBuff() so that it is called before the ReadLine() event - LD 07/18/05 |
3630 | 0 | pcSock->PushBuff( cBuff(), bytes ); |
3631 | 0 | break; |
3632 | 0 | } |
3633 | 0 | } |
3634 | 0 | } |
3635 | 0 | else if( iErrno == SELECT_ERROR ) |
3636 | 0 | { |
3637 | | // a socket came back with an error |
3638 | | // usually means it was closed |
3639 | 0 | DelSockByAddr( pcSock ); |
3640 | 0 | } |
3641 | 0 | } |
3642 | 0 | break; |
3643 | 0 | } |
3644 | | |
3645 | 0 | case SELECT_TIMEOUT: |
3646 | 0 | case SELECT_TRYAGAIN: |
3647 | 0 | case SELECT_ERROR: |
3648 | 0 | default : |
3649 | 0 | break; |
3650 | 0 | } |
3651 | | |
3652 | 0 | uint64_t iMilliNow = millitime(); |
3653 | 0 | if( ( iMilliNow - m_iCallTimeouts ) >= 1000 ) |
3654 | 0 | { |
3655 | 0 | m_iCallTimeouts = iMilliNow; |
3656 | | // call timeout on all the sockets that recieved no data |
3657 | 0 | for( size_t i = 0; i < this->size(); ++i ) |
3658 | 0 | { |
3659 | 0 | if( this->at( i )->GetConState() != Csock::CST_OK ) |
3660 | 0 | continue; |
3661 | | |
3662 | 0 | if( this->at( i )->CheckTimeout( ( time_t )( iMilliNow / 1000 ) ) ) |
3663 | 0 | DelSock( i-- ); |
3664 | 0 | } |
3665 | 0 | } |
3666 | | // run any Manager Crons we may have |
3667 | 0 | Cron(); |
3668 | 0 | } |
3669 | | |
3670 | | void CSocketManager::DynamicSelectLoop( uint64_t iLowerBounds, uint64_t iUpperBounds, time_t iMaxResolution ) |
3671 | 0 | { |
3672 | 0 | SetSelectTimeout( iLowerBounds ); |
3673 | 0 | if( m_errno == SELECT_TIMEOUT ) |
3674 | 0 | { |
3675 | | // only do this if the previous call to select was a timeout |
3676 | 0 | timeval tMaxResolution; |
3677 | 0 | timeval tNow; |
3678 | 0 | tMaxResolution.tv_sec = iMaxResolution; |
3679 | 0 | tMaxResolution.tv_usec = 0; |
3680 | 0 | CS_GETTIMEOFDAY( &tNow, NULL ); |
3681 | 0 | timeval tSelectTimeout = GetDynamicSleepTime( tNow, tMaxResolution ); |
3682 | 0 | uint64_t iSelectTimeout = tSelectTimeout.tv_sec * 1000000 + tSelectTimeout.tv_usec; |
3683 | 0 | iSelectTimeout = std::max( iLowerBounds, iSelectTimeout ); |
3684 | 0 | iSelectTimeout = std::min( iSelectTimeout, iUpperBounds ); |
3685 | 0 | if( iLowerBounds != iSelectTimeout ) |
3686 | 0 | SetSelectTimeout( iSelectTimeout ); |
3687 | 0 | } |
3688 | 0 | Loop(); |
3689 | 0 | } |
3690 | | |
3691 | | void CSocketManager::AddSock( Csock * pcSock, const CS_STRING & sSockName ) |
3692 | 0 | { |
3693 | 0 | pcSock->SetSockName( sSockName ); |
3694 | 0 | this->push_back( pcSock ); |
3695 | 0 | } |
3696 | | |
3697 | | Csock * CSocketManager::FindSockByRemotePort( uint16_t iPort ) |
3698 | 0 | { |
3699 | 0 | for( size_t i = 0; i < this->size(); ++i ) |
3700 | 0 | { |
3701 | 0 | if( this->at( i )->GetRemotePort() == iPort ) |
3702 | 0 | return( this->at( i ) ); |
3703 | 0 | } |
3704 | 0 | return( NULL ); |
3705 | 0 | } |
3706 | | |
3707 | | Csock * CSocketManager::FindSockByLocalPort( uint16_t iPort ) |
3708 | 0 | { |
3709 | 0 | for( size_t i = 0; i < this->size(); ++i ) |
3710 | 0 | { |
3711 | 0 | if( this->at( i )->GetLocalPort() == iPort ) |
3712 | 0 | return( this->at( i ) ); |
3713 | 0 | } |
3714 | 0 | return( NULL ); |
3715 | 0 | } |
3716 | | |
3717 | | Csock * CSocketManager::FindSockByName( const CS_STRING & sName ) |
3718 | 0 | { |
3719 | 0 | std::vector<Csock *>::iterator it; |
3720 | 0 | std::vector<Csock *>::iterator it_end = this->end(); |
3721 | 0 | for( it = this->begin(); it != it_end; it++ ) |
3722 | 0 | { |
3723 | 0 | if( ( *it )->GetSockName() == sName ) |
3724 | 0 | return( *it ); |
3725 | 0 | } |
3726 | 0 | return( NULL ); |
3727 | 0 | } |
3728 | | |
3729 | | Csock * CSocketManager::FindSockByFD( cs_sock_t iFD ) |
3730 | 0 | { |
3731 | 0 | for( size_t i = 0; i < this->size(); ++i ) |
3732 | 0 | { |
3733 | 0 | if( this->at( i )->GetRSock() == iFD || this->at( i )->GetWSock() == iFD ) |
3734 | 0 | return( this->at( i ) ); |
3735 | 0 | } |
3736 | 0 | return( NULL ); |
3737 | 0 | } |
3738 | | |
3739 | | std::vector<Csock *> CSocketManager::FindSocksByName( const CS_STRING & sName ) |
3740 | 0 | { |
3741 | 0 | std::vector<Csock *> vpSocks; |
3742 | 0 | for( size_t i = 0; i < this->size(); ++i ) |
3743 | 0 | { |
3744 | 0 | if( this->at( i )->GetSockName() == sName ) |
3745 | 0 | vpSocks.push_back( this->at( i ) ); |
3746 | 0 | } |
3747 | 0 | return( vpSocks ); |
3748 | 0 | } |
3749 | | |
3750 | | std::vector<Csock *> CSocketManager::FindSocksByRemoteHost( const CS_STRING & sHostname ) |
3751 | 0 | { |
3752 | 0 | std::vector<Csock *> vpSocks; |
3753 | 0 | for( size_t i = 0; i < this->size(); ++i ) |
3754 | 0 | { |
3755 | 0 | if( this->at( i )->GetHostName() == sHostname ) |
3756 | 0 | vpSocks.push_back( this->at( i ) ); |
3757 | 0 | } |
3758 | 0 | return( vpSocks ); |
3759 | 0 | } |
3760 | | |
3761 | | void CSocketManager::DelSockByAddr( Csock * pcSock ) |
3762 | 0 | { |
3763 | 0 | for( size_t a = 0; a < this->size(); ++a ) |
3764 | 0 | { |
3765 | 0 | if( pcSock == this->at( a ) ) |
3766 | 0 | { |
3767 | 0 | DelSock( a ); |
3768 | 0 | return; |
3769 | 0 | } |
3770 | 0 | } |
3771 | 0 | } |
3772 | | |
3773 | | void CSocketManager::DelSock( size_t iPos ) |
3774 | 0 | { |
3775 | 0 | if( iPos >= this->size() ) |
3776 | 0 | { |
3777 | 0 | CS_DEBUG( "Invalid Sock Position Requested! [" << iPos << "]" ); |
3778 | 0 | return; |
3779 | 0 | } |
3780 | | |
3781 | 0 | Csock * pSock = this->at( iPos ); |
3782 | |
|
3783 | 0 | if( pSock->GetCloseType() != Csock::CLT_DEREFERENCE ) |
3784 | 0 | { |
3785 | 0 | if( pSock->IsConnected() ) |
3786 | 0 | { |
3787 | 0 | pSock->SetIsConnected( false ); |
3788 | 0 | pSock->Disconnected(); // only call disconnected event if connected event was called (IE IsConnected was set) |
3789 | 0 | } |
3790 | |
|
3791 | 0 | m_iBytesRead += pSock->GetBytesRead(); |
3792 | 0 | m_iBytesWritten += pSock->GetBytesWritten(); |
3793 | 0 | } |
3794 | |
|
3795 | 0 | CS_Delete( pSock ); |
3796 | 0 | this->erase( this->begin() + iPos ); |
3797 | 0 | } |
3798 | | |
3799 | | bool CSocketManager::SwapSockByIdx( Csock * pNewSock, size_t iOrginalSockIdx ) |
3800 | 0 | { |
3801 | 0 | if( iOrginalSockIdx >= this->size() ) |
3802 | 0 | { |
3803 | 0 | CS_DEBUG( "Invalid Sock Position Requested! [" << iOrginalSockIdx << "]" ); |
3804 | 0 | return( false ); |
3805 | 0 | } |
3806 | | |
3807 | 0 | Csock * pSock = this->at( iOrginalSockIdx ); |
3808 | 0 | pNewSock->Copy( *pSock ); |
3809 | 0 | pSock->Dereference(); |
3810 | 0 | this->at( iOrginalSockIdx ) = ( Csock * )pNewSock; |
3811 | 0 | this->push_back( ( Csock * )pSock ); // this allows it to get cleaned up |
3812 | 0 | return( true ); |
3813 | 0 | } |
3814 | | |
3815 | | bool CSocketManager::SwapSockByAddr( Csock * pNewSock, Csock * pOrigSock ) |
3816 | 0 | { |
3817 | 0 | for( size_t a = 0; a < this->size(); ++a ) |
3818 | 0 | { |
3819 | 0 | if( this->at( a ) == pOrigSock ) |
3820 | 0 | return( SwapSockByIdx( pNewSock, a ) ); |
3821 | 0 | } |
3822 | 0 | return( false ); |
3823 | 0 | } |
3824 | | |
3825 | | uint64_t CSocketManager::GetBytesRead() const |
3826 | 0 | { |
3827 | | // Start with the total bytes read from destroyed sockets |
3828 | 0 | uint64_t iRet = m_iBytesRead; |
3829 | | |
3830 | | // Add in the outstanding bytes read from active sockets |
3831 | 0 | for( size_t a = 0; a < this->size(); ++a ) |
3832 | 0 | iRet += this->at( a )->GetBytesRead(); |
3833 | |
|
3834 | 0 | return( iRet ); |
3835 | 0 | } |
3836 | | |
3837 | | uint64_t CSocketManager::GetBytesWritten() const |
3838 | 0 | { |
3839 | | // Start with the total bytes written to destroyed sockets |
3840 | 0 | uint64_t iRet = m_iBytesWritten; |
3841 | | |
3842 | | // Add in the outstanding bytes written to active sockets |
3843 | 0 | for( size_t a = 0; a < this->size(); ++a ) |
3844 | 0 | iRet += this->at( a )->GetBytesWritten(); |
3845 | |
|
3846 | 0 | return( iRet ); |
3847 | 0 | } |
3848 | | |
3849 | | void CSocketManager::FDSetCheck( cs_sock_t iFd, std::map< cs_sock_t, short > & miiReadyFds, ECheckType eType ) |
3850 | 0 | { |
3851 | 0 | std::map< cs_sock_t, short >::iterator it = miiReadyFds.find( iFd ); |
3852 | 0 | if( it != miiReadyFds.end() ) |
3853 | 0 | it->second = ( short )( it->second | eType ); // TODO need to figure out why |= throws 'short int' from 'int' may alter its value |
3854 | 0 | else |
3855 | 0 | miiReadyFds[iFd] = eType; |
3856 | 0 | } |
3857 | | |
3858 | | bool CSocketManager::FDHasCheck( cs_sock_t iFd, std::map< cs_sock_t, short > & miiReadyFds, ECheckType eType ) |
3859 | 0 | { |
3860 | 0 | std::map< cs_sock_t, short >::iterator it = miiReadyFds.find( iFd ); |
3861 | 0 | if( it != miiReadyFds.end() ) |
3862 | 0 | return( ( it->second & eType ) != 0 ); |
3863 | 0 | return( false ); |
3864 | 0 | } |
3865 | | |
3866 | | int CSocketManager::Select( std::map< cs_sock_t, short > & miiReadyFds, struct timeval *tvtimeout ) |
3867 | 0 | { |
3868 | 0 | AssignFDs( miiReadyFds, tvtimeout ); |
3869 | 0 | #ifdef CSOCK_USE_POLL |
3870 | 0 | if( miiReadyFds.empty() ) |
3871 | 0 | return( select( 0, NULL, NULL, NULL, tvtimeout ) ); |
3872 | | |
3873 | 0 | struct pollfd * pFDs = ( struct pollfd * )malloc( sizeof( struct pollfd ) * miiReadyFds.size() ); |
3874 | 0 | size_t uCurrPoll = 0; |
3875 | 0 | for( std::map< cs_sock_t, short >::iterator it = miiReadyFds.begin(); it != miiReadyFds.end(); ++it, ++uCurrPoll ) |
3876 | 0 | { |
3877 | 0 | short iEvents = 0; |
3878 | 0 | if( it->second & ECT_Read ) |
3879 | 0 | iEvents |= POLLIN; |
3880 | 0 | if( it->second & ECT_Write ) |
3881 | 0 | iEvents |= POLLOUT; |
3882 | 0 | pFDs[uCurrPoll].fd = it->first; |
3883 | 0 | pFDs[uCurrPoll].events = iEvents; |
3884 | 0 | pFDs[uCurrPoll].revents = 0; |
3885 | 0 | } |
3886 | 0 | int iTimeout = ( int )( tvtimeout->tv_usec / 1000 ); |
3887 | 0 | iTimeout += ( int )( tvtimeout->tv_sec * 1000 ); |
3888 | 0 | size_t uMaxFD = miiReadyFds.size(); |
3889 | 0 | int iRet = poll( pFDs, uMaxFD, iTimeout ); |
3890 | 0 | miiReadyFds.clear(); |
3891 | 0 | for( uCurrPoll = 0; uCurrPoll < uMaxFD; ++uCurrPoll ) |
3892 | 0 | { |
3893 | 0 | short iEvents = 0; |
3894 | 0 | if( pFDs[uCurrPoll].revents & ( POLLIN|POLLERR|POLLHUP|POLLNVAL ) ) |
3895 | 0 | iEvents |= ECT_Read; |
3896 | 0 | if( pFDs[uCurrPoll].revents & POLLOUT ) |
3897 | 0 | iEvents |= ECT_Write; |
3898 | 0 | std::map< cs_sock_t, short >::iterator it = miiReadyFds.find( pFDs[uCurrPoll].fd ); |
3899 | 0 | if( it != miiReadyFds.end() ) |
3900 | 0 | it->second = ( short )( it->second | iEvents ); // TODO need to figure out why |= throws 'short int' from 'int' may alter its value |
3901 | 0 | else |
3902 | 0 | miiReadyFds[pFDs[uCurrPoll].fd] = iEvents; |
3903 | 0 | } |
3904 | 0 | free( pFDs ); |
3905 | | #else |
3906 | | fd_set rfds, wfds; |
3907 | | TFD_ZERO( &rfds ); |
3908 | | TFD_ZERO( &wfds ); |
3909 | | bool bHasWrite = false; |
3910 | | int iHighestFD = 0; |
3911 | | for( std::map< cs_sock_t, short >::iterator it = miiReadyFds.begin(); it != miiReadyFds.end(); ++it ) |
3912 | | { |
3913 | | #ifndef _WIN32 |
3914 | | // the first argument to select() is not used on Win32. |
3915 | | iHighestFD = std::max( it->first, iHighestFD ); |
3916 | | #endif /* _WIN32 */ |
3917 | | if( it->second & ECT_Read ) |
3918 | | { |
3919 | | TFD_SET( it->first, &rfds ); |
3920 | | } |
3921 | | if( it->second & ECT_Write ) |
3922 | | { |
3923 | | bHasWrite = true; |
3924 | | TFD_SET( it->first, &wfds ); |
3925 | | } |
3926 | | } |
3927 | | |
3928 | | int iRet = select( iHighestFD + 1, &rfds, ( bHasWrite ? &wfds : NULL ), NULL, tvtimeout ); |
3929 | | if( iRet <= 0 ) |
3930 | | { |
3931 | | miiReadyFds.clear(); |
3932 | | } |
3933 | | else |
3934 | | { |
3935 | | for( std::map< cs_sock_t, short >::iterator it = miiReadyFds.begin(); it != miiReadyFds.end(); ++it ) |
3936 | | { |
3937 | | if( ( it->second & ECT_Read ) && !TFD_ISSET( it->first, &rfds ) ) |
3938 | | it->second &= ~ECT_Read; |
3939 | | if( ( it->second & ECT_Write ) && !TFD_ISSET( it->first, &wfds ) ) |
3940 | | it->second &= ~ECT_Write; |
3941 | | } |
3942 | | } |
3943 | | #endif /* CSOCK_USE_POLL */ |
3944 | |
|
3945 | 0 | return( iRet ); |
3946 | 0 | } |
3947 | | |
3948 | | void CSocketManager::Select( std::map<Csock *, EMessages> & mpeSocks ) |
3949 | 0 | { |
3950 | 0 | mpeSocks.clear(); |
3951 | 0 | struct timeval tv; |
3952 | |
|
3953 | 0 | std::map< cs_sock_t, short > miiReadyFds; |
3954 | 0 | tv.tv_sec = ( time_t )( m_iSelectWait / 1000000 ); |
3955 | 0 | tv.tv_usec = ( time_t )( m_iSelectWait % 1000000 ); |
3956 | 0 | u_int iQuickReset = 1000; |
3957 | 0 | if( m_iSelectWait == 0 ) |
3958 | 0 | iQuickReset = 0; |
3959 | |
|
3960 | 0 | bool bHasAvailSocks = false; |
3961 | 0 | uint64_t iNOW = 0; |
3962 | 0 | for( size_t i = 0; i < this->size(); ++i ) |
3963 | 0 | { |
3964 | 0 | Csock * pcSock = this->at( i ); |
3965 | |
|
3966 | 0 | Csock::ECloseType eCloseType = pcSock->GetCloseType(); |
3967 | |
|
3968 | 0 | if( eCloseType == Csock::CLT_NOW || eCloseType == Csock::CLT_DEREFERENCE || ( eCloseType == Csock::CLT_AFTERWRITE && !pcSock->HasWriteBuffer() ) ) |
3969 | 0 | { |
3970 | 0 | DelSock( i-- ); // close any socks that have requested it |
3971 | 0 | continue; |
3972 | 0 | } |
3973 | 0 | else |
3974 | 0 | { |
3975 | 0 | pcSock->Cron(); // call the Cron handler here |
3976 | 0 | } |
3977 | | |
3978 | 0 | cs_sock_t & iRSock = pcSock->GetRSock(); |
3979 | 0 | cs_sock_t & iWSock = pcSock->GetWSock(); |
3980 | | #if !defined(CSOCK_USE_POLL) && !defined(_WIN32) |
3981 | | if( iRSock > ( cs_sock_t )FD_SETSIZE || iWSock > ( cs_sock_t )FD_SETSIZE ) |
3982 | | { |
3983 | | CS_DEBUG( "FD is larger than select() can handle" ); |
3984 | | DelSock( i-- ); |
3985 | | continue; |
3986 | | } |
3987 | | #endif /* CSOCK_USE_POLL */ |
3988 | |
|
3989 | | #ifdef HAVE_C_ARES |
3990 | | ares_channel pChannel = pcSock->GetAresChannel(); |
3991 | | if( pChannel ) |
3992 | | { |
3993 | | ares_socket_t aiAresSocks[1]; |
3994 | | aiAresSocks[0] = ARES_SOCKET_BAD; |
3995 | | int iSockMask = ares_getsock( pChannel, aiAresSocks, 1 ); |
3996 | | if( ARES_GETSOCK_READABLE( iSockMask, 0 ) ) |
3997 | | FDSetCheck( aiAresSocks[0], miiReadyFds, ECT_Read ); |
3998 | | if( ARES_GETSOCK_WRITABLE( iSockMask, 0 ) ) |
3999 | | FDSetCheck( aiAresSocks[0], miiReadyFds, ECT_Write ); |
4000 | | // let ares drop the timeout if it has something timing out sooner then whats in tv currently |
4001 | | ares_timeout( pChannel, &tv, &tv ); |
4002 | | } |
4003 | | #endif /* HAVE_C_ARES */ |
4004 | 0 | if( pcSock->GetType() == Csock::LISTENER && pcSock->GetConState() == Csock::CST_BINDVHOST ) |
4005 | 0 | { |
4006 | 0 | if( !pcSock->Listen( pcSock->GetPort(), pcSock->GetMaxConns(), pcSock->GetBindHost(), pcSock->GetTimeout(), true ) ) |
4007 | 0 | { |
4008 | 0 | pcSock->Close(); |
4009 | 0 | DelSock( i-- ); |
4010 | 0 | } |
4011 | 0 | continue; |
4012 | 0 | } |
4013 | | |
4014 | 0 | pcSock->AssignFDs( miiReadyFds, &tv ); |
4015 | |
|
4016 | 0 | if( pcSock->GetConState() != Csock::CST_OK && pcSock->GetConState()!=Csock::CST_CONNECTWAIT) |
4017 | 0 | continue; |
4018 | | |
4019 | 0 | bHasAvailSocks = true; |
4020 | |
|
4021 | 0 | bool bIsReadPaused = pcSock->IsReadPaused(); |
4022 | 0 | if( bIsReadPaused ) |
4023 | 0 | { |
4024 | 0 | pcSock->ReadPaused(); |
4025 | 0 | bIsReadPaused = pcSock->IsReadPaused(); // re-read it again, incase it changed status) |
4026 | 0 | } |
4027 | 0 | if( iRSock == CS_INVALID_SOCK || iWSock == CS_INVALID_SOCK ) |
4028 | 0 | { |
4029 | 0 | SelectSock( mpeSocks, SUCCESS, pcSock ); |
4030 | 0 | continue; // invalid sock fd |
4031 | 0 | } |
4032 | | |
4033 | 0 | if( pcSock->GetType() != Csock::LISTENER ) |
4034 | 0 | { |
4035 | 0 | bool bHasWriteBuffer = pcSock->HasWriteBuffer(); |
4036 | |
|
4037 | 0 | if( !bIsReadPaused ) |
4038 | 0 | FDSetCheck( iRSock, miiReadyFds, ECT_Read ); |
4039 | |
|
4040 | 0 | if( pcSock->AllowWrite( iNOW ) && ( !pcSock->IsConnected() || bHasWriteBuffer ) ) |
4041 | 0 | { |
4042 | 0 | if( !pcSock->IsConnected() ) |
4043 | 0 | { |
4044 | | // set the write bit if not connected yet |
4045 | 0 | FDSetCheck( iWSock, miiReadyFds, ECT_Write ); |
4046 | 0 | } |
4047 | 0 | else if( bHasWriteBuffer && !pcSock->GetSSL() ) |
4048 | 0 | { |
4049 | | // always set the write bit if there is data to send when NOT ssl |
4050 | 0 | FDSetCheck( iWSock, miiReadyFds, ECT_Write ); |
4051 | 0 | } |
4052 | 0 | else if( bHasWriteBuffer && pcSock->GetSSL() && pcSock->SslIsEstablished() ) |
4053 | 0 | { |
4054 | | // ONLY set the write bit if there is data to send and the SSL handshake is finished |
4055 | 0 | FDSetCheck( iWSock, miiReadyFds, ECT_Write ); |
4056 | 0 | } |
4057 | 0 | } |
4058 | |
|
4059 | 0 | if( pcSock->GetSSL() && !pcSock->SslIsEstablished() && bHasWriteBuffer ) |
4060 | 0 | { |
4061 | | // if this is an unestabled SSL session with data to send ... try sending it |
4062 | | // do this here, cause otherwise ssl will cause a small |
4063 | | // cpu spike waiting for the handshake to finish |
4064 | | // resend this data |
4065 | 0 | if( !pcSock->Write( "" ) ) |
4066 | 0 | { |
4067 | 0 | pcSock->Close(); |
4068 | 0 | } |
4069 | | // warning ... setting write bit in here causes massive CPU spinning on invalid SSL servers |
4070 | | // http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=631590 |
4071 | | // however, we can set the select WAY down and it will retry quickly, but keep it from spinning at 100% |
4072 | 0 | tv.tv_usec = iQuickReset; |
4073 | 0 | tv.tv_sec = 0; |
4074 | 0 | } |
4075 | 0 | } |
4076 | 0 | else |
4077 | 0 | { |
4078 | 0 | FDSetCheck( iRSock, miiReadyFds, ECT_Read ); |
4079 | 0 | } |
4080 | |
|
4081 | 0 | if( pcSock->GetSSL() && pcSock->GetType() != Csock::LISTENER ) |
4082 | 0 | { |
4083 | 0 | if( pcSock->GetPending() > 0 && !pcSock->IsReadPaused() ) |
4084 | 0 | SelectSock( mpeSocks, SUCCESS, pcSock ); |
4085 | 0 | } |
4086 | 0 | } |
4087 | | |
4088 | | // old fashion select, go fer it |
4089 | 0 | int iSel; |
4090 | |
|
4091 | 0 | if( !mpeSocks.empty() ) // .1 ms pause to see if anything else is ready (IE if there is SSL data pending, don't wait too long) |
4092 | 0 | { |
4093 | 0 | tv.tv_usec = iQuickReset; |
4094 | 0 | tv.tv_sec = 0; |
4095 | 0 | } |
4096 | 0 | else if( !this->empty() && !bHasAvailSocks ) |
4097 | 0 | { |
4098 | 0 | tv.tv_usec = iQuickReset; |
4099 | 0 | tv.tv_sec = 0; |
4100 | 0 | } |
4101 | |
|
4102 | 0 | iSel = Select( miiReadyFds, &tv ); |
4103 | |
|
4104 | 0 | if( iSel == 0 ) |
4105 | 0 | { |
4106 | 0 | if( mpeSocks.empty() ) |
4107 | 0 | m_errno = SELECT_TIMEOUT; |
4108 | 0 | else |
4109 | 0 | m_errno = SUCCESS; |
4110 | | #ifdef HAVE_C_ARES |
4111 | | // run through ares channels and process timeouts |
4112 | | for( size_t uSock = 0; uSock < this->size(); ++uSock ) |
4113 | | { |
4114 | | Csock * pcSock = this->at( uSock ); |
4115 | | ares_channel pChannel = pcSock->GetAresChannel(); |
4116 | | if( pChannel ) |
4117 | | ares_process_fd( pChannel, ARES_SOCKET_BAD, ARES_SOCKET_BAD ); |
4118 | | } |
4119 | | #endif /* HAVE_C_ARES */ |
4120 | |
|
4121 | 0 | return; |
4122 | 0 | } |
4123 | | |
4124 | 0 | if( iSel == -1 && errno == EINTR ) |
4125 | 0 | { |
4126 | 0 | if( mpeSocks.empty() ) |
4127 | 0 | m_errno = SELECT_TRYAGAIN; |
4128 | 0 | else |
4129 | 0 | m_errno = SUCCESS; |
4130 | |
|
4131 | 0 | return; |
4132 | 0 | } |
4133 | 0 | else if( iSel == -1 ) |
4134 | 0 | { |
4135 | 0 | if( mpeSocks.empty() ) |
4136 | 0 | m_errno = SELECT_ERROR; |
4137 | 0 | else |
4138 | 0 | m_errno = SUCCESS; |
4139 | |
|
4140 | 0 | return; |
4141 | 0 | } |
4142 | 0 | else |
4143 | 0 | { |
4144 | 0 | m_errno = SUCCESS; |
4145 | 0 | } |
4146 | | |
4147 | 0 | CheckFDs( miiReadyFds ); |
4148 | | |
4149 | | // find out wich one is ready |
4150 | 0 | for( size_t i = 0; i < this->size(); ++i ) |
4151 | 0 | { |
4152 | 0 | Csock * pcSock = this->at( i ); |
4153 | |
|
4154 | | #ifdef HAVE_C_ARES |
4155 | | ares_channel pChannel = pcSock->GetAresChannel(); |
4156 | | if( pChannel ) |
4157 | | { |
4158 | | ares_socket_t aiAresSocks[1]; |
4159 | | aiAresSocks[0] = ARES_SOCKET_BAD; |
4160 | | ares_getsock( pChannel, aiAresSocks, 1 ); |
4161 | | if( FDHasCheck( aiAresSocks[0], miiReadyFds, ECT_Read ) || FDHasCheck( aiAresSocks[0], miiReadyFds, ECT_Write ) ) |
4162 | | ares_process_fd( pChannel, aiAresSocks[0], aiAresSocks[0] ); |
4163 | | } |
4164 | | #endif /* HAVE_C_ARES */ |
4165 | 0 | pcSock->CheckFDs( miiReadyFds ); |
4166 | |
|
4167 | 0 | if( pcSock->GetConState() != Csock::CST_OK && pcSock->GetConState() != Csock::CST_CONNECTWAIT ) |
4168 | 0 | continue; |
4169 | | |
4170 | 0 | cs_sock_t & iRSock = pcSock->GetRSock(); |
4171 | 0 | cs_sock_t & iWSock = pcSock->GetWSock(); |
4172 | 0 | EMessages iErrno = SUCCESS; |
4173 | |
|
4174 | 0 | if( iRSock == CS_INVALID_SOCK || iWSock == CS_INVALID_SOCK ) |
4175 | 0 | { |
4176 | | // trigger a success so it goes through the normal motions |
4177 | | // and an error is produced |
4178 | 0 | SelectSock( mpeSocks, SUCCESS, pcSock ); |
4179 | 0 | continue; // watch for invalid socks |
4180 | 0 | } |
4181 | | |
4182 | 0 | if( FDHasCheck( iWSock, miiReadyFds, ECT_Write ) ) |
4183 | 0 | { |
4184 | 0 | if( iSel > 0 ) |
4185 | 0 | { |
4186 | 0 | iErrno = SUCCESS; |
4187 | 0 | if( pcSock->HasWriteBuffer() && pcSock->IsConnected() ) |
4188 | 0 | { |
4189 | | // write whats in the socks send buffer |
4190 | 0 | if( !pcSock->Write( "" ) ) |
4191 | 0 | { |
4192 | | // write failed, sock died :( |
4193 | 0 | iErrno = SELECT_ERROR; |
4194 | 0 | } |
4195 | 0 | } |
4196 | 0 | } |
4197 | 0 | else |
4198 | 0 | { |
4199 | 0 | iErrno = SELECT_ERROR; |
4200 | 0 | } |
4201 | |
|
4202 | 0 | SelectSock( mpeSocks, iErrno, pcSock ); |
4203 | |
|
4204 | 0 | } |
4205 | 0 | else if( FDHasCheck( iRSock, miiReadyFds, ECT_Read ) ) |
4206 | 0 | { |
4207 | 0 | if( iSel > 0 ) |
4208 | 0 | iErrno = SUCCESS; |
4209 | 0 | else |
4210 | 0 | iErrno = SELECT_ERROR; |
4211 | |
|
4212 | 0 | if( pcSock->GetType() != Csock::LISTENER ) |
4213 | 0 | { |
4214 | 0 | SelectSock( mpeSocks, iErrno, pcSock ); |
4215 | 0 | } |
4216 | 0 | else // someone is coming in! |
4217 | 0 | { |
4218 | 0 | CS_STRING sHost; |
4219 | 0 | uint16_t port; |
4220 | 0 | cs_sock_t inSock = pcSock->Accept( sHost, port ); |
4221 | |
|
4222 | 0 | if( inSock != CS_INVALID_SOCK ) |
4223 | 0 | { |
4224 | 0 | if( Csock::TMO_ACCEPT & pcSock->GetTimeoutType() ) |
4225 | 0 | pcSock->ResetTimer(); // let them now it got dinged |
4226 | | |
4227 | | // if we have a new sock, then add it |
4228 | 0 | Csock * NewpcSock = ( Csock * )pcSock->GetSockObj( sHost, port ); |
4229 | |
|
4230 | 0 | if( !NewpcSock ) |
4231 | 0 | NewpcSock = GetSockObj( sHost, port ); |
4232 | |
|
4233 | 0 | NewpcSock->SetType( Csock::INBOUND ); |
4234 | 0 | NewpcSock->SetRSock( inSock ); |
4235 | 0 | NewpcSock->SetWSock( inSock ); |
4236 | 0 | NewpcSock->SetIPv6( pcSock->GetIPv6() ); |
4237 | |
|
4238 | 0 | bool bAddSock = true; |
4239 | | #ifdef HAVE_LIBSSL |
4240 | | // |
4241 | | // is this ssl ? |
4242 | | if( pcSock->GetSSL() ) |
4243 | | { |
4244 | | NewpcSock->SetCipher( pcSock->GetCipher() ); |
4245 | | NewpcSock->SetDHParamLocation( pcSock->GetDHParamLocation() ); |
4246 | | NewpcSock->SetKeyLocation( pcSock->GetKeyLocation() ); |
4247 | | NewpcSock->SetPemLocation( pcSock->GetPemLocation() ); |
4248 | | NewpcSock->SetPemPass( pcSock->GetPemPass() ); |
4249 | | NewpcSock->SetRequireClientCertFlags( pcSock->GetRequireClientCertFlags() ); |
4250 | | bAddSock = NewpcSock->AcceptSSL(); |
4251 | | } |
4252 | | |
4253 | | #endif /* HAVE_LIBSSL */ |
4254 | 0 | if( bAddSock ) |
4255 | 0 | { |
4256 | | // set the name of the listener |
4257 | 0 | NewpcSock->SetParentSockName( pcSock->GetSockName() ); |
4258 | 0 | NewpcSock->SetRate( pcSock->GetRateBytes(), pcSock->GetRateTime() ); |
4259 | | #ifdef HAVE_ICU |
4260 | | NewpcSock->SetEncoding( pcSock->GetEncoding() ); |
4261 | | #endif |
4262 | 0 | if( NewpcSock->GetSockName().empty() ) |
4263 | 0 | { |
4264 | 0 | std::stringstream s; |
4265 | 0 | s << sHost << ":" << port; |
4266 | 0 | AddSock( NewpcSock, s.str() ); |
4267 | 0 | } |
4268 | 0 | else |
4269 | 0 | { |
4270 | 0 | AddSock( NewpcSock, NewpcSock->GetSockName() ); |
4271 | 0 | } |
4272 | 0 | } |
4273 | 0 | else |
4274 | 0 | { |
4275 | 0 | CS_Delete( NewpcSock ); |
4276 | 0 | } |
4277 | 0 | } |
4278 | | #ifdef _WIN32 |
4279 | | else if( GetSockError() != WSAEWOULDBLOCK ) |
4280 | | #else /* _WIN32 */ |
4281 | 0 | else if( GetSockError() != EAGAIN ) |
4282 | 0 | #endif /* _WIN32 */ |
4283 | 0 | { |
4284 | 0 | pcSock->CallSockError( GetSockError() ); |
4285 | 0 | } |
4286 | 0 | } |
4287 | 0 | } |
4288 | 0 | } |
4289 | 0 | } |
4290 | | |
4291 | | inline void MinimizeTime( timeval& min, const timeval& another ) |
4292 | 0 | { |
4293 | 0 | if( timercmp( &min, &another, > ) ) |
4294 | 0 | { |
4295 | 0 | min = another; |
4296 | 0 | } |
4297 | 0 | } |
4298 | | |
4299 | | timeval CSocketManager::GetDynamicSleepTime( const timeval& tNow, const timeval& tMaxResolution ) const |
4300 | 0 | { |
4301 | 0 | timeval tNextRunTime; |
4302 | 0 | timeradd( &tNow, &tMaxResolution, &tNextRunTime ); |
4303 | 0 | std::vector<Csock *>::const_iterator it; |
4304 | | // This is safe, because we don't modify the vector. |
4305 | 0 | std::vector<Csock *>::const_iterator it_end = this->end(); |
4306 | 0 | for( it = this->begin(); it != it_end; ++it ) |
4307 | 0 | { |
4308 | 0 | Csock* pSock = *it; |
4309 | |
|
4310 | 0 | if( pSock->GetConState() != Csock::CST_OK ) |
4311 | 0 | tNextRunTime = tNow; // this is in a nebulous state, need to let it proceed like normal |
4312 | |
|
4313 | 0 | time_t iTimeoutInSeconds = pSock->GetTimeout(); |
4314 | 0 | if( iTimeoutInSeconds > 0 ) |
4315 | 0 | { |
4316 | 0 | timeval tNextTimeout; |
4317 | 0 | tNextTimeout.tv_sec = pSock->GetNextCheckTimeout( 0 ); // TODO convert socket timeouts to timeval too? |
4318 | 0 | tNextTimeout.tv_usec = 0; |
4319 | 0 | MinimizeTime( tNextRunTime, tNextTimeout ); |
4320 | 0 | } |
4321 | |
|
4322 | 0 | const std::vector<CCron *> & vCrons = pSock->GetCrons(); |
4323 | 0 | std::vector<CCron *>::const_iterator cit; |
4324 | 0 | std::vector<CCron *>::const_iterator cit_end = vCrons.end(); |
4325 | 0 | for( cit = vCrons.begin(); cit != cit_end; ++cit ) |
4326 | 0 | MinimizeTime( tNextRunTime, ( *cit )->GetNextRun() ); |
4327 | 0 | } |
4328 | 0 | std::vector<CCron *>::const_iterator cit; |
4329 | 0 | std::vector<CCron *>::const_iterator cit_end = m_vcCrons.end(); |
4330 | 0 | for( cit = m_vcCrons.begin(); cit != cit_end; ++cit ) |
4331 | 0 | MinimizeTime( tNextRunTime, ( *cit )->GetNextRun() ); |
4332 | |
|
4333 | 0 | timeval tReturnValue; |
4334 | 0 | if( timercmp( &tNextRunTime, &tNow, < ) ) |
4335 | 0 | { |
4336 | 0 | timerclear( &tReturnValue ); |
4337 | 0 | return( tReturnValue ); // smallest unit possible |
4338 | 0 | } |
4339 | 0 | timersub( &tNextRunTime, &tNow, &tReturnValue ); |
4340 | 0 | MinimizeTime( tReturnValue, tMaxResolution ); |
4341 | 0 | return( tReturnValue ); |
4342 | 0 | } |
4343 | | |
4344 | | void CSocketManager::SelectSock( std::map<Csock *, EMessages> & mpeSocks, EMessages eErrno, Csock * pcSock ) |
4345 | 0 | { |
4346 | 0 | if( mpeSocks.find( pcSock ) != mpeSocks.end() ) |
4347 | 0 | return; |
4348 | | |
4349 | 0 | mpeSocks[pcSock] = eErrno; |
4350 | 0 | } |
4351 | | |