OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 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 "tools/android/forwarder2/socket.h" | |
6 | |
7 #include <arpa/inet.h> | |
8 #include <fcntl.h> | |
9 #include <netdb.h> | |
10 #include <netinet/in.h> | |
11 #include <stdio.h> | |
12 #include <string.h> | |
13 #include <sys/socket.h> | |
14 #include <sys/types.h> | |
15 | |
16 #include "base/eintr_wrapper.h" | |
17 #include "base/logging.h" | |
18 #include "base/safe_strerror_posix.h" | |
19 #include "tools/android/common/net.h" | |
20 | |
21 #define PRESERVE_ERRNO_HANDLE_EINTR(Func) \ | |
22 do { \ | |
23 int local_errno = errno; \ | |
24 (void) HANDLE_EINTR(Func); \ | |
25 errno = local_errno; \ | |
26 } while (false); | |
27 | |
28 namespace forwarder { | |
29 | |
30 bool Socket::BindUnix(const string& path, bool abstract) { | |
31 errno = 0; | |
32 if (!InitUnixSocket(path, abstract) || !BindAndListen()) { | |
33 Close(); | |
34 return false; | |
35 } | |
36 return true; | |
37 } | |
38 | |
39 bool Socket::BindTcp(const string& host, int port) { | |
40 errno = 0; | |
41 if (!InitTcpSocket(host, port) || !BindAndListen()) { | |
42 Close(); | |
43 return false; | |
44 } | |
45 return true; | |
46 } | |
47 | |
48 bool Socket::ConnectUnix(const string& path, bool abstract) { | |
49 errno = 0; | |
50 if (!InitUnixSocket(path, abstract) || !Connect()) { | |
51 Close(); | |
52 return false; | |
53 } | |
54 return true; | |
55 } | |
56 | |
57 bool Socket::ConnectTcp(const string& host, int port) { | |
58 errno = 0; | |
59 if (!InitTcpSocket(host, port) || !Connect()) { | |
60 Close(); | |
61 return false; | |
62 } | |
63 return true; | |
64 } | |
65 | |
66 Socket::Socket() | |
67 : socket_(-1), | |
68 port_(0), | |
69 socket_error_(false), | |
70 family_(AF_INET), | |
71 abstract_(false), | |
72 addr_ptr_(reinterpret_cast<sockaddr*>(&addr_.addr4)), | |
73 addr_len_(sizeof(sockaddr)), | |
74 exit_notifier_fd_(-1) { | |
75 memset(&addr_, 0, sizeof(addr_)); | |
76 } | |
77 | |
78 Socket::~Socket() { | |
79 CHECK(IsClosed()); | |
80 } | |
81 | |
82 void Socket::Shutdown() { | |
83 if (!IsClosed()) { | |
84 // Preserving the errno in case we are trying to shutdown a socket that | |
85 // already had an error. | |
86 PRESERVE_ERRNO_HANDLE_EINTR(shutdown(socket_, SHUT_RDWR)); | |
87 } | |
88 } | |
89 | |
90 void Socket::Close() { | |
91 if (!IsClosed()) { | |
92 PRESERVE_ERRNO_HANDLE_EINTR(close(socket_)); | |
93 socket_ = -1; | |
94 } | |
95 } | |
96 | |
97 bool Socket::InitSocketInternal() { | |
98 socket_ = socket(family_, SOCK_STREAM, 0); | |
99 if (socket_ < 0) | |
100 return false; | |
101 tools::DisableNagle(socket_); | |
102 int reuse_addr = 1; | |
103 setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, | |
104 &reuse_addr, sizeof(reuse_addr)); | |
105 tools::DeferAccept(socket_); | |
106 return true; | |
107 } | |
108 | |
109 bool Socket::InitUnixSocket(const string& path, bool abstract) { | |
110 static const size_t kPathMax = sizeof(addr_.addr_un.sun_path); | |
111 if (abstract && path.size() + 2 /* '\0' */ > kPathMax || | |
112 !abstract && path.size() + 1 /* '\0' */ > kPathMax) { | |
113 LOG(ERROR) << "The provided path is too big to create a unix " | |
114 << "domain socket: " << path; | |
115 return false; | |
116 } | |
117 abstract_ = abstract; | |
118 family_ = PF_UNIX; | |
119 addr_.addr_un.sun_family = family_; | |
120 | |
121 if (abstract) { | |
122 // Copied from net/base/unix_domain_socket_posix.cc | |
123 // Convert the path given into abstract socket name. It must start with | |
124 // the '\0' character, so we are adding it. |addr_len| must specify the | |
125 // length of the structure exactly, as potentially the socket name may | |
126 // have '\0' characters embedded (although we don't support this). | |
127 // Note that addr_.addr_un.sun_path is already zero initialized. | |
128 memcpy(addr_.addr_un.sun_path + 1, path.c_str(), path.size()); | |
129 addr_len_ = path.size() + offsetof(struct sockaddr_un, sun_path) + 1; | |
130 } else { | |
131 memcpy(addr_.addr_un.sun_path, path.c_str(), path.size()); | |
132 addr_len_ = sizeof(sockaddr_un); | |
133 } | |
134 | |
135 addr_ptr_ = reinterpret_cast<sockaddr*>(&addr_.addr_un); | |
136 return InitSocketInternal(); | |
137 } | |
138 | |
139 bool Socket::InitTcpSocket(const string& host, int port) { | |
140 port_ = port; | |
141 | |
142 if (host.empty()) { | |
143 // Use localhost: INADDR_LOOPBACK | |
144 family_ = AF_INET; | |
145 addr_.addr4.sin_family = family_; | |
146 addr_.addr4.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | |
147 } else if (!Resolve(host)) { | |
148 return false; | |
149 } | |
150 CHECK(family_ == AF_INET || family_ == AF_INET6) | |
151 << "Invalid socket family."; | |
152 if (family_ == AF_INET) { | |
153 addr_.addr4.sin_port = htons(port_); | |
154 addr_ptr_ = reinterpret_cast<sockaddr*>(&addr_.addr4); | |
155 addr_len_ = sizeof(addr_.addr4); | |
156 } else if (family_ == AF_INET6) { | |
157 addr_.addr6.sin6_port = htons(port_); | |
158 addr_ptr_ = reinterpret_cast<sockaddr*>(&addr_.addr6); | |
159 addr_len_ = sizeof(addr_.addr6); | |
160 } | |
161 return InitSocketInternal(); | |
162 } | |
163 | |
164 bool Socket::BindAndListen() { | |
165 errno = 0; | |
166 if (HANDLE_EINTR(bind(socket_, addr_ptr_, addr_len_)) < 0 || | |
167 HANDLE_EINTR(listen(socket_, 5)) < 0) { | |
168 SetSocketError(); | |
169 return false; | |
170 } | |
171 return true; | |
172 } | |
173 | |
174 bool Socket::Accept(Socket* new_socket) { | |
175 DCHECK(new_socket != NULL); | |
176 if (!WaitForEvent()) { | |
177 SetSocketError(); | |
178 return false; | |
179 } | |
180 errno = 0; | |
181 int new_socket_fd = HANDLE_EINTR(accept(socket_, NULL, NULL)); | |
182 if (new_socket_fd < 0) { | |
183 SetSocketError(); | |
184 return false; | |
185 } | |
186 | |
187 tools::DisableNagle(new_socket_fd); | |
188 new_socket->socket_ = new_socket_fd; | |
189 return true; | |
190 } | |
191 | |
192 bool Socket::Connect() { | |
193 errno = 0; | |
194 if (HANDLE_EINTR(connect(socket_, addr_ptr_, addr_len_)) < 0) { | |
195 SetSocketError(); | |
196 return false; | |
197 } | |
198 return true; | |
199 } | |
200 | |
201 bool Socket::Resolve(const string& host) { | |
202 struct addrinfo hints; | |
203 struct addrinfo* res; | |
204 memset(&hints, 0, sizeof(hints)); | |
205 hints.ai_family = PF_UNSPEC; | |
digit
2012/09/07 13:47:12
nit: should be AF_UNSPEC since this is an address
felipeg
2012/09/07 13:56:57
Done.
| |
206 hints.ai_socktype = SOCK_STREAM; | |
207 hints.ai_flags |= AI_CANONNAME; | |
208 | |
209 int errcode = getaddrinfo(host.c_str(), NULL, &hints, &res); | |
210 if (errcode != 0) { | |
211 SetSocketError(); | |
212 return false; | |
213 } | |
214 family_ = res->ai_family; | |
215 switch (res->ai_family) { | |
216 case AF_INET: | |
217 memcpy(&addr_.addr4, | |
218 reinterpret_cast<sockaddr_in*>(res->ai_addr), | |
219 sizeof(sockaddr_in)); | |
220 break; | |
221 case AF_INET6: | |
222 memcpy(&addr_.addr6, | |
223 reinterpret_cast<sockaddr_in6*>(res->ai_addr), | |
224 sizeof(sockaddr_in6)); | |
225 break; | |
226 } | |
227 return true; | |
228 } | |
229 | |
230 bool Socket::IsFdInSet(const fd_set& fds) const { | |
231 if (IsClosed()) | |
232 return false; | |
233 return FD_ISSET(socket_, &fds); | |
234 } | |
235 | |
236 bool Socket::AddFdToSet(fd_set* fds) const { | |
237 if (IsClosed()) | |
238 return false; | |
239 FD_SET(socket_, fds); | |
240 return true; | |
241 } | |
242 | |
243 int Socket::ReadNumBytes(char* buffer, size_t num_bytes) { | |
244 int bytes_read = 0; | |
245 int ret = 1; | |
246 while (bytes_read < num_bytes && ret > 0) { | |
247 ret = Read(buffer + bytes_read, num_bytes - bytes_read); | |
248 if (ret >= 0) | |
249 bytes_read += ret; | |
250 } | |
251 return bytes_read; | |
252 } | |
253 | |
254 void Socket::SetSocketError() { | |
255 socket_error_ = true; | |
256 // We never use non-blocking socket. | |
257 DCHECK(errno != EAGAIN && errno != EWOULDBLOCK); | |
258 Close(); | |
259 } | |
260 | |
261 int Socket::Read(char* buffer, size_t buffer_size) { | |
262 if (!WaitForEvent()) { | |
263 SetSocketError(); | |
264 return 0; | |
265 } | |
266 int ret = HANDLE_EINTR(read(socket_, buffer, buffer_size)); | |
267 if (ret < 0) | |
268 SetSocketError(); | |
269 return ret; | |
270 } | |
271 | |
272 int Socket::Write(const char* buffer, size_t count) { | |
273 int ret = HANDLE_EINTR(send(socket_, buffer, count, MSG_NOSIGNAL)); | |
274 if (ret < 0) | |
275 SetSocketError(); | |
276 return ret; | |
277 } | |
278 | |
279 int Socket::WriteNumBytes(const char* buffer, size_t num_bytes) { | |
280 int bytes_written = 0; | |
281 int ret = 1; | |
282 while (bytes_written < num_bytes && ret > 0) { | |
283 ret = Write(buffer + bytes_written, num_bytes - bytes_written); | |
284 if (ret >= 0) | |
285 bytes_written += ret; | |
286 } | |
287 return bytes_written; | |
288 } | |
289 | |
290 bool Socket::WaitForEvent() const { | |
291 if (exit_notifier_fd_ == -1 || socket_ == -1) | |
292 return true; | |
293 const int nfds = std::max(socket_, exit_notifier_fd_) + 1; | |
294 fd_set read_fds; | |
295 FD_ZERO(&read_fds); | |
296 FD_SET(socket_, &read_fds); | |
297 FD_SET(exit_notifier_fd_, &read_fds); | |
298 if (HANDLE_EINTR(select(nfds, &read_fds, NULL, NULL, NULL)) <= 0) | |
299 return false; | |
300 return !FD_ISSET(exit_notifier_fd_, &read_fds); | |
301 } | |
302 | |
303 // static | |
304 int Socket::GetHighestFileDescriptor( | |
305 const Socket& s1, const Socket& s2) { | |
306 return std::max(s1.socket_, s2.socket_); | |
307 } | |
308 | |
309 } // namespace forwarder | |
OLD | NEW |