]> granicus.if.org Git - icinga2/blob - lib/base/tcpsocket.cpp
Update copyright headers for 2016
[icinga2] / lib / base / tcpsocket.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2016 Icinga Development Team (https://www.icinga.org/)  *
4  *                                                                            *
5  * This program is free software; you can redistribute it and/or              *
6  * modify it under the terms of the GNU General Public License                *
7  * as published by the Free Software Foundation; either version 2             *
8  * of the License, or (at your option) any later version.                     *
9  *                                                                            *
10  * This program is distributed in the hope that it will be useful,            *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
13  * GNU General Public License for more details.                               *
14  *                                                                            *
15  * You should have received a copy of the GNU General Public License          *
16  * along with this program; if not, write to the Free Software Foundation     *
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
18  ******************************************************************************/
19
20 #include "base/tcpsocket.hpp"
21 #include "base/logger.hpp"
22 #include "base/utility.hpp"
23 #include "base/exception.hpp"
24 #include <boost/exception/errinfo_api_function.hpp>
25 #include <boost/exception/errinfo_errno.hpp>
26 #include <iostream>
27
28 using namespace icinga;
29
30 /**
31  * Creates a socket and binds it to the specified service.
32  *
33  * @param service The service.
34  * @param family The address family for the socket.
35  */
36 void TcpSocket::Bind(const String& service, int family)
37 {
38         Bind(String(), service, family);
39 }
40
41 /**
42  * Creates a socket and binds it to the specified node and service.
43  *
44  * @param node The node.
45  * @param service The service.
46  * @param family The address family for the socket.
47  */
48 void TcpSocket::Bind(const String& node, const String& service, int family)
49 {
50         addrinfo hints;
51         addrinfo *result;
52         int error;
53         const char *func;
54
55         memset(&hints, 0, sizeof(hints));
56         hints.ai_family = family;
57         hints.ai_socktype = SOCK_STREAM;
58         hints.ai_protocol = IPPROTO_TCP;
59         hints.ai_flags = AI_PASSIVE;
60
61         int rc = getaddrinfo(node.IsEmpty() ? NULL : node.CStr(),
62             service.CStr(), &hints, &result);
63
64         if (rc != 0) {
65                 Log(LogCritical, "TcpSocket")
66                     << "getaddrinfo() failed with error code " << rc << ", \"" << gai_strerror(rc) << "\"";
67
68                 BOOST_THROW_EXCEPTION(socket_error()
69                     << boost::errinfo_api_function("getaddrinfo")
70                     << errinfo_getaddrinfo_error(rc));
71         }
72
73         int fd = INVALID_SOCKET;
74
75         for (addrinfo *info = result; info != NULL; info = info->ai_next) {
76                 fd = socket(info->ai_family, info->ai_socktype, info->ai_protocol);
77
78                 if (fd == INVALID_SOCKET) {
79 #ifdef _WIN32           
80                         error = WSAGetLastError();
81 #else /* _WIN32 */
82                         error = errno;
83 #endif /* _WIN32 */
84                         func = "socket";
85
86                         continue;
87                 }
88
89                 const int optFalse = 0;
90                 setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char *>(&optFalse), sizeof(optFalse));
91
92 #ifndef _WIN32
93                 const int optTrue = 1;
94                 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char *>(&optTrue), sizeof(optTrue));
95 #endif /* _WIN32 */
96
97                 int rc = bind(fd, info->ai_addr, info->ai_addrlen);
98
99                 if (rc < 0) {
100 #ifdef _WIN32
101                         error = WSAGetLastError();
102 #else /* _WIN32 */
103                         error = errno;
104 #endif /* _WIN32 */
105                         func = "bind";
106
107                         closesocket(fd);
108
109                         continue;
110                 }
111
112                 SetFD(fd);
113
114                 break;
115         }
116
117         freeaddrinfo(result);
118
119         if (GetFD() == INVALID_SOCKET) {
120                 Log(LogCritical, "TcpSocket")
121                     << "Invalid socket: " << Utility::FormatErrorNumber(error);
122
123 #ifndef _WIN32
124                 BOOST_THROW_EXCEPTION(socket_error()
125                     << boost::errinfo_api_function(func)
126                     << boost::errinfo_errno(error));
127 #else /* _WIN32 */
128                 BOOST_THROW_EXCEPTION(socket_error()
129                     << boost::errinfo_api_function(func)
130                     << errinfo_win32_error(error));
131 #endif /* _WIN32 */
132         }
133 }
134
135 /**
136  * Creates a socket and connects to the specified node and service.
137  *
138  * @param node The node.
139  * @param service The service.
140  */
141 void TcpSocket::Connect(const String& node, const String& service)
142 {
143         addrinfo hints;
144         addrinfo *result;
145         int error;
146         const char *func;
147
148         memset(&hints, 0, sizeof(hints));
149         hints.ai_family = AF_UNSPEC;
150         hints.ai_socktype = SOCK_STREAM;
151         hints.ai_protocol = IPPROTO_TCP;
152
153         int rc = getaddrinfo(node.CStr(), service.CStr(), &hints, &result);
154
155         if (rc != 0) {
156                 Log(LogCritical, "TcpSocket")
157                     << "getaddrinfo() failed with error code " << rc << ", \"" << gai_strerror(rc) << "\"";
158
159                 BOOST_THROW_EXCEPTION(socket_error()
160                     << boost::errinfo_api_function("getaddrinfo")
161                     << errinfo_getaddrinfo_error(rc));
162         }
163
164         int fd = INVALID_SOCKET;
165
166         for (addrinfo *info = result; info != NULL; info = info->ai_next) {
167                 fd = socket(info->ai_family, info->ai_socktype, info->ai_protocol);
168
169                 if (fd == INVALID_SOCKET) {
170 #ifdef _WIN32
171                         error = WSAGetLastError();
172 #else /* _WIN32 */
173                         error = errno;
174 #endif /* _WIN32 */
175                         func = "socket";
176
177                         continue;
178                 }
179
180                 rc = connect(fd, info->ai_addr, info->ai_addrlen);
181
182                 if (rc < 0) {
183 #ifdef _WIN32
184                         error = WSAGetLastError();
185 #else /* _WIN32 */
186                         error = errno;
187 #endif /* _WIN32 */
188                         func = "connect";
189
190                         closesocket(fd);
191
192                         continue;
193                 }
194
195                 SetFD(fd);
196
197                 break;
198         }
199
200         freeaddrinfo(result);
201
202         if (GetFD() == INVALID_SOCKET) {
203                 Log(LogCritical, "TcpSocket")
204                     << "Invalid socket: " << Utility::FormatErrorNumber(error);
205
206 #ifndef _WIN32
207                 BOOST_THROW_EXCEPTION(socket_error()
208                     << boost::errinfo_api_function(func)
209                     << boost::errinfo_errno(error));
210 #else /* _WIN32 */
211                 BOOST_THROW_EXCEPTION(socket_error()
212                     << boost::errinfo_api_function(func)
213                     << errinfo_win32_error(error));
214 #endif /* _WIN32 */
215         }
216 }