1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "Utils.h"
18
19 #include <webrtc/STUNClient.h>
20 #include <webrtc/STUNMessage.h>
21
22 #include <https/SafeCallbackable.h>
23 #include <https/Support.h>
24
25 #include <android-base/logging.h>
26
STUNClient(std::shared_ptr<RunLoop> runLoop,const sockaddr_in & addr,Callback cb)27 STUNClient::STUNClient(
28 std::shared_ptr<RunLoop> runLoop,
29 const sockaddr_in &addr,
30 Callback cb)
31 : mRunLoop(runLoop),
32 mRemoteAddr(addr),
33 mCallback(cb),
34 mTimeoutToken(0),
35 mNumRetriesLeft(kMaxNumRetries) {
36
37 int sock = socket(PF_INET, SOCK_DGRAM, 0);
38 makeFdNonblocking(sock);
39
40 sockaddr_in addrV4;
41 memset(addrV4.sin_zero, 0, sizeof(addrV4.sin_zero));
42 addrV4.sin_family = AF_INET;
43 addrV4.sin_port = 0;
44 addrV4.sin_addr.s_addr = INADDR_ANY;
45
46 int res = bind(
47 sock,
48 reinterpret_cast<const sockaddr *>(&addrV4),
49 sizeof(addrV4));
50
51 CHECK(!res);
52
53 sockaddr_in tmp;
54 socklen_t tmpLen = sizeof(tmp);
55
56 res = getsockname(sock, reinterpret_cast<sockaddr *>(&tmp), &tmpLen);
57 CHECK(!res);
58
59 LOG(VERBOSE) << "local port: " << ntohs(tmp.sin_port);
60
61 mSocket = std::make_shared<PlainSocket>(mRunLoop, sock);
62 }
63
run()64 void STUNClient::run() {
65 LOG(VERBOSE) << "STUNClient::run()";
66
67 scheduleRequest();
68 }
69
onSendRequest()70 void STUNClient::onSendRequest() {
71 LOG(VERBOSE) << "STUNClient::onSendRequest";
72
73 std::vector<uint8_t> transactionID { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
74
75 STUNMessage msg(0x0001 /* Binding Request */, transactionID.data());
76 msg.addFingerprint();
77
78 ssize_t n;
79
80 do {
81 n = sendto(
82 mSocket->fd(),
83 msg.data(),
84 msg.size(),
85 0 /* flags */,
86 reinterpret_cast<const sockaddr *>(&mRemoteAddr),
87 sizeof(mRemoteAddr));
88
89 } while (n < 0 && errno == EINTR);
90
91 CHECK_GT(n, 0);
92
93 LOG(VERBOSE) << "Sent BIND request, awaiting response";
94
95 mSocket->postRecv(
96 makeSafeCallback(this, &STUNClient::onReceiveResponse));
97 }
98
onReceiveResponse()99 void STUNClient::onReceiveResponse() {
100 LOG(VERBOSE) << "Received STUN response";
101
102 std::vector<uint8_t> buffer(kMaxUDPPayloadSize);
103
104 uint8_t *data = buffer.data();
105
106 sockaddr_storage addr;
107 socklen_t addrLen = sizeof(addr);
108
109 auto n = mSocket->recvfrom(
110 data, buffer.size(), reinterpret_cast<sockaddr *>(&addr), &addrLen);
111
112 CHECK_GT(n, 0);
113
114 STUNMessage msg(data, n);
115 CHECK(msg.isValid());
116
117 // msg.dump();
118
119 if (msg.type() == 0x0101 /* Binding Response */) {
120 const uint8_t *data;
121 size_t size;
122 if (msg.findAttribute(
123 0x0020 /* XOR-MAPPED-ADDRESS */,
124 reinterpret_cast<const void **>(&data),
125 &size)) {
126
127 CHECK_EQ(size, 8u);
128 CHECK_EQ(data[1], 0x01u); // We only deal with IPv4 for now.
129
130 static constexpr uint32_t kMagicCookie = 0x2112a442;
131
132 uint16_t port = U16_AT(&data[2]) ^ (kMagicCookie >> 16);
133 uint32_t ip = U32_AT(&data[4]) ^ kMagicCookie;
134
135 LOG(VERBOSE) << "translated port: " << port;
136
137 mCallback(
138 0 /* result */,
139 StringPrintf(
140 "%u.%u.%u.%u",
141 ip >> 24,
142 (ip >> 16) & 0xff,
143 (ip >> 8) & 0xff,
144 ip & 0xff));
145
146 mRunLoop->cancelToken(mTimeoutToken);
147 mTimeoutToken = 0;
148 }
149 }
150 }
151
scheduleRequest()152 void STUNClient::scheduleRequest() {
153 CHECK_EQ(mTimeoutToken, 0);
154
155 mSocket->postSend(
156 makeSafeCallback(this, &STUNClient::onSendRequest));
157
158 mTimeoutToken = mRunLoop->postWithDelay(
159 kTimeoutDelay,
160 makeSafeCallback(this, &STUNClient::onTimeout));
161
162 }
163
onTimeout()164 void STUNClient::onTimeout() {
165 mTimeoutToken = 0;
166
167 if (mNumRetriesLeft == 0) {
168 mCallback(-ETIMEDOUT, "");
169 return;
170 }
171
172 --mNumRetriesLeft;
173 scheduleRequest();
174 }
175
176