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