blocxx
ServerSocketImpl.cpp
Go to the documentation of this file.
1/*******************************************************************************
2* Copyright (C) 2005, Vintela, Inc. All rights reserved.
3* Copyright (C) 2006, Novell, Inc. All rights reserved.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are met:
7*
8* * Redistributions of source code must retain the above copyright notice,
9* this list of conditions and the following disclaimer.
10* * Redistributions in binary form must reproduce the above copyright
11* notice, this list of conditions and the following disclaimer in the
12* documentation and/or other materials provided with the distribution.
13* * Neither the name of
14* Vintela, Inc.,
15* nor Novell, Inc.,
16* nor the names of its contributors or employees may be used to
17* endorse or promote products derived from this software without
18* specific prior written permission.
19*
20* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30* POSSIBILITY OF SUCH DAMAGE.
31*******************************************************************************/
32
33
38
39#include "blocxx/BLOCXX_config.h"
41#include "blocxx/Format.hpp"
42#include "blocxx/ByteSwap.hpp"
43#include "blocxx/FileSystem.hpp"
44#include "blocxx/File.hpp"
45#include "blocxx/Thread.hpp"
47#include "blocxx/System.hpp"
49
50extern "C"
51{
52#if !defined(BLOCXX_WIN32)
53#include <sys/types.h>
54#include <sys/stat.h>
55#include <sys/socket.h>
56#include <sys/time.h>
57#include <netinet/in.h>
58#include <arpa/inet.h>
59#include <netdb.h>
60#include <unistd.h>
61#include <fcntl.h>
62
63#define INVALID_SOCKET -1
64#endif
65}
66
67#include <cerrno>
68
69namespace BLOCXX_NAMESPACE
70{
74 , m_localAddress(SocketAddress::allocEmptyAddress(SocketAddress::INET))
75 , m_isActive(false)
76 , m_sslCtx(sslCtx)
77#if defined(BLOCXX_WIN32)
78 , m_event(NULL)
79 , m_shuttingDown(false)
80{
81 m_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
82 BLOCXX_ASSERT(m_event != NULL);
83}
84#else
85 , m_udsFile()
86{
87}
88#endif
89
93 , m_localAddress(SocketAddress::allocEmptyAddress(SocketAddress::INET))
94 , m_isActive(false)
95 , m_isSSL(isSSL)
96#if defined(BLOCXX_WIN32)
97 , m_event(NULL)
98 , m_shuttingDown(false)
99{
100 m_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
101 BLOCXX_ASSERT(m_event != NULL);
102}
103#else
104 , m_udsFile()
105{
106}
107#endif
108
111{
112 try
113 {
114 close();
115 }
116 catch (...)
117 {
118 // don't let exceptions escape
119 }
120#if defined(BLOCXX_WIN32)
121 ::CloseHandle(m_event);
122#endif
123}
124
128{
129#if defined(BLOCXX_WIN32)
130 Select_t st;
131 st.event = m_event;
132 st.sockfd = m_sockfd;
133 st.isSocket = true;
134 st.networkevents = FD_ACCEPT;
135 return st;
136#else
137 return m_sockfd;
138#endif
139}
140
142void
144 int queueSize, const String& listenAddr,
146{
147 m_isSSL = isSSL;
148 doListen(port, queueSize,listenAddr, reuseAddr);
149}
150
151#if defined(BLOCXX_WIN32)
153void
155 int queueSize, const String& listenAddr,
157{
158 SocketHandle_t sock_ipv6 = INVALID_SOCKET;
160 close();
161
162#ifdef BLOCXX_HAVE_IPV6
163 // This will try to use IPv6 (AF_INET6). If it is not supported, it will
164 // fall back to the normal IPv4 (AF_INET).
165 sock_ipv6 = ::socket(AF_INET6, SOCK_STREAM, 0);
166 if (sock_ipv6 == INVALID_SOCKET)
167 {
168 switch(errno)
169 {
170 case EPROTONOSUPPORT:
171 case EAFNOSUPPORT:
172 case EPFNOSUPPORT:
173 case EINVAL:
174 m_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
175 break;
176 default:
177 break;
178 }
179 }
180 else
181 {
182 m_sockfd = sock_ipv6;
183 }
184#else
185 m_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
186#endif
188 {
189 BLOCXX_THROW(SocketException, Format("ServerSocketImpl: %1",
190 System::lastErrorMsg(true)).c_str());
191 }
192 // Set listen socket to nonblocking
193 unsigned long cmdArg = 1;
194 if (::ioctlsocket(m_sockfd, FIONBIO, &cmdArg) == SOCKET_ERROR)
195 {
196 BLOCXX_THROW(SocketException, Format("ServerSocketImpl: %1",
197 System::lastErrorMsg(true)).c_str());
198 }
199
200 if (reuseAddr)
201 {
202 DWORD reuse = 1;
203 ::setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR,
204 (const char*)&reuse, sizeof(reuse));
205 }
206
207 if ( sock_ipv6 == INVALID_SOCKET )
208 {
209 // IPv4
210 ServerSocketImpl::doListenIPv4( port, queueSize, listenAddr);
211 }
212#ifdef BLOCXX_HAVE_IPV6
213 else
214 {
215 // IPv6
216 ServerSocketImpl::doListenIPv6( port, queueSize, listenAddr, reuseAddr);
217 }
218#endif
219}
221void
222ServerSocketImpl::doListenIPv4(UInt16 port, int queueSize, const String& listenAddr )
223{
224 // use IPv4 protocol
225 InetSocketAddress_t inetAddr;
226 memset(&inetAddr, 0, sizeof(inetAddr));
227 reinterpret_cast<sockaddr_in*>(&inetAddr)->sin_family = AF_INET;
228 reinterpret_cast<sockaddr_in*>(&inetAddr)->sin_port = hton16(port);
229 if (listenAddr == SocketAddress::ALL_LOCAL_ADDRESSES)
230 {
231 reinterpret_cast<sockaddr_in*>(&inetAddr)->sin_addr.s_addr = hton32(INADDR_ANY);
232 }
233 else
234 {
235 SocketAddress addr = SocketAddress::getByName(listenAddr);
236 reinterpret_cast<sockaddr_in*>(&inetAddr)->sin_addr.s_addr =
237 reinterpret_cast<const sockaddr_in*>(addr.getInetAddress())->sin_addr.s_addr;
238 }
239 if (::bind(m_sockfd, reinterpret_cast<sockaddr*>(&inetAddr), sizeof(inetAddr)) == -1)
240 {
241 close();
242 BLOCXX_THROW(SocketException, Format("ServerSocketImpl: %1",
243 System::lastErrorMsg(true)).c_str());
244 }
245 if (::listen(m_sockfd, queueSize) == -1)
246 {
247 close();
248 BLOCXX_THROW(SocketException, Format("ServerSocketImpl: %1",
249 System::lastErrorMsg(true)).c_str());
250 }
252 m_isActive = true;
253}
255#ifdef BLOCXX_HAVE_IPV6
256void
257ServerSocketImpl::doListenIPv6(UInt16 port, int queueSize, const String& listenAddr)
258{
259 // use IPv6 protocol
260 InetSocketAddress_t inetAddr;
261 memset(&inetAddr, 0, sizeof(inetAddr));
262 reinterpret_cast<sockaddr_in6*>(&inetAddr)->sin6_family = AF_INET6;
263 reinterpret_cast<sockaddr_in6*>(&inetAddr)->sin6_port = hton16(port);
264 if (listenAddr == SocketAddress::ALL_LOCAL_ADDRESSES)
265 {
266 // copy IPv6 address any
267 memcpy(reinterpret_cast<sockaddr_in6*>(&inetAddr)->sin6_addr.s6_addr, &in6addr_any, sizeof(struct in6_addr));
268 }
269 else
270 {
271 // create network address structure for IPv6 protocol
272 if(!inet_pton(AF_INET6, listenAddr.c_str(), (void*)&reinterpret_cast<sockaddr_in6*>(&inetAddr)->sin6_addr))
273 {
274 addrinfo *addrinfos;
275 addrinfo hints;
276 memset(&hints, 0, sizeof(hints));
277 hints.ai_socktype = SOCK_STREAM;
278 hints.ai_family = AF_INET6;
279 if(getaddrinfo(listenAddr.c_str(), NULL, &hints, &addrinfos))
280 {
281 close();
282 BLOCXX_THROW(SocketException, Format("ServerSocketImpl:: doListen(): getaddrinfo() %1",
283 System::lastErrorMsg(true)).c_str());
284 }
285 memcpy(reinterpret_cast<sockaddr_in6*>(&inetAddr), addrinfos->ai_addr, addrinfos->ai_addrlen);
286 freeaddrinfo(addrinfos);
287 }
288 }
289 if (::bind(m_sockfd, reinterpret_cast<sockaddr*>(&inetAddr), sizeof(inetAddr)) == -1)
290 {
291 close();
292 BLOCXX_THROW(SocketException, Format("ServerSocketImpl: %1",
293 System::lastErrorMsg(true)).c_str());
294 }
295 if (::listen(m_sockfd, queueSize) == -1)
296 {
297 close();
298 BLOCXX_THROW(SocketException, Format("ServerSocketImpl: %1",
299 System::lastErrorMsg(true)).c_str());
300 }
302 m_isActive = true;
303}
304#endif // BLOCXX_HAVE_IPV6
305
306namespace
307{
308
309int
310waitForAcceptIO(SocketHandle_t fd, HANDLE eventArg, const Timeout& timeOutSecs,
311 long networkEvents)
312{
313 TimeoutTimer timer(timeOutSecs);
314
315 if (networkEvents != -1L)
316 {
317 if(::WSAEventSelect(fd, eventArg, networkEvents) != 0)
318 {
320 Format("WSAEventSelect failed in waitForAcceptIO: %1",
321 System::lastErrorMsg(true)).c_str());
322 }
323 }
324
325 int cc;
326 switch(::WaitForSingleObject(eventArg, timer.asDWORDMs()))
327 {
328 case WAIT_OBJECT_0:
329 ::ResetEvent(eventArg);
330 cc = 0;
331 break;
332 case WAIT_TIMEOUT:
333 cc = ETIMEDOUT;
334 break;
335 default:
336 cc = -1;
337 break;
338 }
339
340 return cc;
341}
342
343}
344
346Socket
347ServerSocketImpl::accept(const Timeout& timeoutSecs)
348{
350
351 if (!m_isActive)
352 {
353 BLOCXX_THROW(SocketException, "ServerSocketImpl::accept: NONE");
354 }
355
356 // Register interest in FD_ACCEPT events
357 if(::WSAEventSelect(m_sockfd, m_event, FD_ACCEPT) != 0)
358 {
359 BLOCXX_THROW(SocketException,
360 Format("WSAEventSelect failed in accept: %1",
361 System::lastErrorMsg(true)).c_str());
362 }
363
364 SOCKET clntfd;
365 socklen_t clntlen;
366 InetSocketAddress_t clntInetAddr;
367 HANDLE events[2];
368 int cc;
369
370 while (true)
371 {
372 clntlen = sizeof(clntInetAddr);
373 clntfd = ::accept(m_sockfd,
374 reinterpret_cast<struct sockaddr*>(&clntInetAddr), &clntlen);
375 if (clntfd != INVALID_SOCKET)
376 {
377 // Got immediate connection
378 break;
379 }
380
381 if (::WSAGetLastError() != WSAEWOULDBLOCK)
382 {
383 BLOCXX_THROW(SocketException, Format("ServerSocketImpl: %1",
384 System::lastErrorMsg(true)).c_str());
385 }
386
387 //cc = SocketUtils::waitForIO(m_sockfd, m_event, INFINITE, FD_ACCEPT);
388 cc = waitForAcceptIO(m_sockfd, m_event, timeoutSecs, FD_ACCEPT);
389 if(m_shuttingDown)
390 {
391 cc = -2;
392 }
393
394 switch (cc)
395 {
396 case -1:
397 BLOCXX_THROW(SocketException, "Error while waiting for network events");
398 case -2:
399 BLOCXX_THROW(SocketException, "Shutdown event was signaled");
400 case ETIMEDOUT:
401 BLOCXX_THROW(SocketTimeoutException,"Timed out waiting for a connection");
402 }
403 }
404
405 // Unregister for any events. necessary to put us back in blocking mode.
406 if(::WSAEventSelect(clntfd, NULL, 0) != 0)
407 {
408 BLOCXX_THROW(SocketException,
409 Format("WSAEventSelect failed in accept: %1",
410 System::lastErrorMsg(true)).c_str());
411 }
412
413 // set socket back to blocking; otherwise it'll inherit non-blocking from the listening socket
414 unsigned long cmdArg = 0;
415 if (::ioctlsocket(clntfd, FIONBIO, &cmdArg) == SOCKET_ERROR)
416 {
417 BLOCXX_THROW(SocketException, Format("ServerSocketImpl: %1",
418 System::lastErrorMsg(true)).c_str());
419 }
420
422 {
423 return Socket(clntfd, m_localAddress.getType(), m_isSSL);
424 }
425
426 return Socket(clntfd, m_localAddress.getType(), m_sslCtx);
427}
428#else
430void
432 int queueSize, const String& listenAddr,
434{
435 SocketHandle_t sock_ipv6 = INVALID_SOCKET;
437 close();
438
439#ifdef BLOCXX_HAVE_IPV6
440 // This will try to use IPv6 (AF_INET6). If it is not supported, it will
441 // fall back to the normal IPv4 (AF_INET).
442 sock_ipv6 = ::socket(AF_INET6, SOCK_STREAM, 0);
443 if (sock_ipv6 == INVALID_SOCKET)
444 {
445 switch(errno)
446 {
447 case EPROTONOSUPPORT:
448 case EAFNOSUPPORT:
449 case EPFNOSUPPORT:
450 case EINVAL:
451 // open socket for IPv4 protocol
452 m_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
453 break;
454 default:
455 break;
456 }
457 }
458 else
459 {
460 m_sockfd = sock_ipv6;
461#ifdef IPV6_V6ONLY
462 int ipv6_proto = IPPROTO_IPV6;
463#ifdef SOL_IP
464 ipv6_proto = SOL_IP;
465#endif
466 int ipv6_only=0;
467 ::setsockopt(m_sockfd, ipv6_proto, IPV6_V6ONLY, &ipv6_only, sizeof(ipv6_only));
468#endif
469 }
470#else
471 m_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
472#endif
474 {
475 BLOCXX_THROW_ERRNO_MSG(SocketException, "ServerSocketImpl::doListen(): socket()");
476 }
477 // set the close on exec flag so child process can't keep the socket.
478 if (::fcntl(m_sockfd, F_SETFD, FD_CLOEXEC) == -1)
479 {
480 close();
481 BLOCXX_THROW_ERRNO_MSG(SocketException, "ServerSocketImpl::doListen(): fcntl() failed to set "
482 "close-on-exec flag on listen socket");
483 }
484 // set listen socket to nonblocking; see Unix Network Programming,
485 // pages 422-424.
486 int fdflags = ::fcntl(m_sockfd, F_GETFL, 0);
487 ::fcntl(m_sockfd, F_SETFL, fdflags | O_NONBLOCK);
488 // is this safe? Should be, but some OS kernels have problems with it.
489 // It's OK on current linux versions. Definitely not on
490 // OLD (kernel < 1.3.60) ones. Who knows about on other OS's like UnixWare or
491 // OpenServer?
492 // See http://monkey.org/openbsd/archive/misc/9601/msg00031.html
493 // or just google for "bind() Security Problems"
494 // Let the kernel reuse the port without waiting for it to time out.
495 // Without this line, you can't stop and immediately re-start the daemon.
496 if (reuseAddr)
497 {
498 int reuse = 1;
499#if defined(BLOCXX_NCR)
500 ::setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse));
501#else
502 ::setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
503#endif
504 }
505 if ( sock_ipv6 == INVALID_SOCKET)
506 {
507 // IPv4
508 ServerSocketImpl::doListenIPv4( port, queueSize, listenAddr );
509 }
510#ifdef BLOCXX_HAVE_IPV6
511 else
512 { // IPv6
513 ServerSocketImpl::doListenIPv6( port, queueSize, listenAddr );
514 }
515#endif
516}
517
519void
520ServerSocketImpl::doListenIPv4(UInt16 port, int queueSize, const String& listenAddr )
521{
522 // use IPv4 protocol
523 InetSocketAddress_t inetAddr;
524 memset(&inetAddr, 0, sizeof(inetAddr));
525 reinterpret_cast<sockaddr_in*>(&inetAddr)->sin_family = AF_INET;
526 reinterpret_cast<sockaddr_in*>(&inetAddr)->sin_port = hton16(port);
527 if (listenAddr == SocketAddress::ALL_LOCAL_ADDRESSES)
528 {
529 reinterpret_cast<sockaddr_in*>(&inetAddr)->sin_addr.s_addr = hton32(INADDR_ANY);
530 }
531 else
532 {
533 SocketAddress addr = SocketAddress::getByName(listenAddr);
534 reinterpret_cast<sockaddr_in*>(&inetAddr)->sin_addr.s_addr =
535 reinterpret_cast<const sockaddr_in*>(addr.getInetAddress())->sin_addr.s_addr;
536 }
537 if (::bind(m_sockfd, reinterpret_cast<sockaddr*>(&inetAddr), sizeof(inetAddr)) == -1)
538 {
539 close();
540 BLOCXX_THROW_ERRNO_MSG(SocketException, "ServerSocketImpl::doListen(): bind");
541 }
542 if (::listen(m_sockfd, queueSize) == -1)
543 {
544 close();
545 BLOCXX_THROW_ERRNO_MSG(SocketException, "ServerSocketImpl::doListen(): listen");
546 }
548 m_isActive = true;
549}
550
551#ifdef BLOCXX_HAVE_IPV6
553void
554ServerSocketImpl::doListenIPv6(UInt16 port, int queueSize, const String& listenAddr)
555{
556 // use IPv6 protocol
557 InetSocketAddress_t inetAddr;
558 memset(&inetAddr, 0, sizeof(inetAddr));
559 reinterpret_cast<sockaddr_in6*>(&inetAddr)->sin6_family = AF_INET6;
560 reinterpret_cast<sockaddr_in6*>(&inetAddr)->sin6_port = hton16(port);
561 if (listenAddr == SocketAddress::ALL_LOCAL_ADDRESSES)
562 {
563 // copy IPv6 address any
564 memcpy(reinterpret_cast<sockaddr_in6*>(&inetAddr)->sin6_addr.s6_addr, &in6addr_any, sizeof(struct in6_addr));
565 }
566 else
567 {
568 // create network address structure for IPv6 protocol
569 if(!inet_pton(AF_INET6, listenAddr.c_str(), (void*)&reinterpret_cast<sockaddr_in6*>(&inetAddr)->sin6_addr))
570 {
571 addrinfo *addrinfos;
572 addrinfo hints;
573 memset(&hints, 0, sizeof(hints));
574 hints.ai_socktype = SOCK_STREAM;
575 hints.ai_family = AF_INET6;
576 if(getaddrinfo(listenAddr.c_str(), NULL, &hints, &addrinfos))
577 {
578 close();
579 BLOCXX_THROW_ERRNO_MSG(SocketException,"ServerSocketImpl::doListen(): getaddrinfo()");
580 }
581 memcpy(reinterpret_cast<sockaddr_in6*>(&inetAddr), addrinfos->ai_addr, addrinfos->ai_addrlen);
582 freeaddrinfo(addrinfos);
583 }
584 }
585 if (::bind(m_sockfd, reinterpret_cast<sockaddr*>(&inetAddr), sizeof(inetAddr)) == -1)
586 {
587 close();
588 BLOCXX_THROW_ERRNO_MSG(SocketException, "ServerSocketImpl::doListen(): bind");
589 }
590 if (::listen(m_sockfd, queueSize) == -1)
591 {
592 close();
593 BLOCXX_THROW_ERRNO_MSG(SocketException, "ServerSocketImpl::doListen(): listen");
594 }
596 m_isActive = true;
597}
598#endif
600void
601ServerSocketImpl::doListenUDS(const String& filename, int queueSize, bool reuseAddr)
602{
604 close();
605 m_sockfd = ::socket(PF_UNIX,SOCK_STREAM, 0);
607 {
608 BLOCXX_THROW_ERRNO_MSG(SocketException, "ServerSocketImpl::doListen(): socket()");
609 }
610 // set the close on exec flag so child process can't keep the socket.
611 if (::fcntl(m_sockfd, F_SETFD, FD_CLOEXEC) == -1)
612 {
613 close();
614 BLOCXX_THROW_ERRNO_MSG(SocketException, "ServerSocketImpl::doListen(): fcntl() failed to set "
615 "close-on-exec flag on listen socket");
616 }
617
618 // set listen socket to nonblocking; see Unix Network Programming,
619 // pages 422-424.
620 int fdflags = ::fcntl(m_sockfd, F_GETFL, 0);
621 ::fcntl(m_sockfd, F_SETFL, fdflags | O_NONBLOCK);
622
623 if (reuseAddr)
624 {
625 // Let the kernel reuse the port without waiting for it to time out.
626 // Without this line, you can't stop and immediately re-start the daemon.
627 int reuse = 1;
628#if defined(BLOCXX_NCR)
629 ::setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse));
630#else
631 ::setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
632#endif
633 }
634 String lockfilename = filename + ".lock";
636 if (!m_udsFile)
637 {
639 Format("ServerSocketImpl::doListen(): Unable to open or create Unix Domain Socket lock: %1",
640 lockfilename).c_str());
641 }
642 // if we can't get a lock, someone else has got it open.
643 if (m_udsFile.tryLock() == -1)
644 {
646 Format("ServerSocketImpl::doListen(): Unable to lock Unix Domain Socket: %1",
647 filename).c_str());
648 }
649 // We got the lock, so clobber the UDS if it's there so bind will succeed.
650 // If it's not gone, bind will fail.
651 if (FileSystem::exists(filename))
652 {
653 if (!FileSystem::removeFile(filename.c_str()))
654 {
656 Format("ServerSocketImpl::doListen(): Unable to unlink Unix Domain Socket: %1",
657 filename).c_str());
658 }
659 }
660
661#if defined(BLOCXX_NCR)
662 if (::bind(m_sockfd, const_cast<SocketAddress_t *>(m_localAddress.getNativeForm()), m_localAddress.getNativeFormSize()) == -1)
663#else
664 if (::bind(m_sockfd, m_localAddress.getNativeForm(), m_localAddress.getNativeFormSize()) == -1)
665#endif
666 {
667 close();
668 BLOCXX_THROW_ERRNO_MSG(SocketException, "ServerSocketImpl::doListen(): bind()");
669 }
670 // give anybody access to the socket
671 // unfortunately, fchmod() doesn't work on a UDS
672 if (::chmod(filename.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) == -1)
673 {
674 close();
675 BLOCXX_THROW_ERRNO_MSG(SocketException, "ServerSocketImpl::doListen(): chmod()");
676 }
677 if (::listen(m_sockfd, queueSize) == -1)
678 {
679 close();
680 BLOCXX_THROW_ERRNO_MSG(SocketException, "ServerSocketImpl::doListen(): listen()");
681 }
683 m_isActive = true;
684}
685
686/*
687String
688ServerSocketImpl::addrString()
689{
690 return inetAddrToString(m_localAddress, m_localPort);
691}
692*/
694Socket
696{
697 if (!m_isActive)
698 {
699 BLOCXX_THROW(SocketException, "ServerSocketImpl::accept(): m_isActive == false");
700 }
701 int rc = 0;
703 {
704 int clntfd;
705 socklen_t clntlen;
706 InetSocketAddress_t clntInetAddr;
707 struct sockaddr_un clntUnixAddr;
708 struct sockaddr* pSA(0);
709 if (m_localAddress.getType() == SocketAddress::INET)
710 {
711 pSA = reinterpret_cast<struct sockaddr*>(&clntInetAddr);
712 clntlen = sizeof(clntInetAddr);
713 }
714 else if (m_localAddress.getType() == SocketAddress::UDS)
715 {
716 pSA = reinterpret_cast<struct sockaddr*>(&clntUnixAddr);
717 clntlen = sizeof(clntUnixAddr);
718 }
719 else
720 {
721 BLOCXX_ASSERT(0);
722 }
723
724 clntfd = ::accept(m_sockfd, pSA, &clntlen);
725 if (clntfd < 0)
726 {
727 // check to see if client aborts connection between select and accept.
728 // see Unix Network Programming pages 422-424.
729 if (errno == EWOULDBLOCK
730 || errno == ECONNABORTED
731#ifdef EPROTO
732 || errno == EPROTO
733#endif
734 )
735 {
736 BLOCXX_THROW(SocketException, "Client aborted TCP connection "
737 "between select() and accept()");
738 }
739
740 if (errno == EINTR)
741 {
743 }
744 BLOCXX_THROW_ERRNO_MSG(SocketException, "ServerSocketImpl::accept(): accept()");
745 }
746 // set socket back to blocking; see Unix Network Programming,
747 // pages 422-424.
748 int fdflags = ::fcntl(clntfd, F_GETFL, 0);
749 // On most OSs non-blocking is inherited from the listen socket,
750 // but it's not on Openserver.
751 if ((fdflags & O_NONBLOCK) == O_NONBLOCK)
752 {
753 ::fcntl(clntfd, F_SETFL, fdflags ^ O_NONBLOCK);
754 }
755 // TODO, how to make this bw compatible?
756 //return Socket(clntfd, m_localAddress.getType(), m_isSSL);
758 {
759 return Socket(clntfd, m_localAddress.getType(), m_isSSL); // for bw compat.
760 }
761 return Socket(clntfd, m_localAddress.getType(), m_sslCtx);
762 }
763 else if (rc == ETIMEDOUT)
764 {
765 // The timeout expired.
766 BLOCXX_THROW(SocketTimeoutException,"Timed out waiting for a connection");
767 }
768 else
769 {
770 BLOCXX_THROW_ERRNO_MSG(SocketException,"Timed out waiting for a connection");
771 }
772}
773#endif
774
776void
778{
779 if (m_isActive)
780 {
781#if defined(BLOCXX_WIN32)
782 ::closesocket(m_sockfd);
784#else
786 if (m_localAddress.getType() == SocketAddress::UDS)
787 {
788 String filename = m_localAddress.toString();
789 if (!FileSystem::removeFile(filename.c_str()))
790 {
792 Format("ServerSocketImpl::close(): Unable to unlink Unix Domain Socket: %1",
793 filename).c_str());
794 }
795 if (m_udsFile)
796 {
797 String lockfilename = filename + ".lock";
798 if (m_udsFile.unlock() == -1)
799 {
801 Format("ServerSocketImpl::close(): Failed to unlock Unix Domain Socket: %1",
802 lockfilename).c_str());
803 }
804 m_udsFile.close();
805 if (!FileSystem::removeFile(lockfilename.c_str()))
806 {
808 Format("ServerSocketImpl::close(): Unable to unlink Unix Domain Socket lock: %1",
809 lockfilename).c_str());
810 }
811 }
812 }
813#endif
814 m_isActive = false;
815 }
816}
817
818void
820{
821 socklen_t len;
822 if (m_localAddress.getType() == SocketAddress::INET)
823 {
824 // get information of local address for IPv6 protocol
825 struct sockaddr *p_addr;
827 memset(&ss, 0, sizeof(ss));
828 len = sizeof(ss);
829 p_addr = reinterpret_cast<struct sockaddr*>(&ss);
830 if (getsockname(m_sockfd, p_addr, &len) == -1)
831 {
832 BLOCXX_THROW_ERRNO_MSG(SocketException, "SocketImpl::fillAddrParms(): getsockname");
833 }
834 m_localAddress.assignFromNativeForm(&ss, len);
835 }
836#if !defined(BLOCXX_WIN32)
837 else if (m_localAddress.getType() == SocketAddress::UDS)
838 {
839 struct sockaddr_un addr;
840 memset(&addr, 0, sizeof(addr));
841 len = sizeof(addr);
842 if (getsockname(m_sockfd, reinterpret_cast<struct sockaddr*>(&addr), &len) == -1)
843 {
844 BLOCXX_THROW_ERRNO_MSG(SocketException, "SocketImpl::fillAddrParms(): getsockname");
845 }
846 m_localAddress.assignFromNativeForm(&addr, len);
847 }
848#endif
849 else
850 {
851 BLOCXX_ASSERT(0);
852 }
853}
854
855} // end namespace BLOCXX_NAMESPACE
856
#define BLOCXX_ASSERT(CON)
BLOCXX_ASSERT works similar to the assert() macro, but instead of calling abort(),...
Definition Assertion.hpp:57
#define BLOCXX_THROW(exType, msg)
Throw an exception using FILE and LINE.
#define BLOCXX_THROW_ERRNO_MSG(exType, msg)
Throw an exception using FILE, LINE, errno and strerror(errno)
unsigned socklen_t
#define INVALID_SOCKET
#define ETIMEDOUT
void doListenIPv4(UInt16 port, int queueSize, const String &listenAddr)
void doListen(UInt16 port, SocketFlags::ESSLFlag isSSL, int queueSize=10, const String &listenAddr=SocketAddress::ALL_LOCAL_ADDRESSES, SocketFlags::EReuseAddrFlag reuseAddr=SocketFlags::E_REUSE_ADDR)
Socket accept(const Timeout &timeout)
void doListenUDS(const String &filename, int queueSize=10, bool reuseAddr=true)
ServerSocketImpl(SSLServerCtxRef sslCtx)
const InetSocketAddress_t * getInetAddress() const
Get a pointer to the InetSocketAddress_t precondition: getType() == INET.
static SocketAddress allocEmptyAddress(AddressType type)
Allocate an empty SocketAddress.
static const char *const ALL_LOCAL_ADDRESSES
static SocketAddress getByName(const String &host, unsigned short port=0)
Do a DNS lookup on a hostname and return an SocketAddress for that host.
static SocketAddress getUDS(const String &filename)
This String class is an abstract data type that represents as NULL terminated string of characters.
Definition String.hpp:67
const char * c_str() const
Definition String.cpp:905
static void testCancel()
Test if this thread has been cancelled.
Definition Thread.cpp:432
A timeout can be absolute, which means that it will happen at the specified DateTime.
Definition Timeout.hpp:56
A TimeoutTimer is used by an algorithm to determine when a timeout has expired.
BLOCXX_COMMON_API bool exists(const String &path)
BLOCXX_COMMON_API bool removeFile(const String &path)
Remove the given file.
BLOCXX_COMMON_API File openOrCreateFile(const String &path)
Opens or creates the file for the given name.
int waitForIO(SocketHandle_t fd, int timeOutSecs, SocketFlags::EWaitDirectionFlag waitFlag)
Wait for input or output on a socket.
String lastErrorMsg(bool socketError)
Definition System.cpp:96
Taken from RFC 1321.
sockaddr_in InetSocketAddress_t
IntrusiveReference< SSLServerCtx > SSLServerCtxRef
UInt16 hton16(UInt16 v)
Definition ByteSwap.hpp:91
sockaddr SocketAddress_t
UInt32 hton32(UInt32 v)
Definition ByteSwap.hpp:98