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