blob: 843fd1734ff2a6f691a78dade029be8eb9768ff6 [file] [log] [blame]
Eric Shienbrood3e20a232012-02-16 11:35:56 -05001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Paul Stewartf65320c2011-10-13 14:34:52 -07002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "shill/async_connection.h"
6
Eric Shienbrood3e20a232012-02-16 11:35:56 -05007#include <base/bind.h>
Paul Stewartf65320c2011-10-13 14:34:52 -07008#include <errno.h>
9#include <netinet/in.h>
10
11#include <string>
12
13#include "shill/event_dispatcher.h"
Peter Qiu8d6b5972014-10-28 15:33:34 -070014#include "shill/net/ip_address.h"
15#include "shill/net/sockets.h"
Paul Stewartf65320c2011-10-13 14:34:52 -070016
Eric Shienbrood3e20a232012-02-16 11:35:56 -050017using base::Bind;
18using base::Callback;
19using base::Unretained;
Paul Stewartf65320c2011-10-13 14:34:52 -070020using std::string;
21
22namespace shill {
23
24AsyncConnection::AsyncConnection(const string &interface_name,
25 EventDispatcher *dispatcher,
26 Sockets *sockets,
Eric Shienbrood3e20a232012-02-16 11:35:56 -050027 const Callback<void(bool, int)> &callback)
Paul Stewartf65320c2011-10-13 14:34:52 -070028 : interface_name_(interface_name),
29 dispatcher_(dispatcher),
30 sockets_(sockets),
31 callback_(callback),
32 fd_(-1),
33 connect_completion_callback_(
Eric Shienbrood3e20a232012-02-16 11:35:56 -050034 Bind(&AsyncConnection::OnConnectCompletion, Unretained(this))) { }
Paul Stewartf65320c2011-10-13 14:34:52 -070035
36AsyncConnection::~AsyncConnection() {
37 Stop();
38}
39
40bool AsyncConnection::Start(const IPAddress &address, int port) {
Alex Vakulenko8a532292014-06-16 17:18:44 -070041 DCHECK_LT(fd_, 0);
Paul Stewartf65320c2011-10-13 14:34:52 -070042
Peter Qiuf3a8f902014-08-20 10:05:42 -070043 int family = PF_INET;
44 if (address.family() == IPAddress::kFamilyIPv6) {
45 family = PF_INET6;
46 }
47 fd_ = sockets_->Socket(family, SOCK_STREAM, 0);
Paul Stewartf65320c2011-10-13 14:34:52 -070048 if (fd_ < 0 ||
49 sockets_->SetNonBlocking(fd_) < 0) {
50 error_ = sockets_->ErrorString();
51 PLOG(ERROR) << "Async socket setup failed";
52 Stop();
53 return false;
54 }
55
56 if (!interface_name_.empty() &&
57 sockets_->BindToDevice(fd_, interface_name_) < 0) {
58 error_ = sockets_->ErrorString();
59 PLOG(ERROR) << "Async socket failed to bind to device";
60 Stop();
61 return false;
62 }
63
Peter Qiuf3a8f902014-08-20 10:05:42 -070064 int ret = ConnectTo(address, port);
Paul Stewartf65320c2011-10-13 14:34:52 -070065 if (ret == 0) {
Eric Shienbrood3e20a232012-02-16 11:35:56 -050066 callback_.Run(true, fd_); // Passes ownership
Paul Stewartf65320c2011-10-13 14:34:52 -070067 fd_ = -1;
68 return true;
69 }
70
71 if (sockets_->Error() != EINPROGRESS) {
72 error_ = sockets_->ErrorString();
73 PLOG(ERROR) << "Async socket connection failed";
74 Stop();
75 return false;
76 }
77
78 connect_completion_handler_.reset(
79 dispatcher_->CreateReadyHandler(fd_,
80 IOHandler::kModeOutput,
Eric Shienbrood3e20a232012-02-16 11:35:56 -050081 connect_completion_callback_));
Paul Stewartf65320c2011-10-13 14:34:52 -070082 error_ = string();
83
84 return true;
85}
86
87void AsyncConnection::Stop() {
88 connect_completion_handler_.reset();
89 if (fd_ >= 0) {
90 sockets_->Close(fd_);
91 fd_ = -1;
92 }
93}
94
95void AsyncConnection::OnConnectCompletion(int fd) {
96 CHECK_EQ(fd_, fd);
Paul Stewart8585b202012-09-21 14:03:01 -070097 bool success = false;
98 int returned_fd = -1;
Paul Stewartf65320c2011-10-13 14:34:52 -070099
100 if (sockets_->GetSocketError(fd_) != 0) {
101 error_ = sockets_->ErrorString();
102 PLOG(ERROR) << "Async GetSocketError returns failure";
Paul Stewartf65320c2011-10-13 14:34:52 -0700103 } else {
Paul Stewart8585b202012-09-21 14:03:01 -0700104 returned_fd = fd_;
Paul Stewartf65320c2011-10-13 14:34:52 -0700105 fd_ = -1;
Paul Stewart8585b202012-09-21 14:03:01 -0700106 success = true;
Paul Stewartf65320c2011-10-13 14:34:52 -0700107 }
108 Stop();
Paul Stewart8585b202012-09-21 14:03:01 -0700109
110 // Run the callback last, since it may end up freeing this instance.
111 callback_.Run(success, returned_fd); // Passes ownership
Paul Stewartf65320c2011-10-13 14:34:52 -0700112}
113
Peter Qiuf3a8f902014-08-20 10:05:42 -0700114int AsyncConnection::ConnectTo(const IPAddress &address, int port) {
Ben Chancc225ef2014-09-30 13:26:51 -0700115 struct sockaddr *sock_addr = nullptr;
Peter Qiuf3a8f902014-08-20 10:05:42 -0700116 socklen_t addr_len = 0;
117 struct sockaddr_in iaddr;
118 struct sockaddr_in6 iaddr6;
119 if (address.family() == IPAddress::kFamilyIPv4) {
120 CHECK_EQ(sizeof(iaddr.sin_addr.s_addr), address.GetLength());
121
122 memset(&iaddr, 0, sizeof(iaddr));
123 iaddr.sin_family = AF_INET;
124 memcpy(&iaddr.sin_addr.s_addr, address.address().GetConstData(),
125 sizeof(iaddr.sin_addr.s_addr));
126 iaddr.sin_port = htons(port);
127
128 sock_addr = reinterpret_cast<struct sockaddr *>(&iaddr);
129 addr_len = sizeof(iaddr);
130 } else if (address.family() == IPAddress::kFamilyIPv6) {
131 CHECK_EQ(sizeof(iaddr6.sin6_addr.s6_addr), address.GetLength());
132
133 memset(&iaddr6, 0, sizeof(iaddr6));
134 iaddr6.sin6_family = AF_INET6;
135 memcpy(&iaddr6.sin6_addr.s6_addr, address.address().GetConstData(),
136 sizeof(iaddr6.sin6_addr.s6_addr));
137 iaddr6.sin6_port = htons(port);
138
139 sock_addr = reinterpret_cast<struct sockaddr *>(&iaddr6);
140 addr_len = sizeof(iaddr6);
141 } else {
142 NOTREACHED();
143 }
144
145 return sockets_->Connect(fd_, sock_addr, addr_len);
146}
147
Paul Stewartf65320c2011-10-13 14:34:52 -0700148} // namespace shill