• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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