1/*
2 * libjingle
3 * Copyright 2004--2005, Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 *  1. Redistributions of source code must retain the above copyright notice,
9 *     this list of conditions and the following disclaimer.
10 *  2. Redistributions in binary form must reproduce the above copyright notice,
11 *     this list of conditions and the following disclaimer in the documentation
12 *     and/or other materials provided with the distribution.
13 *  3. The name of the author may not be used to endorse or promote products
14 *     derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#ifndef TALK_P2P_CLIENT_BASICPORTALLOCATOR_H_
29#define TALK_P2P_CLIENT_BASICPORTALLOCATOR_H_
30
31#include <string>
32#include <vector>
33
34#include "talk/base/messagequeue.h"
35#include "talk/base/network.h"
36#include "talk/base/thread.h"
37#include "talk/p2p/base/portallocator.h"
38
39namespace cricket {
40
41class BasicPortAllocator : public PortAllocator {
42 public:
43  BasicPortAllocator(talk_base::NetworkManager* network_manager,
44                     talk_base::PacketSocketFactory* socket_factory);
45  BasicPortAllocator(talk_base::NetworkManager* network_manager,
46                     talk_base::PacketSocketFactory* socket_factory,
47                     const talk_base::SocketAddress& stun_server,
48                     const talk_base::SocketAddress& relay_server_udp,
49                     const talk_base::SocketAddress& relay_server_tcp,
50                     const talk_base::SocketAddress& relay_server_ssl);
51  virtual ~BasicPortAllocator();
52
53  talk_base::NetworkManager* network_manager() { return network_manager_; }
54
55  talk_base::PacketSocketFactory* socket_factory() { return socket_factory_; }
56
57  const talk_base::SocketAddress& stun_address() const {
58    return stun_address_;
59  }
60  const talk_base::SocketAddress& relay_address_udp() const {
61    return relay_address_udp_;
62  }
63  const talk_base::SocketAddress& relay_address_tcp() const {
64    return relay_address_tcp_;
65  }
66  const talk_base::SocketAddress& relay_address_ssl() const {
67    return relay_address_ssl_;
68  }
69
70  // Returns the best (highest preference) phase that has produced a port that
71  // produced a writable connection.  If no writable connections have been
72  // produced, this returns -1.
73  int best_writable_phase() const;
74
75  virtual PortAllocatorSession* CreateSession(const std::string& name,
76                                              const std::string& session_type);
77
78  // Called whenever a connection becomes writable with the argument being the
79  // phase that the corresponding port was created in.
80  void AddWritablePhase(int phase);
81
82  bool allow_tcp_listen() const {
83    return allow_tcp_listen_;
84  }
85  void set_allow_tcp_listen(bool allow_tcp_listen) {
86    allow_tcp_listen_ = allow_tcp_listen;
87  }
88
89 private:
90  talk_base::NetworkManager* network_manager_;
91  talk_base::PacketSocketFactory* socket_factory_;
92  const talk_base::SocketAddress stun_address_;
93  const talk_base::SocketAddress relay_address_udp_;
94  const talk_base::SocketAddress relay_address_tcp_;
95  const talk_base::SocketAddress relay_address_ssl_;
96  int best_writable_phase_;
97  bool allow_tcp_listen_;
98};
99
100struct PortConfiguration;
101class AllocationSequence;
102
103class BasicPortAllocatorSession : public PortAllocatorSession,
104    public talk_base::MessageHandler {
105 public:
106  BasicPortAllocatorSession(BasicPortAllocator* allocator,
107                            const std::string& name,
108                            const std::string& session_type);
109  ~BasicPortAllocatorSession();
110
111  virtual BasicPortAllocator* allocator() { return allocator_; }
112  const std::string& name() const { return name_; }
113  const std::string& session_type() const { return session_type_; }
114  talk_base::Thread* network_thread() { return network_thread_; }
115
116  virtual void GetInitialPorts();
117  virtual void StartGetAllPorts();
118  virtual void StopGetAllPorts();
119  virtual bool IsGettingAllPorts() { return running_; }
120
121 protected:
122  // Starts the process of getting the port configurations.
123  virtual void GetPortConfigurations();
124
125  // Adds a port configuration that is now ready.  Once we have one for each
126  // network (or a timeout occurs), we will start allocating ports.
127  void ConfigReady(PortConfiguration* config);
128
129  // MessageHandler.  Can be overriden if message IDs do not conflict.
130  virtual void OnMessage(talk_base::Message *message);
131
132 private:
133  void OnConfigReady(PortConfiguration* config);
134  void OnConfigTimeout();
135  void AllocatePorts();
136  void OnAllocate();
137  void DisableEquivalentPhases(talk_base::Network* network,
138      PortConfiguration* config, uint32* flags);
139  void AddAllocatedPort(Port* port, AllocationSequence* seq, float pref,
140      bool prepare_address = true);
141  void OnAddressReady(Port* port);
142  void OnProtocolEnabled(AllocationSequence* seq, ProtocolType proto);
143  void OnPortDestroyed(Port* port);
144  void OnConnectionCreated(Port* port, Connection* conn);
145  void OnConnectionStateChange(Connection* conn);
146  void OnShake();
147
148  BasicPortAllocator* allocator_;
149  std::string name_;
150  std::string session_type_;
151  talk_base::Thread* network_thread_;
152  bool configuration_done_;
153  bool allocation_started_;
154  bool running_;  // set when StartGetAllPorts is called
155  std::vector<PortConfiguration*> configs_;
156  std::vector<AllocationSequence*> sequences_;
157
158  struct PortData {
159    Port* port;
160    AllocationSequence* sequence;
161    bool ready;
162
163    bool operator==(Port* rhs) const { return (port == rhs); }
164  };
165  std::vector<PortData> ports_;
166
167  friend class AllocationSequence;
168};
169
170// Records configuration information useful in creating ports.
171struct PortConfiguration : public talk_base::MessageData {
172  talk_base::SocketAddress stun_address;
173  std::string username;
174  std::string password;
175  std::string magic_cookie;
176
177  typedef std::vector<ProtocolAddress> PortList;
178  struct RelayServer {
179    PortList ports;
180    float pref_modifier;  // added to the protocol modifier to get the
181                          // preference for this particular server
182  };
183
184  typedef std::vector<RelayServer> RelayList;
185  RelayList relays;
186
187  PortConfiguration(const talk_base::SocketAddress& stun_address,
188                    const std::string& username,
189                    const std::string& password,
190                    const std::string& magic_cookie);
191
192  // Adds another relay server, with the given ports and modifier, to the list.
193  void AddRelay(const PortList& ports, float pref_modifier);
194
195  bool ResolveStunAddress();
196
197  // Determines whether the given relay server supports the given protocol.
198  static bool SupportsProtocol(const PortConfiguration::RelayServer& relay,
199                               ProtocolType type);
200};
201
202}  // namespace cricket
203
204#endif  // TALK_P2P_CLIENT_BASICPORTALLOCATOR_H_
205