1 // Module:  Log4CPLUS
2 // File:    socket-unix.cxx
3 // Created: 4/2003
4 // Author:  Tad E. Smith
5 //
6 //
7 // Copyright 2003-2013 Tad E. Smith
8 //
9 // Licensed under the Apache License, Version 2.0 (the "License");
10 // you may not use this file except in compliance with the License.
11 // You may obtain a copy of the License at
12 //
13 //     http://www.apache.org/licenses/LICENSE-2.0
14 //
15 // Unless required by applicable law or agreed to in writing, software
16 // distributed under the License is distributed on an "AS IS" BASIS,
17 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 // See the License for the specific language governing permissions and
19 // limitations under the License.
20 
21 
22 #include <log4cplus/config.hxx>
23 #if defined (LOG4CPLUS_USE_BSD_SOCKETS)
24 
25 #include <cstring>
26 #include <vector>
27 #include <algorithm>
28 #include <cerrno>
29 #include <log4cplus/internal/socket.h>
30 #include <log4cplus/helpers/loglog.h>
31 #include <log4cplus/thread/syncprims-pub-impl.h>
32 #include <log4cplus/spi/loggingevent.h>
33 #include <log4cplus/helpers/stringhelper.h>
34 
35 #ifdef LOG4CPLUS_HAVE_SYS_TYPES_H
36 #include <sys/types.h>
37 #endif
38 
39 #ifdef LOG4CPLUS_HAVE_SYS_SOCKET_H
40 #include <sys/socket.h>
41 #endif
42 
43 #if defined (LOG4CPLUS_HAVE_NETINET_IN_H)
44 #include <netinet/in.h>
45 #endif
46 
47 #if defined (LOG4CPLUS_HAVE_NETINET_TCP_H)
48 #include <netinet/tcp.h>
49 #endif
50 
51 #if defined (LOG4CPLUS_HAVE_ARPA_INET_H)
52 #include <arpa/inet.h>
53 #endif
54 
55 #if defined (LOG4CPLUS_HAVE_ERRNO_H)
56 #include <errno.h>
57 #endif
58 
59 #ifdef LOG4CPLUS_HAVE_NETDB_H
60 #include <netdb.h>
61 #endif
62 
63 #ifdef LOG4CPLUS_HAVE_FCNTL_H
64 #include <fcntl.h>
65 #endif
66 
67 #ifdef LOG4CPLUS_HAVE_UNISTD_H
68 #include <unistd.h>
69 #endif
70 
71 #ifdef LOG4CPLUS_HAVE_POLL_H
72 #include <poll.h>
73 #endif
74 
75 
76 namespace log4cplus { namespace helpers {
77 
78 // from lockfile.cxx
79 LOG4CPLUS_PRIVATE bool trySetCloseOnExec (int fd,
80     helpers::LogLog & loglog = helpers::getLogLog ());
81 
82 
83 namespace
84 {
85 
86 
87 #if ! defined (LOG4CPLUS_SINGLE_THREADED)
88 // We need to use log4cplus::thread here to work around compilation
89 // problem on AIX.
90 static log4cplus::thread::Mutex ghbn_mutex;
91 
92 #endif
93 
94 
95 static
96 int
get_host_by_name(char const * hostname,std::string * name,struct sockaddr_in * addr)97 get_host_by_name (char const * hostname, std::string * name,
98     struct sockaddr_in * addr)
99 {
100 #if defined (LOG4CPLUS_HAVE_GETADDRINFO)
101     struct addrinfo hints;
102     std::memset (&hints, 0, sizeof (hints));
103     hints.ai_family = AF_INET;
104     hints.ai_socktype = SOCK_STREAM;
105     hints.ai_protocol = IPPROTO_TCP;
106     hints.ai_flags = AI_CANONNAME;
107 
108     if (inet_addr (hostname) != static_cast<in_addr_t>(-1))
109         hints.ai_flags |= AI_NUMERICHOST;
110 
111     struct addrinfo * res = 0;
112     int ret = getaddrinfo (hostname, 0, &hints, &res);
113     if (ret != 0)
114         return ret;
115 
116     struct addrinfo const & ai = *res;
117     assert (ai.ai_family == AF_INET);
118 
119     if (name)
120         *name = ai.ai_canonname;
121 
122     if (addr)
123         std::memcpy (addr, ai.ai_addr, ai.ai_addrlen);
124 
125     freeaddrinfo (res);
126 
127 #else
128     #if ! defined (LOG4CPLUS_SINGLE_THREADED)
129     // We need to use log4cplus::thread here to work around
130     // compilation problem on AIX.
131     log4cplus::thread::MutexGuard guard (ghbn_mutex);
132 
133     #endif
134 
135     struct ::hostent * hp = gethostbyname (hostname);
136     if (! hp)
137         return 1;
138     assert (hp->h_addrtype == AF_INET);
139 
140     if (name)
141         *name = hp->h_name;
142 
143     if (addr)
144     {
145         assert (hp->h_length <= sizeof (addr->sin_addr));
146         std::memcpy (&addr->sin_addr, hp->h_addr_list[0], hp->h_length);
147     }
148 
149 #endif
150 
151     return 0;
152 }
153 
154 
155 } // namespace
156 
157 
158 /////////////////////////////////////////////////////////////////////////////
159 // Global Methods
160 /////////////////////////////////////////////////////////////////////////////
161 
162 SOCKET_TYPE
openSocket(unsigned short port,SocketState & state)163 openSocket(unsigned short port, SocketState& state)
164 {
165     int sock = ::socket(AF_INET, SOCK_STREAM, 0);
166     if(sock < 0) {
167         return INVALID_SOCKET_VALUE;
168     }
169 
170     struct sockaddr_in server = sockaddr_in ();
171     server.sin_family = AF_INET;
172     server.sin_addr.s_addr = INADDR_ANY;
173     server.sin_port = htons(port);
174 
175     int optval = 1;
176     socklen_t optlen = sizeof (optval);
177     int ret = setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &optval, optlen );
178     if (ret != 0)
179     {
180         int const eno = errno;
181         helpers::getLogLog ().warn (LOG4CPLUS_TEXT ("setsockopt() failed: ")
182             + helpers::convertIntegerToString (eno));
183     }
184 
185     int retval = bind(sock, reinterpret_cast<struct sockaddr*>(&server),
186         sizeof(server));
187     if (retval < 0)
188         goto error;
189 
190     if (::listen(sock, 10))
191         goto error;
192 
193     state = ok;
194     return to_log4cplus_socket (sock);
195 
196 error:
197     close (sock);
198     return INVALID_SOCKET_VALUE;
199 }
200 
201 
202 SOCKET_TYPE
connectSocket(const tstring & hostn,unsigned short port,bool udp,SocketState & state)203 connectSocket(const tstring& hostn, unsigned short port, bool udp, SocketState& state)
204 {
205     struct sockaddr_in server;
206     int sock;
207     int retval;
208 
209     std::memset (&server, 0, sizeof (server));
210     retval = get_host_by_name (LOG4CPLUS_TSTRING_TO_STRING(hostn).c_str(),
211         0, &server);
212     if (retval != 0)
213         return INVALID_SOCKET_VALUE;
214 
215     server.sin_port = htons(port);
216     server.sin_family = AF_INET;
217 
218     sock = ::socket(AF_INET, (udp ? SOCK_DGRAM : SOCK_STREAM), 0);
219     if(sock < 0) {
220         return INVALID_SOCKET_VALUE;
221     }
222 
223     socklen_t namelen = sizeof (server);
224     while (
225         (retval = ::connect(sock, reinterpret_cast<struct sockaddr*>(&server),
226             namelen))
227         == -1
228         && (errno == EINTR))
229         ;
230     if (retval == INVALID_OS_SOCKET_VALUE)
231     {
232         ::close(sock);
233         return INVALID_SOCKET_VALUE;
234     }
235 
236     state = ok;
237     return to_log4cplus_socket (sock);
238 }
239 
240 
241 namespace
242 {
243 
244 //! Helper for accept_wrap().
245 template <typename T, typename U>
246 struct socklen_var
247 {
248     typedef T type;
249 };
250 
251 
252 template <typename U>
253 struct socklen_var<void, U>
254 {
255     typedef U type;
256 };
257 
258 
259 // Some systems like HP-UX have socklen_t but accept() does not use it
260 // as type of its 3rd parameter. This wrapper works around this
261 // incompatibility.
262 template <typename accept_sockaddr_ptr_type, typename accept_socklen_type>
263 static
264 SOCKET_TYPE
accept_wrap(int (* accept_func)(int,accept_sockaddr_ptr_type,accept_socklen_type *),SOCKET_TYPE sock,struct sockaddr * sa,socklen_t * len)265 accept_wrap (
266     int (* accept_func) (int, accept_sockaddr_ptr_type, accept_socklen_type *),
267     SOCKET_TYPE sock, struct sockaddr * sa, socklen_t * len)
268 {
269     typedef typename socklen_var<accept_socklen_type, socklen_t>::type
270         socklen_var_type;
271     socklen_var_type l = static_cast<socklen_var_type>(*len);
272     SOCKET_TYPE result
273         = static_cast<SOCKET_TYPE>(
274             accept_func (sock, sa,
275                 reinterpret_cast<accept_socklen_type *>(&l)));
276     *len = static_cast<socklen_t>(l);
277     return result;
278 }
279 
280 
281 } // namespace
282 
283 
284 SOCKET_TYPE
acceptSocket(SOCKET_TYPE sock,SocketState & state)285 acceptSocket(SOCKET_TYPE sock, SocketState& state)
286 {
287     struct sockaddr_in net_client;
288     socklen_t len = sizeof(struct sockaddr);
289     int clientSock;
290 
291     while(
292         (clientSock = accept_wrap (accept, to_os_socket (sock),
293             reinterpret_cast<struct sockaddr*>(&net_client), &len))
294         == -1
295         && (errno == EINTR))
296         ;
297 
298     if(clientSock != INVALID_OS_SOCKET_VALUE) {
299         state = ok;
300     }
301 
302     return to_log4cplus_socket (clientSock);
303 }
304 
305 
306 
307 int
closeSocket(SOCKET_TYPE sock)308 closeSocket(SOCKET_TYPE sock)
309 {
310     return ::close(to_os_socket (sock));
311 }
312 
313 
314 int
shutdownSocket(SOCKET_TYPE sock)315 shutdownSocket(SOCKET_TYPE sock)
316 {
317     return ::shutdown(to_os_socket (sock), SHUT_RDWR);
318 }
319 
320 
321 long
read(SOCKET_TYPE sock,SocketBuffer & buffer)322 read(SOCKET_TYPE sock, SocketBuffer& buffer)
323 {
324     long res, readbytes = 0;
325 
326     do
327     {
328         res = ::read(to_os_socket (sock), buffer.getBuffer() + readbytes,
329             buffer.getMaxSize() - readbytes);
330         if( res <= 0 ) {
331             return res;
332         }
333         readbytes += res;
334     } while( readbytes < static_cast<long>(buffer.getMaxSize()) );
335 
336     return readbytes;
337 }
338 
339 
340 
341 long
write(SOCKET_TYPE sock,const SocketBuffer & buffer)342 write(SOCKET_TYPE sock, const SocketBuffer& buffer)
343 {
344 #if defined(MSG_NOSIGNAL)
345     int flags = MSG_NOSIGNAL;
346 #else
347     int flags = 0;
348 #endif
349     return ::send( to_os_socket (sock), buffer.getBuffer(), buffer.getSize(),
350         flags );
351 }
352 
353 
354 long
write(SOCKET_TYPE sock,const std::string & buffer)355 write(SOCKET_TYPE sock, const std::string & buffer)
356 {
357 #if defined(MSG_NOSIGNAL)
358     int flags = MSG_NOSIGNAL;
359 #else
360     int flags = 0;
361 #endif
362     return ::send (to_os_socket (sock), buffer.c_str (), buffer.size (),
363         flags);
364 }
365 
366 
367 tstring
getHostname(bool fqdn)368 getHostname (bool fqdn)
369 {
370     char const * hostname = "unknown";
371     int ret;
372     std::vector<char> hn (1024, 0);
373 
374     while (true)
375     {
376         ret = ::gethostname (&hn[0], static_cast<int>(hn.size ()) - 1);
377         if (ret == 0)
378         {
379             hostname = &hn[0];
380             break;
381         }
382 #if defined (LOG4CPLUS_HAVE_ENAMETOOLONG)
383         else if (ret != 0 && errno == ENAMETOOLONG)
384             // Out buffer was too short. Retry with buffer twice the size.
385             hn.resize (hn.size () * 2, 0);
386 #endif
387         else
388             break;
389     }
390 
391     if (ret != 0 || (ret == 0 && ! fqdn))
392         return LOG4CPLUS_STRING_TO_TSTRING (hostname);
393 
394     std::string full_hostname;
395     ret = get_host_by_name (hostname, &full_hostname, 0);
396     if (ret == 0)
397         hostname = full_hostname.c_str ();
398 
399     return LOG4CPLUS_STRING_TO_TSTRING (hostname);
400 }
401 
402 
403 int
setTCPNoDelay(SOCKET_TYPE sock,bool val)404 setTCPNoDelay (SOCKET_TYPE sock, bool val)
405 {
406 #if (defined (SOL_TCP) || defined (IPPROTO_TCP)) && defined (TCP_NODELAY)
407 #if defined (SOL_TCP)
408     int level = SOL_TCP;
409 
410 #elif defined (IPPROTO_TCP)
411     int level = IPPROTO_TCP;
412 
413 #endif
414 
415     int result;
416     int enabled = static_cast<int>(val);
417     if ((result = setsockopt(sock, level, TCP_NODELAY, &enabled,
418                 sizeof(enabled))) != 0)
419         set_last_socket_error (errno);
420 
421     return result;
422 
423 #else
424     // No recognizable TCP_NODELAY option.
425     return 0;
426 
427 #endif
428 }
429 
430 
431 //
432 // ServerSocket OS dependent stuff
433 //
434 
ServerSocket(unsigned short port)435 ServerSocket::ServerSocket(unsigned short port)
436 {
437     int fds[2] = {-1, -1};
438     int ret;
439 
440     sock = openSocket (port, state);
441     if (sock == INVALID_SOCKET_VALUE)
442         goto error;
443 
444 #if defined (LOG4CPLUS_HAVE_PIPE2) && defined (O_CLOEXEC)
445     ret = pipe2 (fds, O_CLOEXEC);
446     if (ret != 0)
447         goto error;
448 
449 #elif defined (LOG4CPLUS_HAVE_PIPE)
450     ret = pipe (fds);
451     if (ret != 0)
452         goto error;
453 
454     trySetCloseOnExec (fds[0]);
455     trySetCloseOnExec (fds[1]);
456 
457 #else
458 #  error You are missing both pipe() or pipe2().
459 #endif
460 
461     interruptHandles[0] = fds[0];
462     interruptHandles[1] = fds[1];
463     return;
464 
465 error:;
466     err = get_last_socket_error ();
467     state = not_opened;
468 
469     if (sock != INVALID_SOCKET_VALUE)
470         closeSocket (sock);
471 
472     if (fds[0] != -1)
473         ::close (fds[0]);
474 
475     if (fds[1] != -1)
476         ::close (fds[1]);
477 }
478 
479 Socket
accept()480 ServerSocket::accept ()
481 {
482     struct pollfd pollfds[2];
483 
484     struct pollfd & interrupt_pipe = pollfds[0];
485     interrupt_pipe.fd = interruptHandles[0];
486     interrupt_pipe.events = POLLIN;
487     interrupt_pipe.revents = 0;
488 
489     struct pollfd & accept_fd = pollfds[1];
490     accept_fd.fd = to_os_socket (sock);
491     accept_fd.events = POLLIN;
492     accept_fd.revents = 0;
493 
494     do
495     {
496         interrupt_pipe.revents = 0;
497         accept_fd.revents = 0;
498 
499         int ret = poll (pollfds, 2, -1);
500         switch (ret)
501         {
502         // Error.
503         case -1:
504             if (errno == EINTR)
505                 // Signal has interrupted the call. Just re-run it.
506                 continue;
507 
508             set_last_socket_error (errno);
509             return Socket (INVALID_SOCKET_VALUE, not_opened, errno);
510 
511         // Timeout. This should not happen though.
512         case 0:
513             continue;
514 
515         default:
516             // Some descriptor is ready.
517 
518             if ((interrupt_pipe.revents & POLLIN) == POLLIN)
519             {
520                 // Read byte from interruption pipe.
521 
522                 helpers::getLogLog ().debug (
523                     LOG4CPLUS_TEXT ("ServerSocket::accept- ")
524                     LOG4CPLUS_TEXT ("accept() interrupted by other thread"));
525 
526                 char ch;
527                 ret = ::read (interrupt_pipe.fd, &ch, 1);
528                 if (ret == -1)
529                 {
530                     int const eno = errno;
531                     helpers::getLogLog ().warn (
532                         LOG4CPLUS_TEXT ("ServerSocket::accept- read() failed: ")
533                         + helpers::convertIntegerToString (eno));
534                     set_last_socket_error (eno);
535                     return Socket (INVALID_SOCKET_VALUE, not_opened, eno);
536                 }
537 
538                 // Return Socket with state set to accept_interrupted.
539 
540                 return Socket (INVALID_SOCKET_VALUE, accept_interrupted, 0);
541             }
542             else if ((accept_fd.revents & POLLIN) == POLLIN)
543             {
544                 helpers::getLogLog ().debug (
545                     LOG4CPLUS_TEXT ("ServerSocket::accept- ")
546                     LOG4CPLUS_TEXT ("accepting connection"));
547 
548                 SocketState st = not_opened;
549                 SOCKET_TYPE clientSock = acceptSocket (sock, st);
550                 int eno = 0;
551                 if (clientSock == INVALID_SOCKET_VALUE)
552                     eno = get_last_socket_error ();
553 
554                 return Socket (clientSock, st, eno);
555             }
556             else
557                 return Socket (INVALID_SOCKET_VALUE, not_opened, 0);
558         }
559     }
560     while (true);
561 }
562 
563 
564 void
interruptAccept()565 ServerSocket::interruptAccept ()
566 {
567     char ch = 'I';
568     int ret;
569 
570     do
571     {
572         ret = ::write (interruptHandles[1], &ch, 1);
573     }
574     while (ret == -1 && errno == EINTR);
575 
576     if (ret == -1)
577     {
578         int const eno = errno;
579         helpers::getLogLog ().warn (
580             LOG4CPLUS_TEXT ("ServerSocket::interruptAccept- write() failed: ")
581             + helpers::convertIntegerToString (eno));
582     }
583 }
584 
585 
~ServerSocket()586 ServerSocket::~ServerSocket()
587 {
588     if (interruptHandles[0] != -1)
589         ::close (interruptHandles[0]);
590 
591     if (interruptHandles[1] != -1)
592         ::close (interruptHandles[1]);
593 }
594 
595 } } // namespace log4cplus
596 
597 #endif // LOG4CPLUS_USE_BSD_SOCKETS
598