• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2013 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 "chrome/test/chromedriver/net/port_server.h"
6 
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/logging.h"
10 #include "base/process/process_handle.h"
11 #include "base/rand_util.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/sync_socket.h"
14 #include "chrome/test/chromedriver/chrome/status.h"
15 #include "net/base/net_errors.h"
16 #include "net/base/net_log.h"
17 #include "net/base/net_util.h"
18 #include "net/base/sys_addrinfo.h"
19 #include "net/socket/tcp_server_socket.h"
20 
21 #if defined(OS_LINUX)
22 #include <sys/socket.h>
23 #include <sys/un.h>
24 #endif
25 
PortReservation(const base::Closure & on_free_func,int port)26 PortReservation::PortReservation(const base::Closure& on_free_func, int port)
27     : on_free_func_(on_free_func), port_(port) {}
28 
~PortReservation()29 PortReservation::~PortReservation() {
30   if (!on_free_func_.is_null())
31     on_free_func_.Run();
32 }
33 
Leak()34 void PortReservation::Leak() {
35   LOG(ERROR) << "Port leaked: " << port_;
36   on_free_func_.Reset();
37 }
38 
PortServer(const std::string & path)39 PortServer::PortServer(const std::string& path) : path_(path) {
40   CHECK(path_.size() && path_[0] == 0)
41       << "path must be for Linux abstract namespace";
42 }
43 
~PortServer()44 PortServer::~PortServer() {}
45 
ReservePort(int * port,scoped_ptr<PortReservation> * reservation)46 Status PortServer::ReservePort(int* port,
47                                scoped_ptr<PortReservation>* reservation) {
48   int port_to_use = 0;
49   {
50     base::AutoLock lock(free_lock_);
51     if (free_.size()) {
52       port_to_use = free_.front();
53       free_.pop_front();
54     }
55   }
56   if (!port_to_use) {
57     Status status = RequestPort(&port_to_use);
58     if (status.IsError())
59       return status;
60   }
61   *port = port_to_use;
62   reservation->reset(new PortReservation(
63       base::Bind(&PortServer::ReleasePort, base::Unretained(this), port_to_use),
64       port_to_use));
65   return Status(kOk);
66 }
67 
RequestPort(int * port)68 Status PortServer::RequestPort(int* port) {
69   // The client sends its PID + \n, and the server responds with a port + \n,
70   // which is valid for the lifetime of the referred process.
71 #if defined(OS_LINUX)
72   int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
73   if (sock_fd < 0)
74     return Status(kUnknownError, "unable to create socket");
75   base::SyncSocket sock(sock_fd);
76   struct timeval tv;
77   tv.tv_sec = 10;
78   tv.tv_usec = 0;
79   if (setsockopt(sock_fd,
80                  SOL_SOCKET,
81                  SO_RCVTIMEO,
82                  reinterpret_cast<char*>(&tv),
83                  sizeof(tv)) < 0 ||
84       setsockopt(sock_fd,
85                  SOL_SOCKET,
86                  SO_SNDTIMEO,
87                  reinterpret_cast<char*>(&tv),
88                  sizeof(tv)) < 0) {
89     return Status(kUnknownError, "unable to set socket timeout");
90   }
91 
92   struct sockaddr_un addr;
93   memset(&addr, 0, sizeof(addr));
94   addr.sun_family = AF_UNIX;
95   memcpy(addr.sun_path, &path_[0], path_.length());
96   if (connect(sock.handle(),
97               reinterpret_cast<struct sockaddr*>(&addr),
98               sizeof(sa_family_t) + path_.length())) {
99     return Status(kUnknownError, "unable to connect");
100   }
101 
102   int proc_id = static_cast<int>(base::GetCurrentProcId());
103   std::string request = base::IntToString(proc_id);
104   request += "\n";
105   VLOG(0) << "PORTSERVER REQUEST " << request;
106   if (sock.Send(request.c_str(), request.length()) != request.length())
107     return Status(kUnknownError, "failed to send portserver request");
108 
109   std::string response;
110   do {
111     char c = 0;
112     size_t rv = sock.Receive(&c, 1);
113     if (!rv)
114       break;
115     response.push_back(c);
116   } while (sock.Peek());
117   if (response.empty())
118     return Status(kUnknownError, "failed to receive portserver response");
119   VLOG(0) << "PORTSERVER RESPONSE " << response;
120 
121   int new_port = 0;
122   if (*response.rbegin() != '\n' ||
123       !base::StringToInt(response.substr(0, response.length() - 1), &new_port))
124     return Status(kUnknownError, "failed to parse portserver response");
125   *port = new_port;
126   return Status(kOk);
127 #else
128   return Status(kUnknownError, "not implemented for this platform");
129 #endif
130 }
131 
ReleasePort(int port)132 void PortServer::ReleasePort(int port) {
133   base::AutoLock lock(free_lock_);
134   free_.push_back(port);
135 }
136 
PortManager(int min_port,int max_port)137 PortManager::PortManager(int min_port, int max_port)
138     : min_port_(min_port), max_port_(max_port) {
139   CHECK_GE(max_port_, min_port_);
140 }
141 
~PortManager()142 PortManager::~PortManager() {}
143 
FindAvailablePort() const144 int PortManager::FindAvailablePort() const {
145   int start = base::RandInt(min_port_, max_port_);
146   bool wrapped = false;
147   for (int try_port = start; try_port != start || !wrapped; ++try_port) {
148     if (try_port > max_port_) {
149       wrapped = true;
150       if (min_port_ == max_port_)
151         break;
152       try_port = min_port_;
153     }
154     if (taken_.count(try_port))
155       continue;
156 
157     char parts[] = {127, 0, 0, 1};
158     net::IPAddressNumber address(parts, parts + arraysize(parts));
159     net::NetLog::Source source;
160     net::TCPServerSocket sock(NULL, source);
161     if (sock.Listen(net::IPEndPoint(address, try_port), 1) == net::OK)
162       return try_port;
163   }
164   return 0;
165 }
166 
ReservePort(int * port,scoped_ptr<PortReservation> * reservation)167 Status PortManager::ReservePort(int* port,
168                                 scoped_ptr<PortReservation>* reservation) {
169   base::AutoLock lock(lock_);
170   int port_to_use = FindAvailablePort();
171   if (!port_to_use)
172     return Status(kUnknownError, "unable to find open port");
173 
174   taken_.insert(port_to_use);
175   *port = port_to_use;
176   reservation->reset(new PortReservation(
177       base::Bind(&PortManager::ReleasePort, base::Unretained(this),
178                  port_to_use),
179       port_to_use));
180   return Status(kOk);
181 }
182 
ReservePortFromPool(int * port,scoped_ptr<PortReservation> * reservation)183 Status PortManager::ReservePortFromPool(
184     int* port, scoped_ptr<PortReservation>* reservation) {
185   base::AutoLock lock(lock_);
186   int port_to_use = 0;
187   if (unused_forwarded_port_.size()) {
188     port_to_use = unused_forwarded_port_.front();
189     unused_forwarded_port_.pop_front();
190   } else {
191     port_to_use = FindAvailablePort();
192   }
193   if (!port_to_use)
194     return Status(kUnknownError, "unable to find open port");
195 
196   taken_.insert(port_to_use);
197   *port = port_to_use;
198   reservation->reset(new PortReservation(
199       base::Bind(&PortManager::ReleasePortToPool, base::Unretained(this),
200                  port_to_use),
201       port_to_use));
202   return Status(kOk);
203 }
204 
ReleasePort(int port)205 void PortManager::ReleasePort(int port) {
206   base::AutoLock lock(lock_);
207   taken_.erase(port);
208 }
209 
ReleasePortToPool(int port)210 void PortManager::ReleasePortToPool(int port) {
211   base::AutoLock lock(lock_);
212   taken_.erase(port);
213   unused_forwarded_port_.push_back(port);
214 }
215