]> granicus.if.org Git - icinga2/blob - lib/base/tcpsocket.cpp
Merge pull request #7185 from Icinga/bugfix/gelfwriter-wrong-log-facility
[icinga2] / lib / base / tcpsocket.cpp
1 /* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
2
3 #include "base/tcpsocket.hpp"
4 #include "base/logger.hpp"
5 #include "base/utility.hpp"
6 #include "base/exception.hpp"
7 #include <boost/exception/errinfo_api_function.hpp>
8 #include <boost/exception/errinfo_errno.hpp>
9 #include <iostream>
10
11 using namespace icinga;
12
13 /**
14  * Creates a socket and binds it to the specified service.
15  *
16  * @param service The service.
17  * @param family The address family for the socket.
18  */
19 void TcpSocket::Bind(const String& service, int family)
20 {
21         Bind(String(), service, family);
22 }
23
24 /**
25  * Creates a socket and binds it to the specified node and service.
26  *
27  * @param node The node.
28  * @param service The service.
29  * @param family The address family for the socket.
30  */
31 void TcpSocket::Bind(const String& node, const String& service, int family)
32 {
33         addrinfo hints;
34         addrinfo *result;
35         int error;
36         const char *func;
37
38         memset(&hints, 0, sizeof(hints));
39         hints.ai_family = family;
40         hints.ai_socktype = SOCK_STREAM;
41         hints.ai_protocol = IPPROTO_TCP;
42         hints.ai_flags = AI_PASSIVE;
43
44         int rc = getaddrinfo(node.IsEmpty() ? nullptr : node.CStr(),
45                 service.CStr(), &hints, &result);
46
47         if (rc != 0) {
48                 Log(LogCritical, "TcpSocket")
49                         << "getaddrinfo() failed with error code " << rc << ", \"" << gai_strerror(rc) << "\"";
50
51                 BOOST_THROW_EXCEPTION(socket_error()
52                         << boost::errinfo_api_function("getaddrinfo")
53                         << errinfo_getaddrinfo_error(rc));
54         }
55
56         int fd = INVALID_SOCKET;
57
58         for (addrinfo *info = result; info != nullptr; info = info->ai_next) {
59                 fd = socket(info->ai_family, info->ai_socktype, info->ai_protocol);
60
61                 if (fd == INVALID_SOCKET) {
62 #ifdef _WIN32
63                         error = WSAGetLastError();
64 #else /* _WIN32 */
65                         error = errno;
66 #endif /* _WIN32 */
67                         func = "socket";
68
69                         continue;
70                 }
71
72                 const int optFalse = 0;
73                 setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&optFalse), sizeof(optFalse));
74
75                 const int optTrue = 1;
76                 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char *>(&optTrue), sizeof(optTrue));
77 #ifdef SO_REUSEPORT
78                 setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<const char *>(&optTrue), sizeof(optTrue));
79 #endif /* SO_REUSEPORT */
80
81                 int rc = bind(fd, info->ai_addr, info->ai_addrlen);
82
83                 if (rc < 0) {
84 #ifdef _WIN32
85                         error = WSAGetLastError();
86 #else /* _WIN32 */
87                         error = errno;
88 #endif /* _WIN32 */
89                         func = "bind";
90
91                         closesocket(fd);
92
93                         continue;
94                 }
95
96                 SetFD(fd);
97
98                 break;
99         }
100
101         freeaddrinfo(result);
102
103         if (GetFD() == INVALID_SOCKET) {
104                 Log(LogCritical, "TcpSocket")
105                         << "Invalid socket: " << Utility::FormatErrorNumber(error);
106
107 #ifndef _WIN32
108                 BOOST_THROW_EXCEPTION(socket_error()
109                         << boost::errinfo_api_function(func)
110                         << boost::errinfo_errno(error));
111 #else /* _WIN32 */
112                 BOOST_THROW_EXCEPTION(socket_error()
113                         << boost::errinfo_api_function(func)
114                         << errinfo_win32_error(error));
115 #endif /* _WIN32 */
116         }
117 }
118
119 /**
120  * Creates a socket and connects to the specified node and service.
121  *
122  * @param node The node.
123  * @param service The service.
124  */
125 void TcpSocket::Connect(const String& node, const String& service)
126 {
127         addrinfo hints;
128         addrinfo *result;
129         int error;
130         const char *func;
131
132         memset(&hints, 0, sizeof(hints));
133         hints.ai_family = AF_UNSPEC;
134         hints.ai_socktype = SOCK_STREAM;
135         hints.ai_protocol = IPPROTO_TCP;
136
137         int rc = getaddrinfo(node.CStr(), service.CStr(), &hints, &result);
138
139         if (rc != 0) {
140                 Log(LogCritical, "TcpSocket")
141                         << "getaddrinfo() failed with error code " << rc << ", \"" << gai_strerror(rc) << "\"";
142
143                 BOOST_THROW_EXCEPTION(socket_error()
144                         << boost::errinfo_api_function("getaddrinfo")
145                         << errinfo_getaddrinfo_error(rc));
146         }
147
148         SOCKET fd = INVALID_SOCKET;
149
150         for (addrinfo *info = result; info != nullptr; info = info->ai_next) {
151                 fd = socket(info->ai_family, info->ai_socktype, info->ai_protocol);
152
153                 if (fd == INVALID_SOCKET) {
154 #ifdef _WIN32
155                         error = WSAGetLastError();
156 #else /* _WIN32 */
157                         error = errno;
158 #endif /* _WIN32 */
159                         func = "socket";
160
161                         continue;
162                 }
163
164                 const int optTrue = 1;
165                 if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast<const char *>(&optTrue), sizeof(optTrue)) != 0) {
166 #ifdef _WIN32
167                         error = WSAGetLastError();
168 #else /* _WIN32 */
169                         error = errno;
170 #endif /* _WIN32 */
171                         Log(LogWarning, "TcpSocket")
172                                 << "setsockopt() unable to enable TCP keep-alives with error code " << rc;
173                 }
174
175                 rc = connect(fd, info->ai_addr, info->ai_addrlen);
176
177                 if (rc < 0) {
178 #ifdef _WIN32
179                         error = WSAGetLastError();
180 #else /* _WIN32 */
181                         error = errno;
182 #endif /* _WIN32 */
183                         func = "connect";
184
185                         closesocket(fd);
186
187                         continue;
188                 }
189
190                 SetFD(fd);
191
192                 break;
193         }
194
195         freeaddrinfo(result);
196
197         if (GetFD() == INVALID_SOCKET) {
198                 Log(LogCritical, "TcpSocket")
199                         << "Invalid socket: " << Utility::FormatErrorNumber(error);
200
201 #ifndef _WIN32
202                 BOOST_THROW_EXCEPTION(socket_error()
203                         << boost::errinfo_api_function(func)
204                         << boost::errinfo_errno(error));
205 #else /* _WIN32 */
206                 BOOST_THROW_EXCEPTION(socket_error()
207                         << boost::errinfo_api_function(func)
208                         << errinfo_win32_error(error));
209 #endif /* _WIN32 */
210         }
211 }