1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/tools/flip_server/create_listener.h"
6
7 #include <arpa/inet.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <netdb.h>
11 #include <netinet/in.h>
12 #include <netinet/tcp.h>
13 #include <stdlib.h>
14 #include <sys/socket.h>
15 #include <sys/types.h>
16 #include <unistd.h>
17
18 #include "base/logging.h"
19
20 namespace net {
21
22 // used to ensure we delete the addrinfo structure
23 // alloc'd by getaddrinfo
24 class AddrinfoGuard {
25 protected:
26 struct addrinfo* addrinfo_ptr_;
27
28 public:
AddrinfoGuard(struct addrinfo * addrinfo_ptr)29 explicit AddrinfoGuard(struct addrinfo* addrinfo_ptr)
30 : addrinfo_ptr_(addrinfo_ptr) {}
31
~AddrinfoGuard()32 ~AddrinfoGuard() { freeaddrinfo(addrinfo_ptr_); }
33 };
34
35 // Summary:
36 // Closes a socket, with option to attempt it multiple times.
37 // Why do this? Well, if the system-call gets interrupted, close
38 // can fail with EINTR. In that case you should just retry.. Unfortunately,
39 // we can't be sure that errno is properly set since we're using a
40 // multithreaded approach in the filter proxy, so we should just retry.
41 // Args:
42 // fd - the socket to close
43 // tries - the number of tries to close the socket.
44 // Returns:
45 // true - if socket was closed
46 // false - if socket was NOT closed.
47 // Side-effects:
48 // sets *fd to -1 if socket was closed.
49 //
CloseSocket(int * fd,int tries)50 bool CloseSocket(int* fd, int tries) {
51 for (int i = 0; i < tries; ++i) {
52 if (!close(*fd)) {
53 *fd = -1;
54 return true;
55 }
56 }
57 return false;
58 }
59
60 // Sets an FD to be nonblocking.
FlipSetNonBlocking(int fd)61 void FlipSetNonBlocking(int fd) {
62 DCHECK_GE(fd, 0);
63
64 int fcntl_return = fcntl(fd, F_GETFL, 0);
65 CHECK_NE(fcntl_return, -1) << "error doing fcntl(fd, F_GETFL, 0) fd: " << fd
66 << " errno=" << errno;
67
68 if (fcntl_return & O_NONBLOCK)
69 return;
70
71 fcntl_return = fcntl(fd, F_SETFL, fcntl_return | O_NONBLOCK);
72 CHECK_NE(fcntl_return, -1)
73 << "error doing fcntl(fd, F_SETFL, fcntl_return) fd: " << fd
74 << " errno=" << errno;
75 }
76
SetDisableNagle(int fd)77 int SetDisableNagle(int fd) {
78 int on = 1;
79 int rc;
80 rc = setsockopt(
81 fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&on), sizeof(on));
82 if (rc < 0) {
83 close(fd);
84 LOG(FATAL) << "setsockopt() TCP_NODELAY: failed on fd " << fd;
85 return 0;
86 }
87 return 1;
88 }
89
90 // see header for documentation of this function.
CreateListeningSocket(const std::string & host,const std::string & port,bool is_numeric_host_address,int backlog,bool reuseaddr,bool reuseport,bool wait_for_iface,bool disable_nagle,int * listen_fd)91 int CreateListeningSocket(const std::string& host,
92 const std::string& port,
93 bool is_numeric_host_address,
94 int backlog,
95 bool reuseaddr,
96 bool reuseport,
97 bool wait_for_iface,
98 bool disable_nagle,
99 int* listen_fd) {
100 // start out by assuming things will fail.
101 *listen_fd = -1;
102
103 const char* node = NULL;
104 const char* service = NULL;
105
106 if (!host.empty())
107 node = host.c_str();
108 if (!port.empty())
109 service = port.c_str();
110
111 struct addrinfo* results = 0;
112 struct addrinfo hints;
113 memset(&hints, 0, sizeof(hints));
114
115 if (is_numeric_host_address) {
116 hints.ai_flags = AI_NUMERICHOST; // iff you know the name is numeric.
117 }
118 hints.ai_flags |= AI_PASSIVE;
119
120 hints.ai_family = PF_INET;
121 hints.ai_socktype = SOCK_STREAM;
122
123 int err = 0;
124 if ((err = getaddrinfo(node, service, &hints, &results))) {
125 // gai_strerror -is- threadsafe, so we get to use it here.
126 LOG(ERROR) << "getaddrinfo "
127 << " for (" << host << ":" << port << ") " << gai_strerror(err)
128 << "\n";
129 return -1;
130 }
131 // this will delete the addrinfo memory when we return from this function.
132 AddrinfoGuard addrinfo_guard(results);
133
134 int sock =
135 socket(results->ai_family, results->ai_socktype, results->ai_protocol);
136 if (sock == -1) {
137 LOG(ERROR) << "Unable to create socket for (" << host << ":" << port
138 << "): " << strerror(errno) << "\n";
139 return -1;
140 }
141
142 if (reuseaddr) {
143 // set SO_REUSEADDR on the listening socket.
144 int on = 1;
145 int rc;
146 rc = setsockopt(sock,
147 SOL_SOCKET,
148 SO_REUSEADDR,
149 reinterpret_cast<char*>(&on),
150 sizeof(on));
151 if (rc < 0) {
152 close(sock);
153 LOG(FATAL) << "setsockopt() failed fd=" << listen_fd << "\n";
154 }
155 }
156 #ifndef SO_REUSEPORT
157 #define SO_REUSEPORT 15
158 #endif
159 if (reuseport) {
160 // set SO_REUSEADDR on the listening socket.
161 int on = 1;
162 int rc;
163 rc = setsockopt(sock,
164 SOL_SOCKET,
165 SO_REUSEPORT,
166 reinterpret_cast<char*>(&on),
167 sizeof(on));
168 if (rc < 0) {
169 close(sock);
170 LOG(FATAL) << "setsockopt() failed fd=" << listen_fd << "\n";
171 }
172 }
173
174 if (bind(sock, results->ai_addr, results->ai_addrlen)) {
175 // If we are waiting for the interface to be raised, such as in an
176 // HA environment, ignore reporting any errors.
177 int saved_errno = errno;
178 if (!wait_for_iface || errno != EADDRNOTAVAIL) {
179 LOG(ERROR) << "Bind was unsuccessful for (" << host << ":" << port
180 << "): " << strerror(errno) << "\n";
181 }
182 // if we knew that we were not multithreaded, we could do the following:
183 // " : " << strerror(errno) << "\n";
184 if (CloseSocket(&sock, 100)) {
185 if (saved_errno == EADDRNOTAVAIL) {
186 return -3;
187 }
188 return -2;
189 } else {
190 // couldn't even close the dang socket?!
191 LOG(ERROR) << "Unable to close the socket.. Considering this a fatal "
192 "error, and exiting\n";
193 exit(EXIT_FAILURE);
194 return -1;
195 }
196 }
197
198 if (disable_nagle) {
199 if (!SetDisableNagle(sock)) {
200 return -1;
201 }
202 }
203
204 if (listen(sock, backlog)) {
205 // listen was unsuccessful.
206 LOG(ERROR) << "Listen was unsuccessful for (" << host << ":" << port
207 << "): " << strerror(errno) << "\n";
208 // if we knew that we were not multithreaded, we could do the following:
209 // " : " << strerror(errno) << "\n";
210
211 if (CloseSocket(&sock, 100)) {
212 sock = -1;
213 return -1;
214 } else {
215 // couldn't even close the dang socket?!
216 LOG(FATAL) << "Unable to close the socket.. Considering this a fatal "
217 "error, and exiting\n";
218 }
219 }
220
221 // If we've gotten to here, Yeay! Success!
222 *listen_fd = sock;
223
224 return 0;
225 }
226
CreateConnectedSocket(int * connect_fd,const std::string & host,const std::string & port,bool is_numeric_host_address,bool disable_nagle)227 int CreateConnectedSocket(int* connect_fd,
228 const std::string& host,
229 const std::string& port,
230 bool is_numeric_host_address,
231 bool disable_nagle) {
232 const char* node = NULL;
233 const char* service = NULL;
234
235 *connect_fd = -1;
236 if (!host.empty())
237 node = host.c_str();
238 if (!port.empty())
239 service = port.c_str();
240
241 struct addrinfo* results = 0;
242 struct addrinfo hints;
243 memset(&hints, 0, sizeof(hints));
244
245 if (is_numeric_host_address)
246 hints.ai_flags = AI_NUMERICHOST; // iff you know the name is numeric.
247 hints.ai_flags |= AI_PASSIVE;
248
249 hints.ai_family = PF_INET;
250 hints.ai_socktype = SOCK_STREAM;
251
252 int err = 0;
253 if ((err = getaddrinfo(node, service, &hints, &results))) {
254 // gai_strerror -is- threadsafe, so we get to use it here.
255 LOG(ERROR) << "getaddrinfo for (" << node << ":" << service
256 << "): " << gai_strerror(err);
257 return -1;
258 }
259 // this will delete the addrinfo memory when we return from this function.
260 AddrinfoGuard addrinfo_guard(results);
261
262 int sock =
263 socket(results->ai_family, results->ai_socktype, results->ai_protocol);
264 if (sock == -1) {
265 LOG(ERROR) << "Unable to create socket for (" << node << ":" << service
266 << "): " << strerror(errno);
267 return -1;
268 }
269
270 FlipSetNonBlocking(sock);
271
272 if (disable_nagle) {
273 if (!SetDisableNagle(sock)) {
274 return -1;
275 }
276 }
277
278 int ret_val = 0;
279 if (connect(sock, results->ai_addr, results->ai_addrlen)) {
280 if (errno != EINPROGRESS) {
281 LOG(ERROR) << "Connect was unsuccessful for (" << node << ":" << service
282 << "): " << strerror(errno);
283 close(sock);
284 return -1;
285 }
286 } else {
287 ret_val = 1;
288 }
289
290 // If we've gotten to here, Yeay! Success!
291 *connect_fd = sock;
292
293 return ret_val;
294 }
295
296 } // namespace net
297